/* tc-mn10300.c -- Assembler code for the Matsushita 10300
   Copyright 1996, 1997, 1998, 1999, 2000, 2001
   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 <stdio.h>
#include "as.h"
#include "safe-ctype.h"
#include "subsegs.h"
#include "opcode/mn10300.h"
#include "dwarf2dbg.h"

/* Structure to hold information about predefined registers.  */
struct reg_name
{
  const char *name;
  int value;
};

/* Generic assembler global variables which must be defined by all
   targets.  */

/* Characters which always start a comment.  */
const char comment_chars[] = "#";

/* Characters which start a comment at the beginning of a line.  */
const char line_comment_chars[] = ";#";

/* Characters which may be used to separate multiple commands on a
   single line.  */
const char line_separator_chars[] = ";";

/* Characters which are used to indicate an exponent in a floating
   point number.  */
const char EXP_CHARS[] = "eE";

/* Characters which mean that a number is a floating point constant,
   as in 0d1.0.  */
const char FLT_CHARS[] = "dD";

const relax_typeS md_relax_table[] = {
  /* bCC relaxing  */
  {0x7f, -0x80, 2, 1},
  {0x7fff, -0x8000, 5, 2},
  {0x7fffffff, -0x80000000, 7, 0},

  /* bCC relaxing (uncommon cases)  */
  {0x7f, -0x80, 3, 4},
  {0x7fff, -0x8000, 6, 5},
  {0x7fffffff, -0x80000000, 8, 0},

  /* call relaxing  */
  {0x7fff, -0x8000, 5, 7},
  {0x7fffffff, -0x80000000, 7, 0},

  /* calls relaxing  */
  {0x7fff, -0x8000, 4, 9},
  {0x7fffffff, -0x80000000, 6, 0},

  /* jmp relaxing  */
  {0x7f, -0x80, 2, 11},
  {0x7fff, -0x8000, 3, 12},
  {0x7fffffff, -0x80000000, 5, 0},

};

/* Local functions.  */
static void mn10300_insert_operand PARAMS ((unsigned long *, unsigned long *,
					    const struct mn10300_operand *,
					    offsetT, char *, unsigned,
					    unsigned));
static unsigned long check_operand PARAMS ((unsigned long,
					    const struct mn10300_operand *,
					    offsetT));
static int reg_name_search PARAMS ((const struct reg_name *, int, const char *));
static boolean data_register_name PARAMS ((expressionS *expressionP));
static boolean address_register_name PARAMS ((expressionS *expressionP));
static boolean other_register_name PARAMS ((expressionS *expressionP));
static void set_arch_mach PARAMS ((unsigned int));

/*  Set linkrelax here to avoid fixups in most sections.  */
int linkrelax = 1;

static int current_machine;

/* Fixups.  */
#define MAX_INSN_FIXUPS (5)
struct mn10300_fixup
{
  expressionS exp;
  int opindex;
  bfd_reloc_code_real_type reloc;
};
struct mn10300_fixup fixups[MAX_INSN_FIXUPS];
static int fc;

/* We must store the value of each register operand so that we can
   verify that certain registers do not match.  */
int mn10300_reg_operands[MN10300_MAX_OPERANDS];

const char *md_shortopts = "";
struct option md_longopts[] = {
  {NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);

/* The target specific pseudo-ops which we support.  */
const pseudo_typeS md_pseudo_table[] =
{
  { "file",     dwarf2_directive_file,  0 },
  { "loc",      dwarf2_directive_loc,   0 },
  { "am30",	set_arch_mach,		AM30 },
  { "am33",	set_arch_mach,		AM33 },
  { "mn10300",	set_arch_mach,		MN103 },
  {NULL, 0, 0}
};

#define HAVE_AM33 (current_machine == AM33)
#define HAVE_AM30 (current_machine == AM30)

/* Opcode hash table.  */
static struct hash_control *mn10300_hash;

/* This table is sorted. Suitable for searching by a binary search.  */
static const struct reg_name data_registers[] =
{
  { "d0", 0 },
  { "d1", 1 },
  { "d2", 2 },
  { "d3", 3 },
};
#define DATA_REG_NAME_CNT				\
  (sizeof (data_registers) / sizeof (struct reg_name))

static const struct reg_name address_registers[] =
{
  { "a0", 0 },
  { "a1", 1 },
  { "a2", 2 },
  { "a3", 3 },
};

#define ADDRESS_REG_NAME_CNT					\
  (sizeof (address_registers) / sizeof (struct reg_name))

static const struct reg_name r_registers[] =
{
  { "a0", 8 },
  { "a1", 9 },
  { "a2", 10 },
  { "a3", 11 },
  { "d0", 12 },
  { "d1", 13 },
  { "d2", 14 },
  { "d3", 15 },
  { "e0", 0 },
  { "e1", 1 },
  { "e10", 10 },
  { "e11", 11 },
  { "e12", 12 },
  { "e13", 13 },
  { "e14", 14 },
  { "e15", 15 },
  { "e2", 2 },
  { "e3", 3 },
  { "e4", 4 },
  { "e5", 5 },
  { "e6", 6 },
  { "e7", 7 },
  { "e8", 8 },
  { "e9", 9 },
  { "r0", 0 },
  { "r1", 1 },
  { "r10", 10 },
  { "r11", 11 },
  { "r12", 12 },
  { "r13", 13 },
  { "r14", 14 },
  { "r15", 15 },
  { "r2", 2 },
  { "r3", 3 },
  { "r4", 4 },
  { "r5", 5 },
  { "r6", 6 },
  { "r7", 7 },
  { "r8", 8 },
  { "r9", 9 },
};

#define R_REG_NAME_CNT					\
  (sizeof (r_registers) / sizeof (struct reg_name))

static const struct reg_name xr_registers[] =
{
  { "mcrh", 2 },
  { "mcrl", 3 },
  { "mcvf", 4 },
  { "mdrq", 1 },
  { "pc", 0 },
  { "sp", 0 },
  { "xr0", 0 },
  { "xr1", 1 },
  { "xr10", 10 },
  { "xr11", 11 },
  { "xr12", 12 },
  { "xr13", 13 },
  { "xr14", 14 },
  { "xr15", 15 },
  { "xr2", 2 },
  { "xr3", 3 },
  { "xr4", 4 },
  { "xr5", 5 },
  { "xr6", 6 },
  { "xr7", 7 },
  { "xr8", 8 },
  { "xr9", 9 },
};

#define XR_REG_NAME_CNT					\
  (sizeof (xr_registers) / sizeof (struct reg_name))

static const struct reg_name other_registers[] =
{
  { "mdr", 0 },
  { "psw", 0 },
  { "sp", 0 },
};

#define OTHER_REG_NAME_CNT				\
  (sizeof (other_registers) / sizeof (struct reg_name))

/* reg_name_search does a binary search of the given register table
   to see if "name" is a valid regiter name.  Returns the register
   number from the array on success, or -1 on failure.  */

static int
reg_name_search (regs, regcount, name)
     const struct reg_name *regs;
     int regcount;
     const char *name;
{
  int middle, low, high;
  int cmp;

  low = 0;
  high = regcount - 1;

  do
    {
      middle = (low + high) / 2;
      cmp = strcasecmp (name, regs[middle].name);
      if (cmp < 0)
	high = middle - 1;
      else if (cmp > 0)
	low = middle + 1;
      else
	return regs[middle].value;
    }
  while (low <= high);
  return -1;
}

/* Summary of register_name().
 *
 * in: Input_line_pointer points to 1st char of operand.
 *
 * out: An expressionS.
 *	The operand may have been a register: in this case, X_op == O_register,
 *	X_add_number is set to the register number, and truth is returned.
 *	Input_line_pointer->(next non-blank) char after operand, or is in
 *	its original state.
 */

static boolean
r_register_name (expressionP)
     expressionS *expressionP;
{
  int reg_number;
  char *name;
  char *start;
  char c;

  /* Find the spelling of the operand.  */
  start = name = input_line_pointer;

  c = get_symbol_end ();
  reg_number = reg_name_search (r_registers, R_REG_NAME_CNT, name);

  /* Put back the delimiting char.  */
  *input_line_pointer = c;

  /* Look to see if it's in the register table.  */
  if (reg_number >= 0)
    {
      expressionP->X_op = O_register;
      expressionP->X_add_number = reg_number;

      /* Make the rest nice.  */
      expressionP->X_add_symbol = NULL;
      expressionP->X_op_symbol = NULL;

      return true;
    }

  /* Reset the line as if we had not done anything.  */
  input_line_pointer = start;
  return false;
}

/* Summary of register_name().
 *
 * in: Input_line_pointer points to 1st char of operand.
 *
 * out: An expressionS.
 *	The operand may have been a register: in this case, X_op == O_register,
 *	X_add_number is set to the register number, and truth is returned.
 *	Input_line_pointer->(next non-blank) char after operand, or is in
 *	its original state.
 */

static boolean
xr_register_name (expressionP)
     expressionS *expressionP;
{
  int reg_number;
  char *name;
  char *start;
  char c;

  /* Find the spelling of the operand.  */
  start = name = input_line_pointer;

  c = get_symbol_end ();
  reg_number = reg_name_search (xr_registers, XR_REG_NAME_CNT, name);

  /* Put back the delimiting char.  */
  *input_line_pointer = c;

  /* Look to see if it's in the register table.  */
  if (reg_number >= 0)
    {
      expressionP->X_op = O_register;
      expressionP->X_add_number = reg_number;

      /* Make the rest nice.  */
      expressionP->X_add_symbol = NULL;
      expressionP->X_op_symbol = NULL;

      return true;
    }

  /* Reset the line as if we had not done anything.  */
  input_line_pointer = start;
  return false;
}

/* Summary of register_name().
 *
 * in: Input_line_pointer points to 1st char of operand.
 *
 * out: An expressionS.
 *	The operand may have been a register: in this case, X_op == O_register,
 *	X_add_number is set to the register number, and truth is returned.
 *	Input_line_pointer->(next non-blank) char after operand, or is in
 *	its original state.
 */

static boolean
data_register_name (expressionP)
     expressionS *expressionP;
{
  int reg_number;
  char *name;
  char *start;
  char c;

  /* Find the spelling of the operand.  */
  start = name = input_line_pointer;

  c = get_symbol_end ();
  reg_number = reg_name_search (data_registers, DATA_REG_NAME_CNT, name);

  /* Put back the delimiting char.  */
  *input_line_pointer = c;

  /* Look to see if it's in the register table.  */
  if (reg_number >= 0)
    {
      expressionP->X_op = O_register;
      expressionP->X_add_number = reg_number;

      /* Make the rest nice.  */
      expressionP->X_add_symbol = NULL;
      expressionP->X_op_symbol = NULL;

      return true;
    }

  /* Reset the line as if we had not done anything.  */
  input_line_pointer = start;
  return false;
}

/* Summary of register_name().
 *
 * in: Input_line_pointer points to 1st char of operand.
 *
 * out: An expressionS.
 *	The operand may have been a register: in this case, X_op == O_register,
 *	X_add_number is set to the register number, and truth is returned.
 *	Input_line_pointer->(next non-blank) char after operand, or is in
 *	its original state.
 */

static boolean
address_register_name (expressionP)
     expressionS *expressionP;
{
  int reg_number;
  char *name;
  char *start;
  char c;

  /* Find the spelling of the operand.  */
  start = name = input_line_pointer;

  c = get_symbol_end ();
  reg_number = reg_name_search (address_registers, ADDRESS_REG_NAME_CNT, name);

  /* Put back the delimiting char.  */
  *input_line_pointer = c;

  /* Look to see if it's in the register table.  */
  if (reg_number >= 0)
    {
      expressionP->X_op = O_register;
      expressionP->X_add_number = reg_number;

      /* Make the rest nice.  */
      expressionP->X_add_symbol = NULL;
      expressionP->X_op_symbol = NULL;

      return true;
    }

  /* Reset the line as if we had not done anything.  */
  input_line_pointer = start;
  return false;
}

/* Summary of register_name().
 *
 * in: Input_line_pointer points to 1st char of operand.
 *
 * out: An expressionS.
 *	The operand may have been a register: in this case, X_op == O_register,
 *	X_add_number is set to the register number, and truth is returned.
 *	Input_line_pointer->(next non-blank) char after operand, or is in
 *	its original state.
 */

static boolean
other_register_name (expressionP)
     expressionS *expressionP;
{
  int reg_number;
  char *name;
  char *start;
  char c;

  /* Find the spelling of the operand.  */
  start = name = input_line_pointer;

  c = get_symbol_end ();
  reg_number = reg_name_search (other_registers, OTHER_REG_NAME_CNT, name);

  /* Put back the delimiting char.  */
  *input_line_pointer = c;

  /* Look to see if it's in the register table.  */
  if (reg_number >= 0)
    {
      expressionP->X_op = O_register;
      expressionP->X_add_number = reg_number;

      /* Make the rest nice.  */
      expressionP->X_add_symbol = NULL;
      expressionP->X_op_symbol = NULL;

      return true;
    }

  /* Reset the line as if we had not done anything.  */
  input_line_pointer = start;
  return false;
}

void
md_show_usage (stream)
     FILE *stream;
{
  fprintf (stream, _("MN10300 options:\n\
none yet\n"));
}

int
md_parse_option (c, arg)
     int c ATTRIBUTE_UNUSED;
     char *arg ATTRIBUTE_UNUSED;
{
  return 0;
}

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

char *
md_atof (type, litp, sizep)
     int type;
     char *litp;
     int *sizep;
{
  int prec;
  LITTLENUM_TYPE words[4];
  char *t;
  int i;

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

    case 'd':
      prec = 4;
      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 * 2;

  for (i = prec - 1; i >= 0; i--)
    {
      md_number_to_chars (litp, (valueT) words[i], 2);
      litp += 2;
    }

  return NULL;
}

void
md_convert_frag (abfd, sec, fragP)
     bfd *abfd ATTRIBUTE_UNUSED;
     asection *sec;
     fragS *fragP;
{
  static unsigned long label_count = 0;
  char buf[40];

  subseg_change (sec, 0);
  if (fragP->fr_subtype == 0)
    {
      fix_new (fragP, fragP->fr_fix + 1, 1, fragP->fr_symbol,
	       fragP->fr_offset + 1, 1, BFD_RELOC_8_PCREL);
      fragP->fr_var = 0;
      fragP->fr_fix += 2;
    }
  else if (fragP->fr_subtype == 1)
    {
      /* Reverse the condition of the first branch.  */
      int offset = fragP->fr_fix;
      int opcode = fragP->fr_literal[offset] & 0xff;

      switch (opcode)
	{
	case 0xc8:
	  opcode = 0xc9;
	  break;
	case 0xc9:
	  opcode = 0xc8;
	  break;
	case 0xc0:
	  opcode = 0xc2;
	  break;
	case 0xc2:
	  opcode = 0xc0;
	  break;
	case 0xc3:
	  opcode = 0xc1;
	  break;
	case 0xc1:
	  opcode = 0xc3;
	  break;
	case 0xc4:
	  opcode = 0xc6;
	  break;
	case 0xc6:
	  opcode = 0xc4;
	  break;
	case 0xc7:
	  opcode = 0xc5;
	  break;
	case 0xc5:
	  opcode = 0xc7;
	  break;
	default:
	  abort ();
	}
      fragP->fr_literal[offset] = opcode;

      /* Create a fixup for the reversed conditional branch.  */
      sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
      fix_new (fragP, fragP->fr_fix + 1, 1,
	       symbol_new (buf, sec, 0, fragP->fr_next),
	       fragP->fr_offset + 1, 1, BFD_RELOC_8_PCREL);

      /* Now create the unconditional branch + fixup to the
	 final target.  */
      fragP->fr_literal[offset + 2] = 0xcc;
      fix_new (fragP, fragP->fr_fix + 3, 2, fragP->fr_symbol,
	       fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
      fragP->fr_var = 0;
      fragP->fr_fix += 5;
    }
  else if (fragP->fr_subtype == 2)
    {
      /* Reverse the condition of the first branch.  */
      int offset = fragP->fr_fix;
      int opcode = fragP->fr_literal[offset] & 0xff;

      switch (opcode)
	{
	case 0xc8:
	  opcode = 0xc9;
	  break;
	case 0xc9:
	  opcode = 0xc8;
	  break;
	case 0xc0:
	  opcode = 0xc2;
	  break;
	case 0xc2:
	  opcode = 0xc0;
	  break;
	case 0xc3:
	  opcode = 0xc1;
	  break;
	case 0xc1:
	  opcode = 0xc3;
	  break;
	case 0xc4:
	  opcode = 0xc6;
	  break;
	case 0xc6:
	  opcode = 0xc4;
	  break;
	case 0xc7:
	  opcode = 0xc5;
	  break;
	case 0xc5:
	  opcode = 0xc7;
	  break;
	default:
	  abort ();
	}
      fragP->fr_literal[offset] = opcode;

      /* Create a fixup for the reversed conditional branch.  */
      sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
      fix_new (fragP, fragP->fr_fix + 1, 1,
	       symbol_new (buf, sec, 0, fragP->fr_next),
	       fragP->fr_offset + 1, 1, BFD_RELOC_8_PCREL);

      /* Now create the unconditional branch + fixup to the
	 final target.  */
      fragP->fr_literal[offset + 2] = 0xdc;
      fix_new (fragP, fragP->fr_fix + 3, 4, fragP->fr_symbol,
	       fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
      fragP->fr_var = 0;
      fragP->fr_fix += 7;
    }
  else if (fragP->fr_subtype == 3)
    {
      fix_new (fragP, fragP->fr_fix + 2, 1, fragP->fr_symbol,
	       fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
      fragP->fr_var = 0;
      fragP->fr_fix += 3;
    }
  else if (fragP->fr_subtype == 4)
    {
      /* Reverse the condition of the first branch.  */
      int offset = fragP->fr_fix;
      int opcode = fragP->fr_literal[offset + 1] & 0xff;

      switch (opcode)
	{
	case 0xe8:
	  opcode = 0xe9;
	  break;
	case 0xe9:
	  opcode = 0xe8;
	  break;
	case 0xea:
	  opcode = 0xeb;
	  break;
	case 0xeb:
	  opcode = 0xea;
	  break;
	default:
	  abort ();
	}
      fragP->fr_literal[offset + 1] = opcode;

      /* Create a fixup for the reversed conditional branch.  */
      sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
      fix_new (fragP, fragP->fr_fix + 2, 1,
	       symbol_new (buf, sec, 0, fragP->fr_next),
	       fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);

      /* Now create the unconditional branch + fixup to the
	 final target.  */
      fragP->fr_literal[offset + 3] = 0xcc;
      fix_new (fragP, fragP->fr_fix + 4, 2, fragP->fr_symbol,
	       fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
      fragP->fr_var = 0;
      fragP->fr_fix += 6;
    }
  else if (fragP->fr_subtype == 5)
    {
      /* Reverse the condition of the first branch.  */
      int offset = fragP->fr_fix;
      int opcode = fragP->fr_literal[offset + 1] & 0xff;

      switch (opcode)
	{
	case 0xe8:
	  opcode = 0xe9;
	  break;
	case 0xea:
	  opcode = 0xeb;
	  break;
	case 0xeb:
	  opcode = 0xea;
	  break;
	default:
	  abort ();
	}
      fragP->fr_literal[offset + 1] = opcode;

      /* Create a fixup for the reversed conditional branch.  */
      sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
      fix_new (fragP, fragP->fr_fix + 2, 1,
	       symbol_new (buf, sec, 0, fragP->fr_next),
	       fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);

      /* Now create the unconditional branch + fixup to the
	 final target.  */
      fragP->fr_literal[offset + 3] = 0xdc;
      fix_new (fragP, fragP->fr_fix + 4, 4, fragP->fr_symbol,
	       fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
      fragP->fr_var = 0;
      fragP->fr_fix += 8;
    }
  else if (fragP->fr_subtype == 6)
    {
      int offset = fragP->fr_fix;
      fragP->fr_literal[offset] = 0xcd;
      fix_new (fragP, fragP->fr_fix + 1, 2, fragP->fr_symbol,
	       fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
      fragP->fr_var = 0;
      fragP->fr_fix += 5;
    }
  else if (fragP->fr_subtype == 7)
    {
      int offset = fragP->fr_fix;
      fragP->fr_literal[offset] = 0xdd;
      fragP->fr_literal[offset + 5] = fragP->fr_literal[offset + 3];
      fragP->fr_literal[offset + 6] = fragP->fr_literal[offset + 4];

      fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol,
	       fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
      fragP->fr_var = 0;
      fragP->fr_fix += 7;
    }
  else if (fragP->fr_subtype == 8)
    {
      int offset = fragP->fr_fix;
      fragP->fr_literal[offset] = 0xfa;
      fragP->fr_literal[offset + 1] = 0xff;
      fix_new (fragP, fragP->fr_fix + 2, 2, fragP->fr_symbol,
	       fragP->fr_offset + 2, 1, BFD_RELOC_16_PCREL);
      fragP->fr_var = 0;
      fragP->fr_fix += 4;
    }
  else if (fragP->fr_subtype == 9)
    {
      int offset = fragP->fr_fix;
      fragP->fr_literal[offset] = 0xfc;
      fragP->fr_literal[offset + 1] = 0xff;

      fix_new (fragP, fragP->fr_fix + 2, 4, fragP->fr_symbol,
	       fragP->fr_offset + 2, 1, BFD_RELOC_32_PCREL);
      fragP->fr_var = 0;
      fragP->fr_fix += 6;
    }
  else if (fragP->fr_subtype == 10)
    {
      fragP->fr_literal[fragP->fr_fix] = 0xca;
      fix_new (fragP, fragP->fr_fix + 1, 1, fragP->fr_symbol,
	       fragP->fr_offset + 1, 1, BFD_RELOC_8_PCREL);
      fragP->fr_var = 0;
      fragP->fr_fix += 2;
    }
  else if (fragP->fr_subtype == 11)
    {
      int offset = fragP->fr_fix;
      fragP->fr_literal[offset] = 0xcc;

      fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol,
	       fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
      fragP->fr_var = 0;
      fragP->fr_fix += 3;
    }
  else if (fragP->fr_subtype == 12)
    {
      int offset = fragP->fr_fix;
      fragP->fr_literal[offset] = 0xdc;

      fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol,
	       fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
      fragP->fr_var = 0;
      fragP->fr_fix += 5;
    }
  else
    abort ();
}

valueT
md_section_align (seg, addr)
     asection *seg;
     valueT addr;
{
  int align = bfd_get_section_alignment (stdoutput, seg);
  return ((addr + (1 << align) - 1) & (-1 << align));
}

void
md_begin ()
{
  char *prev_name = "";
  register const struct mn10300_opcode *op;

  mn10300_hash = hash_new ();

  /* Insert unique names into hash table.  The MN10300 instruction set
     has many identical opcode names that have different opcodes based
     on the operands.  This hash table then provides a quick index to
     the first opcode with a particular name in the opcode table.  */

  op = mn10300_opcodes;
  while (op->name)
    {
      if (strcmp (prev_name, op->name))
	{
	  prev_name = (char *) op->name;
	  hash_insert (mn10300_hash, op->name, (char *) op);
	}
      op++;
    }

  /* Set the default machine type.  */
  if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, MN103))
    as_warn (_("could not set architecture and machine"));

  current_machine = MN103;
}

void
md_assemble (str)
     char *str;
{
  char *s;
  struct mn10300_opcode *opcode;
  struct mn10300_opcode *next_opcode;
  const unsigned char *opindex_ptr;
  int next_opindex, relaxable;
  unsigned long insn, extension, size = 0;
  char *f;
  int i;
  int match;

  /* Get the opcode.  */
  for (s = str; *s != '\0' && !ISSPACE (*s); s++)
    ;
  if (*s != '\0')
    *s++ = '\0';

  /* Find the first opcode with the proper name.  */
  opcode = (struct mn10300_opcode *) hash_find (mn10300_hash, str);
  if (opcode == NULL)
    {
      as_bad (_("Unrecognized opcode: `%s'"), str);
      return;
    }

  str = s;
  while (ISSPACE (*str))
    ++str;

  input_line_pointer = str;

  for (;;)
    {
      const char *errmsg;
      int op_idx;
      char *hold;
      int extra_shift = 0;

      errmsg = _("Invalid opcode/operands");

      /* Reset the array of register operands.  */
      memset (mn10300_reg_operands, -1, sizeof (mn10300_reg_operands));

      relaxable = 0;
      fc = 0;
      match = 0;
      next_opindex = 0;
      insn = opcode->opcode;
      extension = 0;

      /* If the instruction is not available on the current machine
	 then it can not possibly match.  */
      if (opcode->machine
	  && !(opcode->machine == AM33 && HAVE_AM33)
	  && !(opcode->machine == AM30 && HAVE_AM30))
	goto error;

      for (op_idx = 1, opindex_ptr = opcode->operands;
	   *opindex_ptr != 0;
	   opindex_ptr++, op_idx++)
	{
	  const struct mn10300_operand *operand;
	  expressionS ex;

	  if (next_opindex == 0)
	    {
	      operand = &mn10300_operands[*opindex_ptr];
	    }
	  else
	    {
	      operand = &mn10300_operands[next_opindex];
	      next_opindex = 0;
	    }

	  while (*str == ' ' || *str == ',')
	    ++str;

	  if (operand->flags & MN10300_OPERAND_RELAX)
	    relaxable = 1;

	  /* Gather the operand.  */
	  hold = input_line_pointer;
	  input_line_pointer = str;

	  if (operand->flags & MN10300_OPERAND_PAREN)
	    {
	      if (*input_line_pointer != ')' && *input_line_pointer != '(')
		{
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}
	      input_line_pointer++;
	      goto keep_going;
	    }
	  /* See if we can match the operands.  */
	  else if (operand->flags & MN10300_OPERAND_DREG)
	    {
	      if (!data_register_name (&ex))
		{
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}
	    }
	  else if (operand->flags & MN10300_OPERAND_AREG)
	    {
	      if (!address_register_name (&ex))
		{
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}
	    }
	  else if (operand->flags & MN10300_OPERAND_SP)
	    {
	      char *start = input_line_pointer;
	      char c = get_symbol_end ();

	      if (strcasecmp (start, "sp") != 0)
		{
		  *input_line_pointer = c;
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}
	      *input_line_pointer = c;
	      goto keep_going;
	    }
	  else if (operand->flags & MN10300_OPERAND_RREG)
	    {
	      if (!r_register_name (&ex))
		{
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}
	    }
	  else if (operand->flags & MN10300_OPERAND_XRREG)
	    {
	      if (!xr_register_name (&ex))
		{
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}
	    }
	  else if (operand->flags & MN10300_OPERAND_USP)
	    {
	      char *start = input_line_pointer;
	      char c = get_symbol_end ();

	      if (strcasecmp (start, "usp") != 0)
		{
		  *input_line_pointer = c;
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}
	      *input_line_pointer = c;
	      goto keep_going;
	    }
	  else if (operand->flags & MN10300_OPERAND_SSP)
	    {
	      char *start = input_line_pointer;
	      char c = get_symbol_end ();

	      if (strcasecmp (start, "ssp") != 0)
		{
		  *input_line_pointer = c;
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}
	      *input_line_pointer = c;
	      goto keep_going;
	    }
	  else if (operand->flags & MN10300_OPERAND_MSP)
	    {
	      char *start = input_line_pointer;
	      char c = get_symbol_end ();

	      if (strcasecmp (start, "msp") != 0)
		{
		  *input_line_pointer = c;
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}
	      *input_line_pointer = c;
	      goto keep_going;
	    }
	  else if (operand->flags & MN10300_OPERAND_PC)
	    {
	      char *start = input_line_pointer;
	      char c = get_symbol_end ();

	      if (strcasecmp (start, "pc") != 0)
		{
		  *input_line_pointer = c;
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}
	      *input_line_pointer = c;
	      goto keep_going;
	    }
	  else if (operand->flags & MN10300_OPERAND_EPSW)
	    {
	      char *start = input_line_pointer;
	      char c = get_symbol_end ();

	      if (strcasecmp (start, "epsw") != 0)
		{
		  *input_line_pointer = c;
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}
	      *input_line_pointer = c;
	      goto keep_going;
	    }
	  else if (operand->flags & MN10300_OPERAND_PLUS)
	    {
	      if (*input_line_pointer != '+')
		{
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}
	      input_line_pointer++;
	      goto keep_going;
	    }
	  else if (operand->flags & MN10300_OPERAND_PSW)
	    {
	      char *start = input_line_pointer;
	      char c = get_symbol_end ();

	      if (strcasecmp (start, "psw") != 0)
		{
		  *input_line_pointer = c;
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}
	      *input_line_pointer = c;
	      goto keep_going;
	    }
	  else if (operand->flags & MN10300_OPERAND_MDR)
	    {
	      char *start = input_line_pointer;
	      char c = get_symbol_end ();

	      if (strcasecmp (start, "mdr") != 0)
		{
		  *input_line_pointer = c;
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}
	      *input_line_pointer = c;
	      goto keep_going;
	    }
	  else if (operand->flags & MN10300_OPERAND_REG_LIST)
	    {
	      unsigned int value = 0;
	      if (*input_line_pointer != '[')
		{
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}

	      /* Eat the '['.  */
	      input_line_pointer++;

	      /* We used to reject a null register list here; however,
		 we accept it now so the compiler can emit "call"
		 instructions for all calls to named functions.

		 The linker can then fill in the appropriate bits for the
		 register list and stack size or change the instruction
		 into a "calls" if using "call" is not profitable.  */
	      while (*input_line_pointer != ']')
		{
		  char *start;
		  char c;

		  if (*input_line_pointer == ',')
		    input_line_pointer++;

		  start = input_line_pointer;
		  c = get_symbol_end ();

		  if (strcasecmp (start, "d2") == 0)
		    {
		      value |= 0x80;
		      *input_line_pointer = c;
		    }
		  else if (strcasecmp (start, "d3") == 0)
		    {
		      value |= 0x40;
		      *input_line_pointer = c;
		    }
		  else if (strcasecmp (start, "a2") == 0)
		    {
		      value |= 0x20;
		      *input_line_pointer = c;
		    }
		  else if (strcasecmp (start, "a3") == 0)
		    {
		      value |= 0x10;
		      *input_line_pointer = c;
		    }
		  else if (strcasecmp (start, "other") == 0)
		    {
		      value |= 0x08;
		      *input_line_pointer = c;
		    }
		  else if (HAVE_AM33
			   && strcasecmp (start, "exreg0") == 0)
		    {
		      value |= 0x04;
		      *input_line_pointer = c;
		    }
		  else if (HAVE_AM33
			   && strcasecmp (start, "exreg1") == 0)
		    {
		      value |= 0x02;
		      *input_line_pointer = c;
		    }
		  else if (HAVE_AM33
			   && strcasecmp (start, "exother") == 0)
		    {
		      value |= 0x01;
		      *input_line_pointer = c;
		    }
		  else if (HAVE_AM33
			   && strcasecmp (start, "all") == 0)
		    {
		      value |= 0xff;
		      *input_line_pointer = c;
		    }
		  else
		    {
		      input_line_pointer = hold;
		      str = hold;
		      goto error;
		    }
		}
	      input_line_pointer++;
              mn10300_insert_operand (&insn, &extension, operand,
                                      value, (char *) NULL, 0, 0);
	      goto keep_going;

	    }
	  else if (data_register_name (&ex))
	    {
	      input_line_pointer = hold;
	      str = hold;
	      goto error;
	    }
	  else if (address_register_name (&ex))
	    {
	      input_line_pointer = hold;
	      str = hold;
	      goto error;
	    }
	  else if (other_register_name (&ex))
	    {
	      input_line_pointer = hold;
	      str = hold;
	      goto error;
	    }
	  else if (HAVE_AM33 && r_register_name (&ex))
	    {
	      input_line_pointer = hold;
	      str = hold;
	      goto error;
	    }
	  else if (HAVE_AM33 && xr_register_name (&ex))
	    {
	      input_line_pointer = hold;
	      str = hold;
	      goto error;
	    }
	  else if (*str == ')' || *str == '(')
	    {
	      input_line_pointer = hold;
	      str = hold;
	      goto error;
	    }
	  else
	    {
	      expression (&ex);
	    }

	  switch (ex.X_op)
	    {
	    case O_illegal:
	      errmsg = _("illegal operand");
	      goto error;
	    case O_absent:
	      errmsg = _("missing operand");
	      goto error;
	    case O_register:
	      {
		int mask;

		mask = MN10300_OPERAND_DREG | MN10300_OPERAND_AREG;
		if (HAVE_AM33)
		  mask |= MN10300_OPERAND_RREG | MN10300_OPERAND_XRREG;
		if ((operand->flags & mask) == 0)
		  {
		    input_line_pointer = hold;
		    str = hold;
		    goto error;
		  }

		if (opcode->format == FMT_D1 || opcode->format == FMT_S1)
		  extra_shift = 8;
		else if (opcode->format == FMT_D2
			 || opcode->format == FMT_D4
			 || opcode->format == FMT_S2
			 || opcode->format == FMT_S4
			 || opcode->format == FMT_S6
			 || opcode->format == FMT_D5)
		  extra_shift = 16;
		else if (opcode->format == FMT_D7)
		  extra_shift = 8;
		else if (opcode->format == FMT_D8 || opcode->format == FMT_D9)
		  extra_shift = 8;
		else
		  extra_shift = 0;

		mn10300_insert_operand (&insn, &extension, operand,
					ex.X_add_number, (char *) NULL,
					0, extra_shift);

		/* And note the register number in the register array.  */
		mn10300_reg_operands[op_idx - 1] = ex.X_add_number;
		break;
	      }

	    case O_constant:
	      /* If this operand can be promoted, and it doesn't
		 fit into the allocated bitfield for this insn,
		 then promote it (ie this opcode does not match).  */
	      if (operand->flags
		  & (MN10300_OPERAND_PROMOTE | MN10300_OPERAND_RELAX)
		  && !check_operand (insn, operand, ex.X_add_number))
		{
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}

	      mn10300_insert_operand (&insn, &extension, operand,
				      ex.X_add_number, (char *) NULL,
				      0, 0);
	      break;

	    default:
	      /* If this operand can be promoted, then this opcode didn't
		 match since we can't know if it needed promotion!  */
	      if (operand->flags & MN10300_OPERAND_PROMOTE)
		{
		  input_line_pointer = hold;
		  str = hold;
		  goto error;
		}

	      /* We need to generate a fixup for this expression.  */
	      if (fc >= MAX_INSN_FIXUPS)
		as_fatal (_("too many fixups"));
	      fixups[fc].exp = ex;
	      fixups[fc].opindex = *opindex_ptr;
	      fixups[fc].reloc = BFD_RELOC_UNUSED;
	      ++fc;
	      break;
	    }

keep_going:
	  str = input_line_pointer;
	  input_line_pointer = hold;

	  while (*str == ' ' || *str == ',')
	    ++str;

	}

      /* Make sure we used all the operands!  */
      if (*str != ',')
	match = 1;

      /* If this instruction has registers that must not match, verify
	 that they do indeed not match.  */
      if (opcode->no_match_operands)
	{
	  int i;

	  /* Look at each operand to see if it's marked.  */
	  for (i = 0; i < MN10300_MAX_OPERANDS; i++)
	    {
	      if ((1 << i) & opcode->no_match_operands)
		{
		  int j;

		  /* operand I is marked.  Check that it does not match any
		     operands > I which are marked.  */
		  for (j = i + 1; j < MN10300_MAX_OPERANDS; j++)
		    {
		      if (((1 << j) & opcode->no_match_operands)
			  && mn10300_reg_operands[i] == mn10300_reg_operands[j])
			{
			  errmsg = _("Invalid register specification.");
			  match = 0;
			  goto error;
			}
		    }
		}
	    }
	}

    error:
      if (match == 0)
	{
	  next_opcode = opcode + 1;
	  if (!strcmp (next_opcode->name, opcode->name))
	    {
	      opcode = next_opcode;
	      continue;
	    }

	  as_bad ("%s", errmsg);
	  return;
	}
      break;
    }

  while (ISSPACE (*str))
    ++str;

  if (*str != '\0')
    as_bad (_("junk at end of line: `%s'"), str);

  input_line_pointer = str;

  /* Determine the size of the instruction.  */
  if (opcode->format == FMT_S0)
    size = 1;

  if (opcode->format == FMT_S1 || opcode->format == FMT_D0)
    size = 2;

  if (opcode->format == FMT_S2 || opcode->format == FMT_D1)
    size = 3;

  if (opcode->format == FMT_D6)
    size = 3;

  if (opcode->format == FMT_D7 || opcode->format == FMT_D10)
    size = 4;

  if (opcode->format == FMT_D8)
    size = 6;

  if (opcode->format == FMT_D9)
    size = 7;

  if (opcode->format == FMT_S4)
    size = 5;

  if (opcode->format == FMT_S6 || opcode->format == FMT_D5)
    size = 7;

  if (opcode->format == FMT_D2)
    size = 4;

  if (opcode->format == FMT_D4)
    size = 6;

  if (relaxable && fc > 0)
    {
      int type;

      /* We want to anchor the line info to the previous frag (if
	 there isn't one, create it), so that, when the insn is
	 resized, we still get the right address for the beginning of
	 the region.  */
      f = frag_more (0);
      dwarf2_emit_insn (0);

      /* bCC  */
      if (size == 2)
	{
	  /* Handle bra specially.  Basically treat it like jmp so
	     that we automatically handle 8, 16 and 32 bit offsets
	     correctly as well as jumps to an undefined address.

	     It is also important to not treat it like other bCC
	     instructions since the long forms of bra is different
	     from other bCC instructions.  */
	  if (opcode->opcode == 0xca00)
	    type = 10;
	  else
	    type = 0;
	}
      /* call  */
      else if (size == 5)
	type = 6;
      /* calls  */
      else if (size == 4)
	type = 8;
      /* jmp  */
      else if (size == 3 && opcode->opcode == 0xcc0000)
	type = 10;
      /* bCC (uncommon cases)  */
      else
	type = 3;

      f = frag_var (rs_machine_dependent, 8, 8 - size, type,
		    fixups[0].exp.X_add_symbol,
		    fixups[0].exp.X_add_number,
		    (char *)fixups[0].opindex);

      /* This is pretty hokey.  We basically just care about the
	 opcode, so we have to write out the first word big endian.

	 The exception is "call", which has two operands that we
	 care about.

	 The first operand (the register list) happens to be in the
	 first instruction word, and will be in the right place if
	 we output the first word in big endian mode.

	 The second operand (stack size) is in the extension word,
	 and we want it to appear as the first character in the extension
	 word (as it appears in memory).  Luckily, writing the extension
	 word in big endian format will do what we want.  */
      number_to_chars_bigendian (f, insn, size > 4 ? 4 : size);
      if (size > 8)
	{
	  number_to_chars_bigendian (f + 4, extension, 4);
	  number_to_chars_bigendian (f + 8, 0, size - 8);
	}
      else if (size > 4)
	number_to_chars_bigendian (f + 4, extension, size - 4);
    }
  else
    {
      /* Allocate space for the instruction.  */
      f = frag_more (size);

      /* Fill in bytes for the instruction.  Note that opcode fields
	 are written big-endian, 16 & 32bit immediates are written
	 little endian.  Egad.  */
      if (opcode->format == FMT_S0
	  || opcode->format == FMT_S1
	  || opcode->format == FMT_D0
	  || opcode->format == FMT_D6
	  || opcode->format == FMT_D7
	  || opcode->format == FMT_D10
	  || opcode->format == FMT_D1)
	{
	  number_to_chars_bigendian (f, insn, size);
	}
      else if (opcode->format == FMT_S2
	       && opcode->opcode != 0xdf0000
	       && opcode->opcode != 0xde0000)
	{
	  /* A format S2 instruction that is _not_ "ret" and "retf".  */
	  number_to_chars_bigendian (f, (insn >> 16) & 0xff, 1);
	  number_to_chars_littleendian (f + 1, insn & 0xffff, 2);
	}
      else if (opcode->format == FMT_S2)
	{
	  /* This must be a ret or retf, which is written entirely in
	     big-endian format.  */
	  number_to_chars_bigendian (f, insn, 3);
	}
      else if (opcode->format == FMT_S4
	       && opcode->opcode != 0xdc000000)
	{
	  /* This must be a format S4 "call" instruction.  What a pain.  */
	  unsigned long temp = (insn >> 8) & 0xffff;
	  number_to_chars_bigendian (f, (insn >> 24) & 0xff, 1);
	  number_to_chars_littleendian (f + 1, temp, 2);
	  number_to_chars_bigendian (f + 3, insn & 0xff, 1);
	  number_to_chars_bigendian (f + 4, extension & 0xff, 1);
	}
      else if (opcode->format == FMT_S4)
	{
	  /* This must be a format S4 "jmp" instruction.  */
	  unsigned long temp = ((insn & 0xffffff) << 8) | (extension & 0xff);
	  number_to_chars_bigendian (f, (insn >> 24) & 0xff, 1);
	  number_to_chars_littleendian (f + 1, temp, 4);
	}
      else if (opcode->format == FMT_S6)
	{
	  unsigned long temp = ((insn & 0xffffff) << 8)
	    | ((extension >> 16) & 0xff);
	  number_to_chars_bigendian (f, (insn >> 24) & 0xff, 1);
	  number_to_chars_littleendian (f + 1, temp, 4);
	  number_to_chars_bigendian (f + 5, (extension >> 8) & 0xff, 1);
	  number_to_chars_bigendian (f + 6, extension & 0xff, 1);
	}
      else if (opcode->format == FMT_D2
	       && opcode->opcode != 0xfaf80000
	       && opcode->opcode != 0xfaf00000
	       && opcode->opcode != 0xfaf40000)
	{
	  /* A format D2 instruction where the 16bit immediate is
	     really a single 16bit value, not two 8bit values.  */
	  number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
	  number_to_chars_littleendian (f + 2, insn & 0xffff, 2);
	}
      else if (opcode->format == FMT_D2)
	{
	  /* A format D2 instruction where the 16bit immediate
	     is really two 8bit immediates.  */
	  number_to_chars_bigendian (f, insn, 4);
	}
      else if (opcode->format == FMT_D4)
	{
	  unsigned long temp = ((insn & 0xffff) << 16) | (extension & 0xffff);

	  number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
	  number_to_chars_littleendian (f + 2, temp, 4);
	}
      else if (opcode->format == FMT_D5)
	{
	  unsigned long temp = (((insn & 0xffff) << 16)
				| ((extension >> 8) & 0xffff));

	  number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
	  number_to_chars_littleendian (f + 2, temp, 4);
	  number_to_chars_bigendian (f + 6, extension & 0xff, 1);
	}
      else if (opcode->format == FMT_D8)
	{
	  unsigned long temp = ((insn & 0xff) << 16) | (extension & 0xffff);

	  number_to_chars_bigendian (f, (insn >> 8) & 0xffffff, 3);
	  number_to_chars_bigendian (f + 3, (temp & 0xff), 1);
	  number_to_chars_littleendian (f + 4, temp >> 8, 2);
	}
      else if (opcode->format == FMT_D9)
	{
	  unsigned long temp = ((insn & 0xff) << 24) | (extension & 0xffffff);

	  number_to_chars_bigendian (f, (insn >> 8) & 0xffffff, 3);
	  number_to_chars_littleendian (f + 3, temp, 4);
	}

      /* Create any fixups.  */
      for (i = 0; i < fc; i++)
	{
	  const struct mn10300_operand *operand;

	  operand = &mn10300_operands[fixups[i].opindex];
	  if (fixups[i].reloc != BFD_RELOC_UNUSED)
	    {
	      reloc_howto_type *reloc_howto;
	      int size;
	      int offset;
	      fixS *fixP;

	      reloc_howto = bfd_reloc_type_lookup (stdoutput,
						   fixups[i].reloc);

	      if (!reloc_howto)
		abort ();

	      size = bfd_get_reloc_size (reloc_howto);

	      if (size < 1 || size > 4)
		abort ();

	      offset = 4 - size;
	      fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset,
				  size, &fixups[i].exp,
				  reloc_howto->pc_relative,
				  fixups[i].reloc);
	    }
	  else
	    {
	      int reloc, pcrel, reloc_size, offset;
	      fixS *fixP;

	      reloc = BFD_RELOC_NONE;
	      /* How big is the reloc?  Remember SPLIT relocs are
		 implicitly 32bits.  */
	      if ((operand->flags & MN10300_OPERAND_SPLIT) != 0)
		reloc_size = 32;
	      else if ((operand->flags & MN10300_OPERAND_24BIT) != 0)
		reloc_size = 24;
	      else
		reloc_size = operand->bits;

	      /* Is the reloc pc-relative?  */
	      pcrel = (operand->flags & MN10300_OPERAND_PCREL) != 0;

	      offset = size - (reloc_size + operand->shift) / 8;

	      /* Choose a proper BFD relocation type.  */
	      if (pcrel)
		{
		  if (reloc_size == 32)
		    reloc = BFD_RELOC_32_PCREL;
		  else if (reloc_size == 16)
		    reloc = BFD_RELOC_16_PCREL;
		  else if (reloc_size == 8)
		    reloc = BFD_RELOC_8_PCREL;
		  else
		    abort ();
		}
	      else
		{
		  if (reloc_size == 32)
		    reloc = BFD_RELOC_32;
		  else if (reloc_size == 16)
		    reloc = BFD_RELOC_16;
		  else if (reloc_size == 8)
		    reloc = BFD_RELOC_8;
		  else
		    abort ();
		}

	      /* Convert the size of the reloc into what fix_new_exp wants.  */
	      reloc_size = reloc_size / 8;
	      if (reloc_size == 8)
		reloc_size = 0;
	      else if (reloc_size == 16)
		reloc_size = 1;
	      else if (reloc_size == 32)
		reloc_size = 2;

	      fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset,
				  reloc_size, &fixups[i].exp, pcrel,
				  ((bfd_reloc_code_real_type) reloc));

	      if (pcrel)
		fixP->fx_offset += offset;
	    }
	}

      dwarf2_emit_insn (size);
    }
}

/* If while processing a fixup, a reloc really needs to be created
   then it is done here.  */

arelent *
tc_gen_reloc (seg, fixp)
     asection *seg ATTRIBUTE_UNUSED;
     fixS *fixp;
{
  arelent *reloc;
  reloc = (arelent *) xmalloc (sizeof (arelent));

  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
  if (reloc->howto == (reloc_howto_type *) NULL)
    {
      as_bad_where (fixp->fx_file, fixp->fx_line,
		    _("reloc %d not supported by object file format"),
		    (int) fixp->fx_r_type);
      return NULL;
    }
  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;

  if (fixp->fx_addsy && fixp->fx_subsy)
    {
      reloc->sym_ptr_ptr = NULL;

      /* If we got a difference between two symbols, and the
	 subtracted symbol is in the current section, use a
	 PC-relative relocation.  If both symbols are in the same
	 section, the difference would have already been simplified
	 to a constant.  */
      if (S_GET_SEGMENT (fixp->fx_subsy) == seg)
	{
	  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
	  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
	  reloc->addend = (reloc->address - S_GET_VALUE (fixp->fx_subsy)
			   + fixp->fx_offset);

	  switch (fixp->fx_r_type)
	    {
	    case BFD_RELOC_8:
	      reloc->howto = bfd_reloc_type_lookup (stdoutput,
						    BFD_RELOC_8_PCREL);
	      return reloc;
	      
	    case BFD_RELOC_16:
	      reloc->howto = bfd_reloc_type_lookup (stdoutput,
						    BFD_RELOC_16_PCREL);
	      return reloc;

	    case BFD_RELOC_24:
	      reloc->howto = bfd_reloc_type_lookup (stdoutput,
						    BFD_RELOC_24_PCREL);
	      return reloc;

	    case BFD_RELOC_32:
	      reloc->howto = bfd_reloc_type_lookup (stdoutput,
						    BFD_RELOC_32_PCREL);
	      return reloc;

	    default:
	      /* Try to compute the absolute value below.  */
	      break;
	    }
	}

      if ((S_GET_SEGMENT (fixp->fx_addsy) != S_GET_SEGMENT (fixp->fx_subsy))
	  || S_GET_SEGMENT (fixp->fx_addsy) == undefined_section)
	{
	  as_bad_where (fixp->fx_file, fixp->fx_line,
			"Difference of symbols in different sections is not supported");
	}
      else
	{
	  char *fixpos = fixp->fx_where + fixp->fx_frag->fr_literal;

	  reloc->addend = (S_GET_VALUE (fixp->fx_addsy)
			   - S_GET_VALUE (fixp->fx_subsy) + fixp->fx_offset);

	  switch (fixp->fx_r_type)
	    {
	    case BFD_RELOC_8:
	      md_number_to_chars (fixpos, reloc->addend, 1);
	      break;
	      
	    case BFD_RELOC_16:
	      md_number_to_chars (fixpos, reloc->addend, 2);
	      break;

	    case BFD_RELOC_24:
	      md_number_to_chars (fixpos, reloc->addend, 3);
	      break;

	    case BFD_RELOC_32:
	      md_number_to_chars (fixpos, reloc->addend, 4);
	      break;

	    default:
	      reloc->sym_ptr_ptr = (asymbol **) &bfd_abs_symbol;
	      return reloc;
	    }
	}

      if (reloc->sym_ptr_ptr)
	free (reloc->sym_ptr_ptr);
      free (reloc);
      return NULL;
    }
  else
    {
      reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
      *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
      reloc->addend = fixp->fx_offset;
    }
  return reloc;
}

int
md_estimate_size_before_relax (fragp, seg)
     fragS *fragp;
     asection *seg;
{
  if (fragp->fr_subtype == 6
      && (!S_IS_DEFINED (fragp->fr_symbol)
	  || seg != S_GET_SEGMENT (fragp->fr_symbol)))
    fragp->fr_subtype = 7;
  else if (fragp->fr_subtype == 8
	   && (!S_IS_DEFINED (fragp->fr_symbol)
	       || seg != S_GET_SEGMENT (fragp->fr_symbol)))
    fragp->fr_subtype = 9;
  else if (fragp->fr_subtype == 10
	   &&  (!S_IS_DEFINED (fragp->fr_symbol)
		|| seg != S_GET_SEGMENT (fragp->fr_symbol)))
    fragp->fr_subtype = 12;

  if (fragp->fr_subtype >= sizeof (md_relax_table) / sizeof (md_relax_table[0]))
    abort ();

  return md_relax_table[fragp->fr_subtype].rlx_length;
}

long
md_pcrel_from (fixp)
     fixS *fixp;
{
  if (fixp->fx_addsy != (symbolS *) NULL && !S_IS_DEFINED (fixp->fx_addsy))
    {
      /* The symbol is undefined.  Let the linker figure it out.  */
      return 0;
    }
  return fixp->fx_frag->fr_address + fixp->fx_where;
}

void
md_apply_fix3 (fixP, valP, seg)
     fixS * fixP;
     valueT * valP;
     segT seg;
{
  char * fixpos = fixP->fx_where + fixP->fx_frag->fr_literal;
  int size = 0;
  int value = (int) * valP;

  assert (fixP->fx_r_type < BFD_RELOC_UNUSED);

  /* This should never happen.  */
  if (seg->flags & SEC_ALLOC)
    abort ();

  /* The value we are passed in *valuep includes the symbol values.
     Since we are using BFD_ASSEMBLER, if we are doing this relocation
     the code in write.c is going to call bfd_install_relocation, which
     is also going to use the symbol value.  That means that if the
     reloc is fully resolved we want to use *valuep since
     bfd_install_relocation is not being used.

     However, if the reloc is not fully resolved we do not want to use
     *valuep, and must use fx_offset instead.  However, if the reloc
     is PC relative, we do want to use *valuep since it includes the
     result of md_pcrel_from.  */
  if (fixP->fx_addsy != (symbolS *) NULL && ! fixP->fx_pcrel)
    value = fixP->fx_offset;

  /* If the fix is relative to a symbol which is not defined, or not
     in the same segment as the fix, we cannot resolve it here.  */
  if (fixP->fx_addsy != NULL
      && (! S_IS_DEFINED (fixP->fx_addsy)
	  || (S_GET_SEGMENT (fixP->fx_addsy) != seg)))
    {
      fixP->fx_done = 0;
      return;
    }

  switch (fixP->fx_r_type)
    {
    case BFD_RELOC_8:
    case BFD_RELOC_8_PCREL:
      size = 1;
      break;

    case BFD_RELOC_16:
    case BFD_RELOC_16_PCREL:
      size = 2;
      break;

    case BFD_RELOC_32:
    case BFD_RELOC_32_PCREL:
      size = 4;
      break;

    case BFD_RELOC_VTABLE_INHERIT:
    case BFD_RELOC_VTABLE_ENTRY:
      fixP->fx_done = 0;
      return;

    case BFD_RELOC_NONE:
    default:
      as_bad_where (fixP->fx_file, fixP->fx_line,
                   _("Bad relocation fixup type (%d)"), fixP->fx_r_type);
    }

  md_number_to_chars (fixpos, value, size);

  /* If a symbol remains, pass the fixup, as a reloc, onto the linker.  */
  if (fixP->fx_addsy == NULL)
    fixP->fx_done = 1;
}

/* Return nonzero if the fixup in FIXP will require a relocation,
   even it if appears that the fixup could be completely handled
   within GAS.  */

int
mn10300_force_relocation (fixp)
     struct fix *fixp;
{
  if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
      || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
    return 1;

  /* Do not adjust relocations involving symbols in code sections,
     because it breaks linker relaxations.  This could be fixed in the
     linker, but this fix is simpler, and it pretty much only affects
     object size a little bit.  */
  if ((S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_CODE)
      && fixp->fx_subsy
      && S_GET_SEGMENT (fixp->fx_addsy) == S_GET_SEGMENT (fixp->fx_subsy))
    return 1;

  return 0;
}

/* Return zero if the fixup in fixp should be left alone and not
   adjusted.  */

boolean
mn10300_fix_adjustable (fixp)
     struct fix *fixp;
{
  /* Prevent all adjustments to global symbols.  */
  if (S_IS_EXTERN (fixp->fx_addsy) || S_IS_WEAK (fixp->fx_addsy))
    return 0;

  if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
      || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
    return 0;

  /* Do not adjust relocations involving symbols in code sections,
     because it breaks linker relaxations.  This could be fixed in the
     linker, but this fix is simpler, and it pretty much only affects
     object size a little bit.  */
  if (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_CODE)
    return 0;

  return 1;
}

/* Insert an operand value into an instruction.  */

static void
mn10300_insert_operand (insnp, extensionp, operand, val, file, line, shift)
     unsigned long *insnp;
     unsigned long *extensionp;
     const struct mn10300_operand *operand;
     offsetT val;
     char *file;
     unsigned int line;
     unsigned int shift;
{
  /* No need to check 32bit operands for a bit.  Note that
     MN10300_OPERAND_SPLIT is an implicit 32bit operand.  */
  if (operand->bits != 32
      && (operand->flags & MN10300_OPERAND_SPLIT) == 0)
    {
      long min, max;
      offsetT test;
      int bits;

      bits = operand->bits;
      if (operand->flags & MN10300_OPERAND_24BIT)
	bits = 24;

      if ((operand->flags & MN10300_OPERAND_SIGNED) != 0)
	{
	  max = (1 << (bits - 1)) - 1;
	  min = - (1 << (bits - 1));
	}
      else
	{
	  max = (1 << bits) - 1;
	  min = 0;
	}

      test = val;

      if (test < (offsetT) min || test > (offsetT) max)
	{
	  const char *err =
	    _("operand out of range (%s not between %ld and %ld)");
	  char buf[100];

	  sprint_value (buf, test);
	  if (file == (char *) NULL)
	    as_warn (err, buf, min, max);
	  else
	    as_warn_where (file, line, err, buf, min, max);
	}
    }

  if ((operand->flags & MN10300_OPERAND_SPLIT) != 0)
    {
      *insnp |= (val >> (32 - operand->bits)) & ((1 << operand->bits) - 1);
      *extensionp |= ((val & ((1 << (32 - operand->bits)) - 1))
		      << operand->shift);
    }
  else if ((operand->flags & MN10300_OPERAND_24BIT) != 0)
    {
      *insnp |= (val >> (24 - operand->bits)) & ((1 << operand->bits) - 1);
      *extensionp |= ((val & ((1 << (24 - operand->bits)) - 1))
		      << operand->shift);
    }
  else if ((operand->flags & MN10300_OPERAND_EXTENDED) == 0)
    {
      *insnp |= (((long) val & ((1 << operand->bits) - 1))
		 << (operand->shift + shift));

      if ((operand->flags & MN10300_OPERAND_REPEATED) != 0)
	*insnp |= (((long) val & ((1 << operand->bits) - 1))
		   << (operand->shift + shift + operand->bits));
    }
  else
    {
      *extensionp |= (((long) val & ((1 << operand->bits) - 1))
		      << (operand->shift + shift));

      if ((operand->flags & MN10300_OPERAND_REPEATED) != 0)
	*extensionp |= (((long) val & ((1 << operand->bits) - 1))
			<< (operand->shift + shift + operand->bits));
    }
}

static unsigned long
check_operand (insn, operand, val)
     unsigned long insn ATTRIBUTE_UNUSED;
     const struct mn10300_operand *operand;
     offsetT val;
{
  /* No need to check 32bit operands for a bit.  Note that
     MN10300_OPERAND_SPLIT is an implicit 32bit operand.  */
  if (operand->bits != 32
      && (operand->flags & MN10300_OPERAND_SPLIT) == 0)
    {
      long min, max;
      offsetT test;
      int bits;

      bits = operand->bits;
      if (operand->flags & MN10300_OPERAND_24BIT)
	bits = 24;

      if ((operand->flags & MN10300_OPERAND_SIGNED) != 0)
	{
	  max = (1 << (bits - 1)) - 1;
	  min = - (1 << (bits - 1));
	}
      else
	{
	  max = (1 << bits) - 1;
	  min = 0;
	}

      test = val;

      if (test < (offsetT) min || test > (offsetT) max)
	return 0;
      else
	return 1;
    }
  return 1;
}

static void
set_arch_mach (mach)
     unsigned int mach;
{
  if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, mach))
    as_warn (_("could not set architecture and machine"));

  current_machine = mach;
}