This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
Re: [RFA] H8/300sx port for gas, sim, and opcodes (2/5)
- From: Michael Snyder <msnyder at redhat dot com>
- To: kazu at cs dot umass dot edu, dvenkat at noida dot hcltech dot com, gdb-patches at sources dot redhat dot com, binutils at sources dot redhat dot com
- Date: Tue, 03 Jun 2003 14:30:26 -0700
- Subject: Re: [RFA] H8/300sx port for gas, sim, and opcodes (2/5)
- Organization: Red Hat, Inc.
- References: <3ECBE9E6.68A8D741@redhat.com>
Michael Snyder wrote:
>
> 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.
Committed.
>
> -------------------------------------------------------------------------------
> /* 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