/* tc-h8300.c -- Assemble code for the Renesas H8/300
   Copyright (C) 1991-2020 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 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.  */

/* Written By Steve Chamberlain <sac@cygnus.com>.  */

#include "as.h"
#include "subsegs.h"
#include "dwarf2dbg.h"

#define DEFINE_TABLE
#define h8_opcodes ops
#include "opcode/h8300.h"
#include "safe-ctype.h"
#include "elf/h8.h"

const char comment_chars[] = ";";
const char line_comment_chars[] = "#";
#ifdef TE_LINUX
const char line_separator_chars[] = "!";
#else
const char line_separator_chars[] = "";
#endif

static void sbranch (int);
static void h8300hmode (int);
static void h8300smode (int);
static void h8300hnmode (int);
static void h8300snmode (int);
static void h8300sxmode (int);
static void h8300sxnmode (int);
static void pint (int);

int Hmode;
int Smode;
int Nmode;
int SXmode;

static int default_mach = bfd_mach_h8300;

#define PSIZE (Hmode && !Nmode ? L_32 : L_16)

static int bsize = L_8;		/* Default branch displacement.  */

struct h8_instruction
{
  int length;
  int noperands;
  int idx;
  int size;
  const struct h8_opcode *opcode;
};

static struct h8_instruction *h8_instructions;

static void
h8300hmode (int arg ATTRIBUTE_UNUSED)
{
  Hmode = 1;
  Smode = 0;
  if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300h))
    as_warn (_("could not set architecture and machine"));
}

static void
h8300smode (int arg ATTRIBUTE_UNUSED)
{
  Smode = 1;
  Hmode = 1;
  if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300s))
    as_warn (_("could not set architecture and machine"));
}

static void
h8300hnmode (int arg ATTRIBUTE_UNUSED)
{
  Hmode = 1;
  Smode = 0;
  Nmode = 1;
  if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300hn))
    as_warn (_("could not set architecture and machine"));
}

static void
h8300snmode (int arg ATTRIBUTE_UNUSED)
{
  Smode = 1;
  Hmode = 1;
  Nmode = 1;
  if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300sn))
    as_warn (_("could not set architecture and machine"));
}

static void
h8300sxmode (int arg ATTRIBUTE_UNUSED)
{
  Smode = 1;
  Hmode = 1;
  SXmode = 1;
  if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300sx))
    as_warn (_("could not set architecture and machine"));
}

static void
h8300sxnmode (int arg ATTRIBUTE_UNUSED)
{
  Smode = 1;
  Hmode = 1;
  SXmode = 1;
  Nmode = 1;
  if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300sxn))
    as_warn (_("could not set architecture and machine"));
}

static void
sbranch (int size)
{
  bsize = size;
}

static void
pint (int arg ATTRIBUTE_UNUSED)
{
  cons (Hmode ? 4 : 2);
}

/* Like obj_elf_section, but issues a warning for new
   sections which do not have an attribute specification.  */

static void
h8300_elf_section (int push)
{
  static const char * known_data_sections [] = { ".rodata", ".tdata", ".tbss" };
  static const char * known_data_prefixes [] = { ".debug", ".zdebug", ".gnu.warning" };
  char * saved_ilp = input_line_pointer;
  const char * name;

  name = obj_elf_section_name ();
  if (name == NULL)
    return;

  if (* input_line_pointer != ','
      && bfd_get_section_by_name (stdoutput, name) == NULL)
    {
      signed int i;

      /* Ignore this warning for well known data sections.  */
      for (i = ARRAY_SIZE (known_data_sections); i--;)
	if (strcmp (name, known_data_sections[i]) == 0)
	  break;

      if (i < 0)
	for (i = ARRAY_SIZE (known_data_prefixes); i--;)
	  if (strncmp (name, known_data_prefixes[i],
		       strlen (known_data_prefixes[i])) == 0)
	    break;

      if (i < 0)
	as_warn (_("new section '%s' defined without attributes - this might cause problems"), name);
    }

  /* FIXME: We ought to free the memory allocated by obj_elf_section_name()
     for 'name', but we do not know if it was taken from the obstack, via
     demand_copy_C_string(), or xmalloc()ed.  */
  input_line_pointer = saved_ilp;
  obj_elf_section (push);
}

/* This table describes all the machine specific pseudo-ops the assembler
   has to support.  The fields are:
   pseudo-op name without dot
   function to call to execute this pseudo-op
   Integer arg to pass to the function.  */

const pseudo_typeS md_pseudo_table[] =
{
  {"h8300h",  h8300hmode,  0},
  {"h8300hn", h8300hnmode, 0},
  {"h8300s",  h8300smode,  0},
  {"h8300sn", h8300snmode, 0},
  {"h8300sx", h8300sxmode, 0},
  {"h8300sxn", h8300sxnmode, 0},
  {"sbranch", sbranch, L_8},
  {"lbranch", sbranch, L_16},

  {"int", pint, 0},
  {"data.b", cons, 1},
  {"data.w", cons, 2},
  {"data.l", cons, 4},
  {"form", listing_psize, 0},
  {"heading", listing_title, 0},
  {"import",  s_ignore, 0},
  {"page",    listing_eject, 0},
  {"program", s_ignore, 0},

  {"section",   h8300_elf_section, 0},
  {"section.s", h8300_elf_section, 0},
  {"sect",      h8300_elf_section, 0},
  {"sect.s",    h8300_elf_section, 0},

  {0, 0, 0}
};

const char EXP_CHARS[] = "eE";

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

static struct hash_control *opcode_hash_control;	/* Opcode mnemonics.  */

/* This function is called once, at assembler startup time.  This
   should set up all the tables, etc. that the MD part of the assembler
   needs.  */

void
md_begin (void)
{
  unsigned int nopcodes;
  struct h8_opcode *p, *p1;
  struct h8_instruction *pi;
  char prev_buffer[100];
  int idx = 0;

  if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, default_mach))
    as_warn (_("could not set architecture and machine"));

  opcode_hash_control = hash_new ();
  prev_buffer[0] = 0;

  nopcodes = sizeof (h8_opcodes) / sizeof (struct h8_opcode);

  h8_instructions = XNEWVEC (struct h8_instruction, nopcodes);

  pi = h8_instructions;
  p1 = h8_opcodes;
  /* We do a minimum amount of sorting on the opcode table; this is to
     make it easy to describe the mova instructions without unnecessary
     code duplication.
     Sorting only takes place inside blocks of instructions of the form
     X/Y, so for example mova/b, mova/w and mova/l can be intermixed.  */
  while (p1)
    {
      struct h8_opcode *first_skipped = 0;
      int len, cmplen = 0;
      const char *src = p1->name;
      char *dst, *buffer;

      if (p1->name == 0)
	break;
      /* Strip off any . part when inserting the opcode and only enter
	 unique codes into the hash table.  */
      dst = buffer = XNEWVEC (char, strlen (src) + 1);
      while (*src)
	{
	  if (*src == '.')
	    {
	      src++;
	      break;
	    }
	  if (*src == '/')
	    cmplen = src - p1->name + 1;
	  *dst++ = *src++;
	}
      *dst = 0;
      len = dst - buffer;
      if (cmplen == 0)
	cmplen = len;
      hash_insert (opcode_hash_control, buffer, (char *) pi);
      strcpy (prev_buffer, buffer);
      idx++;

      for (p = p1; p->name; p++)
	{
	  /* A negative TIME is used to indicate that we've added this opcode
	     already.  */
	  if (p->time == -1)
	    continue;
	  if (strncmp (p->name, buffer, cmplen) != 0
	      || (p->name[cmplen] != '\0' && p->name[cmplen] != '.'
		  && p->name[cmplen - 1] != '/'))
	    {
	      if (first_skipped == 0)
		first_skipped = p;
	      break;
	    }
	  if (strncmp (p->name, buffer, len) != 0)
	    {
	      if (first_skipped == 0)
		first_skipped = p;
	      continue;
	    }

	  p->time = -1;
	  pi->size = p->name[len] == '.' ? p->name[len + 1] : 0;
	  pi->idx = idx;

	  /* Find the number of operands.  */
	  pi->noperands = 0;
	  while (pi->noperands < 3 && p->args.nib[pi->noperands] != (op_type) E)
	    pi->noperands++;

	  /* Find the length of the opcode in bytes.  */
	  pi->length = 0;
	  while (p->data.nib[pi->length * 2] != (op_type) E)
	    pi->length++;

	  pi->opcode = p;
	  pi++;
	}
      p1 = first_skipped;
    }

  /* Add entry for the NULL vector terminator.  */
  pi->length = 0;
  pi->noperands = 0;
  pi->idx = 0;
  pi->size = 0;
  pi->opcode = 0;

  linkrelax = 1;
}

struct h8_op
{
  op_type mode;
  unsigned reg;
  expressionS exp;
};

static void clever_message (const struct h8_instruction *, struct h8_op *);
static void fix_operand_size (struct h8_op *, int);
static void build_bytes (const struct h8_instruction *, struct h8_op *);
static void do_a_fix_imm (int, int, struct h8_op *, int, const struct h8_instruction *);
static void check_operand (struct h8_op *, unsigned int, const char *);
static const struct h8_instruction * get_specific (const struct h8_instruction *, struct h8_op *, int) ;
static char *get_operands (unsigned, char *, struct h8_op *);
static void get_operand (char **, struct h8_op *, int);
static int parse_reg (char *, op_type *, unsigned *, int);
static char *skip_colonthing (char *, int *);
static char *parse_exp (char *, struct h8_op *);

static int constant_fits_size_p (struct h8_op *, int, int);

/*
  parse operands
  WREG r0,r1,r2,r3,r4,r5,r6,r7,fp,sp
  r0l,r0h,..r7l,r7h
  @WREG
  @WREG+
  @-WREG
  #const
  ccr
*/

/* Try to parse a reg name.  Return the number of chars consumed.  */

static int
parse_reg (char *src, op_type *mode, unsigned int *reg, int direction)
{
  char *end;
  int len;

  /* Cribbed from get_symbol_name.  */
  if (!is_name_beginner (*src) || *src == '\001')
    return 0;
  end = src + 1;
  while ((is_part_of_name (*end) && *end != '.') || *end == '\001')
    end++;
  len = end - src;

  if (len == 2 && TOLOWER (src[0]) == 's' && TOLOWER (src[1]) == 'p')
    {
      *mode = PSIZE | REG | direction;
      *reg = 7;
      return len;
    }
  if (len == 3 &&
      TOLOWER (src[0]) == 'c' &&
      TOLOWER (src[1]) == 'c' &&
      TOLOWER (src[2]) == 'r')
    {
      *mode = CCR;
      *reg = 0;
      return len;
    }
  if (len == 3 &&
      TOLOWER (src[0]) == 'e' &&
      TOLOWER (src[1]) == 'x' &&
      TOLOWER (src[2]) == 'r')
    {
      *mode = EXR;
      *reg = 1;
      return len;
    }
  if (len == 3 &&
      TOLOWER (src[0]) == 'v' &&
      TOLOWER (src[1]) == 'b' &&
      TOLOWER (src[2]) == 'r')
    {
      *mode = VBR;
      *reg = 6;
      return len;
    }
  if (len == 3 &&
      TOLOWER (src[0]) == 's' &&
      TOLOWER (src[1]) == 'b' &&
      TOLOWER (src[2]) == 'r')
    {
      *mode = SBR;
      *reg = 7;
      return len;
    }
  if (len == 2 && TOLOWER (src[0]) == 'f' && TOLOWER (src[1]) == 'p')
    {
      *mode = PSIZE | REG | direction;
      *reg = 6;
      return len;
    }
  if (len == 3 && TOLOWER (src[0]) == 'e' && TOLOWER (src[1]) == 'r' &&
      src[2] >= '0' && src[2] <= '7')
    {
      *mode = L_32 | REG | direction;
      *reg = src[2] - '0';
      if (!Hmode)
	as_warn (_("Reg not valid for H8/300"));
      return len;
    }
  if (len == 2 && TOLOWER (src[0]) == 'e' && src[1] >= '0' && src[1] <= '7')
    {
      *mode = L_16 | REG | direction;
      *reg = src[1] - '0' + 8;
      if (!Hmode)
	as_warn (_("Reg not valid for H8/300"));
      return len;
    }

  if (TOLOWER (src[0]) == 'r')
    {
      if (src[1] >= '0' && src[1] <= '7')
	{
	  if (len == 3 && TOLOWER (src[2]) == 'l')
	    {
	      *mode = L_8 | REG | direction;
	      *reg = (src[1] - '0') + 8;
	      return len;
	    }
	  if (len == 3 && TOLOWER (src[2]) == 'h')
	    {
	      *mode = L_8 | REG | direction;
	      *reg = (src[1] - '0');
	      return len;
	    }
	  if (len == 2)
	    {
	      *mode = L_16 | REG | direction;
	      *reg = (src[1] - '0');
	      return len;
	    }
	}
    }

  return 0;
}


/* Parse an immediate or address-related constant and store it in OP.
   If the user also specifies the operand's size, store that size
   in OP->MODE, otherwise leave it for later code to decide.  */

static char *
parse_exp (char *src, struct h8_op *op)
{
  char *save;

  save = input_line_pointer;
  input_line_pointer = src;
  expression (&op->exp);
  if (op->exp.X_op == O_absent)
    as_bad (_("missing operand"));
  src = input_line_pointer;
  input_line_pointer = save;

  return skip_colonthing (src, &op->mode);
}


/* If SRC starts with an explicit operand size, skip it and store the size
   in *MODE.  Leave *MODE unchanged otherwise.  */

static char *
skip_colonthing (char *src, int *mode)
{
  if (*src == ':')
    {
      src++;
      *mode &= ~SIZE;
      if (src[0] == '8' && !ISDIGIT (src[1]))
	*mode |= L_8;
      else if (src[0] == '2' && !ISDIGIT (src[1]))
	*mode |= L_2;
      else if (src[0] == '3' && !ISDIGIT (src[1]))
	*mode |= L_3;
      else if (src[0] == '4' && !ISDIGIT (src[1]))
	*mode |= L_4;
      else if (src[0] == '5' && !ISDIGIT (src[1]))
	*mode |= L_5;
      else if (src[0] == '2' && src[1] == '4' && !ISDIGIT (src[2]))
	*mode |= L_24;
      else if (src[0] == '3' && src[1] == '2' && !ISDIGIT (src[2]))
	*mode |= L_32;
      else if (src[0] == '1' && src[1] == '6' && !ISDIGIT (src[2]))
	*mode |= L_16;
      else
	as_bad (_("invalid operand size requested"));

      while (ISDIGIT (*src))
	src++;
    }
  return src;
}

/* The many forms of operand:

   Rn			Register direct
   @Rn			Register indirect
   @(exp[:16], Rn)	Register indirect with displacement
   @Rn+
   @-Rn
   @aa:8		absolute 8 bit
   @aa:16		absolute 16 bit
   @aa			absolute 16 bit

   #xx[:size]		immediate data
   @(exp:[8], pc)	pc rel
   @@aa[:8]		memory indirect.  */

static int
constant_fits_width_p (struct h8_op *operand, offsetT width)
{
  offsetT num;

  num = ((operand->exp.X_add_number & 0xffffffff) ^ 0x80000000) - 0x80000000;
  return (num & ~width) == 0 || (num | width) == ~0;
}

static int
constant_fits_size_p (struct h8_op *operand, int size, int no_symbols)
{
  offsetT num;

  if (no_symbols
      && (operand->exp.X_add_symbol != 0 || operand->exp.X_op_symbol != 0))
    return 0;
  num = operand->exp.X_add_number & 0xffffffff;
  switch (size)
    {
    case L_2:
      return (num & ~3) == 0;
    case L_3:
      return (num & ~7) == 0;
    case L_3NZ:
      return num >= 1 && num < 8;
    case L_4:
      return (num & ~15) == 0;
    case L_5:
      return num >= 1 && num < 32;
    case L_8:
      num = (num ^ 0x80000000) - 0x80000000;
      return (num & ~0xFF) == 0 || (num | 0x7F) == ~0;
    case L_8U:
      return (num & ~0xFF) == 0;
    case L_16:
      num = (num ^ 0x80000000) - 0x80000000;
      return (num & ~0xFFFF) == 0 || (num | 0x7FFF) == ~0;
    case L_16U:
      return (num & ~0xFFFF) == 0;
    case L_32:
      return 1;
    default:
      abort ();
    }
}

static void
get_operand (char **ptr, struct h8_op *op, int direction)
{
  char *src = *ptr;
  op_type mode;
  unsigned int num;
  unsigned int len;

  op->mode = 0;

  /* Check for '(' and ')' for instructions ldm and stm.  */
  if (src[0] == '(' && src[8] == ')')
    ++ src;

  /* Gross.  Gross.  ldm and stm have a format not easily handled
     by get_operand.  We deal with it explicitly here.  */
  if (TOLOWER (src[0]) == 'e' && TOLOWER (src[1]) == 'r' &&
      ISDIGIT (src[2]) && src[3] == '-' &&
      TOLOWER (src[4]) == 'e' && TOLOWER (src[5]) == 'r' && ISDIGIT (src[6]))
    {
      int low, high;

      low = src[2] - '0';
      high = src[6] - '0';

       /* Check register pair's validity as per tech note TN-H8*-193A/E
	  from Renesas for H8S and H8SX hardware manual.  */
      if (   !(low == 0 && (high == 1 || high == 2 || high == 3))
          && !(low == 1 && (high == 2 || high == 3 || high == 4) && SXmode)
          && !(low == 2 && (high == 3 || ((high == 4 || high == 5) && SXmode)))
          && !(low == 3 && (high == 4 || high == 5 || high == 6) && SXmode)
          && !(low == 4 && (high == 5 || high == 6))
          && !(low == 4 && high == 7 && SXmode)
          && !(low == 5 && (high == 6 || high == 7) && SXmode)
          && !(low == 6 && high == 7 && SXmode))
	as_bad (_("Invalid register list for ldm/stm\n"));

      /* Even sicker.  We encode two registers into op->reg.  One
	 for the low register to save, the other for the high
	 register to save;  we also set the high bit in op->reg
	 so we know this is "very special".  */
      op->reg = 0x80000000 | (high << 8) | low;
      op->mode = REG;
      if (src[7] == ')')
	*ptr = src + 8;
      else
	*ptr = src + 7;
      return;
    }

  len = parse_reg (src, &op->mode, &op->reg, direction);
  if (len)
    {
      src += len;
      if (*src == '.')
	{
	  int size = op->mode & SIZE;
	  switch (src[1])
	    {
	    case 'l': case 'L':
	      if (size != L_32)
		as_warn (_("mismatch between register and suffix"));
	      op->mode = (op->mode & ~MODE) | LOWREG;
	      break;
	    case 'w': case 'W':
	      if (size != L_32 && size != L_16)
		as_warn (_("mismatch between register and suffix"));
	      op->mode = (op->mode & ~MODE) | LOWREG;
	      op->mode = (op->mode & ~SIZE) | L_16;
	      break;
	    case 'b': case 'B':
	      op->mode = (op->mode & ~MODE) | LOWREG;
	      if (size != L_32 && size != L_8)
		as_warn (_("mismatch between register and suffix"));
	      op->mode = (op->mode & ~MODE) | LOWREG;
	      op->mode = (op->mode & ~SIZE) | L_8;
	      break;
	    default:
	      as_warn (_("invalid suffix after register."));
	      break;
	    }
	  src += 2;
	}
      *ptr = src;
      return;
    }

  if (*src == '@')
    {
      src++;
      if (*src == '@')
	{
	  *ptr = parse_exp (src + 1, op);
	  if (op->exp.X_add_number >= 0x100)
	    {
	      int divisor = 1;

	      op->mode = VECIND;
	      /* FIXME : 2?  or 4?  */
	      if (op->exp.X_add_number >= 0x400)
		as_bad (_("address too high for vector table jmp/jsr"));
	      else if (op->exp.X_add_number >= 0x200)
		divisor = 4;
	      else
		divisor = 2;

	      op->exp.X_add_number = op->exp.X_add_number / divisor - 0x80;
	    }
	  else
	    op->mode = MEMIND;
	  return;
	}

      if (*src == '-' || *src == '+')
	{
	  len = parse_reg (src + 1, &mode, &num, direction);
	  if (len == 0)
	    {
	      /* Oops, not a reg after all, must be ordinary exp.  */
	      op->mode = ABS | direction;
	      *ptr = parse_exp (src, op);
	      return;
	    }

	  if (((mode & SIZE) != PSIZE)
	      /* For Normal mode accept 16 bit and 32 bit pointer registers.  */
	      && (!Nmode || ((mode & SIZE) != L_32)))
	    as_bad (_("Wrong size pointer register for architecture."));

	  op->mode = src[0] == '-' ? RDPREDEC : RDPREINC;
	  op->reg = num;
	  *ptr = src + 1 + len;
	  return;
	}
      if (*src == '(')
	{
	  src++;

	  /* See if this is @(ERn.x, PC).  */
	  len = parse_reg (src, &mode, &op->reg, direction);
	  if (len != 0 && (mode & MODE) == REG && src[len] == '.')
	    {
	      switch (TOLOWER (src[len + 1]))
		{
		case 'b':
		  mode = PCIDXB | direction;
		  break;
		case 'w':
		  mode = PCIDXW | direction;
		  break;
		case 'l':
		  mode = PCIDXL | direction;
		  break;
		default:
		  mode = 0;
		  break;
		}
	      if (mode
		  && src[len + 2] == ','
		  && TOLOWER (src[len + 3]) != 'p'
		  && TOLOWER (src[len + 4]) != 'c'
		  && src[len + 5] != ')')
		{
		  *ptr = src + len + 6;
		  op->mode |= mode;
		  return;
		}
	      /* Fall through into disp case - the grammar is somewhat
		 ambiguous, so we should try whether it's a DISP operand
		 after all ("ER3.L" might be a poorly named label...).  */
	    }

	  /* Disp.  */

	  /* Start off assuming a 16 bit offset.  */

	  src = parse_exp (src, op);
	  if (*src == ')')
	    {
	      op->mode |= ABS | direction;
	      *ptr = src + 1;
	      return;
	    }

	  if (*src != ',')
	    {
	      as_bad (_("expected @(exp, reg16)"));
	      return;
	    }
	  src++;

	  len = parse_reg (src, &mode, &op->reg, direction);
	  if (len == 0 || (mode & MODE) != REG)
	    {
	      as_bad (_("expected @(exp, reg16)"));
	      return;
	    }
	  src += len;
	  if (src[0] == '.')
	    {
	      switch (TOLOWER (src[1]))
		{
		case 'b':
		  op->mode |= INDEXB | direction;
		  break;
		case 'w':
		  op->mode |= INDEXW | direction;
		  break;
		case 'l':
		  op->mode |= INDEXL | direction;
		  break;
		default:
		  as_bad (_("expected .L, .W or .B for register in indexed addressing mode"));
		}
	      src += 2;
	      op->reg &= 7;
	    }
	  else
	    op->mode |= DISP | direction;
	  src = skip_colonthing (src, &op->mode);

	  if (*src != ')')
	    {
	      as_bad (_("expected @(exp, reg16)"));
	      return;
	    }
	  *ptr = src + 1;
	  return;
	}
      len = parse_reg (src, &mode, &num, direction);

      if (len)
	{
	  src += len;
	  if (*src == '+' || *src == '-')
	    {
	      if (((mode & SIZE) != PSIZE)
		  /* For Normal mode accept 16 bit and 32 bit pointer registers.  */
		  && (!Nmode || ((mode & SIZE) != L_32)))
		as_bad (_("Wrong size pointer register for architecture."));
	      op->mode = *src == '+' ? RSPOSTINC : RSPOSTDEC;
	      op->reg = num;
	      src++;
	      *ptr = src;
	      return;
	    }
	  if (((mode & SIZE) != PSIZE)
	      /* For Normal mode accept 16 bit and 32 bit pointer registers.  */
	      && (!Nmode || ((mode & SIZE) != L_32)))
	    as_bad (_("Wrong size pointer register for architecture."));

	  op->mode = direction | IND | PSIZE;
	  op->reg = num;
	  *ptr = src;

	  return;
	}
      else
	{
	  /* must be a symbol */

	  op->mode = ABS | direction;
	  *ptr = parse_exp (src, op);
	  return;
	}
    }

  if (*src == '#')
    {
      op->mode = IMM;
      *ptr = parse_exp (src + 1, op);
      return;
    }
  else if (strncmp (src, "mach", 4) == 0 ||
	   strncmp (src, "macl", 4) == 0 ||
	   strncmp (src, "MACH", 4) == 0 ||
	   strncmp (src, "MACL", 4) == 0)
    {
      op->reg = TOLOWER (src[3]) == 'l';
      op->mode = MACREG;
      *ptr = src + 4;
      return;
    }
  else
    {
      op->mode = PCREL;
      *ptr = parse_exp (src, op);
    }
}

static char *
get_operands (unsigned int noperands, char *op_end, struct h8_op *operand)
{
  char *ptr = op_end;

  switch (noperands)
    {
    case 0:
      break;

    case 1:
      ptr++;
      get_operand (&ptr, operand + 0, SRC);
      if (*ptr == ',')
	{
	  ptr++;
	  get_operand (&ptr, operand + 1, DST);
	}
      break;

    case 2:
      ptr++;
      get_operand (&ptr, operand + 0, SRC);
      if (*ptr == ',')
	ptr++;
      get_operand (&ptr, operand + 1, DST);
      break;

    case 3:
      ptr++;
      get_operand (&ptr, operand + 0, SRC);
      if (*ptr == ',')
	ptr++;
      get_operand (&ptr, operand + 1, DST);
      if (*ptr == ',')
	ptr++;
      get_operand (&ptr, operand + 2, OP3);
      break;

    default:
      abort ();
    }

  return ptr;
}

/* MOVA has special requirements.  Rather than adding twice the amount of
   addressing modes, we simply special case it a bit.  */
static void
get_mova_operands (char *op_end, struct h8_op *operand)
{
  char *ptr = op_end;

  if (ptr[1] != '@' || ptr[2] != '(')
    goto error;
  ptr += 3;
  operand[0].mode = 0;
  ptr = parse_exp (ptr, &operand[0]);

  if (*ptr !=',')
    goto error;
  ptr++;
  get_operand (&ptr, operand + 1, DST);

  if (*ptr =='.')
    {
      ptr++;
      switch (*ptr++)
	{
	case 'b': case 'B':
	  operand[0].mode = (operand[0].mode & ~MODE) | INDEXB;
	  break;
	case 'w': case 'W':
	  operand[0].mode = (operand[0].mode & ~MODE) | INDEXW;
	  break;
	case 'l': case 'L':
	  operand[0].mode = (operand[0].mode & ~MODE) | INDEXL;
	  break;
	default:
	  goto error;
	}
    }
  else if ((operand[1].mode & MODE) == LOWREG)
    {
      switch (operand[1].mode & SIZE)
	{
	case L_8:
	  operand[0].mode = (operand[0].mode & ~MODE) | INDEXB;
	  break;
	case L_16:
	  operand[0].mode = (operand[0].mode & ~MODE) | INDEXW;
	  break;
	case L_32:
	  operand[0].mode = (operand[0].mode & ~MODE) | INDEXL;
	  break;
	default:
	  goto error;
	}
    }
  else
    goto error;

  if (*ptr++ != ')' || *ptr++ != ',')
    goto error;
  get_operand (&ptr, operand + 2, OP3);
  /* See if we can use the short form of MOVA.  */
  if (((operand[1].mode & MODE) == REG || (operand[1].mode & MODE) == LOWREG)
      && (operand[2].mode & MODE) == REG
      && (operand[1].reg & 7) == (operand[2].reg & 7))
    {
      operand[1].mode = operand[2].mode = 0;
      operand[0].reg = operand[2].reg & 7;
    }
  return;

 error:
  as_bad (_("expected valid addressing mode for mova: \"@(disp, ea.sz),ERn\""));
}

static void
get_rtsl_operands (char *ptr, struct h8_op *operand)
{
  int mode, len, type = 0;
  unsigned int num, num2;

  ptr++;
  if (*ptr == '(')
    {
      ptr++;
      type = 1;
    }
  len = parse_reg (ptr, &mode, &num, SRC);
  if (len == 0 || (mode & MODE) != REG)
    {
      as_bad (_("expected register"));
      return;
    }
  ptr += len;
  if (*ptr == '-')
    {
      len = parse_reg (++ptr, &mode, &num2, SRC);
      if (len == 0 || (mode & MODE) != REG)
	{
	  as_bad (_("expected register"));
	  return;
	}
      ptr += len;
      /* CONST_xxx are used as placeholders in the opcode table.  */
      num = num2 - num;
      if (num > 3)
	{
	  as_bad (_("invalid register list"));
	  return;
	}
    }
  else
    num2 = num, num = 0;
  if (type == 1 && *ptr++ != ')')
    {
      as_bad (_("expected closing paren"));
      return;
    }
  operand[0].mode = RS32;
  operand[1].mode = RD32;
  operand[0].reg = num;
  operand[1].reg = num2;
}

/* Passed a pointer to a list of opcodes which use different
   addressing modes, return the opcode which matches the opcodes
   provided.  */

static const struct h8_instruction *
get_specific (const struct h8_instruction *instruction,
	      struct h8_op *operands, int size)
{
  const struct h8_instruction *this_try = instruction;
  const struct h8_instruction *found_other = 0, *found_mismatched = 0;
  int found = 0;
  int this_index = instruction->idx;
  int noperands = 0;

  /* There's only one ldm/stm and it's easier to just
     get out quick for them.  */
  if (OP_KIND (instruction->opcode->how) == O_LDM
      || OP_KIND (instruction->opcode->how) == O_STM)
    return this_try;

  while (noperands < 3 && operands[noperands].mode != 0)
    noperands++;

  while (this_index == instruction->idx && !found)
    {
      int this_size;

      found = 1;
      this_try = instruction++;
      this_size = this_try->opcode->how & SN;

      if (this_try->noperands != noperands)
	found = 0;
      else if (this_try->noperands > 0)
	{
	  int i;

	  for (i = 0; i < this_try->noperands && found; i++)
	    {
	      op_type op = this_try->opcode->args.nib[i];
	      int op_mode = op & MODE;
	      int op_size = op & SIZE;
	      int x = operands[i].mode;
	      int x_mode = x & MODE;
	      int x_size = x & SIZE;

	      if (op_mode == LOWREG && (x_mode == REG || x_mode == LOWREG))
		{
		  if ((x_size == L_8 && (operands[i].reg & 8) == 0)
		      || (x_size == L_16 && (operands[i].reg & 8) == 8))
		    as_warn (_("can't use high part of register in operand %d"), i);

		  if (x_size != op_size)
		    found = 0;
		}
	      else if (op_mode == REG)
		{
		  if (x_mode == LOWREG)
		    x_mode = REG;
		  if (x_mode != REG)
		    found = 0;

		  if (x_size == L_P)
		    x_size = (Hmode ? L_32 : L_16);
		  if (op_size == L_P)
		    op_size = (Hmode ? L_32 : L_16);

		  /* The size of the reg is v important.  */
		  if (op_size != x_size)
		    found = 0;
		}
	      else if (op_mode & CTRL)	/* control register */
		{
		  if (!(x_mode & CTRL))
		    found = 0;

		  switch (x_mode)
		    {
		    case CCR:
		      if (op_mode != CCR &&
			  op_mode != CCR_EXR &&
			  op_mode != CC_EX_VB_SB)
			found = 0;
		      break;
		    case EXR:
		      if (op_mode != EXR &&
			  op_mode != CCR_EXR &&
			  op_mode != CC_EX_VB_SB)
			found = 0;
		      break;
		    case MACH:
		      if (op_mode != MACH &&
			  op_mode != MACREG)
			found = 0;
		      break;
		    case MACL:
		      if (op_mode != MACL &&
			  op_mode != MACREG)
			found = 0;
		      break;
		    case VBR:
		      if (op_mode != VBR &&
			  op_mode != VBR_SBR &&
			  op_mode != CC_EX_VB_SB)
			found = 0;
		      break;
		    case SBR:
		      if (op_mode != SBR &&
			  op_mode != VBR_SBR &&
			  op_mode != CC_EX_VB_SB)
			found = 0;
		      break;
		    }
		}
	      else if ((op & ABSJMP) && (x_mode == ABS || x_mode == PCREL))
		{
		  operands[i].mode &= ~MODE;
		  operands[i].mode |= ABSJMP;
		  /* But it may not be 24 bits long.  */
		  if (x_mode == ABS && !Hmode)
		    {
		      operands[i].mode &= ~SIZE;
		      operands[i].mode |= L_16;
		    }
		  if ((operands[i].mode & SIZE) == L_32
		      && (op_mode & SIZE) != L_32)
		   found = 0;
		}
	      else if (x_mode == IMM && op_mode != IMM)
		{
		  offsetT num = operands[i].exp.X_add_number & 0xffffffff;
		  if (op_mode == KBIT || op_mode == DBIT)
		    /* This is ok if the immediate value is sensible.  */;
		  else if (op_mode == CONST_2)
		    found = num == 2;
		  else if (op_mode == CONST_4)
		    found = num == 4;
		  else if (op_mode == CONST_8)
		    found = num == 8;
		  else if (op_mode == CONST_16)
		    found = num == 16;
		  else
		    found = 0;
		}
	      else if (op_mode == PCREL && op_mode == x_mode)
		{
		  /* movsd, bsr/bc and bsr/bs only come in PCREL16 flavour:
		     If x_size is L_8, promote it.  */
		  if (OP_KIND (this_try->opcode->how) == O_MOVSD
		      || OP_KIND (this_try->opcode->how) == O_BSRBC
		      || OP_KIND (this_try->opcode->how) == O_BSRBS)
		    if (x_size == L_8)
		      x_size = L_16;

		  /* The size of the displacement is important.  */
		  if (op_size != x_size)
		    found = 0;
		}
	      else if ((op_mode == DISP || op_mode == IMM || op_mode == ABS
			|| op_mode == INDEXB || op_mode == INDEXW
			|| op_mode == INDEXL)
		       && op_mode == x_mode)
		{
		  /* Promote a L_24 to L_32 if it makes us match.  */
		  if (x_size == L_24 && op_size == L_32)
		    {
		      x &= ~SIZE;
		      x |= x_size = L_32;
		    }

		  if (((x_size == L_16 && op_size == L_16U)
		       || (x_size == L_8 && op_size == L_8U)
		       || (x_size == L_3 && op_size == L_3NZ))
		      /* We're deliberately more permissive for ABS modes.  */
		      && (op_mode == ABS
			  || constant_fits_size_p (operands + i, op_size,
						   op & NO_SYMBOLS)))
		    x_size = op_size;

		  if (x_size != 0 && op_size != x_size)
		    found = 0;
		  else if (x_size == 0
			   && ! constant_fits_size_p (operands + i, op_size,
						      op & NO_SYMBOLS))
		    found = 0;
		}
	      else if (op_mode != x_mode)
		{
		  found = 0;
		}
	    }
	}
      if (found)
	{
	  if ((this_try->opcode->available == AV_H8SX && ! SXmode)
	      || (this_try->opcode->available == AV_H8S && ! Smode)
	      || (this_try->opcode->available == AV_H8H && ! Hmode))
	    found = 0, found_other = this_try;
	  else if (this_size != size && (this_size != SN && size != SN))
	    found_mismatched = this_try, found = 0;

	}
    }
  if (found)
    return this_try;
  if (found_other)
    {
      as_warn (_("Opcode `%s' with these operand types not available in %s mode"),
	       found_other->opcode->name,
	       (! Hmode && ! Smode ? "H8/300"
		: SXmode ? "H8sx"
		: Smode ? "H8/300S"
		: "H8/300H"));
    }
  else if (found_mismatched)
    {
      as_warn (_("mismatch between opcode size and operand size"));
      return found_mismatched;
    }
  return 0;
}

static void
check_operand (struct h8_op *operand, unsigned int width, const char *string)
{
  if (operand->exp.X_add_symbol == 0
      && operand->exp.X_op_symbol == 0)
    {
      /* No symbol involved, let's look at offset, it's dangerous if
	 any of the high bits are not 0 or ff's, find out by oring or
	 anding with the width and seeing if the answer is 0 or all
	 fs.  */

      if (! constant_fits_width_p (operand, width))
	{
	  if (width == 255
	      && (operand->exp.X_add_number & 0xff00) == 0xff00)
	    {
	      /* Just ignore this one - which happens when trying to
		 fit a 16 bit address truncated into an 8 bit address
		 of something like bset.  */
	    }
	  else if (strcmp (string, "@") == 0
		   && width == 0xffff
		   && (operand->exp.X_add_number & 0xff8000) == 0xff8000)
	    {
	      /* Just ignore this one - which happens when trying to
		 fit a 24 bit address truncated into a 16 bit address
		 of something like mov.w.  */
	    }
	  else
	    {
	      as_warn (_("operand %s0x%lx out of range."), string,
		       (unsigned long) operand->exp.X_add_number);
	    }
	}
    }
}

/* RELAXMODE has one of 3 values:

   0 Output a "normal" reloc, no relaxing possible for this insn/reloc

   1 Output a relaxable 24bit absolute mov.w address relocation
     (may relax into a 16bit absolute address).

   2 Output a relaxable 16/24 absolute mov.b address relocation
     (may relax into an 8bit absolute address).  */

static void
do_a_fix_imm (int offset, int nibble, struct h8_op *operand, int relaxmode, const struct h8_instruction *this_try)
{
  int idx;
  int size;
  int where;
  char *bytes = frag_now->fr_literal + offset;

  const char *t = ((operand->mode & MODE) == IMM) ? "#" : "@";

  if (operand->exp.X_add_symbol == 0)
    {
      switch (operand->mode & SIZE)
	{
	case L_2:
	  check_operand (operand, 0x3, t);
	  bytes[0] |= (operand->exp.X_add_number & 3) << (nibble ? 0 : 4);
	  break;
	case L_3:
	case L_3NZ:
	  check_operand (operand, 0x7, t);
	  bytes[0] |= (operand->exp.X_add_number & 7) << (nibble ? 0 : 4);
	  break;
	case L_4:
	  check_operand (operand, 0xF, t);
	  bytes[0] |= (operand->exp.X_add_number & 15) << (nibble ? 0 : 4);
	  break;
	case L_5:
	  check_operand (operand, 0x1F, t);
	  bytes[0] |= operand->exp.X_add_number & 31;
	  break;
	case L_8:
	case L_8U:
	  check_operand (operand, 0xff, t);
	  bytes[0] |= operand->exp.X_add_number;
	  break;
	case L_16:
	case L_16U:
	  check_operand (operand, 0xffff, t);
	  bytes[0] |= operand->exp.X_add_number >> 8;
	  bytes[1] |= operand->exp.X_add_number >> 0;
	  /* MOVA needs both relocs to relax the second operand properly.  */
	  if (relaxmode != 0
	      && (OP_KIND(this_try->opcode->how) == O_MOVAB
		  || OP_KIND(this_try->opcode->how) == O_MOVAW
		  || OP_KIND(this_try->opcode->how) == O_MOVAL))
	    {
	      idx = BFD_RELOC_16;
	      fix_new_exp (frag_now, offset, 2, &operand->exp, 0, idx);
	    }
	  break;
	case L_24:
	  check_operand (operand, 0xffffff, t);
	  bytes[0] |= operand->exp.X_add_number >> 16;
	  bytes[1] |= operand->exp.X_add_number >> 8;
	  bytes[2] |= operand->exp.X_add_number >> 0;
	  break;

	case L_32:
	  /* This should be done with bfd.  */
	  bytes[0] |= operand->exp.X_add_number >> 24;
	  bytes[1] |= operand->exp.X_add_number >> 16;
	  bytes[2] |= operand->exp.X_add_number >> 8;
	  bytes[3] |= operand->exp.X_add_number >> 0;
	  if (relaxmode != 0)
	    {
	      if ((operand->mode & MODE) == DISP && relaxmode == 1)
		idx = BFD_RELOC_H8_DISP32A16;
	      else
		idx = (relaxmode == 2) ? R_MOV24B1 : R_MOVL1;
	      fix_new_exp (frag_now, offset, 4, &operand->exp, 0, idx);
	    }
	  break;
	}
    }
  else
    {
      switch (operand->mode & SIZE)
	{
	case L_24:
	case L_32:
	  size = 4;
	  where = (operand->mode & SIZE) == L_24 ? -1 : 0;
	  if ((operand->mode & MODE) == DISP && relaxmode == 1)
	    idx = BFD_RELOC_H8_DISP32A16;
	  else if (relaxmode == 2)
	    idx = R_MOV24B1;
	  else if (relaxmode == 1)
	    idx = R_MOVL1;
	  else
	    idx = R_RELLONG;
	  break;
	default:
	  as_bad (_("Can't work out size of operand.\n"));
	  /* Fall through.  */
	case L_16:
	case L_16U:
	  size = 2;
	  where = 0;
	  if (relaxmode == 2)
	    idx = R_MOV16B1;
	  else
	    idx = R_RELWORD;
	  operand->exp.X_add_number =
	    ((operand->exp.X_add_number & 0xffff) ^ 0x8000) - 0x8000;
	  operand->exp.X_add_number |= (bytes[0] << 8) | bytes[1];
	  break;
	case L_8:
	  size = 1;
	  where = 0;
	  idx = R_RELBYTE;
	  operand->exp.X_add_number =
	    ((operand->exp.X_add_number & 0xff) ^ 0x80) - 0x80;
	  operand->exp.X_add_number |= bytes[0];
	}

      fix_new_exp (frag_now,
		   offset + where,
		   size,
		   &operand->exp,
		   0,
		   idx);
    }
}

/* Now we know what sort of opcodes it is, let's build the bytes.  */

static void
build_bytes (const struct h8_instruction *this_try, struct h8_op *operand)
{
  int i;
  char *output = frag_more (this_try->length);
  const op_type *nibble_ptr = this_try->opcode->data.nib;
  op_type c;
  unsigned int nibble_count = 0;
  int op_at[3];
  int nib = 0;
  int movb = 0;
  char asnibbles[100];
  char *p = asnibbles;
  int high, low;

  if (!Hmode && this_try->opcode->available != AV_H8)
    as_warn (_("Opcode `%s' with these operand types not available in H8/300 mode"),
	     this_try->opcode->name);
  else if (!Smode
	   && this_try->opcode->available != AV_H8
	   && this_try->opcode->available != AV_H8H)
    as_warn (_("Opcode `%s' with these operand types not available in H8/300H mode"),
	     this_try->opcode->name);
  else if (!SXmode
	   && this_try->opcode->available != AV_H8
	   && this_try->opcode->available != AV_H8H
	   && this_try->opcode->available != AV_H8S)
    as_warn (_("Opcode `%s' with these operand types not available in H8/300S mode"),
	     this_try->opcode->name);

  while (*nibble_ptr != (op_type) E)
    {
      int d;

      nib = 0;
      c = *nibble_ptr++;

      d = (c & OP3) == OP3 ? 2 : (c & DST) == DST ? 1 : 0;

      if (c < 16)
	nib = c;
      else
	{
	  int c2 = c & MODE;

	  if (c2 == REG || c2 == LOWREG
	      || c2 == IND || c2 == PREINC || c2 == PREDEC
	      || c2 == POSTINC || c2 == POSTDEC)
	    {
	      nib = operand[d].reg;
	      if (c2 == LOWREG)
		nib &= 7;
	    }

	  else if (c & CTRL)	/* Control reg operand.  */
	    nib = operand[d].reg;

	  else if ((c & DISPREG) == (DISPREG))
	    {
	      nib = operand[d].reg;
	    }
	  else if (c2 == ABS)
	    {
	      operand[d].mode = c;
	      op_at[d] = nibble_count;
	      nib = 0;
	    }
	  else if (c2 == IMM || c2 == PCREL || c2 == ABS
		   || (c & ABSJMP) || c2 == DISP)
	    {
	      operand[d].mode = c;
	      op_at[d] = nibble_count;
	      nib = 0;
	    }
	  else if ((c & IGNORE) || (c & DATA))
	    nib = 0;

	  else if (c2 == DBIT)
	    {
	      switch (operand[0].exp.X_add_number)
		{
		case 1:
		  nib = c;
		  break;
		case 2:
		  nib = 0x8 | c;
		  break;
		default:
		  as_bad (_("Need #1 or #2 here"));
		}
	    }
	  else if (c2 == KBIT)
	    {
	      switch (operand[0].exp.X_add_number)
		{
		case 1:
		  nib = 0;
		  break;
		case 2:
		  nib = 8;
		  break;
		case 4:
		  if (!Hmode)
		    as_warn (_("#4 not valid on H8/300."));
		  nib = 9;
		  break;

		default:
		  as_bad (_("Need #1 or #2 here"));
		  break;
		}
	      /* Stop it making a fix.  */
	      operand[0].mode = 0;
	    }

	  if (c & MEMRELAX)
	    operand[d].mode |= MEMRELAX;

	  if (c & B31)
	    nib |= 0x8;

	  if (c & B21)
	    nib |= 0x4;

	  if (c & B11)
	    nib |= 0x2;

	  if (c & B01)
	    nib |= 0x1;

	  if (c2 == MACREG)
	    {
	      if (operand[0].mode == MACREG)
		/* stmac has mac[hl] as the first operand.  */
		nib = 2 + operand[0].reg;
	      else
		/* ldmac has mac[hl] as the second operand.  */
		nib = 2 + operand[1].reg;
	    }
	}
      nibble_count++;

      *p++ = nib;
    }

  /* Disgusting.  Why, oh why didn't someone ask us for advice
     on the assembler format.  */
  if (OP_KIND (this_try->opcode->how) == O_LDM)
    {
      high = (operand[1].reg >> 8) & 0xf;
      low  = (operand[1].reg) & 0xf;
      asnibbles[2] = high - low;
      asnibbles[7] = high;
    }
  else if (OP_KIND (this_try->opcode->how) == O_STM)
    {
      high = (operand[0].reg >> 8) & 0xf;
      low  = (operand[0].reg) & 0xf;
      asnibbles[2] = high - low;
      asnibbles[7] = low;
    }

  for (i = 0; i < this_try->length; i++)
    output[i] = (asnibbles[i * 2] << 4) | asnibbles[i * 2 + 1];

  /* Note if this is a mov.b or a bit manipulation instruction
     there is a special relaxation which only applies.  */
  if (   this_try->opcode->how == O (O_MOV,   SB)
      || this_try->opcode->how == O (O_BCLR,  SB)
      || this_try->opcode->how == O (O_BAND,  SB)
      || this_try->opcode->how == O (O_BIAND, SB)
      || this_try->opcode->how == O (O_BILD,  SB)
      || this_try->opcode->how == O (O_BIOR,  SB)
      || this_try->opcode->how == O (O_BIST,  SB)
      || this_try->opcode->how == O (O_BIXOR, SB)
      || this_try->opcode->how == O (O_BLD,   SB)
      || this_try->opcode->how == O (O_BNOT,  SB)
      || this_try->opcode->how == O (O_BOR,   SB)
      || this_try->opcode->how == O (O_BSET,  SB)
      || this_try->opcode->how == O (O_BST,   SB)
      || this_try->opcode->how == O (O_BTST,  SB)
      || this_try->opcode->how == O (O_BXOR,  SB))
    movb = 1;

  /* Output any fixes.  */
  for (i = 0; i < this_try->noperands; i++)
    {
      int x = operand[i].mode;
      int x_mode = x & MODE;

      if (x_mode == IMM || x_mode == DISP)
	do_a_fix_imm (output - frag_now->fr_literal + op_at[i] / 2,
		      op_at[i] & 1, operand + i, (x & MEMRELAX) != 0,
		      this_try);
      else if (x_mode == ABS)
	do_a_fix_imm (output - frag_now->fr_literal + op_at[i] / 2,
		      op_at[i] & 1, operand + i,
		      (x & MEMRELAX) ? movb + 1 : 0,
		      this_try);

      else if (x_mode == PCREL)
	{
	  int size16 = (x & SIZE) == L_16;
	  int size = size16 ? 2 : 1;
	  int type = size16 ? R_PCRWORD : R_PCRBYTE;
	  fixS *fixP;

	  check_operand (operand + i, size16 ? 0x7fff : 0x7f, "@");

	  if (operand[i].exp.X_add_number & 1)
	    as_warn (_("branch operand has odd offset (%lx)\n"),
		     (unsigned long) operand->exp.X_add_number);
	  if (size16)
	    {
	      operand[i].exp.X_add_number =
		((operand[i].exp.X_add_number & 0xffff) ^ 0x8000) - 0x8000;
	    }
	  else
	    {
	      operand[i].exp.X_add_number =
		((operand[i].exp.X_add_number & 0xff) ^ 0x80) - 0x80;
	    }

	  /* For BRA/S.  */
	  if (! size16)
	    operand[i].exp.X_add_number |= output[op_at[i] / 2];

	  fixP = fix_new_exp (frag_now,
			      output - frag_now->fr_literal + op_at[i] / 2,
			      size,
			      &operand[i].exp,
			      1,
			      type);
	  fixP->fx_signed = 1;
	}
      else if (x_mode == MEMIND)
	{
	  check_operand (operand + i, 0xff, "@@");
	  fix_new_exp (frag_now,
		       output - frag_now->fr_literal + 1,
		       1,
		       &operand[i].exp,
		       0,
		       R_MEM_INDIRECT);
	}
      else if (x_mode == VECIND)
	{
	  check_operand (operand + i, 0x7f, "@@");
	  /* FIXME: approximating the effect of "B31" here...
	     This is very hackish, and ought to be done a better way.  */
	  operand[i].exp.X_add_number |= 0x80;
	  fix_new_exp (frag_now,
		       output - frag_now->fr_literal + 1,
		       1,
		       &operand[i].exp,
		       0,
		       R_MEM_INDIRECT);
	}
      else if (x & ABSJMP)
	{
	  int where = 0;
	  bfd_reloc_code_real_type reloc_type = R_JMPL1;

	  /* To be compatible with the proposed H8 ELF format, we
	     want the relocation's offset to point to the first byte
	     that will be modified, not to the start of the instruction.  */

	  if ((operand->mode & SIZE) == L_32)
	    {
	      where = 2;
	      reloc_type = R_RELLONG;
	    }
	  else
	    where = 1;

	  /* This jmp may be a jump or a branch.  */

	  check_operand (operand + i,
			 SXmode ? 0xffffffff : Hmode ? 0xffffff : 0xffff,
			 "@");

	  if (operand[i].exp.X_add_number & 1)
	    as_warn (_("branch operand has odd offset (%lx)\n"),
		     (unsigned long) operand->exp.X_add_number);

	  if (!Hmode)
	    operand[i].exp.X_add_number =
	      ((operand[i].exp.X_add_number & 0xffff) ^ 0x8000) - 0x8000;
	  fix_new_exp (frag_now,
		       output - frag_now->fr_literal + where,
		       4,
		       &operand[i].exp,
		       0,
		       reloc_type);
	}
    }
}

/* Try to give an intelligent error message for common and simple to
   detect errors.  */

static void
clever_message (const struct h8_instruction *instruction,
		struct h8_op *operand)
{
  /* Find out if there was more than one possible opcode.  */

  if ((instruction + 1)->idx != instruction->idx)
    {
      int argn;

      /* Only one opcode of this flavour, try to guess which operand
         didn't match.  */
      for (argn = 0; argn < instruction->noperands; argn++)
	{
	  switch (instruction->opcode->args.nib[argn])
	    {
	    case RD16:
	      if (operand[argn].mode != RD16)
		{
		  as_bad (_("destination operand must be 16 bit register"));
		  return;

		}
	      break;

	    case RS8:
	      if (operand[argn].mode != RS8)
		{
		  as_bad (_("source operand must be 8 bit register"));
		  return;
		}
	      break;

	    case ABS16DST:
	      if (operand[argn].mode != ABS16DST)
		{
		  as_bad (_("destination operand must be 16bit absolute address"));
		  return;
		}
	      break;
	    case RD8:
	      if (operand[argn].mode != RD8)
		{
		  as_bad (_("destination operand must be 8 bit register"));
		  return;
		}
	      break;

	    case ABS16SRC:
	      if (operand[argn].mode != ABS16SRC)
		{
		  as_bad (_("source operand must be 16bit absolute address"));
		  return;
		}
	      break;

	    }
	}
    }
  as_bad (_("invalid operands"));
}


/* If OPERAND is part of an address, adjust its size and value given
   that it addresses SIZE bytes.

   This function decides how big non-immediate constants are when no
   size was explicitly given.  It also scales down the assembly-level
   displacement in an @(d:2,ERn) operand.  */

static void
fix_operand_size (struct h8_op *operand, int size)
{
  if (SXmode && (operand->mode & MODE) == DISP)
    {
      /* If the user didn't specify an operand width, see if we
	 can use @(d:2,ERn).  */
      if ((operand->mode & SIZE) == 0
	  && operand->exp.X_add_symbol == 0
	  && operand->exp.X_op_symbol == 0
	  && (operand->exp.X_add_number == size
	      || operand->exp.X_add_number == size * 2
	      || operand->exp.X_add_number == size * 3))
	operand->mode |= L_2;

      /* Scale down the displacement in an @(d:2,ERn) operand.
	 X_add_number then contains the desired field value.  */
      if ((operand->mode & SIZE) == L_2)
	{
	  if (operand->exp.X_add_number % size != 0)
	    as_warn (_("operand/size mis-match"));
	  operand->exp.X_add_number /= size;
	}
    }

  if ((operand->mode & SIZE) == 0)
    switch (operand->mode & MODE)
      {
      case DISP:
      case INDEXB:
      case INDEXW:
      case INDEXL:
      case ABS:
	/* Pick a 24-bit address unless we know that a 16-bit address
	   is safe.  get_specific() will relax L_24 into L_32 where
	   necessary.  */
	if (Hmode
	    && !Nmode
	    && ((((addressT) operand->exp.X_add_number + 0x8000)
		 & 0xffffffff) > 0xffff
		|| operand->exp.X_add_symbol != 0
		|| operand->exp.X_op_symbol != 0))
	  operand->mode |= L_24;
	else
	  operand->mode |= L_16;
	break;

      case PCREL:
	if ((((addressT) operand->exp.X_add_number + 0x80)
	     & 0xffffffff) <= 0xff)
	  {
	    if (operand->exp.X_add_symbol != NULL)
	      operand->mode |= bsize;
	    else
	      operand->mode |= L_8;
	  }
	else
	  operand->mode |= L_16;
	break;
      }
}


/* This is the guts of the machine-dependent assembler.  STR points to
   a machine dependent instruction.  This function is supposed to emit
   the frags/bytes it assembles.  */

void
md_assemble (char *str)
{
  char *op_start;
  char *op_end;
  struct h8_op operand[3];
  const struct h8_instruction *instruction;
  const struct h8_instruction *prev_instruction;

  char *dot = 0;
  char *slash = 0;
  char c;
  int size, i;

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

  /* Find the op code end.  */
  for (op_start = op_end = str;
       *op_end != 0 && *op_end != ' ';
       op_end++)
    {
      if (*op_end == '.')
	{
	  dot = op_end + 1;
	  *op_end = 0;
	  op_end += 2;
	  break;
	}
      else if (*op_end == '/' && ! slash)
	slash = op_end;
    }

  if (op_end == op_start)
    {
      as_bad (_("can't find opcode "));
    }
  c = *op_end;

  *op_end = 0;

  /* The assembler stops scanning the opcode at slashes, so it fails
     to make characters following them lower case.  Fix them.  */
  if (slash)
    while (*++slash)
      *slash = TOLOWER (*slash);

  instruction = (const struct h8_instruction *)
    hash_find (opcode_hash_control, op_start);

  if (instruction == NULL)
    {
      as_bad (_("unknown opcode"));
      return;
    }

  /* We used to set input_line_pointer to the result of get_operands,
     but that is wrong.  Our caller assumes we don't change it.  */

  operand[0].mode = 0;
  operand[1].mode = 0;
  operand[2].mode = 0;

  if (OP_KIND (instruction->opcode->how) == O_MOVAB
      || OP_KIND (instruction->opcode->how) == O_MOVAW
      || OP_KIND (instruction->opcode->how) == O_MOVAL)
    get_mova_operands (op_end, operand);
  else if (OP_KIND (instruction->opcode->how) == O_RTEL
	   || OP_KIND (instruction->opcode->how) == O_RTSL)
    get_rtsl_operands (op_end, operand);
  else
    get_operands (instruction->noperands, op_end, operand);

  *op_end = c;
  prev_instruction = instruction;

  /* Now we have operands from instruction.
     Let's check them out for ldm and stm.  */
  if (OP_KIND (instruction->opcode->how) == O_LDM)
    {
      /* The first operand must be @er7+, and the
	 second operand must be a register pair.  */
      if ((operand[0].mode != RSINC)
           || (operand[0].reg != 7)
           || ((operand[1].reg & 0x80000000) == 0))
	as_bad (_("invalid operand in ldm"));
    }
  else if (OP_KIND (instruction->opcode->how) == O_STM)
    {
      /* The first operand must be a register pair,
	 and the second operand must be @-er7.  */
      if (((operand[0].reg & 0x80000000) == 0)
            || (operand[1].mode != RDDEC)
            || (operand[1].reg != 7))
	as_bad (_("invalid operand in stm"));
    }

  size = SN;
  if (dot)
    {
      switch (TOLOWER (*dot))
	{
	case 'b':
	  size = SB;
	  break;

	case 'w':
	  size = SW;
	  break;

	case 'l':
	  size = SL;
	  break;
	}
    }
  if (OP_KIND (instruction->opcode->how) == O_MOVAB ||
      OP_KIND (instruction->opcode->how) == O_MOVAW ||
      OP_KIND (instruction->opcode->how) == O_MOVAL)
    {
      switch (operand[0].mode & MODE)
	{
	case INDEXB:
	default:
	  fix_operand_size (&operand[1], 1);
	  break;
	case INDEXW:
	  fix_operand_size (&operand[1], 2);
	  break;
	case INDEXL:
	  fix_operand_size (&operand[1], 4);
	  break;
	}
    }
  else
    {
      for (i = 0; i < 3 && operand[i].mode != 0; i++)
	switch (size)
	  {
	  case SN:
	  case SB:
	  default:
	    fix_operand_size (&operand[i], 1);
	    break;
	  case SW:
	    fix_operand_size (&operand[i], 2);
	    break;
	  case SL:
	    fix_operand_size (&operand[i], 4);
	    break;
	  }
    }

  instruction = get_specific (instruction, operand, size);

  if (instruction == 0)
    {
      /* Couldn't find an opcode which matched the operands.  */
      char *where = frag_more (2);

      where[0] = 0x0;
      where[1] = 0x0;
      clever_message (prev_instruction, operand);

      return;
    }

  build_bytes (instruction, operand);

  dwarf2_emit_insn (instruction->length);
}

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

/* Various routines to kill one day.  */

const char *
md_atof (int type, char *litP, int *sizeP)
{
  return ieee_md_atof (type, litP, sizeP, TRUE);
}

#define OPTION_H_TICK_HEX      (OPTION_MD_BASE)
#define OPTION_MACH            (OPTION_MD_BASE+1)

const char *md_shortopts = "";
struct option md_longopts[] =
{
  { "h-tick-hex", no_argument,	      NULL, OPTION_H_TICK_HEX  },
  { "mach", required_argument, NULL, OPTION_MACH },
  {NULL, no_argument, NULL, 0}
};

size_t md_longopts_size = sizeof (md_longopts);

struct mach_func
{
  const char *name;
  void (*func) (void);
};

static void
mach_h8300h (void)
{
  Hmode = 1;
  Smode = 0;
  Nmode = 0;
  SXmode = 0;
  default_mach = bfd_mach_h8300h;
}

static void
mach_h8300hn (void)
{
  Hmode = 1;
  Smode = 0;
  Nmode = 1;
  SXmode = 0;
  default_mach = bfd_mach_h8300hn;
}

static void
mach_h8300s (void)
{
  Hmode = 1;
  Smode = 1;
  Nmode = 0;
  SXmode = 0;
  default_mach = bfd_mach_h8300s;
}

static void
mach_h8300sn (void)
{
  Hmode = 1;
  Smode = 1;
  Nmode = 1;
  SXmode = 0;
  default_mach = bfd_mach_h8300sn;
}

static void
mach_h8300sx (void)
{
  Hmode = 1;
  Smode = 1;
  Nmode = 0;
  SXmode = 1;
  default_mach = bfd_mach_h8300sx;
}

static void
mach_h8300sxn (void)
{
  Hmode = 1;
  Smode = 1;
  Nmode = 1;
  SXmode = 1;
  default_mach = bfd_mach_h8300sxn;
}

const struct mach_func mach_table[] =
{
  {"h8300h",  mach_h8300h},
  {"h8300hn", mach_h8300hn},
  {"h8300s",  mach_h8300s},
  {"h8300sn", mach_h8300sn},
  {"h8300sx", mach_h8300sx},
  {"h8300sxn", mach_h8300sxn}
};

int
md_parse_option (int c ATTRIBUTE_UNUSED, const char *arg ATTRIBUTE_UNUSED)
{
  unsigned int i;
  switch (c)
    {
    case OPTION_H_TICK_HEX:
      enable_h_tick_hex = 1;
      break;
    case OPTION_MACH:
      for (i = 0; i < sizeof(mach_table) / sizeof(struct mach_func); i++)
	{
	  if (strcasecmp (arg, mach_table[i].name) == 0)
	    {
	      mach_table[i].func();
	      break;
	    }
	}
      if (i >= sizeof(mach_table) / sizeof(struct mach_func))
	as_bad (_("Invalid argument to --mach option: %s"), arg);
      break;
    default:
      return 0;
    }
  return 1;
}

void
md_show_usage (FILE *stream)
{
  fprintf (stream, _(" H8300-specific assembler options:\n"));
  fprintf (stream, _("\
  -mach=<name>             Set the H8300 machine type to one of:\n\
                           h8300h, h8300hn, h8300s, h8300sn, h8300sx, h8300sxn\n"));
  fprintf (stream, _("\
  -h-tick-hex              Support H'00 style hex constants\n"));
}

void tc_aout_fix_to_chars (void);

void
tc_aout_fix_to_chars (void)
{
  printf (_("call to tc_aout_fix_to_chars \n"));
  abort ();
}

void
md_convert_frag (bfd *headers ATTRIBUTE_UNUSED,
		 segT seg ATTRIBUTE_UNUSED,
		 fragS *fragP ATTRIBUTE_UNUSED)
{
  printf (_("call to md_convert_frag \n"));
  abort ();
}

valueT
md_section_align (segT segment, valueT size)
{
  int align = bfd_section_alignment (segment);
  return ((size + (1 << align) - 1) & (-1U << align));
}

void
md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
{
  char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
  long val = *valP;

  switch (fixP->fx_size)
    {
    case 1:
      *buf++ = val;
      break;
    case 2:
      *buf++ = (val >> 8);
      *buf++ = val;
      break;
    case 4:
      *buf++ = (val >> 24);
      *buf++ = (val >> 16);
      *buf++ = (val >> 8);
      *buf++ = val;
      break;
    case 8:
      /* This can arise when the .quad or .8byte pseudo-ops are used.
	 Returning here (without setting fx_done) will cause the code
	 to attempt to generate a reloc which will then fail with the
	 slightly more helpful error message: "Cannot represent
	 relocation type BFD_RELOC_64".  */
      return;
    default:
      abort ();
    }

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

int
md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
			       segT segment_type ATTRIBUTE_UNUSED)
{
  printf (_("call to md_estimate_size_before_relax \n"));
  abort ();
}

/* Put number into target byte order.  */
void
md_number_to_chars (char *ptr, valueT use, int nbytes)
{
  number_to_chars_bigendian (ptr, use, nbytes);
}

long
md_pcrel_from (fixS *fixp)
{
  as_bad_where (fixp->fx_file, fixp->fx_line,
		_("Unexpected reference to a symbol in a non-code section"));
  return 0;
}

arelent *
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
{
  arelent *rel;
  bfd_reloc_code_real_type r_type;

  if (fixp->fx_addsy && fixp->fx_subsy)
    {
      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"));
	  return NULL;
	}
    }

  rel = XNEW (arelent);
  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 = fixp->fx_offset;

  r_type = fixp->fx_r_type;

#define DEBUG 0
#if DEBUG
  fprintf (stderr, "%s\n", bfd_get_reloc_code_name (r_type));
  fflush (stderr);
#endif
  rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
  if (rel->howto == NULL)
    {
      as_bad_where (fixp->fx_file, fixp->fx_line,
		    _("Cannot represent relocation type %s"),
		    bfd_get_reloc_code_name (r_type));
      return NULL;
    }

  return rel;
}
