/* tc-c30.c -- Assembly code for the Texas Instruments TMS320C30
   Copyright (C) 1998-2020 Free Software Foundation, Inc.
   Contributed by Steven Haworth (steve@pm.cse.rmit.edu.au)

   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 3, 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, 51 Franklin Street - Fifth Floor, Boston, MA
   02110-1301, USA.  */

/* Texas Instruments TMS320C30 machine specific gas.
   Written by Steven Haworth (steve@pm.cse.rmit.edu.au).
   Bugs & suggestions are completely welcome.  This is free software.
   Please help us make it better.  */

#include "as.h"
#include "safe-ctype.h"
#include "opcode/tic30.h"

/* Put here all non-digit non-letter characters that may occur in an
   operand.  */
static char operand_special_chars[] = "%$-+(,)*._~/<>&^!:[@]";
static const char *ordinal_names[] =
{
  N_("first"), N_("second"), N_("third"), N_("fourth"), N_("fifth")
};

const char comment_chars[]        = ";";
const char line_comment_chars[]   = "*";
const char line_separator_chars[] = "";

const char *md_shortopts = "";
struct option md_longopts[] =
{
  {NULL, no_argument, NULL, 0}
};

size_t md_longopts_size = sizeof (md_longopts);

/* Chars that mean this number is a floating point constant.
   As in 0f12.456
   or    0d1.2345e12.  */
const char FLT_CHARS[] = "fFdDxX";

/* Chars that can be used to separate mant from exp in floating point
   nums.  */
const char EXP_CHARS[] = "eE";

/* Tables for lexical analysis.  */
static char opcode_chars[256];
static char register_chars[256];
static char operand_chars[256];
static char space_chars[256];
static char identifier_chars[256];
static char digit_chars[256];

/* Lexical macros.  */
#define is_opcode_char(x)     (opcode_chars     [(unsigned char) x])
#define is_operand_char(x)    (operand_chars    [(unsigned char) x])
#define is_register_char(x)   (register_chars   [(unsigned char) x])
#define is_space_char(x)      (space_chars      [(unsigned char) x])
#define is_identifier_char(x) (identifier_chars [(unsigned char) x])
#define is_digit_char(x)      (digit_chars      [(unsigned char) x])

const pseudo_typeS md_pseudo_table[] =
{
  {0, 0, 0}
};

static int ATTRIBUTE_PRINTF_1
debug (const char *string, ...)
{
  if (flag_debug)
    {
      char str[100];
      va_list argptr;

      va_start (argptr, string);
      vsprintf (str, string, argptr);
      va_end (argptr);
      if (str[0] == '\0')
	return (0);
      fputs (str, USE_STDOUT ? stdout : stderr);
      return strlen (str);
    }
  else
    return 0;
}

/* Hash table for opcode lookup.  */
static struct hash_control *op_hash;
/* Hash table for parallel opcode lookup.  */
static struct hash_control *parop_hash;
/* Hash table for register lookup.  */
static struct hash_control *reg_hash;
/* Hash table for indirect addressing lookup.  */
static struct hash_control *ind_hash;

void
md_begin (void)
{
  const char *hash_err;

  debug ("In md_begin()\n");
  op_hash = hash_new ();

  {
    const insn_template *current_optab = tic30_optab;

    for (; current_optab < tic30_optab_end; current_optab++)
      {
	hash_err = hash_insert (op_hash, current_optab->name,
				(char *) current_optab);
	if (hash_err)
	  as_fatal ("Internal Error: Can't Hash %s: %s",
		    current_optab->name, hash_err);
      }
  }

  parop_hash = hash_new ();

  {
    const partemplate *current_parop = tic30_paroptab;

    for (; current_parop < tic30_paroptab_end; current_parop++)
      {
	hash_err = hash_insert (parop_hash, current_parop->name,
				(char *) current_parop);
	if (hash_err)
	  as_fatal ("Internal Error: Can't Hash %s: %s",
		    current_parop->name, hash_err);
      }
  }

  reg_hash = hash_new ();

  {
    const reg *current_reg = tic30_regtab;

    for (; current_reg < tic30_regtab_end; current_reg++)
      {
	hash_err = hash_insert (reg_hash, current_reg->name,
				(char *) current_reg);
	if (hash_err)
	  as_fatal ("Internal Error: Can't Hash %s: %s",
		    current_reg->name, hash_err);
      }
  }

  ind_hash = hash_new ();

  {
    const ind_addr_type *current_ind = tic30_indaddr_tab;

    for (; current_ind < tic30_indaddrtab_end; current_ind++)
      {
	hash_err = hash_insert (ind_hash, current_ind->syntax,
				(char *) current_ind);
	if (hash_err)
	  as_fatal ("Internal Error: Can't Hash %s: %s",
		    current_ind->syntax, hash_err);
      }
  }

  /* Fill in lexical tables:  opcode_chars, operand_chars, space_chars.  */
  {
    int c;
    char *p;

    for (c = 0; c < 256; c++)
      {
	if (ISLOWER (c) || ISDIGIT (c))
	  {
	    opcode_chars[c] = c;
	    register_chars[c] = c;
	  }
	else if (ISUPPER (c))
	  {
	    opcode_chars[c] = TOLOWER (c);
	    register_chars[c] = opcode_chars[c];
	  }
	else if (c == ')' || c == '(')
	  register_chars[c] = c;

	if (ISUPPER (c) || ISLOWER (c) || ISDIGIT (c))
	  operand_chars[c] = c;

	if (ISDIGIT (c) || c == '-')
	  digit_chars[c] = c;

	if (ISALPHA (c) || c == '_' || c == '.' || ISDIGIT (c))
	  identifier_chars[c] = c;

	if (c == ' ' || c == '\t')
	  space_chars[c] = c;

	if (c == '_')
	  opcode_chars[c] = c;
      }
    for (p = operand_special_chars; *p != '\0'; p++)
      operand_chars[(unsigned char) *p] = *p;
  }
}

/* Address Mode OR values.  */
#define AM_Register  0x00000000
#define AM_Direct    0x00200000
#define AM_Indirect  0x00400000
#define AM_Immediate 0x00600000
#define AM_NotReq    0xFFFFFFFF

/* PC Relative OR values.  */
#define PC_Register 0x00000000
#define PC_Relative 0x02000000

typedef struct
{
  unsigned op_type;
  struct
  {
    int resolved;
    unsigned address;
    char *label;
    expressionS direct_expr;
  } direct;
  struct
  {
    unsigned mod;
    int ARnum;
    unsigned char disp;
  } indirect;
  struct
  {
    unsigned opcode;
  } reg;
  struct
  {
    int resolved;
    int decimal_found;
    float f_number;
    int s_number;
    unsigned int u_number;
    char *label;
    expressionS imm_expr;
  } immediate;
} operand;

insn_template *opcode;

struct tic30_insn
{
  insn_template *tm;		/* Template of current instruction.  */
  unsigned opcode;		/* Final opcode.  */
  unsigned int operands;	/* Number of given operands.  */
  /* Type of operand given in instruction.  */
  operand *operand_type[MAX_OPERANDS];
  unsigned addressing_mode;	/* Final addressing mode of instruction.  */
};

struct tic30_insn insn;
static int found_parallel_insn;

static char output_invalid_buf[sizeof (unsigned char) * 2 + 6];

static char *
output_invalid (char c)
{
  if (ISPRINT (c))
    snprintf (output_invalid_buf, sizeof (output_invalid_buf),
	      "'%c'", c);
  else
    snprintf (output_invalid_buf, sizeof (output_invalid_buf),
	      "(0x%x)", (unsigned char) c);
  return output_invalid_buf;
}

/* next_line points to the next line after the current instruction
   (current_line).  Search for the parallel bars, and if found, merge two
   lines into internal syntax for a parallel instruction:
     q_[INSN1]_[INSN2] [OPERANDS1] | [OPERANDS2]
   By this stage, all comments are scrubbed, and only the bare lines are
   given.  */

#define NONE           0
#define START_OPCODE   1
#define END_OPCODE     2
#define START_OPERANDS 3
#define END_OPERANDS   4

static char *
tic30_find_parallel_insn (char *current_line, char *next_line)
{
  int found_parallel = 0;
  char first_opcode[256];
  char second_opcode[256];
  char first_operands[256];
  char second_operands[256];
  char *parallel_insn;

  debug ("In tic30_find_parallel_insn()\n");
  while (!is_end_of_line[(unsigned char) *next_line])
    {
      if (*next_line == PARALLEL_SEPARATOR
	  && *(next_line + 1) == PARALLEL_SEPARATOR)
	{
	  found_parallel = 1;
	  next_line++;
	  break;
	}
      next_line++;
    }
  if (!found_parallel)
    return NULL;
  debug ("Found a parallel instruction\n");

  {
    int i;
    char *op, *operands, *line;

    for (i = 0; i < 2; i++)
      {
	if (i == 0)
	  {
	    op = &first_opcode[0];
	    operands = &first_operands[0];
	    line = current_line;
	  }
	else
	  {
	    op = &second_opcode[0];
	    operands = &second_operands[0];
	    line = next_line;
	  }

	{
	  int search_status = NONE;
	  int char_ptr = 0;
	  char c;

	  while (!is_end_of_line[(unsigned char) (c = *line)])
	    {
	      if (is_opcode_char (c) && search_status == NONE)
		{
		  op[char_ptr++] = TOLOWER (c);
		  search_status = START_OPCODE;
		}
	      else if (is_opcode_char (c) && search_status == START_OPCODE)
		op[char_ptr++] = TOLOWER (c);
	      else if (!is_opcode_char (c) && search_status == START_OPCODE)
		{
		  op[char_ptr] = '\0';
		  char_ptr = 0;
		  search_status = END_OPCODE;
		}
	      else if (is_operand_char (c) && search_status == START_OPERANDS)
		operands[char_ptr++] = c;

	      if (is_operand_char (c) && search_status == END_OPCODE)
		{
		  operands[char_ptr++] = c;
		  search_status = START_OPERANDS;
		}

	      line++;
	    }
	  if (search_status != START_OPERANDS)
	    return NULL;
	  operands[char_ptr] = '\0';
	}
      }
  }

  parallel_insn = concat ("q_", first_opcode, "_", second_opcode, " ",
			  first_operands, " | ", second_operands,
			  (char *) NULL);
  debug ("parallel insn = %s\n", parallel_insn);
  return parallel_insn;
}

#undef NONE
#undef START_OPCODE
#undef END_OPCODE
#undef START_OPERANDS
#undef END_OPERANDS

static operand *
tic30_operand (char *token)
{
  unsigned int count;
  operand *current_op;

  debug ("In tic30_operand with %s\n", token);
  current_op = XCNEW (operand);

  if (*token == DIRECT_REFERENCE)
    {
      char *token_posn = token + 1;
      int direct_label = 0;

      debug ("Found direct reference\n");
      while (*token_posn)
	{
	  if (!is_digit_char (*token_posn))
	    direct_label = 1;
	  token_posn++;
	}

      if (direct_label)
	{
	  char *save_input_line_pointer;
	  segT retval;

	  debug ("Direct reference is a label\n");
	  current_op->direct.label = token + 1;
	  save_input_line_pointer = input_line_pointer;
	  input_line_pointer = token + 1;
	  debug ("Current input_line_pointer: %s\n", input_line_pointer);
	  retval = expression (&current_op->direct.direct_expr);

	  debug ("Expression type: %d\n",
		 current_op->direct.direct_expr.X_op);
	  debug ("Expression addnum: %ld\n",
		 (long) current_op->direct.direct_expr.X_add_number);
	  debug ("Segment: %p\n", retval);

	  input_line_pointer = save_input_line_pointer;

	  if (current_op->direct.direct_expr.X_op == O_constant)
	    {
	      current_op->direct.address =
		current_op->direct.direct_expr.X_add_number;
	      current_op->direct.resolved = 1;
	    }
	}
      else
	{
	  debug ("Direct reference is a number\n");
	  current_op->direct.address = atoi (token + 1);
	  current_op->direct.resolved = 1;
	}
      current_op->op_type = Direct;
    }
  else if (*token == INDIRECT_REFERENCE)
    {
      /* Indirect reference operand.  */
      int found_ar = 0;
      int found_disp = 0;
      int ar_number = -1;
      int disp_number = 0;
      int buffer_posn = 1;
      ind_addr_type *ind_addr_op;
      char * ind_buffer;

      ind_buffer = XNEWVEC (char, strlen (token));

      debug ("Found indirect reference\n");
      ind_buffer[0] = *token;

      for (count = 1; count < strlen (token); count++)
	{
	  /* Strip operand.  */
	  ind_buffer[buffer_posn] = TOLOWER (*(token + count));

	  if ((*(token + count - 1) == 'a' || *(token + count - 1) == 'A')
	      && (*(token + count) == 'r' || *(token + count) == 'R'))
	    {
	      /* AR reference is found, so get its number and remove
		 it from the buffer so it can pass through hash_find().  */
	      if (found_ar)
		{
		  as_bad (_("More than one AR register found in indirect reference"));
		  free (ind_buffer);
		  return NULL;
		}
	      if (*(token + count + 1) < '0' || *(token + count + 1) > '7')
		{
		  as_bad (_("Illegal AR register in indirect reference"));
		  free (ind_buffer);
		  return NULL;
		}
	      ar_number = *(token + count + 1) - '0';
	      found_ar = 1;
	      count++;
	    }

	  if (*(token + count) == '(')
	    {
	      /* Parenthesis found, so check if a displacement value is
		 inside.  If so, get the value and remove it from the
		 buffer.  */
	      if (is_digit_char (*(token + count + 1)))
		{
		  char disp[10];
		  int disp_posn = 0;

		  if (found_disp)
		    {
		      as_bad (_("More than one displacement found in indirect reference"));
		      free (ind_buffer);
		      return NULL;
		    }
		  count++;
		  while (*(token + count) != ')')
		    {
		      if (!is_digit_char (*(token + count)))
			{
			  as_bad (_("Invalid displacement in indirect reference"));
			  free (ind_buffer);
			  return NULL;
			}
		      disp[disp_posn++] = *(token + (count++));
		    }
		  disp[disp_posn] = '\0';
		  disp_number = atoi (disp);
		  count--;
		  found_disp = 1;
		}
	    }
	  buffer_posn++;
	}

      ind_buffer[buffer_posn] = '\0';
      if (!found_ar)
	{
	  as_bad (_("AR register not found in indirect reference"));
	  free (ind_buffer);
	  return NULL;
	}

      ind_addr_op = (ind_addr_type *) hash_find (ind_hash, ind_buffer);
      if (ind_addr_op)
	{
	  debug ("Found indirect reference: %s\n", ind_addr_op->syntax);
	  if (ind_addr_op->displacement == IMPLIED_DISP)
	    {
	      found_disp = 1;
	      disp_number = 1;
	    }
	  else if ((ind_addr_op->displacement == DISP_REQUIRED) && !found_disp)
	    {
	      /* Maybe an implied displacement of 1 again.  */
	      as_bad (_("required displacement wasn't given in indirect reference"));
	      free (ind_buffer);
	      return NULL;
	    }
	}
      else
	{
	  as_bad (_("illegal indirect reference"));
	  free (ind_buffer);
	  return NULL;
	}

      if (found_disp && (disp_number < 0 || disp_number > 255))
	{
	  as_bad (_("displacement must be an unsigned 8-bit number"));
	  free (ind_buffer);
	  return NULL;
	}

      current_op->indirect.mod = ind_addr_op->modfield;
      current_op->indirect.disp = disp_number;
      current_op->indirect.ARnum = ar_number;
      current_op->op_type = Indirect;
      free (ind_buffer);
    }
  else
    {
      reg *regop = (reg *) hash_find (reg_hash, token);

      if (regop)
	{
	  debug ("Found register operand: %s\n", regop->name);
	  if (regop->regtype == REG_ARn)
	    current_op->op_type = ARn;
	  else if (regop->regtype == REG_Rn)
	    current_op->op_type = Rn;
	  else if (regop->regtype == REG_DP)
	    current_op->op_type = DPReg;
	  else
	    current_op->op_type = OtherReg;
	  current_op->reg.opcode = regop->opcode;
	}
      else
	{
	  if (!is_digit_char (*token)
	      || *(token + 1) == 'x'
	      || strchr (token, 'h'))
	    {
	      char *save_input_line_pointer;
	      segT retval;

	      debug ("Probably a label: %s\n", token);
	      current_op->immediate.label = xstrdup (token);
	      save_input_line_pointer = input_line_pointer;
	      input_line_pointer = token;

	      debug ("Current input_line_pointer: %s\n", input_line_pointer);
	      retval = expression (&current_op->immediate.imm_expr);
	      debug ("Expression type: %d\n",
		     current_op->immediate.imm_expr.X_op);
	      debug ("Expression addnum: %ld\n",
		     (long) current_op->immediate.imm_expr.X_add_number);
	      debug ("Segment: %p\n", retval);
	      input_line_pointer = save_input_line_pointer;

	      if (current_op->immediate.imm_expr.X_op == O_constant)
		{
		  current_op->immediate.s_number
		    = current_op->immediate.imm_expr.X_add_number;
		  current_op->immediate.u_number
		    = (unsigned int) current_op->immediate.imm_expr.X_add_number;
		  current_op->immediate.resolved = 1;
		}
	    }
	  else
	    {
	      debug ("Found a number or displacement\n");
	      for (count = 0; count < strlen (token); count++)
		if (*(token + count) == '.')
		  current_op->immediate.decimal_found = 1;
	      current_op->immediate.label = xstrdup (token);
	      current_op->immediate.f_number = (float) atof (token);
	      current_op->immediate.s_number = (int) atoi (token);
	      current_op->immediate.u_number = (unsigned int) atoi (token);
	      current_op->immediate.resolved = 1;
	    }
	  current_op->op_type = Disp | Abs24 | Imm16 | Imm24;
	  if (current_op->immediate.u_number <= 31)
	    current_op->op_type |= IVector;
	}
    }
  return current_op;
}

struct tic30_par_insn
{
  partemplate *tm;		/* Template of current parallel instruction.  */
  unsigned operands[2];		/* Number of given operands for each insn.  */
  /* Type of operand given in instruction.  */
  operand *operand_type[2][MAX_OPERANDS];
  int swap_operands;		/* Whether to swap operands around.  */
  unsigned p_field;		/* Value of p field in multiply add/sub instructions.  */
  unsigned opcode;		/* Final opcode.  */
};

struct tic30_par_insn p_insn;

static int
tic30_parallel_insn (char *token)
{
  static partemplate *p_opcode;
  char *current_posn = token;
  char *token_start;
  char save_char;

  debug ("In tic30_parallel_insn with %s\n", token);
  memset (&p_insn, '\0', sizeof (p_insn));

  while (is_opcode_char (*current_posn))
    current_posn++;
  {
    /* Find instruction.  */
    save_char = *current_posn;
    *current_posn = '\0';
    p_opcode = (partemplate *) hash_find (parop_hash, token);
    if (p_opcode)
      {
	debug ("Found instruction %s\n", p_opcode->name);
	p_insn.tm = p_opcode;
      }
    else
      {
	char first_opcode[6] = {0};
	char second_opcode[6] = {0};
	unsigned int i;
	int current_opcode = -1;
	int char_ptr = 0;

	for (i = 0; i < strlen (token); i++)
	  {
	    char ch = *(token + i);

	    if (ch == '_' && current_opcode == -1)
	      {
		current_opcode = 0;
		continue;
	      }

	    if (ch == '_' && current_opcode == 0)
	      {
		current_opcode = 1;
		char_ptr = 0;
		continue;
	      }

	    switch (current_opcode)
	      {
	      case 0:
		first_opcode[char_ptr++] = ch;
		break;
	      case 1:
		second_opcode[char_ptr++] = ch;
		break;
	      }
	  }

	debug ("first_opcode = %s\n", first_opcode);
	debug ("second_opcode = %s\n", second_opcode);
	sprintf (token, "q_%s_%s", second_opcode, first_opcode);
	p_opcode = (partemplate *) hash_find (parop_hash, token);

	if (p_opcode)
	  {
	    debug ("Found instruction %s\n", p_opcode->name);
	    p_insn.tm = p_opcode;
	    p_insn.swap_operands = 1;
	  }
	else
	  return 0;
      }
    *current_posn = save_char;
  }

  {
    /* Find operands.  */
    int paren_not_balanced;
    int expecting_operand = 0;
    int found_separator = 0;

    do
      {
	/* Skip optional white space before operand.  */
	while (!is_operand_char (*current_posn)
	       && *current_posn != END_OF_INSN)
	  {
	    if (!is_space_char (*current_posn)
		&& *current_posn != PARALLEL_SEPARATOR)
	      {
		as_bad (_("Invalid character %s before %s operand"),
			output_invalid (*current_posn),
			ordinal_names[insn.operands]);
		return 1;
	      }
	    if (*current_posn == PARALLEL_SEPARATOR)
	      found_separator = 1;
	    current_posn++;
	  }

	token_start = current_posn;
	paren_not_balanced = 0;

	while (paren_not_balanced || *current_posn != ',')
	  {
	    if (*current_posn == END_OF_INSN)
	      {
		if (paren_not_balanced)
		  {
		    as_bad (_("Unbalanced parenthesis in %s operand."),
			    ordinal_names[insn.operands]);
		    return 1;
		  }
		else
		  break;
	      }
	    else if (*current_posn == PARALLEL_SEPARATOR)
	      {
		while (is_space_char (*(current_posn - 1)))
		  current_posn--;
		break;
	      }
	    else if (!is_operand_char (*current_posn)
		     && !is_space_char (*current_posn))
	      {
		as_bad (_("Invalid character %s in %s operand"),
			output_invalid (*current_posn),
			ordinal_names[insn.operands]);
		return 1;
	      }

	    if (*current_posn == '(')
	      ++paren_not_balanced;
	    if (*current_posn == ')')
	      --paren_not_balanced;
	    current_posn++;
	  }

	if (current_posn != token_start)
	  {
	    /* Yes, we've read in another operand.  */
	    p_insn.operands[found_separator]++;
	    if (p_insn.operands[found_separator] > MAX_OPERANDS)
	      {
		as_bad (_("Spurious operands; (%d operands/instruction max)"),
			MAX_OPERANDS);
		return 1;
	      }

	    /* Now parse operand adding info to 'insn' as we go along.  */
	    save_char = *current_posn;
	    *current_posn = '\0';
	    p_insn.operand_type[found_separator][p_insn.operands[found_separator] - 1] =
	      tic30_operand (token_start);
	    *current_posn = save_char;
	    if (!p_insn.operand_type[found_separator][p_insn.operands[found_separator] - 1])
	      return 1;
	  }
	else
	  {
	    if (expecting_operand)
	      {
		as_bad (_("Expecting operand after ','; got nothing"));
		return 1;
	      }
	    if (*current_posn == ',')
	      {
		as_bad (_("Expecting operand before ','; got nothing"));
		return 1;
	      }
	  }

	/* Now *current_posn must be either ',' or END_OF_INSN.  */
	if (*current_posn == ',')
	  {
	    if (*++current_posn == END_OF_INSN)
	      {
		/* Just skip it, if it's \n complain.  */
		as_bad (_("Expecting operand after ','; got nothing"));
		return 1;
	      }
	    expecting_operand = 1;
	  }
      }
    while (*current_posn != END_OF_INSN);
  }

  if (p_insn.swap_operands)
    {
      int temp_num, i;
      operand *temp_op;

      temp_num = p_insn.operands[0];
      p_insn.operands[0] = p_insn.operands[1];
      p_insn.operands[1] = temp_num;
      for (i = 0; i < MAX_OPERANDS; i++)
	{
	  temp_op = p_insn.operand_type[0][i];
	  p_insn.operand_type[0][i] = p_insn.operand_type[1][i];
	  p_insn.operand_type[1][i] = temp_op;
	}
    }

  if (p_insn.operands[0] != p_insn.tm->operands_1)
    {
      as_bad (_("incorrect number of operands given in the first instruction"));
      return 1;
    }

  if (p_insn.operands[1] != p_insn.tm->operands_2)
    {
      as_bad (_("incorrect number of operands given in the second instruction"));
      return 1;
    }

  debug ("Number of operands in first insn: %d\n", p_insn.operands[0]);
  debug ("Number of operands in second insn: %d\n", p_insn.operands[1]);

  {
    /* Now check if operands are correct.  */
    int count;
    int num_rn = 0;
    int num_ind = 0;

    for (count = 0; count < 2; count++)
      {
	unsigned int i;
	for (i = 0; i < p_insn.operands[count]; i++)
	  {
	    if ((p_insn.operand_type[count][i]->op_type &
		 p_insn.tm->operand_types[count][i]) == 0)
	      {
		as_bad (_("%s instruction, operand %d doesn't match"),
			ordinal_names[count], i + 1);
		return 1;
	      }

	    /* Get number of R register and indirect reference contained
	       within the first two operands of each instruction.  This is
	       required for the multiply parallel instructions which require
	       two R registers and two indirect references, but not in any
	       particular place.  */
	    if ((p_insn.operand_type[count][i]->op_type & Rn) && i < 2)
	      num_rn++;
	    else if ((p_insn.operand_type[count][i]->op_type & Indirect)
		     && i < 2)
	      num_ind++;
	  }
      }

    if ((p_insn.tm->operand_types[0][0] & (Indirect | Rn))
	== (Indirect | Rn))
      {
	/* Check for the multiply instructions.  */
	if (num_rn != 2)
	  {
	    as_bad (_("incorrect format for multiply parallel instruction"));
	    return 1;
	  }

	if (num_ind != 2)
	  {
	    /* Shouldn't get here.  */
	    as_bad (_("incorrect format for multiply parallel instruction"));
	    return 1;
	  }

	if ((p_insn.operand_type[0][2]->reg.opcode != 0x00)
	    && (p_insn.operand_type[0][2]->reg.opcode != 0x01))
	  {
	    as_bad (_("destination for multiply can only be R0 or R1"));
	    return 1;
	  }

	if ((p_insn.operand_type[1][2]->reg.opcode != 0x02)
	    && (p_insn.operand_type[1][2]->reg.opcode != 0x03))
	  {
	    as_bad (_("destination for add/subtract can only be R2 or R3"));
	    return 1;
	  }

	/* Now determine the P field for the instruction.  */
	if (p_insn.operand_type[0][0]->op_type & Indirect)
	  {
	    if (p_insn.operand_type[0][1]->op_type & Indirect)
	      p_insn.p_field = 0x00000000;	/* Ind * Ind, Rn  +/- Rn.  */
	    else if (p_insn.operand_type[1][0]->op_type & Indirect)
	      p_insn.p_field = 0x01000000;	/* Ind * Rn,  Ind +/- Rn.  */
	    else
	      p_insn.p_field = 0x03000000;	/* Ind * Rn,  Rn  +/- Ind.  */
	  }
	else
	  {
	    if (p_insn.operand_type[0][1]->op_type & Rn)
	      p_insn.p_field = 0x02000000;	/* Rn  * Rn,  Ind +/- Ind.  */
	    else if (p_insn.operand_type[1][0]->op_type & Indirect)
	      {
		operand *temp;
		p_insn.p_field = 0x01000000;	/* Rn  * Ind, Ind +/- Rn.  */
		/* Need to swap the two multiply operands around so that
		   everything is in its place for the opcode makeup.
		   ie so Ind * Rn, Ind +/- Rn.  */
		temp = p_insn.operand_type[0][0];
		p_insn.operand_type[0][0] = p_insn.operand_type[0][1];
		p_insn.operand_type[0][1] = temp;
	      }
	    else
	      {
		operand *temp;
		p_insn.p_field = 0x03000000;	/* Rn  * Ind, Rn  +/- Ind.  */
		temp = p_insn.operand_type[0][0];
		p_insn.operand_type[0][0] = p_insn.operand_type[0][1];
		p_insn.operand_type[0][1] = temp;
	      }
	  }
      }
  }

  debug ("P field: %08X\n", p_insn.p_field);

  /* Finalise opcode.  This is easier for parallel instructions as they have
     to be fully resolved, there are no memory addresses allowed, except
     through indirect addressing, so there are no labels to resolve.  */
  p_insn.opcode = p_insn.tm->base_opcode;

  switch (p_insn.tm->oporder)
    {
    case OO_4op1:
      p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum);
      p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 3);
      p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8);
      p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11);
      p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
      p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 22);
      break;

    case OO_4op2:
      p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum);
      p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 3);
      p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.ARnum << 8);
      p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.mod << 11);
      p_insn.opcode |= (p_insn.operand_type[1][1]->reg.opcode << 19);
      p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 22);
      if (p_insn.operand_type[1][1]->reg.opcode == p_insn.operand_type[0][1]->reg.opcode)
	as_warn (_("loading the same register in parallel operation"));
      break;

    case OO_4op3:
      p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.ARnum);
      p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.mod << 3);
      p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8);
      p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11);
      p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
      p_insn.opcode |= (p_insn.operand_type[0][0]->reg.opcode << 22);
      break;

    case OO_5op1:
      p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum);
      p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 3);
      p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8);
      p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11);
      p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
      p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 19);
      p_insn.opcode |= (p_insn.operand_type[0][2]->reg.opcode << 22);
      break;

    case OO_5op2:
      p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.ARnum);
      p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.mod << 3);
      p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8);
      p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11);
      p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
      p_insn.opcode |= (p_insn.operand_type[0][0]->reg.opcode << 19);
      p_insn.opcode |= (p_insn.operand_type[0][2]->reg.opcode << 22);
      break;

    case OO_PField:
      p_insn.opcode |= p_insn.p_field;
      if (p_insn.operand_type[0][2]->reg.opcode == 0x01)
	p_insn.opcode |= 0x00800000;
      if (p_insn.operand_type[1][2]->reg.opcode == 0x03)
	p_insn.opcode |= 0x00400000;

      switch (p_insn.p_field)
	{
	case 0x00000000:
	  p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.ARnum);
	  p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.mod << 3);
	  p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum << 8);
	  p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 11);
	  p_insn.opcode |= (p_insn.operand_type[1][1]->reg.opcode << 16);
	  p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 19);
	  break;
	case 0x01000000:
	  p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.ARnum);
	  p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.mod << 3);
	  p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum << 8);
	  p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 11);
	  p_insn.opcode |= (p_insn.operand_type[1][1]->reg.opcode << 16);
	  p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 19);
	  break;
	case 0x02000000:
	  p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum);
	  p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 3);
	  p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.ARnum << 8);
	  p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.mod << 11);
	  p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 16);
	  p_insn.opcode |= (p_insn.operand_type[0][0]->reg.opcode << 19);
	  break;
	case 0x03000000:
	  p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum);
	  p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 3);
	  p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum << 8);
	  p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 11);
	  p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
	  p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 19);
	  break;
	}
      break;
    }

  {
    char *p;

    p = frag_more (INSN_SIZE);
    md_number_to_chars (p, (valueT) p_insn.opcode, INSN_SIZE);
  }

  {
    unsigned int i, j;

    for (i = 0; i < 2; i++)
      for (j = 0; j < p_insn.operands[i]; j++)
	free (p_insn.operand_type[i][j]);
  }

  debug ("Final opcode: %08X\n", p_insn.opcode);
  debug ("\n");

  return 1;
}

/* In order to get gas to ignore any | chars at the start of a line,
   this function returns true if a | is found in a line.  */

int
tic30_unrecognized_line (int c)
{
  debug ("In tc_unrecognized_line\n");
  return (c == PARALLEL_SEPARATOR);
}

int
md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
			       segT segment ATTRIBUTE_UNUSED)
{
  debug ("In md_estimate_size_before_relax()\n");
  return 0;
}

void
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
		 segT sec ATTRIBUTE_UNUSED,
		 fragS *fragP ATTRIBUTE_UNUSED)
{
  debug ("In md_convert_frag()\n");
}

void
md_apply_fix (fixS *fixP,
	       valueT *valP,
	       segT seg ATTRIBUTE_UNUSED)
{
  valueT value = *valP;

  debug ("In md_apply_fix() with value = %ld\n", (long) value);
  debug ("Values in fixP\n");
  debug ("fx_size = %d\n", fixP->fx_size);
  debug ("fx_pcrel = %d\n", fixP->fx_pcrel);
  debug ("fx_where = %ld\n", fixP->fx_where);
  debug ("fx_offset = %d\n", (int) fixP->fx_offset);
  {
    char *buf = fixP->fx_frag->fr_literal + fixP->fx_where;

    value /= INSN_SIZE;
    if (fixP->fx_size == 1)
      /* Special fix for LDP instruction.  */
      value = (value & 0x00FF0000) >> 16;

    debug ("new value = %ld\n", (long) value);
    md_number_to_chars (buf, value, fixP->fx_size);
  }

  if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
    fixP->fx_done = 1;
}

int
md_parse_option (int c ATTRIBUTE_UNUSED,
		 const char *arg ATTRIBUTE_UNUSED)
{
  debug ("In md_parse_option()\n");
  return 0;
}

void
md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
{
  debug ("In md_show_usage()\n");
}

symbolS *
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
{
  debug ("In md_undefined_symbol()\n");
  return (symbolS *) 0;
}

valueT
md_section_align (segT segment, valueT size)
{
  debug ("In md_section_align() segment = %p and size = %lu\n",
	 segment, (unsigned long) size);
  size = (size + 3) / 4;
  size *= 4;
  debug ("New size value = %lu\n", (unsigned long) size);
  return size;
}

long
md_pcrel_from (fixS *fixP)
{
  int offset;

  debug ("In md_pcrel_from()\n");
  debug ("fx_where = %ld\n", fixP->fx_where);
  debug ("fx_size = %d\n", fixP->fx_size);
  /* Find the opcode that represents the current instruction in the
     fr_literal storage area, and check bit 21.  Bit 21 contains whether the
     current instruction is a delayed one or not, and then set the offset
     value appropriately.  */
  if (fixP->fx_frag->fr_literal[fixP->fx_where - fixP->fx_size + 1] & 0x20)
    offset = 3;
  else
    offset = 1;
  debug ("offset = %d\n", offset);
  /* PC Relative instructions have a format:
     displacement = Label - (PC + offset)
     This function returns PC + offset where:
     fx_where - fx_size = PC
     INSN_SIZE * offset = offset number of instructions.  */
  return fixP->fx_where - fixP->fx_size + (INSN_SIZE * offset);
}

const char *
md_atof (int what_statement_type,
	 char *literalP,
	 int *sizeP)
{
  int prec;
  char *token;
  char keepval;
  unsigned long value;
  float float_value;

  debug ("In md_atof()\n");
  debug ("precision = %c\n", what_statement_type);
  debug ("literal = %s\n", literalP);
  debug ("line = ");
  token = input_line_pointer;
  while (!is_end_of_line[(unsigned char) *input_line_pointer]
	 && (*input_line_pointer != ','))
    {
      debug ("%c", *input_line_pointer);
      input_line_pointer++;
    }

  keepval = *input_line_pointer;
  *input_line_pointer = '\0';
  debug ("\n");
  float_value = (float) atof (token);
  *input_line_pointer = keepval;
  debug ("float_value = %f\n", float_value);

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

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

    default:
      *sizeP = 0;
      return _("Unrecognized or unsupported floating point constant");
    }

  if (float_value == 0.0)
    value = (prec == 2) ? 0x00008000L : 0x80000000L;
  else
    {
      unsigned long exp, sign, mant, tmsfloat;
      union
      {
	float f;
	long  l;
      }
      converter;

      converter.f = float_value;
      tmsfloat = converter.l;
      sign = tmsfloat & 0x80000000;
      mant = tmsfloat & 0x007FFFFF;
      exp = tmsfloat & 0x7F800000;
      exp <<= 1;
      if (exp == 0xFF000000)
	{
	  if (mant == 0)
	    value = 0x7F7FFFFF;
	  else if (sign == 0)
	    value = 0x7F7FFFFF;
	  else
	    value = 0x7F800000;
	}
      else
	{
	  exp -= 0x7F000000;
	  if (sign)
	    {
	      mant = mant & 0x007FFFFF;
	      mant = -mant;
	      mant = mant & 0x00FFFFFF;
	      if (mant == 0)
		{
		  mant |= 0x00800000;
		  exp = (long) exp - 0x01000000;
		}
	    }
	  tmsfloat = exp | mant;
	  value = tmsfloat;
	}
      if (prec == 2)
	{
	  long expon, mantis;

	  if (tmsfloat == 0x80000000)
	    value = 0x8000;
	  else
	    {
	      value = 0;
	      expon = (tmsfloat & 0xFF000000);
	      expon >>= 24;
	      mantis = tmsfloat & 0x007FFFFF;
	      if (tmsfloat & 0x00800000)
		{
		  mantis |= 0xFF000000;
		  mantis += 0x00000800;
		  mantis >>= 12;
		  mantis |= 0x00000800;
		  mantis &= 0x0FFF;
		  if (expon > 7)
		    value = 0x7800;
		}
	      else
		{
		  mantis |= 0x00800000;
		  mantis += 0x00000800;
		  expon += (mantis >> 24);
		  mantis >>= 12;
		  mantis &= 0x07FF;
		  if (expon > 7)
		    value = 0x77FF;
		}
	      if (expon < -8)
		value = 0x8000;
	      if (value == 0)
		{
		  mantis = (expon << 12) | mantis;
		  value = mantis & 0xFFFF;
		}
	    }
	}
    }
  md_number_to_chars (literalP, value, prec);
  *sizeP = prec;
  return NULL;
}

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

#define F(SZ,PCREL)		(((SZ) << 1) + (PCREL))
#define MAP(SZ,PCREL,TYPE)	case F(SZ,PCREL): code = (TYPE); break

arelent *
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
{
  arelent *rel;
  bfd_reloc_code_real_type code = 0;

  debug ("In tc_gen_reloc()\n");
  debug ("fixP.size = %d\n", fixP->fx_size);
  debug ("fixP.pcrel = %d\n", fixP->fx_pcrel);
  debug ("addsy.name = %s\n", S_GET_NAME (fixP->fx_addsy));

  switch (F (fixP->fx_size, fixP->fx_pcrel))
    {
      MAP (1, 0, BFD_RELOC_TIC30_LDP);
      MAP (2, 0, BFD_RELOC_16);
      MAP (3, 0, BFD_RELOC_24);
      MAP (2, 1, BFD_RELOC_16_PCREL);
      MAP (4, 0, BFD_RELOC_32);
    default:
      as_bad (_("Can not do %d byte %srelocation"), fixP->fx_size,
	      fixP->fx_pcrel ? _("pc-relative ") : "");
    }
#undef MAP
#undef F

  rel = XNEW (arelent);
  gas_assert (rel != 0);
  rel->sym_ptr_ptr = XNEW (asymbol *);
  *rel->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
  rel->address = fixP->fx_frag->fr_address + fixP->fx_where;
  rel->addend = 0;
  rel->howto = bfd_reloc_type_lookup (stdoutput, code);
  if (!rel->howto)
    {
      const char *name;

      name = S_GET_NAME (fixP->fx_addsy);
      if (name == NULL)
	name = "<unknown>";
      as_fatal ("Cannot generate relocation type for symbol %s, code %s",
		name, bfd_get_reloc_code_name (code));
    }
  return rel;
}

void
md_operand (expressionS *expressionP ATTRIBUTE_UNUSED)
{
  debug ("In md_operand()\n");
}

void
md_assemble (char *line)
{
  insn_template *op;
  char *current_posn;
  char *token_start;
  char save_char;
  unsigned int count;

  debug ("In md_assemble() with argument %s\n", line);
  memset (&insn, '\0', sizeof (insn));
  if (found_parallel_insn)
    {
      debug ("Line is second part of parallel instruction\n\n");
      found_parallel_insn = 0;
      return;
    }
  if ((current_posn =
       tic30_find_parallel_insn (line, input_line_pointer + 1)) == NULL)
    current_posn = line;
  else
    found_parallel_insn = 1;

  while (is_space_char (*current_posn))
    current_posn++;

  token_start = current_posn;

  if (!is_opcode_char (*current_posn))
    {
      as_bad (_("Invalid character %s in opcode"),
	      output_invalid (*current_posn));
      return;
    }
  /* Check if instruction is a parallel instruction
     by seeing if the first character is a q.  */
  if (*token_start == 'q')
    {
      if (tic30_parallel_insn (token_start))
	{
	  if (found_parallel_insn)
	    free (token_start);
	  return;
	}
    }
  while (is_opcode_char (*current_posn))
    current_posn++;
  {
    /* Find instruction.  */
    save_char = *current_posn;
    *current_posn = '\0';
    op = (insn_template *) hash_find (op_hash, token_start);
    if (op)
      {
	debug ("Found instruction %s\n", op->name);
	insn.tm = op;
      }
    else
      {
	debug ("Didn't find insn\n");
	as_bad (_("Unknown TMS320C30 instruction: %s"), token_start);
	return;
      }
    *current_posn = save_char;
  }

  if (*current_posn != END_OF_INSN)
    {
      /* Find operands.  */
      int paren_not_balanced;
      int expecting_operand = 0;
      int this_operand;
      do
	{
	  /* Skip optional white space before operand.  */
	  while (!is_operand_char (*current_posn)
		 && *current_posn != END_OF_INSN)
	    {
	      if (!is_space_char (*current_posn))
		{
		  as_bad (_("Invalid character %s before %s operand"),
			  output_invalid (*current_posn),
			  ordinal_names[insn.operands]);
		  return;
		}
	      current_posn++;
	    }
	  token_start = current_posn;
	  paren_not_balanced = 0;
	  while (paren_not_balanced || *current_posn != ',')
	    {
	      if (*current_posn == END_OF_INSN)
		{
		  if (paren_not_balanced)
		    {
		      as_bad (_("Unbalanced parenthesis in %s operand."),
			      ordinal_names[insn.operands]);
		      return;
		    }
		  else
		    break;
		}
	      else if (!is_operand_char (*current_posn)
		       && !is_space_char (*current_posn))
		{
		  as_bad (_("Invalid character %s in %s operand"),
			  output_invalid (*current_posn),
			  ordinal_names[insn.operands]);
		  return;
		}
	      if (*current_posn == '(')
		++paren_not_balanced;
	      if (*current_posn == ')')
		--paren_not_balanced;
	      current_posn++;
	    }
	  if (current_posn != token_start)
	    {
	      /* Yes, we've read in another operand.  */
	      this_operand = insn.operands++;
	      if (insn.operands > MAX_OPERANDS)
		{
		  as_bad (_("Spurious operands; (%d operands/instruction max)"),
			  MAX_OPERANDS);
		  return;
		}

	      /* Now parse operand adding info to 'insn' as we go along.  */
	      save_char = *current_posn;
	      *current_posn = '\0';
	      insn.operand_type[this_operand] = tic30_operand (token_start);
	      *current_posn = save_char;
	      if (insn.operand_type[this_operand] == NULL)
		return;
	    }
	  else
	    {
	      if (expecting_operand)
		{
		  as_bad (_("Expecting operand after ','; got nothing"));
		  return;
		}
	      if (*current_posn == ',')
		{
		  as_bad (_("Expecting operand before ','; got nothing"));
		  return;
		}
	    }

	  /* Now *current_posn must be either ',' or END_OF_INSN.  */
	  if (*current_posn == ',')
	    {
	      if (*++current_posn == END_OF_INSN)
		{
		  /* Just skip it, if it's \n complain.  */
		  as_bad (_("Expecting operand after ','; got nothing"));
		  return;
		}
	      expecting_operand = 1;
	    }
	}
      while (*current_posn != END_OF_INSN);
    }

  debug ("Number of operands found: %d\n", insn.operands);

  /* Check that number of operands is correct.  */
  if (insn.operands != insn.tm->operands)
    {
      unsigned int i;
      unsigned int numops = insn.tm->operands;

      /* If operands are not the same, then see if any of the operands are
	 not required.  Then recheck with number of given operands.  If they
	 are still not the same, then give an error, otherwise carry on.  */
      for (i = 0; i < insn.tm->operands; i++)
	if (insn.tm->operand_types[i] & NotReq)
	  numops--;
      if (insn.operands != numops)
	{
	  as_bad (_("Incorrect number of operands given"));
	  return;
	}
    }
  insn.addressing_mode = AM_NotReq;
  for (count = 0; count < insn.operands; count++)
    {
      if (insn.operand_type[count]->op_type & insn.tm->operand_types[count])
	{
	  debug ("Operand %d matches\n", count + 1);
	  /* If instruction has two operands and has an AddressMode
	     modifier then set addressing mode type for instruction.  */
	  if (insn.tm->opcode_modifier == AddressMode)
	    {
	      int addr_insn = 0;
	      /* Store instruction uses the second
		 operand for the address mode.  */
	      if ((insn.tm->operand_types[1] & (Indirect | Direct))
		  == (Indirect | Direct))
		addr_insn = 1;

	      if (insn.operand_type[addr_insn]->op_type & (AllReg))
		insn.addressing_mode = AM_Register;
	      else if (insn.operand_type[addr_insn]->op_type & Direct)
		insn.addressing_mode = AM_Direct;
	      else if (insn.operand_type[addr_insn]->op_type & Indirect)
		insn.addressing_mode = AM_Indirect;
	      else
		insn.addressing_mode = AM_Immediate;
	    }
	}
      else
	{
	  as_bad (_("The %s operand doesn't match"), ordinal_names[count]);
	  return;
	}
    }

  /* Now set the addressing mode for 3 operand instructions.  */
  if ((insn.tm->operand_types[0] & op3T1)
      && (insn.tm->operand_types[1] & op3T2))
    {
      /* Set the addressing mode to the values used for 2 operand
	 instructions in the  G addressing field of the opcode.  */
      char *p;
      switch (insn.operand_type[0]->op_type)
	{
	case Rn:
	case ARn:
	case DPReg:
	case OtherReg:
	  if (insn.operand_type[1]->op_type & (AllReg))
	    insn.addressing_mode = AM_Register;
	  else if (insn.operand_type[1]->op_type & Indirect)
	    insn.addressing_mode = AM_Direct;
	  else
	    {
	      /* Shouldn't make it to this stage.  */
	      as_bad (_("Incompatible first and second operands in instruction"));
	      return;
	    }
	  break;
	case Indirect:
	  if (insn.operand_type[1]->op_type & (AllReg))
	    insn.addressing_mode = AM_Indirect;
	  else if (insn.operand_type[1]->op_type & Indirect)
	    insn.addressing_mode = AM_Immediate;
	  else
	    {
	      /* Shouldn't make it to this stage.  */
	      as_bad (_("Incompatible first and second operands in instruction"));
	      return;
	    }
	  break;
	}
      /* Now make up the opcode for the 3 operand instructions.  As in
	 parallel instructions, there will be no unresolved values, so they
	 can be fully formed and added to the frag table.  */
      insn.opcode = insn.tm->base_opcode;
      if (insn.operand_type[0]->op_type & Indirect)
	{
	  insn.opcode |= (insn.operand_type[0]->indirect.ARnum);
	  insn.opcode |= (insn.operand_type[0]->indirect.mod << 3);
	}
      else
	insn.opcode |= (insn.operand_type[0]->reg.opcode);

      if (insn.operand_type[1]->op_type & Indirect)
	{
	  insn.opcode |= (insn.operand_type[1]->indirect.ARnum << 8);
	  insn.opcode |= (insn.operand_type[1]->indirect.mod << 11);
	}
      else
	insn.opcode |= (insn.operand_type[1]->reg.opcode << 8);

      if (insn.operands == 3)
	insn.opcode |= (insn.operand_type[2]->reg.opcode << 16);

      insn.opcode |= insn.addressing_mode;
      p = frag_more (INSN_SIZE);
      md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
    }
  else
    {
      /* Not a three operand instruction.  */
      char *p;
      int am_insn = -1;
      insn.opcode = insn.tm->base_opcode;
      /* Create frag for instruction - all instructions are 4 bytes long.  */
      p = frag_more (INSN_SIZE);
      if ((insn.operands > 0) && (insn.tm->opcode_modifier == AddressMode))
	{
	  insn.opcode |= insn.addressing_mode;
	  if (insn.addressing_mode == AM_Indirect)
	    {
	      /* Determine which operand gives the addressing mode.  */
	      if (insn.operand_type[0]->op_type & Indirect)
		am_insn = 0;
	      if ((insn.operands > 1)
		  && (insn.operand_type[1]->op_type & Indirect))
		am_insn = 1;
	      insn.opcode |= (insn.operand_type[am_insn]->indirect.disp);
	      insn.opcode |= (insn.operand_type[am_insn]->indirect.ARnum << 8);
	      insn.opcode |= (insn.operand_type[am_insn]->indirect.mod << 11);
	      if (insn.operands > 1)
		insn.opcode |= (insn.operand_type[!am_insn]->reg.opcode << 16);
	      md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
	    }
	  else if (insn.addressing_mode == AM_Register)
	    {
	      insn.opcode |= (insn.operand_type[0]->reg.opcode);
	      if (insn.operands > 1)
		insn.opcode |= (insn.operand_type[1]->reg.opcode << 16);
	      md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
	    }
	  else if (insn.addressing_mode == AM_Direct)
	    {
	      if (insn.operand_type[0]->op_type & Direct)
		am_insn = 0;
	      if ((insn.operands > 1)
		  && (insn.operand_type[1]->op_type & Direct))
		am_insn = 1;
	      if (insn.operands > 1)
		insn.opcode |=
		  (insn.operand_type[! am_insn]->reg.opcode << 16);
	      if (insn.operand_type[am_insn]->direct.resolved == 1)
		{
		  /* Resolved values can be placed straight
		     into instruction word, and output.  */
		  insn.opcode |=
		    (insn.operand_type[am_insn]->direct.address & 0x0000FFFF);
		  md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		}
	      else
		{
		  /* Unresolved direct addressing mode instruction.  */
		  md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		  fix_new_exp (frag_now, p + 2 - (frag_now->fr_literal), 2,
			       & insn.operand_type[am_insn]->direct.direct_expr,
			       0, 0);
		}
	    }
	  else if (insn.addressing_mode == AM_Immediate)
	    {
	      if (insn.operand_type[0]->immediate.resolved == 1)
		{
		  char *keeploc;
		  int size;

		  if (insn.operands > 1)
		    insn.opcode |= (insn.operand_type[1]->reg.opcode << 16);

		  switch (insn.tm->imm_arg_type)
		    {
		    case Imm_Float:
		      debug ("Floating point first operand\n");
		      md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);

		      keeploc = input_line_pointer;
		      input_line_pointer =
			insn.operand_type[0]->immediate.label;

		      if (md_atof ('f', p + 2, & size) != 0)
			{
			  as_bad (_("invalid short form floating point immediate operand"));
			  return;
			}

		      input_line_pointer = keeploc;
		      break;

		    case Imm_UInt:
		      debug ("Unsigned int first operand\n");
		      if (insn.operand_type[0]->immediate.decimal_found)
			as_warn (_("rounding down first operand float to unsigned int"));
		      if (insn.operand_type[0]->immediate.u_number > 0xFFFF)
			as_warn (_("only lower 16-bits of first operand are used"));
		      insn.opcode |=
			(insn.operand_type[0]->immediate.u_number & 0x0000FFFFL);
		      md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		      break;

		    case Imm_SInt:
		      debug ("Int first operand\n");

		      if (insn.operand_type[0]->immediate.decimal_found)
			as_warn (_("rounding down first operand float to signed int"));

		      if (insn.operand_type[0]->immediate.s_number < -32768 ||
			  insn.operand_type[0]->immediate.s_number > 32767)
			{
			  as_bad (_("first operand is too large for 16-bit signed int"));
			  return;
			}
		      insn.opcode |=
			(insn.operand_type[0]->immediate.s_number & 0x0000FFFFL);
		      md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		      break;
		    }
		}
	      else
		{
		  /* Unresolved immediate label.  */
		  if (insn.operands > 1)
		    insn.opcode |= (insn.operand_type[1]->reg.opcode << 16);
		  md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		  fix_new_exp (frag_now, p + 2 - (frag_now->fr_literal), 2,
			       & insn.operand_type[0]->immediate.imm_expr,
			       0, 0);
		}
	    }
	}
      else if (insn.tm->opcode_modifier == PCRel)
	{
	  /* Conditional Branch and Call instructions.  */
	  if ((insn.tm->operand_types[0] & (AllReg | Disp))
	      == (AllReg | Disp))
	    {
	      if (insn.operand_type[0]->op_type & (AllReg))
		{
		  insn.opcode |= (insn.operand_type[0]->reg.opcode);
		  insn.opcode |= PC_Register;
		  md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		}
	      else
		{
		  insn.opcode |= PC_Relative;
		  if (insn.operand_type[0]->immediate.resolved == 1)
		    {
		      insn.opcode |=
			(insn.operand_type[0]->immediate.s_number & 0x0000FFFF);
		      md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		    }
		  else
		    {
		      md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		      fix_new_exp (frag_now, p + 2 - (frag_now->fr_literal),
				   2, & insn.operand_type[0]->immediate.imm_expr,
				   1, 0);
		    }
		}
	    }
	  else if ((insn.tm->operand_types[0] & ARn) == ARn)
	    {
	      /* Decrement and Branch instructions.  */
	      insn.opcode |= ((insn.operand_type[0]->reg.opcode - 0x08) << 22);
	      if (insn.operand_type[1]->op_type & (AllReg))
		{
		  insn.opcode |= (insn.operand_type[1]->reg.opcode);
		  insn.opcode |= PC_Register;
		  md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		}
	      else if (insn.operand_type[1]->immediate.resolved == 1)
		{
		  if (insn.operand_type[0]->immediate.decimal_found)
		    {
		      as_bad (_("first operand is floating point"));
		      return;
		    }
		  if (insn.operand_type[0]->immediate.s_number < -32768 ||
		      insn.operand_type[0]->immediate.s_number > 32767)
		    {
		      as_bad (_("first operand is too large for 16-bit signed int"));
		      return;
		    }
		  insn.opcode |= (insn.operand_type[1]->immediate.s_number);
		  insn.opcode |= PC_Relative;
		  md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		}
	      else
		{
		  insn.opcode |= PC_Relative;
		  md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		  fix_new_exp (frag_now, p + 2 - frag_now->fr_literal, 2,
			       & insn.operand_type[1]->immediate.imm_expr,
			       1, 0);
		}
	    }
	}
      else if (insn.tm->operand_types[0] == IVector)
	{
	  /* Trap instructions.  */
	  if (insn.operand_type[0]->op_type & IVector)
	    insn.opcode |= (insn.operand_type[0]->immediate.u_number);
	  else
	    {
	      /* Shouldn't get here.  */
	      as_bad (_("interrupt vector for trap instruction out of range"));
	      return;
	    }
	  md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
	}
      else if (insn.tm->opcode_modifier == StackOp
	       || insn.tm->opcode_modifier == Rotate)
	{
	  /* Push, Pop and Rotate instructions.  */
	  insn.opcode |= (insn.operand_type[0]->reg.opcode << 16);
	  md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
	}
      else if ((insn.tm->operand_types[0] & (Abs24 | Direct))
	       == (Abs24 | Direct))
	{
	  /* LDP Instruction needs to be tested
	     for before the next section.  */
	  if (insn.operand_type[0]->op_type & Direct)
	    {
	      if (insn.operand_type[0]->direct.resolved == 1)
		{
		  /* Direct addressing uses lower 8 bits of direct address.  */
		  insn.opcode |=
		    (insn.operand_type[0]->direct.address & 0x00FF0000) >> 16;
		  md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		}
	      else
		{
		  fixS *fix;

		  md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		  fix = fix_new_exp (frag_now, p + 3 - (frag_now->fr_literal),
				     1, &insn.operand_type[0]->direct.direct_expr, 0, 0);
		  /* Ensure that the assembler doesn't complain
		     about fitting a 24-bit address into 8 bits.  */
		  fix->fx_no_overflow = 1;
		}
	    }
	  else
	    {
	      if (insn.operand_type[0]->immediate.resolved == 1)
		{
		  /* Immediate addressing uses upper 8 bits of address.  */
		  if (insn.operand_type[0]->immediate.u_number > 0x00FFFFFF)
		    {
		      as_bad (_("LDP instruction needs a 24-bit operand"));
		      return;
		    }
		  insn.opcode |=
		    ((insn.operand_type[0]->immediate.u_number & 0x00FF0000) >> 16);
		  md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		}
	      else
		{
		  fixS *fix;
		  md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
		  fix = fix_new_exp (frag_now, p + 3 - (frag_now->fr_literal),
				     1, &insn.operand_type[0]->immediate.imm_expr,
				     0, 0);
		  fix->fx_no_overflow = 1;
		}
	    }
	}
      else if (insn.tm->operand_types[0] & (Imm24))
	{
	  /* Unconditional Branch and Call instructions.  */
	  if (insn.operand_type[0]->immediate.resolved == 1)
	    {
	      if (insn.operand_type[0]->immediate.u_number > 0x00FFFFFF)
		as_warn (_("first operand is too large for a 24-bit displacement"));
	      insn.opcode |=
		(insn.operand_type[0]->immediate.u_number & 0x00FFFFFF);
	      md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
	    }
	  else
	    {
	      md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
	      fix_new_exp (frag_now, p + 1 - (frag_now->fr_literal), 3,
			   & insn.operand_type[0]->immediate.imm_expr, 0, 0);
	    }
	}
      else if (insn.tm->operand_types[0] & NotReq)
	/* Check for NOP instruction without arguments.  */
	md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);

      else if (insn.tm->operands == 0)
	/* Check for instructions without operands.  */
	md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
    }
  debug ("Addressing mode: %08X\n", insn.addressing_mode);
  {
    unsigned int i;

    for (i = 0; i < insn.operands; i++)
      {
	free (insn.operand_type[i]->immediate.label);
	free (insn.operand_type[i]);
      }
  }
  debug ("Final opcode: %08X\n", insn.opcode);
  debug ("\n");
}
