This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFA] H8/300sx port for gas, sim, and opcodes (2/5)


This is the gas portion of the h8/300sx port.
It depends on the changes to include/opcode/h8300.h, submitted separately.


2003-05-14  Michael Snyder  <msnyder@redhat.com>
	From Bernd Schmidt  <bernds@redhat.com>
	and Michael Snyder  <msnyder@redhat.com>
	and Alexandre Oliva <aoliva@redhat.com>
	* config/tc-h8300.c: Add insns and addressing modes for h8300sx.
        * config/tc-h8300.h: Ditto.
/* This file is tc-h8300.h
   Copyright 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996,
   1997, 1998, 2000, 2002, 2003
   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.  */

#define TC_H8300

#define TARGET_BYTES_BIG_ENDIAN 1

#define TARGET_ARCH bfd_arch_h8300

#ifdef BFD_ASSEMBLER
/* Fixup debug sections since we will never relax them.  */
#define TC_LINKRELAX_FIXUP(seg) (seg->flags & SEC_ALLOC)
#endif
#ifdef OBJ_ELF
#define TARGET_FORMAT "elf32-h8300"
#define LOCAL_LABEL_PREFIX '.'
#define LOCAL_LABEL(NAME) (NAME[0] == '.' && NAME[1] == 'L')
#define FAKE_LABEL_NAME ".L0\001"
#endif

#if ANSI_PROTOTYPES
struct fix;
struct internal_reloc;
#endif

#define WORKING_DOT_WORD

/* This macro translates between an internal fix and a coff reloc type.  */
#define TC_COFF_FIX2RTYPE(fixP) abort ();

#define BFD_ARCH bfd_arch_h8300
#define COFF_MAGIC ( Smode && Nmode ? 0x8304 : Hmode && Nmode ? 0x8303 : Smode ? 0x8302 : Hmode ? 0x8301 : 0x8300)
#define TC_COUNT_RELOC(x) (1)
#define IGNORE_NONSTANDARD_ESCAPES

#define tc_coff_symbol_emit_hook(a) ; /* not used */
#define TC_RELOC_MANGLE(s,a,b,c) tc_reloc_mangle(a,b,c)
extern void tc_reloc_mangle
  PARAMS ((struct fix *, struct internal_reloc *, bfd_vma));

/* No shared lib support, so we don't need to ensure externally
   visible symbols can be overridden.  */
#define EXTERN_FORCE_RELOC 0

/* Minimum instruction is of 16 bits.  */
#define DWARF2_LINE_MIN_INSN_LENGTH 2

#ifdef OBJ_ELF
/* Provide mappings from the original H8 COFF relocation names to
   their corresponding BFD relocation names.  This allows us to use
   most of tc-h8300.c without modifications for both ELF and COFF
   ports.  */
#define R_MOV24B1 BFD_RELOC_H8_DIR24A8
#define R_MOVL1 BFD_RELOC_H8_DIR32A16
#define R_MOV24B1 BFD_RELOC_H8_DIR24A8
#define R_MOVL1 BFD_RELOC_H8_DIR32A16
#define R_RELLONG BFD_RELOC_32
#define R_MOV16B1 BFD_RELOC_H8_DIR16A8
#define R_RELWORD BFD_RELOC_16
#define R_RELBYTE BFD_RELOC_8
#define R_PCRWORD BFD_RELOC_16_PCREL
#define R_PCRBYTE BFD_RELOC_8_PCREL
#define R_JMPL1 BFD_RELOC_H8_DIR24R8
#define R_MEM_INDIRECT BFD_RELOC_8

/* We do not want to adjust any relocations to make implementation of
   linker relaxations easier.  */
#define tc_fix_adjustable(FIX) 0
#endif

#define TC_CONS_RELOC          (Hmode ? R_RELLONG: R_RELWORD)

#define DO_NOT_STRIP 0
#define LISTING_HEADER "Renesas H8/300 GAS "
#define NEED_FX_R_TYPE 1
#ifndef OBJ_ELF
#define RELOC_32 1234
#endif

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

#define md_operand(x)
/* tc-h8300.c -- Assemble code for the Renesas H8/300
   Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000,
   2001, 2002, 2003 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.  */

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

#include <stdio.h>
#include "as.h"
#include "subsegs.h"
#include "bfd.h"

#ifdef BFD_ASSEMBLER
#include "dwarf2dbg.h"
#endif

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

#ifdef OBJ_ELF
#include "elf/h8.h"
#endif

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

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

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

#define PSIZE (Hmode ? L_32 : L_16)
#define DMODE (L_16)
#define DSYMMODE (Hmode ? L_24 : L_16)

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

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

struct h8_instruction *h8_instructions;

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

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

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

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

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

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

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

/* 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},
  {"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},
  {0, 0, 0}
};

const int md_reloc_size;

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 ()
{
  unsigned int nopcodes;
  struct h8_opcode *p, *p1;
  struct h8_instruction *pi;
  char prev_buffer[100];
  int idx = 0;

#ifdef BFD_ASSEMBLER
  if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300))
    as_warn (_("could not set architecture and machine"));
#endif

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

  nopcodes = sizeof (h8_opcodes) / sizeof (struct h8_opcode);
  
  h8_instructions = (struct h8_instruction *)
    xmalloc (nopcodes * sizeof (struct h8_instruction));

  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;
      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 = malloc (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_exp
{
  char *e_beg;
  char *e_end;
  expressionS e_exp;
};

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

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

static int constant_fits_width_p PARAMS ((struct h8_op *, unsigned int));
static int constant_fits_size_p PARAMS ((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 (src, mode, reg, direction)
     char *src;
     op_type *mode;
     unsigned int *reg;
     int direction;
{
  char *end;
  int len;

  /* Cribbed from get_symbol_end.  */
  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;
}

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

  input_line_pointer = s;
  expression (op);
  if (op->X_op == O_absent)
    as_bad (_("missing operand"));
  new = input_line_pointer;
  input_line_pointer = save;
  return new;
}

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

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

/* 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.  */

char *
colonmod24 (op, src)
     struct h8_op *op;
     char *src;
{
  int mode = 0;
  src = skip_colonthing (src, &op->exp, &mode);

  if (!mode)
    {
      /* Choose a default mode.  */
      if (op->exp.X_add_number < -32768
	  || op->exp.X_add_number > 32767)
	{
	  if (Hmode)
	    mode = L_24;
	  else
	    mode = L_16;
	}
      else if (op->exp.X_add_symbol
	       || op->exp.X_op_symbol)
	mode = DSYMMODE;
      else
	mode = DMODE;
    }

  op->mode |= mode;
  return src;
}

static int
constant_fits_width_p (operand, width)
     struct h8_op *operand;
     unsigned int width;
{
  return ((operand->exp.X_add_number & ~width) == 0
	  || (operand->exp.X_add_number | width) == (unsigned)(~0));
}

static int
constant_fits_size_p (operand, size, no_symbols)
     struct h8_op *operand;
     int size, no_symbols;
{
  offsetT num = operand->exp.X_add_number;
  if (no_symbols
      && (operand->exp.X_add_symbol != 0 || operand->exp.X_op_symbol != 0))
    return 0;
  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:
      return (num & ~0xFF) == 0 || ((unsigned)num | 0x7F) == ~0u;
    case L_8U:
      return (num & ~0xFF) == 0;
    case L_16:
      return (num & ~0xFFFF) == 0 || ((unsigned)num | 0x7FFF) == ~0u;
    case L_16U:
      return (num & ~0xFFFF) == 0;
    case L_32:
      return 1;
    default:
      abort ();
    }
}

static void
get_operand (ptr, op, direction)
     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';

      if (high == low)
	as_bad (_("Invalid register list for ldm/stm\n"));

      if (high < low)
	as_bad (_("Invalid register list for ldm/stm\n"));

      if (high - low > 3)
	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 == '@')
	{
	  src++;
	  src = parse_exp (src, &op->exp);

	  src = skip_colonthing (src, &op->exp, &op->mode);

	  *ptr = src;

	  if (op->exp.X_add_number >= 0x100)
	    {
	      int divisor;

	      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 == '+')
	{
	  char c = *src;
	  src++;
	  len = parse_reg (src, &mode, &num, direction);
	  if (len == 0)
	    {
	      /* Oops, not a reg after all, must be ordinary exp.  */
	      src--;
	      /* Must be a symbol.  */
	      op->mode = ABS | PSIZE | direction;
	      *ptr = skip_colonthing (parse_exp (src, &op->exp),
				      &op->exp, &op->mode);

	      return;
	    }

	  if ((mode & SIZE) != PSIZE)
	    as_bad (_("Wrong size pointer register for architecture."));
	  op->mode = c == '-' ? RDPREDEC : RDPREINC;
	  op->reg = num;
	  *ptr = src + 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->exp);

	  src = colonmod24 (op, src);

	  if (*src == ')')
	    {
	      src++;
	      op->mode |= ABS | direction;
	      *ptr = src;
	      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->exp, &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)
		as_bad (_("Wrong size pointer register for architecture."));
	      op->mode = *src == '+' ? RSPOSTINC : RSPOSTDEC;
	      op->reg = num;
	      src++;
	      *ptr = src;
	      return;
	    }
	  if ((mode & SIZE) != PSIZE)
	    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;
	  src = parse_exp (src, &op->exp);

	  *ptr = colonmod24 (op, src);

	  return;
	}
    }

  if (*src == '#')
    {
      src++;
      op->mode = IMM;
      src = parse_exp (src, &op->exp);
      *ptr = skip_colonthing (src, &op->exp, &op->mode);

      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
    {
      src = parse_exp (src, &op->exp);
      /* Trailing ':' size ? */
      if (*src == ':')
	{
	  if (src[1] == '1' && src[2] == '6')
	    {
	      op->mode = PCREL | L_16;
	      src += 3;
	    }
	  else if (src[1] == '8')
	    {
	      op->mode = PCREL | L_8;
	      src += 2;
	    }
	  else
	    as_bad (_("expect :8 or :16 here"));
	}
      else
	{
	  int val = op->exp.X_add_number;

	  op->mode = PCREL;
	  if (-128 < val && val < 127)
	    op->mode |= L_8;
	  else
	    op->mode |= L_16;
	}

      *ptr = src;
    }
}

static char *
get_operands (noperands, op_end, operand)
     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].exp);
  ptr = colonmod24 (operand + 0, ptr);

  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\""));
  return;
}

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

  ptr++;
  if (*ptr == '(')
    {
      ptr++;
      type = 1;
    }
  len = parse_reg (ptr, &mode, &num, SRC);
  if (len == 0 || (mode & MODE) != REG)
    {
      as_bad (_("expected register"));
      return;
    }
  if (type == 1)
    {
      ptr += len;
      if (*ptr++ != '-')
	{
	  as_bad (_("expected register list"));
	  return;
	}
      len = parse_reg (ptr, &mode, &num2, SRC);
      if (len == 0 || (mode & MODE) != REG)
	{
	  as_bad (_("expected register"));
	  return;
	}
      ptr += len;
      if (*ptr++ != ')')
	{
	  as_bad (_("expected closing paren"));
	  return;
	}
      /* CONST_xxx are used as placeholders in the opcode table.  */
      num = num2 - num;
      if (num < 1 || num > 3)
	{
	  as_bad (_("invalid register list"));
	  return;
	}
    }
  else
    num2 = num, num = 0;
  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 (instruction, operands, size)
     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;
		  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 only comes in PCREL16 flavour:
		     If x_size is L_8, promote it.  */
		  if (OP_KIND (this_try->opcode->how) == O_MOVSD)
		    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 0 /* ??? */
		  /* Promote an L8 to L_16 if it makes us match.  */
		  if ((op_mode == ABS || op_mode == DISP) && x_size == L_8)
		    {
		      if (op_size == L_16)
			x_size = L_16;
		    }
#endif

		  if (((x_size == L_16 && op_size == L_16U)
		       || (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_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 (operand, width, string)
     struct h8_op *operand;
     unsigned int width;
     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 (offset, nibble, operand, relaxmode)
     int offset, nibble;
     struct h8_op *operand;
     int relaxmode;
{
  int idx;
  int size;
  int where;
  char *bytes = frag_now->fr_literal + offset;

  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;
	  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)
	    {
	      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 (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"));
	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 (this_try, operand)
     const struct h8_instruction *this_try;
     struct h8_op *operand;
{
  int i;
  char *output = frag_more (this_try->length);
  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 (!(this_try->opcode->available == AV_H8 || Hmode))
    as_warn (_("Opcode `%s' with these operand types not available in H8/300 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 movb instruction -- there's a special relaxation
     which only applies to them.  */
  if (this_try->opcode->how == O (O_MOV, 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);

      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);

      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);
#ifndef OBJ_ELF
	  /* The COFF port has always been off by one, changing it
	     now would be an incompatible change, so we leave it as-is.

	     We don't want to do this for ELF as we want to be
	     compatible with the proposed ELF format from Hitachi.  */
	  operand[i].exp.X_add_number -= 1;
#endif
	  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;

#ifdef OBJ_ELF
	  /* 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;
#endif

	  /* 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 (instruction, operand)
     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"));
}

/* 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 (str)
     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 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;
	}
    }

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

  *op_end = 0;

  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;

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

	case 'w':
	  size = SW;
	  break;

	case 'l':
	  size = SL;
	  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;
    }

  /* This is the earliest point at which we can do this:
     any DISP2 operands need to be fixed-up according to 
     the size of the operation.  */
  /* MOVA is a whole different set of rules...  */
  if (OP_KIND (instruction->opcode->how) == O_MOVAB ||
      OP_KIND (instruction->opcode->how) == O_MOVAW ||
      OP_KIND (instruction->opcode->how) == O_MOVAL)
    {
      if ((operand[1].mode & MODE) == DISP &&
	  (operand[1].mode & SIZE) == L_2)
	switch (operand[0].mode & MODE) {
	case INDEXB:
	default:
	  break;
	case INDEXW:
	  if (operand[1].exp.X_add_number % 2)
	    as_warn (_("operand/size mis-match"));
	  operand[1].exp.X_add_number /= 2;
	  break;
	case INDEXL:
	  if (operand[1].exp.X_add_number % 4)
	    as_warn (_("operand/size mis-match"));
	  operand[1].exp.X_add_number /= 4;
	  break;
	}
    }
  else
    {
      for (i = 0; i < instruction->noperands; i++)
	if ((operand[i].mode & MODE) == DISP &&
	    (operand[i].mode & SIZE) == L_2)
	  switch (size) {
	  case SN:
	  case SB:
	  default:
	    break;
	  case SW:
	    if (operand[i].exp.X_add_number % 2)
	      as_warn (_("operand/size mis-match"));
	    operand[i].exp.X_add_number /= 2;
	    break;
	  case SL:
	    if (operand[i].exp.X_add_number % 4)
	      as_warn (_("operand/size mis-match"));
	    operand[i].exp.X_add_number /= 4;
	    break;
	  }
    }

  build_bytes (instruction, operand);

#ifdef BFD_ASSEMBLER
  dwarf2_emit_insn (instruction->length);
#endif
}

#ifndef BFD_ASSEMBLER
void
tc_crawl_symbol_chain (headers)
     object_headers *headers ATTRIBUTE_UNUSED;
{
  printf (_("call to tc_crawl_symbol_chain \n"));
}
#endif

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

#ifndef BFD_ASSEMBLER
void
tc_headers_hook (headers)
     object_headers *headers ATTRIBUTE_UNUSED;
{
  printf (_("call to tc_headers_hook \n"));
}
#endif

/* Various routines to kill one day */
/* Equal to MAX_PRECISION in atof-ieee.c */
#define MAX_LITTLENUMS 6

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

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

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

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

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

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

    default:
      *sizeP = 0;
      return _("Bad call to MD_ATOF()");
    }
  t = atof_ieee (input_line_pointer, type, words);
  if (t)
    input_line_pointer = t;

  *sizeP = prec * sizeof (LITTLENUM_TYPE);
  for (wordP = words; prec--;)
    {
      md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE));
      litP += sizeof (LITTLENUM_TYPE);
    }
  return 0;
}

const char *md_shortopts = "";
struct option md_longopts[] = {
  {NULL, no_argument, NULL, 0}
};

size_t md_longopts_size = sizeof (md_longopts);

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

void
md_show_usage (stream)
     FILE *stream ATTRIBUTE_UNUSED;
{
}

void tc_aout_fix_to_chars PARAMS ((void));

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

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

#ifdef BFD_ASSEMBLER
valueT
md_section_align (segment, size)
     segT segment;
     valueT size;
{
  int align = bfd_get_section_alignment (stdoutput, segment);
  return ((size + (1 << align) - 1) & (-1 << align));
}
#else
valueT
md_section_align (seg, size)
     segT seg;
     valueT size;
{
  return ((size + (1 << section_alignment[(int) seg]) - 1)
	  & (-1 << section_alignment[(int) seg]));
}
#endif


void
md_apply_fix3 (fixP, valP, seg)
     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;
    default:
      abort ();
    }

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

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

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

long
md_pcrel_from (fixP)
     fixS *fixP ATTRIBUTE_UNUSED;
{
  abort ();
}

#ifndef BFD_ASSEMBLER
void
tc_reloc_mangle (fix_ptr, intr, base)
     fixS *fix_ptr;
     struct internal_reloc *intr;
     bfd_vma base;

{
  symbolS *symbol_ptr;

  symbol_ptr = fix_ptr->fx_addsy;

  /* If this relocation is attached to a symbol then it's ok
     to output it.  */
  if (fix_ptr->fx_r_type == TC_CONS_RELOC)
    {
      /* cons likes to create reloc32's whatever the size of the reloc..
       */
      switch (fix_ptr->fx_size)
	{
	case 4:
	  intr->r_type = R_RELLONG;
	  break;
	case 2:
	  intr->r_type = R_RELWORD;
	  break;
	case 1:
	  intr->r_type = R_RELBYTE;
	  break;
	default:
	  abort ();
	}
    }
  else
    {
      intr->r_type = fix_ptr->fx_r_type;
    }

  intr->r_vaddr = fix_ptr->fx_frag->fr_address + fix_ptr->fx_where + base;
  intr->r_offset = fix_ptr->fx_offset;

  if (symbol_ptr)
    {
      if (symbol_ptr->sy_number != -1)
	intr->r_symndx = symbol_ptr->sy_number;
      else
	{
	  symbolS *segsym;

	  /* This case arises when a reference is made to `.'.  */
	  segsym = seg_info (S_GET_SEGMENT (symbol_ptr))->dot;
	  if (segsym == NULL)
	    intr->r_symndx = -1;
	  else
	    {
	      intr->r_symndx = segsym->sy_number;
	      intr->r_offset += S_GET_VALUE (symbol_ptr);
	    }
	}
    }
  else
    intr->r_symndx = -1;
}
#else /* BFD_ASSEMBLER */
arelent *
tc_gen_reloc (section, fixp)
     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 = (arelent *) xmalloc (sizeof (arelent));
  rel->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (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;
}
#endif

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]