/* This is the machine dependent code of the Visium Assembler.

   Copyright (C) 2005-2015 Free Software Foundation, Inc.

   This file is part of GAS, the GNU Assembler.

   GAS is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   GAS is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GAS; see the file COPYING.  If not, write to
   the Free Software Foundation, 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA. */

#include "as.h"
#include "safe-ctype.h"
#include "subsegs.h"
#include "obstack.h"

#include "opcode/visium.h"
#include "elf/visium.h"
#include "dwarf2dbg.h"
#include "dw2gencfi.h"

/* Relocations and fixups:

   There are two different cases where an instruction or data
   directive operand requires relocation, or fixup.

   1. Relative branch instructions, take an 16-bit signed word
   offset. The formula for computing the offset is this:

    offset = (destination - pc) / 4

   Branch instructions never branch to a label not declared
   locally, so the actual offset can always be computed by the assembler.
   However, we provide a relocation type to support this.

   2. Load literal instructions, such as MOVIU, which take a 16-bit
   literal operand. The literal may be the top or bottom half of
   a 32-bit value computed by the assembler, or by the linker. We provide
   two relocation types here.

   3. Data items (long, word and byte) preset with a value computed by
   the linker.  */


/* This string holds the chars that always start a comment. If the
   pre-processor is disabled, these aren't very useful. The macro
   tc_comment_chars points to this.  */
const char *visium_comment_chars = "!;";

/* This array holds the chars that only start a comment at the beginning
   of a line.  If the line seems to have the form '# 123 filename' .line
   and .file directives will appear in the pre-processed output. Note that
   input_file.c hand checks for '#' at the beginning of the first line of
   the input file. This is because the compiler outputs #NO_APP at the
   beginning of its output. Also note that comments like this one will
   always work.  */
const char line_comment_chars[] = "#!;";
const char line_separator_chars[] = "";

/* Chars that can be used to separate mantissa from exponent in floating point
   numbers.  */
const char EXP_CHARS[] = "eE";

/* Chars that mean this number is a floating point constant, as in
   "0f12.456" or "0d1.2345e12".

   ...Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
   changed in read.c. Ideally it shouldn't have to know about it at all,
   but nothing is ideal around here.  */
const char FLT_CHARS[] = "rRsSfFdDxXeE";

/* The size of a relocation record.  */
const int md_reloc_size = 8;

/* The architecture for which we are assembling.  */
enum visium_arch_val
{
  VISIUM_ARCH_DEF,
  VISIUM_ARCH_MCM24,
  VISIUM_ARCH_MCM,
  VISIUM_ARCH_GR6
};

static enum visium_arch_val visium_arch = VISIUM_ARCH_DEF;

/* The opcode architecture for which we are assembling.  In contrast to the
   previous one, this only determines which instructions are supported.  */
static enum visium_opcode_arch_val visium_opcode_arch = VISIUM_OPCODE_ARCH_DEF;

/* Flags to set in the ELF header e_flags field.  */
static flagword visium_flags = 0;

/* More than this number of nops in an alignment op gets a branch instead.  */
static unsigned int nop_limit = 5;


/* Translate internal representation of relocation info to BFD target
   format.  */
arelent *
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
{
  arelent *reloc;
  bfd_reloc_code_real_type code;

  reloc = (arelent *) xmalloc (sizeof (arelent));

  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;

  switch (fixp->fx_r_type)
    {
    case BFD_RELOC_8:
    case BFD_RELOC_16:
    case BFD_RELOC_32:
    case BFD_RELOC_8_PCREL:
    case BFD_RELOC_16_PCREL:
    case BFD_RELOC_32_PCREL:
    case BFD_RELOC_VISIUM_HI16:
    case BFD_RELOC_VISIUM_LO16:
    case BFD_RELOC_VISIUM_IM16:
    case BFD_RELOC_VISIUM_REL16:
    case BFD_RELOC_VISIUM_HI16_PCREL:
    case BFD_RELOC_VISIUM_LO16_PCREL:
    case BFD_RELOC_VISIUM_IM16_PCREL:
    case BFD_RELOC_VTABLE_INHERIT:
    case BFD_RELOC_VTABLE_ENTRY:
      code = fixp->fx_r_type;
      break;
    default:
      as_bad_where (fixp->fx_file, fixp->fx_line,
		    "internal error: unknown relocation type %d (`%s')",
		    fixp->fx_r_type,
		    bfd_get_reloc_code_name (fixp->fx_r_type));
      return 0;
    }

  reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
  if (reloc->howto == 0)
    {
      as_bad_where (fixp->fx_file, fixp->fx_line,
		    "internal error: can't export reloc type %d (`%s')",
		    fixp->fx_r_type, bfd_get_reloc_code_name (code));
      return 0;
    }

  /* Write the addend.  */
  if (reloc->howto->pc_relative == 0)
    reloc->addend = fixp->fx_addnumber;
  else
    reloc->addend = fixp->fx_offset;

  return reloc;
}

extern char *input_line_pointer;


static void s_bss (int);
static void visium_rdata (int);

static void visium_update_parity_bit (char *);
static char *parse_exp (char *, expressionS *);

/* These are the back-ends for the various machine dependent pseudo-ops.  */
void demand_empty_rest_of_line (void);


static void
s_bss (int ignore ATTRIBUTE_UNUSED)
{
  /* We don't support putting frags in the BSS segment, we fake it
     by marking in_bss, then looking at s_skip for clues.  */

  subseg_set (bss_section, 0);
  demand_empty_rest_of_line ();
}


/* This table describes all the machine specific pseudo-ops the assembler
   has to support. The fields are:

   1: Pseudo-op name without dot.
   2: Function to call to execute this pseudo-op.
   3: Integer arg to pass to the function.  */
const pseudo_typeS md_pseudo_table[] =
{
  {"bss", s_bss, 0},
  {"skip", s_space, 0},
  {"align", s_align_bytes, 0},
  {"noopt", s_ignore, 0},
  {"optim", s_ignore, 0},
  {"rdata", visium_rdata, 0},
  {"rodata", visium_rdata, 0},
  {0, 0, 0}
};


static void
visium_rdata (int xxx)
{
  char *save_line = input_line_pointer;
  static char section[] = ".rodata\n";

  /* Just pretend this is .section .rodata */
  input_line_pointer = section;
  obj_elf_section (xxx);
  input_line_pointer = save_line;
}

/* Align a section.  */
valueT
md_section_align (asection *seg, valueT addr)
{
  int align = bfd_get_section_alignment (stdoutput, seg);

  return ((addr + (1 << align) - 1) & -(1 << align));
}

void
md_number_to_chars (char *buf, valueT val, int n)
{
  number_to_chars_bigendian (buf, val, n);
}

symbolS *
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
{
  return 0;
}

/* The parse options.  */
const char *md_shortopts = "m:";

struct option md_longopts[] =
{
  {NULL, no_argument, NULL, 0}
};

size_t md_longopts_size = sizeof (md_longopts);

struct visium_option_table
{
  char *option;			/* Option name to match.  */
  char *help;			/* Help information.  */
  int *var;			/* Variable to change.  */
  int value;			/* To what to change it.  */
  char *deprecated;		/* If non-null, print this message. */
};

static struct visium_option_table visium_opts[] =
{
  {NULL, NULL, NULL, 0, NULL}
};

struct visium_arch_option_table
{
  char *name;
  enum visium_arch_val value;
};

static struct visium_arch_option_table visium_archs[] =
{
  {"mcm24", VISIUM_ARCH_MCM24},
  {"mcm",   VISIUM_ARCH_MCM},
  {"gr5",   VISIUM_ARCH_MCM},
  {"gr6",   VISIUM_ARCH_GR6},
  {NULL, 0}
};

struct visium_long_option_table
{
  char *option;			/* Substring to match.  */
  char *help;			/* Help information.  */
  int (*func) (char *subopt);	/* Function to decode sub-option.  */
  char *deprecated;		/* If non-null, print this message.  */
};

static int
visium_parse_arch (char *str)
{
  struct visium_arch_option_table *opt;

  if (strlen (str) == 0)
    {
      as_bad ("missing architecture name `%s'", str);
      return 0;
    }


  for (opt = visium_archs; opt->name != NULL; opt++)
    if (strcmp (opt->name, str) == 0)
      {
	visium_arch = opt->value;
	return 1;
      }

  as_bad ("unknown architecture `%s'\n", str);
  return 0;
}

static struct visium_long_option_table visium_long_opts[] =
{
  {"mtune=", "<arch_name>\t assemble for architecture <arch name>",
   visium_parse_arch, NULL},
  {NULL, NULL, NULL, NULL}
};

int
md_parse_option (int c, char *arg)
{
  struct visium_option_table *opt;
  struct visium_long_option_table *lopt;

  switch (c)
    {
    case 'a':
      /* Listing option.  Just ignore these, we don't support additional
         ones.  */
      return 0;

    default:
      for (opt = visium_opts; opt->option != NULL; opt++)
	{
	  if (c == opt->option[0]
	      && ((arg == NULL && opt->option[1] == 0)
		  || strcmp (arg, opt->option + 1) == 0))
	    {
	      /* If the option is deprecated, tell the user.  */
	      if (opt->deprecated != NULL)
		as_tsktsk ("option `-%c%s' is deprecated: %s", c,
			   arg ? arg : "", opt->deprecated);

	      if (opt->var != NULL)
		*opt->var = opt->value;

	      return 1;
	    }
	}

      for (lopt = visium_long_opts; lopt->option != NULL; lopt++)
	{
	  /* These options are expected to have an argument.  */
	  if (c == lopt->option[0]
	      && arg != NULL
	      && strncmp (arg, lopt->option + 1,
			  strlen (lopt->option + 1)) == 0)
	    {
	      /* If the option is deprecated, tell the user.  */
	      if (lopt->deprecated != NULL)
		as_tsktsk ("option `-%c%s' is deprecated: %s", c, arg,
			   lopt->deprecated);

	      /* Call the sup-option parser.  */
	      return lopt->func (arg + strlen (lopt->option) - 1);
	    }
	}

      return 0;
    }

  return 1;
}

void
md_show_usage (FILE * fp)
{
  struct visium_option_table *opt;
  struct visium_long_option_table *lopt;

  fprintf (fp, " Visium-specific assembler options:\n");

  for (opt = visium_opts; opt->option != NULL; opt++)
    if (opt->help != NULL)
      fprintf (fp, "  -%-23s%s\n", opt->option, opt->help);

  for (lopt = visium_long_opts; lopt->option != NULL; lopt++)
    if (lopt->help != NULL)
      fprintf (fp, "  -%s%s\n", lopt->option, lopt->help);

}

/* Interface to relax_segment.  */

/* Return the estimate of the size of a machine dependent frag
   before any relaxing is done.  It may also create any necessary
   relocations.  */
int
md_estimate_size_before_relax (fragS * fragP,
			       segT segment ATTRIBUTE_UNUSED)
{
  fragP->fr_var = 4;
  return 4;
}

/* Get the address of a symbol during relaxation.  From tc-arm.c.  */
static addressT
relaxed_symbol_addr (fragS *fragp, long stretch)
{
  fragS *sym_frag;
  addressT addr;
  symbolS *sym;

  sym = fragp->fr_symbol;
  sym_frag = symbol_get_frag (sym);
  know (S_GET_SEGMENT (sym) != absolute_section
	|| sym_frag == &zero_address_frag);
  addr = S_GET_VALUE (sym) + fragp->fr_offset;

  /* If frag has yet to be reached on this pass, assume it will
     move by STRETCH just as we did.  If this is not so, it will
     be because some frag between grows, and that will force
     another pass.  */
  if (stretch != 0
      && sym_frag->relax_marker != fragp->relax_marker)
    {
      fragS *f;

      /* Adjust stretch for any alignment frag.  Note that if have
	 been expanding the earlier code, the symbol may be
	 defined in what appears to be an earlier frag.  FIXME:
	 This doesn't handle the fr_subtype field, which specifies
	 a maximum number of bytes to skip when doing an
	 alignment.  */
      for (f = fragp; f != NULL && f != sym_frag; f = f->fr_next)
	{
	  if (f->fr_type == rs_align || f->fr_type == rs_align_code)
	    {
	      if (stretch < 0)
		stretch = - ((- stretch)
			     & ~ ((1 << (int) f->fr_offset) - 1));
	      else
		stretch &= ~ ((1 << (int) f->fr_offset) - 1);
	      if (stretch == 0)
		break;
	    }
	}
      if (f != NULL)
	addr += stretch;
    }

  return addr;
}

/* Relax a machine dependent frag.  This returns the amount by which
   the current size of the frag should change.  */
int
visium_relax_frag (asection *sec, fragS *fragP, long stretch)
{
  int old_size, new_size;
  addressT addr;

  /* We only handle relaxation for the BRR instruction.  */
  gas_assert (fragP->fr_subtype == mode_ci);

  if (!S_IS_DEFINED (fragP->fr_symbol)
      || sec != S_GET_SEGMENT (fragP->fr_symbol)
      || S_IS_WEAK (fragP->fr_symbol))
    return 0;

  old_size = fragP->fr_var;
  addr = relaxed_symbol_addr (fragP, stretch);

  /* If the target is the address of the instruction, we'll insert a NOP.  */
  if (addr == fragP->fr_address + fragP->fr_fix)
    new_size = 8;
  else
    new_size = 4;

  fragP->fr_var = new_size;
  return new_size - old_size;
}

/* Convert a machine dependent frag.  */
void
md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED,
		 fragS * fragP)
{
  char *buf = fragP->fr_literal + fragP->fr_fix;
  expressionS exp;
  fixS *fixP;

  /* We only handle relaxation for the BRR instruction.  */
  gas_assert (fragP->fr_subtype == mode_ci);

  /* Insert the NOP if requested.  */
  if (fragP->fr_var == 8)
    {
      memcpy (buf + 4, buf, 4);
      memset (buf, 0, 4);
      fragP->fr_fix += 4;
    }

  exp.X_op = O_symbol;
  exp.X_add_symbol = fragP->fr_symbol;
  exp.X_add_number = fragP->fr_offset;

  /* Now we can create the relocation at the correct offset.  */
  fixP = fix_new_exp (fragP, fragP->fr_fix, 4, &exp, 1, BFD_RELOC_VISIUM_REL16);
  fixP->fx_file = fragP->fr_file;
  fixP->fx_line = fragP->fr_line;
  fragP->fr_fix += 4;
  fragP->fr_var = 0;
}

/* The location from which a PC relative jump should be calculated,
   given a PC relative jump reloc.  */
long
visium_pcrel_from_section (fixS *fixP, segT sec)
{
  if (fixP->fx_addsy != (symbolS *) NULL
      && (!S_IS_DEFINED (fixP->fx_addsy)
	  || S_GET_SEGMENT (fixP->fx_addsy) != sec))
    {
      /* The symbol is undefined (or is defined but not in this section).
         Let the linker figure it out.  */
      return 0;
    }

  /* Return the address of the instruction.  */
  return fixP->fx_where + fixP->fx_frag->fr_address;
}

/* Indicate whether a fixup against a locally defined
   symbol should be adjusted to be against the section
   symbol.  */
bfd_boolean
visium_fix_adjustable (fixS *fix)
{
  /* We need the symbol name for the VTABLE entries.  */
  return (fix->fx_r_type != BFD_RELOC_VTABLE_INHERIT
	  && fix->fx_r_type != BFD_RELOC_VTABLE_ENTRY);
}

/* Update the parity bit of the 4-byte instruction in BUF.  */
static void
visium_update_parity_bit (char *buf)
{
  int p1 = (buf[0] & 0x7f) ^ buf[1] ^ buf[2] ^ buf[3];
  int p2 = 0;
  int i;

  for (i = 1; i <= 8; i++)
    {
      p2 ^= (p1 & 1);
      p1 >>= 1;
    }

  buf[0] = (buf[0] & 0x7f) | ((p2 << 7) & 0x80);
}

/* This is called from HANDLE_ALIGN in write.c.  Fill in the contents
   of an rs_align_code fragment.  */
void
visium_handle_align (fragS *fragP)
{
  valueT count
    = fragP->fr_next->fr_address - (fragP->fr_address + fragP->fr_fix);
  valueT fix = count & 3;
  char *p = fragP->fr_literal + fragP->fr_fix;

  if (fix)
    {
      memset (p, 0, fix);
      p += fix;
      count -= fix;
      fragP->fr_fix += fix;
    }

  if (count == 0)
    return;

  fragP->fr_var = 4;

  if (count > 4 * nop_limit && count <= 131068)
    {
      struct frag *rest;

      /* Make a branch, then follow with nops.  Insert another
         frag to handle the nops.  */
      md_number_to_chars (p, 0x78000000 + (count >> 2), 4);
      visium_update_parity_bit (p);

      rest = xmalloc (SIZEOF_STRUCT_FRAG + 4);
      memcpy (rest, fragP, SIZEOF_STRUCT_FRAG);
      fragP->fr_next = rest;
      rest->fr_address += rest->fr_fix + 4;
      rest->fr_fix = 0;
      /* If we leave the next frag as rs_align_code we'll come here
	 again, resulting in a bunch of branches rather than a
	 branch followed by nops.  */
      rest->fr_type = rs_align;
      p = rest->fr_literal;
    }

  memset (p, 0, 4);
}

/* Apply a fixS to the frags, now that we know the value it ought to
   hold.  */
void
md_apply_fix (fixS * fixP, valueT * value, segT segment)
{
  char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
  offsetT val;
  long insn;

  val = *value;

  gas_assert (fixP->fx_r_type < BFD_RELOC_UNUSED);

  /* Remember value for tc_gen_reloc.  */
  fixP->fx_addnumber = val;

  /* Since DIFF_EXPR_OK is defined, .-foo gets turned into PC
     relative relocs. If this has happened, a non-PC relative
     reloc must be reinstalled with its PC relative version here.  */
  if (fixP->fx_pcrel)
    {
      switch (fixP->fx_r_type)
	{
	case BFD_RELOC_8:
	  fixP->fx_r_type = BFD_RELOC_8_PCREL;
	  break;
	case BFD_RELOC_16:
	  fixP->fx_r_type = BFD_RELOC_16_PCREL;
	  break;
	case BFD_RELOC_32:
	  fixP->fx_r_type = BFD_RELOC_32_PCREL;
	  break;
	case BFD_RELOC_VISIUM_HI16:
	  fixP->fx_r_type = BFD_RELOC_VISIUM_HI16_PCREL;
	  break;
	case BFD_RELOC_VISIUM_LO16:
	  fixP->fx_r_type = BFD_RELOC_VISIUM_LO16_PCREL;
	  break;
	case BFD_RELOC_VISIUM_IM16:
	  fixP->fx_r_type = BFD_RELOC_VISIUM_IM16_PCREL;
	  break;
	default:
	  break;
	}
    }

  /* If this is a data relocation, just output VAL.  */
  switch (fixP->fx_r_type)
    {
    case BFD_RELOC_8:
    case BFD_RELOC_8_PCREL:
      md_number_to_chars (buf, val, 1);
      break;
    case BFD_RELOC_16:
    case BFD_RELOC_16_PCREL:
      md_number_to_chars (buf, val, 2);
      break;
    case BFD_RELOC_32:
    case BFD_RELOC_32_PCREL:
      md_number_to_chars (buf, val, 4);
      break;
    case BFD_RELOC_VTABLE_INHERIT:
    case BFD_RELOC_VTABLE_ENTRY:
      fixP->fx_done = 0;
      break;
    default:
      /* It's a relocation against an instruction.  */
      insn = bfd_getb32 ((unsigned char *) buf);

      switch (fixP->fx_r_type)
	{
	case BFD_RELOC_VISIUM_REL16:
	  if (fixP->fx_addsy == NULL
	      || (S_IS_DEFINED (fixP->fx_addsy)
		  && S_GET_SEGMENT (fixP->fx_addsy) == segment))
	    {
	      if (val > 0x1fffc || val < -0x20000)
		as_bad_where
		 (fixP->fx_file, fixP->fx_line,
		  "16-bit word displacement out of range: value = %d",
		  (int) val);
	      val = (val >> 2);

	      insn = (insn & 0xffff0000) | (val & 0x0000ffff);
	    }
	  break;

	case BFD_RELOC_VISIUM_HI16:
	case BFD_RELOC_VISIUM_HI16_PCREL:
	  if (fixP->fx_addsy == NULL)
	    insn = (insn & 0xffff0000) | ((val >> 16) & 0x0000ffff);
	  break;

	case BFD_RELOC_VISIUM_LO16:
	case BFD_RELOC_VISIUM_LO16_PCREL:
	  if (fixP->fx_addsy == NULL)
	    insn = (insn & 0xffff0000) | (val & 0x0000ffff);
	  break;

	case BFD_RELOC_VISIUM_IM16:
	case BFD_RELOC_VISIUM_IM16_PCREL:
	  if (fixP->fx_addsy == NULL)
	    {
	      if ((val & 0xffff0000) != 0)
		as_bad_where (fixP->fx_file, fixP->fx_line,
			      "16-bit immediate out of range: value = %d",
			      (int) val);

	      insn = (insn & 0xffff0000) | val;
	    }
	  break;

	case BFD_RELOC_NONE:
	default:
	  as_bad_where (fixP->fx_file, fixP->fx_line,
			"bad or unhandled relocation type: 0x%02x",
			fixP->fx_r_type);
	  break;
	}

      bfd_putb32 (insn, (unsigned char *) buf);
      visium_update_parity_bit (buf);
      break;
    }

  /* Are we finished with this relocation now?  */
  if (fixP->fx_addsy == NULL)
    fixP->fx_done = 1;
}

char *
parse_exp (char *s, expressionS * op)
{
  char *save = input_line_pointer;
  char *new;

  if (!s)
    {
      return s;
    }

  input_line_pointer = s;
  expression (op);
  new = input_line_pointer;
  input_line_pointer = save;
  return new;
}

/* If the given string is a Visium opcode mnemonic return the code
   otherwise return -1. Use binary chop to find matching entry.  */
static int
get_opcode (int *code, enum addressing_mode *mode, char *flags, char *mnem)
{
  int l = 0;
  int r = sizeof (opcode_table) / sizeof (struct opcode_entry) - 1;

  do
    {
      int mid = (l + r) / 2;
      int ans = strcmp (mnem, opcode_table[mid].mnem);

      if (ans < 0)
	r = mid - 1;
      else if (ans > 0)
	l = mid + 1;
      else
	{
	  *code = opcode_table[mid].code;
	  *mode = opcode_table[mid].mode;
	  *flags = opcode_table[mid].flags;

	  return 0;
	}
    }
  while (l <= r);

  return -1;
}

/* This function is called when the assembler starts up. It is called
   after the options have been parsed and the output file has been
   opened.  */
void
md_begin (void)
{
  switch (visium_arch)
    {
    case VISIUM_ARCH_DEF:
      break;
    case VISIUM_ARCH_MCM24:
      visium_opcode_arch = VISIUM_OPCODE_ARCH_GR5;
      visium_flags |= EF_VISIUM_ARCH_MCM24;
      break;
    case VISIUM_ARCH_MCM:
      visium_opcode_arch = VISIUM_OPCODE_ARCH_GR5;
      visium_flags |= EF_VISIUM_ARCH_MCM;
      break;
    case VISIUM_ARCH_GR6:
      visium_opcode_arch = VISIUM_OPCODE_ARCH_GR6;
      visium_flags |= EF_VISIUM_ARCH_MCM | EF_VISIUM_ARCH_GR6;
      nop_limit = 2;
      break;
    default:
      gas_assert (0);
    }

  bfd_set_private_flags (stdoutput, visium_flags);
}

/* This is identical to the md_atof in m68k.c.  I think this is right,
   but I'm not sure.

   Turn a string in input_line_pointer into a floating point constant of type
   type, and store the appropriate bytes in *litP.  The number of LITTLENUMS
   emitted is stored in *sizeP .  An error message is returned,
   or NULL on OK.  */

/* Equal to MAX_PRECISION in atof-ieee.c.  */
#define MAX_LITTLENUMS 6

char *
md_atof (int type, char *litP, int *sizeP)
{
  int i, prec;
  LITTLENUM_TYPE words[MAX_LITTLENUMS];
  char *t;

  switch (type)
    {
    case 'f':
    case 'F':
    case 's':
    case 'S':
      prec = 2;
      break;

    case 'd':
    case 'D':
    case 'r':
    case 'R':
      prec = 4;
      break;

    case 'x':
    case 'X':
      prec = 6;
      break;

    case 'p':
    case 'P':
      prec = 6;
      break;

    default:
      *sizeP = 0;
      return "Bad call to MD_ATOF()";
    }

  t = atof_ieee (input_line_pointer, type, words);
  if (t)
    input_line_pointer = t;
  *sizeP = prec * sizeof (LITTLENUM_TYPE);

  if (target_big_endian)
    {
      for (i = 0; i < prec; i++)
	{
	  md_number_to_chars (litP, (valueT) words[i],
			      sizeof (LITTLENUM_TYPE));
	  litP += sizeof (LITTLENUM_TYPE);
	}
    }
  else
    {
      for (i = prec - 1; i >= 0; i--)
	{
	  md_number_to_chars (litP, (valueT) words[i],
			      sizeof (LITTLENUM_TYPE));
	  litP += sizeof (LITTLENUM_TYPE);
	}
    }

  return 0;
}

static inline char *
skip_space (char *s)
{
  while (*s == ' ' || *s == '\t')
    ++s;

  return s;
}

static int
parse_gen_reg (char **sptr, int *rptr)
{
  char *s = skip_space (*sptr);
  char buf[10];
  int cnt;
  int l, r;

  cnt = 0;
  memset (buf, '\0', 10);
  while ((ISALNUM (*s)) && cnt < 10)
    buf[cnt++] = TOLOWER (*s++);

  l = 0;
  r = sizeof (gen_reg_table) / sizeof (struct reg_entry) - 1;

  do
    {
      int mid = (l + r) / 2;
      int ans = strcmp (buf, gen_reg_table[mid].name);

      if (ans < 0)
	r = mid - 1;
      else if (ans > 0)
	l = mid + 1;
      else
	{
	  *rptr = gen_reg_table[mid].code;
	  *sptr = s;
	  return 0;
	}
    }
  while (l <= r);

  return -1;
}

static int
parse_fp_reg (char **sptr, int *rptr)
{
  char *s = skip_space (*sptr);
  char buf[10];
  int cnt;
  int l, r;

  cnt = 0;
  memset (buf, '\0', 10);
  while ((ISALNUM (*s)) && cnt < 10)
    buf[cnt++] = TOLOWER (*s++);

  l = 0;
  r = sizeof (fp_reg_table) / sizeof (struct reg_entry) - 1;

  do
    {
      int mid = (l + r) / 2;
      int ans = strcmp (buf, fp_reg_table[mid].name);

      if (ans < 0)
	r = mid - 1;
      else if (ans > 0)
	l = mid + 1;
      else
	{
	  *rptr = fp_reg_table[mid].code;
	  *sptr = s;
	  return 0;
	}
    }
  while (l <= r);

  return -1;
}

static int
parse_cc (char **sptr, int *rptr)
{
  char *s = skip_space (*sptr);
  char buf[10];
  int cnt;
  int l, r;

  cnt = 0;
  memset (buf, '\0', 10);
  while ((ISALNUM (*s)) && cnt < 10)
    buf[cnt++] = TOLOWER (*s++);

  l = 0;
  r = sizeof (cc_table) / sizeof (struct cc_entry) - 1;

  do
    {
      int mid = (l + r) / 2;
      int ans = strcmp (buf, cc_table[mid].name);

      if (ans < 0)
	r = mid - 1;
      else if (ans > 0)
	l = mid + 1;
      else
	{
	  *rptr = cc_table[mid].code;
	  *sptr = s;
	  return 0;
	}
    }
  while (l <= r);

  return -1;
}

/* Previous dest is the destination register number of the instruction
   before the current one.  */
static int previous_dest = 0;
static int previous_mode = 0;
static int condition_code = 0;
static int this_dest = 0;
static int this_mode = 0;


/* This is the main function in this file. It takes a line of assembly language
   source code and assembles it. Note, labels and pseudo ops have already
   been removed, so too has leading white space. */
void
md_assemble (char *str0)
{
  char *str = str0;
  int cnt;
  char mnem[10];
  int opcode;
  enum addressing_mode amode;
  char arch_flags;
  int ans;

  char *output;
  int reloc = 0;
  relax_substateT relax = 0;
  expressionS e1;
  int r1, r2, r3;
  int cc;
  int indx;

  /* Initialize the expression.  */
  e1.X_op = O_absent;

  /* Initialize destination register.
     If the instruction we just looked at is in the delay slot of an
     unconditional branch, then there is no index hazard.  */
  if ((previous_mode == mode_cad || previous_mode == mode_ci)
      && condition_code == 15)
    this_dest = 0;

  previous_dest = this_dest;
  previous_mode = this_mode;
  this_dest = 0;

  /* Drop leading whitespace (probably not required).  */
  while (*str == ' ')
    str++;

  /* Get opcode mnemonic and make sure it's in lower case.  */
  cnt = 0;
  memset (mnem, '\0', 10);
  while ((ISALNUM (*str) || *str == '.' || *str == '_') && cnt < 10)
    mnem[cnt++] = TOLOWER (*str++);

  /* Look up mnemonic in opcode table, and get the code,
     the instruction format, and the flags that indicate
     which family members support this mnenonic.  */
  if (get_opcode (&opcode, &amode, &arch_flags, mnem) < 0)
    {
      as_bad ("Unknown instruction mnenonic `%s'", mnem);
      return;
    }

  if ((VISIUM_OPCODE_ARCH_MASK (visium_opcode_arch) & arch_flags) == 0)
    {
      as_bad ("Architecture mismatch on `%s'", mnem);
      return;
    }

  this_mode = amode;

  switch (amode)
    {
    case mode_d:
      /* register :=
         Example:
         readmda r1  */
      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("Dest register required");
	  return;
	}
      opcode |= (r1 << 10);
      this_dest = r1;
      break;

    case mode_a:
      /* op= register
         Example: asld r1  */
      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("SourceA register required");
	  return;
	}
      opcode |= (r1 << 16);
      break;

    case mode_ab:
      /* register * register
         Example:
         mults r1,r2  */
      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("SourceA register required");
	  return;
	}
      str = skip_space (str);
      if (*str == ',')
	{
	  str++;
	  ans = parse_gen_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("SourceB register required");
	      return;
	    }
	  opcode |= (r1 << 16) | (r2 << 4);
	}
      else
	{
	  as_bad ("SourceB register required");
	  return;
	}
      break;

    case mode_da:
      /* register := register
         Example:
         extb.l  r1,r2  */
      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("Dest register required");
	  return;
	}
      str = skip_space (str);
      if (*str == ',')
	{
	  str++;
	  ans = parse_gen_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("SourceA register required");
	      return;
	    }
	  opcode |= (r1 << 10) | (r2 << 16);
	}
      else
	{
	  as_bad ("SourceB register required");
	  return;
	}
      this_dest = r1;
      break;

    case mode_dab:
      /* register := register * register
         Example:
         add.l r1,r2,r3  */
      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("Dest register required");
	  return;
	}
      str = skip_space (str);
      if (*str == ',')
	{
	  str++;
	  ans = parse_gen_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("SourceA register required");
	      return;
	    }
	  str = skip_space (str);
	  if (*str == ',')
	    {
	      str++;
	      ans = parse_gen_reg (&str, &r3);
	      if (ans < 0)
		{
		  as_bad ("SourceB register required");
		  return;
		}

	      /* Got three regs, assemble instruction.  */
	      opcode |= (r1 << 10) | (r2 << 16) | (r3 << 4);
	    }
	  else
	    {
	      as_bad ("SourceA register required");
	      return;
	    }
	}
      else
	{
	  as_bad ("Dest register required");
	  return;
	}
      this_dest = r1;
      break;

    case mode_iab:
      /* 5-bit immediate * register * register
         Example:
         eamwrite 3,r1,r2  */
      str = parse_exp (str, &e1);
      str = skip_space (str);
      if (e1.X_op != O_absent && *str == ',')
	{
	  int eam_op = e1.X_add_number;

	  str = skip_space (str + 1);
	  ans = parse_gen_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("SourceA register required");
	      return;
	    }
	  str = skip_space (str);
	  if (*str == ',')
	    {
	      str++;
	      ans = parse_gen_reg (&str, &r3);
	      if (ans < 0)
		{
		  as_bad ("SourceB register required");
		  return;
		}

	      /* Got three operands, assemble instruction.  */
	      if (eam_op < 0 || eam_op > 31)
		{
		  as_bad ("eam_op out of range");
		}
	      opcode |= ((eam_op & 0x1f) << 10) | (r2 << 16) | (r3 << 4);
	    }
	}
      else
	{
	  as_bad ("EAM_OP required");
	  return;
	}
      break;

    case mode_0ab:
      /* zero * register * register
         Example:
         cmp.l  r1,r2 */
      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("SourceA register required");
	  return;
	}
      str = skip_space (str);
      if (*str == ',')
	{
	  str++;
	  ans = parse_gen_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("SourceB register required");
	      return;
	    }
	  opcode |= (r1 << 16) | (r2 << 4);
	}
      else
	{
	  as_bad ("SourceB register required");
	  return;
	}
      break;

    case mode_da0:
      /* register * register * zero
         Example:
         move.l  r1,r2  */
      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("Dest register required");
	  return;
	}
      str = skip_space (str);
      if (*str == ',')
	{
	  str++;
	  ans = parse_gen_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("SourceA register required");
	      return;
	    }
	  opcode |= (r1 << 10) | (r2 << 16);
	}
      else
	{
	  as_bad ("SourceA register required");
	  return;
	}
      this_dest = r1;
      break;

    case mode_cad:
      /* condition * register * register
         Example:
         bra  tr,r1,r2  */
      ans = parse_cc (&str, &cc);
      if (ans < 0)
	{
	  as_bad ("condition code required");
	  return;
	}

      str = skip_space (str);
      if (*str == ',')
	{
	  str = skip_space (str + 1);
	  ans = parse_gen_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("SourceA register required");
	      return;
	    }
	  str = skip_space (str);
	  if (*str == ',')
	    {
	      str++;
	      ans = parse_gen_reg (&str, &r3);
	      if (ans < 0)
		{
		  as_bad ("Dest register required");
		  return;
		}

	      /* Got three operands, assemble instruction.  */
	      opcode |= (cc << 27) | (r2 << 16) | (r3 << 10);
	    }
	  else
	    {
	      as_bad ("Dest register required");
	      return;
	    }
	}
      else
	{
	  as_bad ("SourceA register required");
	  return;
	}

      if (previous_mode == mode_cad || previous_mode == mode_ci)
	as_bad ("branch instruction in delay slot");

      this_dest = r3;
      condition_code = cc;
      break;

    case mode_das:
      /* register := register * 5-bit imediate/register shift count
         Example:
         asl.l  r1,r2,4  */
      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("Dest register required");
	  return;
	}
      str = skip_space (str);
      if (*str == ',')
	{
	  str++;
	  ans = parse_gen_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("SourceA register required");
	      return;
	    }
	  str = skip_space (str);
	  if (*str == ',')
	    {
	      str++;
	      ans = parse_gen_reg (&str, &r3);
	      if (ans == 0)
		{
		  opcode |= (r1 << 10) | (r2 << 16) | (r3 << 4);
		}
	      else
		{
		  str = parse_exp (str, &e1);
		  if (e1.X_op == O_constant)
		    {
		      int imm = e1.X_add_number;

		      if (imm < 0 || imm > 31)
			as_bad ("immediate value out of range");

		      opcode |=
			(r1 << 10) | (r2 << 16) | (1 << 9) | ((imm & 0x1f) <<
							      4);
		    }
		  else
		    {
		      as_bad ("immediate operand required");
		      return;
		    }
		}
	    }
	}
      else
	{
	  as_bad ("SourceA register required");
	  return;
	}
      this_dest = r1;
      break;

    case mode_di:
      /* register := 5-bit immediate
         Example:
         eamread r1,3  */
      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("Dest register required");
	  return;
	}
      str = skip_space (str);
      if (*str == ',')
	{
	  str++;
	  str = parse_exp (str, &e1);
	  if (e1.X_op == O_constant)
	    {
	      int opnd2 = e1.X_add_number;

	      if (opnd2 < 0 || opnd2 > 31)
		{
		  as_bad ("immediate operand out of range");
		  return;
		}
	      opcode |= (r1 << 10) | ((opnd2 & 0x1f) << 4);
	    }
	  else
	    {
	      as_bad ("immediate operand required");
	      return;
	    }
	}
      else
	{
	  as_bad ("immediate operand required");
	  return;
	}
      this_dest = r1;
      break;

    case mode_ir:
      /* 5-bit immediate * register, e.g. trace 1,r1  */
      str = parse_exp (str, &e1);
      str = skip_space (str);
      if (e1.X_op == O_constant && *str == ',')
	{
	  int opnd1 = e1.X_add_number;

	  str = skip_space (str + 1);
	  ans = parse_gen_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("SourceA register required");
	      return;
	    }

	  /* Got two operands, assemble instruction.  */
	  if (opnd1 < 0 || opnd1 > 31)
	    {
	      as_bad ("1st operand out of range");
	    }
	  opcode |= ((opnd1 & 0x1f) << 10) | (r2 << 16);
	}
      else
	{
	  as_bad ("Immediate operand required");
	  return;
	}
      break;

    case mode_ai:
      /* register *= 16-bit unsigned immediate
         Example:
         addi  r1,123  */
      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("Dest register required");
	  return;
	}
      opcode |= (r1 << 16);

      str = skip_space (str);
      if (*str != ',')
	{
	  as_bad ("immediate value missing");
	  return;
	}
      this_dest = r1;

      /* fall through...  */

    case mode_i:
      /* MOVIL/WRTL traditionally get an implicit "%l" applied
	 to their immediate value.  For other opcodes, unless
	 the immediate value is decorated with "%u" or "%l"
	 it must be in the range 0 .. 65535.  */
      if ((opcode & 0x7fe00000) == 0x04800000
	  || (opcode & 0x7fe00000) == 0x05000000)
	reloc = BFD_RELOC_VISIUM_LO16;
      else
	reloc = BFD_RELOC_VISIUM_IM16;

      str = skip_space (str + 1);

      if (*str == '%')
	{
	  if (str[1] == 'u')
	    reloc = BFD_RELOC_VISIUM_HI16;
	  else if (str[1] == 'l')
	    reloc = BFD_RELOC_VISIUM_LO16;
	  else
	    {
	      as_bad ("bad char after %%");
	      return;
	    }

	  str += 2;
	}
      str = parse_exp (str, &e1);
      if (e1.X_op != O_absent)
	{
	  if (e1.X_op == O_constant)
	    {
	      int imm = e1.X_add_number;

	      if (reloc == BFD_RELOC_VISIUM_HI16)
		opcode |= ((imm >> 16) & 0xffff);
	      else if (reloc == BFD_RELOC_VISIUM_LO16)
		opcode |= (imm & 0xffff);
	      else
		{
		  if (imm < 0 || imm > 0xffff)
		    as_bad ("immediate value out of range");

		  opcode |= (imm & 0xffff);
		}
	      /* No relocation is needed.  */
	      reloc = 0;
	    }
	}
      else
	{
	  as_bad ("immediate value missing");
	  return;
	}
      break;

    case mode_bax:
      /* register * register * 5-bit immediate,
         SourceB * SourceA * Index
         Examples
         write.l (r1),r2
         write.l 3(r1),r2  */
      str = skip_space (str);

      indx = 0;
      if (*str != '(')
	{
	  str = parse_exp (str, &e1);
	  if (e1.X_op == O_constant)
	    {
	      indx = e1.X_add_number;

	      if (indx < 0 || indx > 31)
		{
		  as_bad ("Index out of range");
		  return;
		}
	    }
	  else
	    {
	      as_bad ("Index(SourceA) required");
	      return;
	    }
	}

      str = skip_space (str);

      if (*str != '(')
	{
	  as_bad ("Index(SourceA) required");
	  return;
	}

      str = skip_space (str + 1);

      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("SourceA register required");
	  return;
	}
      str = skip_space (str);
      if (*str != ')')
	{
	  as_bad ("(SourceA) required");
	  return;
	}
      str = skip_space (str + 1);

      if (*str == ',')
	{
	  str = skip_space (str + 1);
	  ans = parse_gen_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("SourceB register required");
	      return;
	    }
	}
      else
	{
	  as_bad ("SourceB register required");
	  return;
	}

      opcode |= (r1 << 16) | (r2 << 4) | ((indx & 0x1f) << 10);

      if (indx != 0 && previous_mode == mode_cad)
	{
	  /* We're in a delay slot.
	     If the base reg is the destination of the branch, then issue
	     an error message.
	     Otherwise it is safe to use the base and index.  */
	  if (previous_dest != 0 && r1 == previous_dest)
	    {
	      as_bad ("base register not ready");
	      return;
	    }
	}
      else if (previous_dest != 0
	       && r1 == previous_dest
	       && (visium_arch == VISIUM_ARCH_MCM
		   || visium_arch == VISIUM_ARCH_MCM24
		   || (visium_arch == VISIUM_ARCH_DEF && indx != 0)))
	{
	  as_warn ("base register not ready, NOP inserted.");
	  /* Insert a NOP before the write instruction.  */
	  output = frag_more (4);
	  memset (output, 0, 4);
	}
      break;

    case mode_dax:
      /*  register := register * 5-bit immediate
         Examples:
         read.b  r1,(r2)
         read.w  r1,3(r2)  */
      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("Dest register required");
	  return;
	}
      str = skip_space (str);
      if (*str != ',')
	{
	  as_bad ("SourceA required");
	  return;
	}
      str = skip_space (str + 1);

      indx = 0;
      if (*str != '(')
	{
	  str = parse_exp (str, &e1);
	  if (e1.X_op == O_constant)
	    {
	      indx = e1.X_add_number;

	      if (indx < 0 || indx > 31)
		{
		  as_bad ("Index out of range");
		  return;
		}
	    }
	  else
	    {
	      as_bad ("Immediate 0 to 31 required");
	      return;
	    }
	}
      if (*str != '(')
	{
	  as_bad ("(SourceA) required");
	  return;
	}
      str++;
      ans = parse_gen_reg (&str, &r2);
      if (ans < 0)
	{
	  as_bad ("SourceA register required");
	  return;
	}
      str = skip_space (str);
      if (*str != ')')
	{
	  as_bad ("(SourceA) required");
	  return;
	}
      str++;
      opcode |= (r1 << 10) | (r2 << 16) | ((indx & 0x1f) << 4);
      this_dest = r1;

      if (indx != 0 && previous_mode == mode_cad)
	{
	  /* We're in a delay slot.
	     If the base reg is the destination of the branch, then issue
	     an error message.
	     Otherwise it is safe to use the base and index.  */
	  if (previous_dest != 0 && r2 == previous_dest)
	    {
	      as_bad ("base register not ready");
	      return;
	    }
	}
      else if (previous_dest != 0
	       && r2 == previous_dest
	       && (visium_arch == VISIUM_ARCH_MCM
		   || visium_arch == VISIUM_ARCH_MCM24
		   || (visium_arch == VISIUM_ARCH_DEF && indx != 0)))
	{
	  as_warn ("base register not ready, NOP inserted.");
	  /* Insert a NOP before the read instruction.  */
	  output = frag_more (4);
	  memset (output, 0, 4);
	}
      break;

    case mode_s:
      /* special mode
         Example:
         nop  */
      str = skip_space (str);
      break;

    case mode_ci:
      /* condition * 16-bit signed word displacement
         Example:
         brr L1  */
      ans = parse_cc (&str, &cc);
      if (ans < 0)
	{
	  as_bad ("condition code required");
	  return;
	}
      opcode |= (cc << 27);

      str = skip_space (str);
      if (*str == ',')
	{
	  str = skip_space (str + 1);
	  str = parse_exp (str, &e1);
	  if (e1.X_op != O_absent)
	    {
	      if (e1.X_op == O_constant)
		{
		  int imm = e1.X_add_number;

		  if (imm < -32768 || imm > 32767)
		    as_bad ("immediate value out of range");

		  /* The GR6 doesn't correctly handle a 0 displacement
		     so we insert a NOP and change it to -1.  */
		  if (imm == 0 && cc != 0 && visium_arch == VISIUM_ARCH_GR6)
		    {
		      output = frag_more (4);
		      memset (output, 0, 4);
		      imm = -1;
		    }

		  opcode |= (imm & 0xffff);
		}
	      else if (e1.X_op == O_symbol)
		{
		  /* The GR6 doesn't correctly handle a 0 displacement
		     so the instruction requires relaxation.  */
		  if (cc != 0 && visium_arch == VISIUM_ARCH_GR6)
		    relax = amode;
		  else
		    reloc = BFD_RELOC_VISIUM_REL16;
		}
	      else
		{
		  as_bad ("immediate value missing");
		  return;
		}
	    }
	  else
	    {
	      as_bad ("immediate value missing");
	      return;
	    }
	}
      else
	{
	  as_bad ("immediate value missing");
	  return;
	}

      if (previous_mode == mode_cad || previous_mode == mode_ci)
	as_bad ("branch instruction in delay slot");

      condition_code = cc;
      break;

    case mode_fdab:
      /* float := float * float
         Example
         fadd    f4,f3,f2  */
      ans = parse_fp_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("floating point destination register required");
	  return;
	}
      str = skip_space (str);
      if (*str == ',')
	{
	  str++;
	  ans = parse_fp_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("floating point SourceA register required");
	      return;
	    }
	  str = skip_space (str);
	  if (*str == ',')
	    {
	      str++;
	      ans = parse_fp_reg (&str, &r3);
	      if (ans < 0)
		{
		  as_bad ("floating point SourceB register required");
		  return;
		}

	      /* Got 3 floating regs, assemble instruction.  */
	      opcode |= (r1 << 10) | (r2 << 16) | (r3 << 4);
	    }
	  else
	    {
	      as_bad ("floating point SourceB register required");
	      return;
	    }
	}
      else
	{
	  as_bad ("floating point SourceA register required");
	  return;
	}
      break;

    case mode_ifdab:
      /* 4-bit immediate * float * float * float
         Example
         fpinst   10,f1,f2,f3  */
      str = parse_exp (str, &e1);
      str = skip_space (str);
      if (e1.X_op != O_absent && *str == ',')
	{
	  int finst = e1.X_add_number;

	  str = skip_space (str + 1);
	  ans = parse_fp_reg (&str, &r1);
	  if (ans < 0)
	    {
	      as_bad ("floating point destination register required");
	      return;
	    }
	  str = skip_space (str);
	  if (*str == ',')
	    {
	      str++;
	      ans = parse_fp_reg (&str, &r2);
	      if (ans < 0)
		{
		  as_bad ("floating point SourceA register required");
		  return;
		}
	      str = skip_space (str);
	      if (*str == ',')
		{
		  str++;
		  ans = parse_fp_reg (&str, &r3);
		  if (ans < 0)
		    {
		      as_bad ("floating point SourceB register required");
		      return;
		    }

		  /* Got immediate and 3 floating regs,
		     assemble instruction.  */
		  if (finst < 0 || finst > 15)
		    as_bad ("finst out of range");

		  opcode |=
		    ((finst & 0xf) << 27) | (r1 << 10) | (r2 << 16) | (r3 <<
								       4);
		}
	      else
		{
		  as_bad ("floating point SourceB register required");
		  return;
		}
	    }
	  else
	    {
	      as_bad ("floating point SourceA register required");
	      return;
	    }
	}
      else
	{
	  as_bad ("finst missing");
	  return;
	}
      break;

    case mode_idfab:
      /* 4-bit immediate * register * float * float
         Example
         fpuread   4,r25,f2,f3  */
      str = parse_exp (str, &e1);
      str = skip_space (str);
      if (e1.X_op != O_absent && *str == ',')
	{
	  int finst = e1.X_add_number;

	  str = skip_space (str + 1);
	  ans = parse_gen_reg (&str, &r1);
	  if (ans < 0)
	    {
	      as_bad ("destination general register required");
	      return;
	    }
	  str = skip_space (str);
	  if (*str == ',')
	    {
	      str++;
	      ans = parse_fp_reg (&str, &r2);
	      if (ans < 0)
		{
		  as_bad ("floating point SourceA register required");
		  return;
		}
	      str = skip_space (str);
	      if (*str == ',')
		{
		  str++;
		  ans = parse_fp_reg (&str, &r3);
		  if (ans < 0)
		    {
		      as_bad ("floating point SourceB register required");
		      return;
		    }

		  /* Got immediate and 3 floating regs,
		     assemble instruction.  */
		  if (finst < 0 || finst > 15)
		    as_bad ("finst out of range");

		  opcode |=
		    ((finst & 0xf) << 27) | (r1 << 10) | (r2 << 16) | (r3 <<
								       4);
		}
	      else
		{
		  as_bad ("floating point SourceB register required");
		  return;
		}
	    }
	  else
	    {
	      as_bad ("floating point SourceA register required");
	      return;
	    }
	}
      else
	{
	  as_bad ("finst missing");
	  return;
	}
      break;

    case mode_fda:
      /* float := float
         Example
         fsqrt    f4,f3  */
      ans = parse_fp_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("floating point destination register required");
	  return;
	}
      str = skip_space (str);
      if (*str == ',')
	{
	  str++;
	  ans = parse_fp_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("floating point source register required");
	      return;
	    }

	  /* Got 2 floating regs, assemble instruction.  */
	  opcode |= (r1 << 10) | (r2 << 16);
	}
      else
	{
	  as_bad ("floating point source register required");
	  return;
	}
      break;

    case mode_fdra:
      /* float := register
         Example
         fload   f15,r6  */
      ans = parse_fp_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("floating point destination register required");
	  return;
	}
      str = skip_space (str);
      if (*str == ',')
	{
	  str++;
	  ans = parse_gen_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("SourceA general register required");
	      return;
	    }

	  /* Got 2 regs, assemble instruction.  */
	  opcode |= (r1 << 10) | (r2 << 16);
	}
      else
	{
	  as_bad ("SourceA general register required");
	  return;
	}
      break;

    case mode_rdfab:
      /* register := float * float
         Example
         fcmp    r0,f4,f8
         For the GR6, register must be r0 and can be omitted.  */
      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  if (visium_opcode_arch == VISIUM_OPCODE_ARCH_GR5)
	    {
	      as_bad ("Dest general register required");
	      return;
	    }
	  r1 = 0;
	}
      else
	{
	  if (r1 != 0 && visium_opcode_arch != VISIUM_OPCODE_ARCH_GR5)
	    {
	      as_bad ("FCMP/FCMPE can only use r0 as Dest register");
	      return;
	     }

	  str = skip_space (str);
	  if (*str == ',')
	    str++;
	  else
	    {
	      as_bad ("floating point SourceA register required");
	      return;
	    }
	}

      ans = parse_fp_reg (&str, &r2);
      if (ans < 0)
	{
	  as_bad ("floating point SourceA register required");
	  return;
	}
      str = skip_space (str);
      if (*str == ',')
	{
	  str++;
	  ans = parse_fp_reg (&str, &r3);
	  if (ans < 0)
	    {
	      as_bad ("floating point SourceB register required");
	      return;
	    }

	  /* Got 3 regs, assemble instruction.  */
	  opcode |= (r1 << 10) | (r2 << 16) | (r3 << 4);
	}

      this_dest = r1;
      break;

    case mode_rdfa:
      /* register := float
         Example
         fstore r5,f12  */
      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("Dest general register required");
	  return;
	}
      str = skip_space (str);
      if (*str == ',')
	{
	  str++;
	  ans = parse_fp_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("floating point source register required");
	      return;
	    }

	  /* Got 2 regs, assemble instruction.  */
	  opcode |= (r1 << 10) | (r2 << 16);
	}
      else
	{
	  as_bad ("floating point source register required");
	  return;
	}

      this_dest = r1;
      break;

    case mode_rrr:
      /* register register register, all sources and destinations
         Example:
         bmd   r1,r2,r3  */

      ans = parse_gen_reg (&str, &r1);
      if (ans < 0)
	{
	  as_bad ("destination address register required");
	  return;
	}
      str = skip_space (str);
      if (*str == ',')
	{
	  str++;
	  ans = parse_gen_reg (&str, &r2);
	  if (ans < 0)
	    {
	      as_bad ("source address register required");
	      return;
	    }
	  str = skip_space (str);
	  if (*str == ',')
	    {
	      str++;
	      ans = parse_gen_reg (&str, &r3);
	      if (ans < 0)
		{
		  as_bad ("count register required");
		  return;
		}

	      /* We insist on three registers but the opcode can only use
		 r1,r2,r3.  */
	      if (r1 != 1 || r2 != 2 || r3 != 3)
		{
		  as_bad ("BMI/BMD can only use format op r1,r2,r3");
		  return;
		}

	      /* Opcode is unmodified by what comes out of the table.  */
	    }
	  else
	    {
	      as_bad ("register required");
	      return;
	    }
	}
      else
	{
	  as_bad ("register required");
	  return;
	}

      this_dest = r1;
      break;

    default:
      break;
    }

  if (relax)
    output = frag_var (rs_machine_dependent, 8, 4, relax, e1.X_add_symbol,
		       e1.X_add_number, NULL);
  else
    output = frag_more (4);

  /* Build the 32-bit instruction in a host-endian-neutral fashion.  */
  output[0] = (opcode >> 24) & 0xff;
  output[1] = (opcode >> 16) & 0xff;
  output[2] = (opcode >> 8) & 0xff;
  output[3] = (opcode >> 0) & 0xff;

  if (relax)
    /* The size of the instruction is unknown, so tie the debug info to the
       start of the instruction.  */
    dwarf2_emit_insn (0);
  else
    {
      if (reloc)
	fix_new_exp (frag_now, output - frag_now->fr_literal, 4, &e1,
		     reloc == BFD_RELOC_VISIUM_REL16, reloc);
      else
	visium_update_parity_bit (output);

      dwarf2_emit_insn (4);
    }

  if (*str != '\0')
    as_bad ("junk after instruction");
}

void
visium_cfi_frame_initial_instructions (void)
{
  /* The CFA is in SP on function entry.  */
  cfi_add_CFA_def_cfa (23, 0);
}

int
visium_regname_to_dw2regnum (char *regname)
{
  if (!regname[0])
    return -1;

  if (regname[0] == 'f' && regname[1] == 'p' && !regname[2])
    return 22;

  if (regname[0] == 's' && regname[1] == 'p' && !regname[2])
    return 23;

  if (regname[0] == 'm' && regname[1] == 'd' && !regname[3])
    switch (regname[2])
      {
      case 'b': return 32;
      case 'a': return 33;
      case 'c': return 34;
      default : return -1;
      }

  if (regname[0] == 'f' || regname[0] == 'r')
    {
      char *p;
      unsigned int regnum = strtoul (regname + 1, &p, 10);
      if (*p)
	return -1;
      if (regnum >= (regname[0] == 'f' ? 16 : 32))
	return -1;
      if (regname[0] == 'f')
	regnum += 35;
      return regnum;
    }

  return -1;
}
