diff options
Diffstat (limited to 'binutils-2.25/gas/config/tc-m68hc11.c')
-rw-r--r-- | binutils-2.25/gas/config/tc-m68hc11.c | 4501 |
1 files changed, 4501 insertions, 0 deletions
diff --git a/binutils-2.25/gas/config/tc-m68hc11.c b/binutils-2.25/gas/config/tc-m68hc11.c new file mode 100644 index 00000000..3189121c --- /dev/null +++ b/binutils-2.25/gas/config/tc-m68hc11.c @@ -0,0 +1,4501 @@ +/* tc-m68hc11.c -- Assembler code for the Motorola 68HC11 & 68HC12. + Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010, + 2011, 2012 + Free Software Foundation, Inc. + Written by Stephane Carrez (stcarrez@nerim.fr) + XGATE and S12X added by James Murray (jsm@jsm-net.demon.co.uk) + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GAS; see the file COPYING. If not, write to + the Free Software Foundation, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "as.h" +#include "safe-ctype.h" +#include "subsegs.h" +#include "opcode/m68hc11.h" +#include "dwarf2dbg.h" +#include "elf/m68hc11.h" + +const char comment_chars[] = ";!"; +const char line_comment_chars[] = "#*"; +const char line_separator_chars[] = ""; + +const char EXP_CHARS[] = "eE"; +const char FLT_CHARS[] = "dD"; + +#define STATE_CONDITIONAL_BRANCH (1) +#define STATE_PC_RELATIVE (2) +#define STATE_INDEXED_OFFSET (3) +#define STATE_INDEXED_PCREL (4) +#define STATE_XBCC_BRANCH (5) +#define STATE_CONDITIONAL_BRANCH_6812 (6) + +#define STATE_BYTE (0) +#define STATE_BITS5 (0) +#define STATE_WORD (1) +#define STATE_BITS9 (1) +#define STATE_LONG (2) +#define STATE_BITS16 (2) +#define STATE_UNDF (3) /* Symbol undefined in pass1 */ + +/* This macro has no side-effects. */ +#define ENCODE_RELAX(what,length) (((what) << 2) + (length)) +#define RELAX_STATE(s) ((s) >> 2) +#define RELAX_LENGTH(s) ((s) & 3) + +#define IS_OPCODE(C1,C2) (((C1) & 0x0FF) == ((C2) & 0x0FF)) + +/* This table describes how you change sizes for the various types of variable + size expressions. This version only supports two kinds. */ + +/* The fields are: + How far Forward this mode will reach. + How far Backward this mode will reach. + How many bytes this mode will add to the size of the frag. + Which mode to go to if the offset won't fit in this one. */ + +relax_typeS md_relax_table[] = +{ + {1, 1, 0, 0}, /* First entries aren't used. */ + {1, 1, 0, 0}, /* For no good reason except. */ + {1, 1, 0, 0}, /* that the VAX doesn't either. */ + {1, 1, 0, 0}, + + /* Relax for bcc <L>. + These insns are translated into b!cc +3 jmp L. */ + {(127), (-128), 0, ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_WORD)}, + {0, 0, 3, 0}, + {1, 1, 0, 0}, + {1, 1, 0, 0}, + + /* Relax for bsr <L> and bra <L>. + These insns are translated into jsr and jmp. */ + {(127), (-128), 0, ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD)}, + {0, 0, 1, 0}, + {1, 1, 0, 0}, + {1, 1, 0, 0}, + + /* Relax for indexed offset: 5-bits, 9-bits, 16-bits. */ + {(15), (-16), 0, ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9)}, + {(255), (-256), 1, ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16)}, + {0, 0, 2, 0}, + {1, 1, 0, 0}, + + /* Relax for PC relative offset: 5-bits, 9-bits, 16-bits. + For the 9-bit case, there will be a -1 correction to take into + account the new byte that's why the range is -255..256. */ + {(15), (-16), 0, ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS9)}, + {(256), (-255), 1, ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS16)}, + {0, 0, 2, 0}, + {1, 1, 0, 0}, + + /* Relax for dbeq/ibeq/tbeq r,<L>: + These insns are translated into db!cc +3 jmp L. */ + {(255), (-256), 0, ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_WORD)}, + {0, 0, 3, 0}, + {1, 1, 0, 0}, + {1, 1, 0, 0}, + + /* Relax for bcc <L> on 68HC12. + These insns are translated into lbcc <L>. */ + {(127), (-128), 0, ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, STATE_WORD)}, + {0, 0, 2, 0}, + {1, 1, 0, 0}, + {1, 1, 0, 0}, + +}; + +/* 68HC11 and 68HC12 registers. They are numbered according to the 68HC12. */ +typedef enum register_id +{ + REG_NONE = -1, + REG_A = 0, + REG_B = 1, + REG_CCR = 2, + REG_D = 4, + REG_X = 5, + REG_Y = 6, + REG_SP = 7, + REG_PC = 8, + REG_R0 = 0, + REG_R1 = 1, + REG_R2 = 2, + REG_R3 = 3, + REG_R4 = 4, + REG_R5 = 5, + REG_R6 = 6, + REG_R7 = 7, + REG_SP_XG = 8, + REG_PC_XG = 9, + REG_CCR_XG = 10 +} register_id; + +typedef struct operand +{ + expressionS exp; + register_id reg1; + register_id reg2; + int mode; +} operand; + +struct m68hc11_opcode_def +{ + long format; + int min_operands; + int max_operands; + int nb_modes; + int used; + struct m68hc11_opcode *opcode; +}; + +static struct m68hc11_opcode_def *m68hc11_opcode_defs = 0; +static int m68hc11_nb_opcode_defs = 0; + +typedef struct alias +{ + const char *name; + const char *alias; +} alias; + +static alias alias_opcodes[] = +{ + {"cpd", "cmpd"}, + {"cpx", "cmpx"}, + {"cpy", "cmpy"}, + {0, 0} +}; + +struct m9s12xg_opcode_def +{ + long format; + int min_operands; + int max_operands; + int nb_modes; + int used; + struct m9s12xg_opcode *opcode; +}; + +/* Local functions. */ +static register_id reg_name_search (char *); +static register_id register_name (void); +static int cmp_opcode (struct m68hc11_opcode *, struct m68hc11_opcode *); +static char *print_opcode_format (struct m68hc11_opcode *, int); +static char *skip_whites (char *); +static int check_range (long, int); +static void print_opcode_list (void); +static void get_default_target (void); +static void print_insn_format (char *); +static int get_operand (operand *, int, long); +static void fixup8 (expressionS *, int, int); +static void fixup16 (expressionS *, int, int); +static void fixup24 (expressionS *, int, int); +static void fixup8_xg (expressionS *, int, int); +static unsigned char convert_branch (unsigned char); +static char *m68hc11_new_insn (int); +static void build_dbranch_insn (struct m68hc11_opcode *, + operand *, int, int); +static int build_indexed_byte (operand *, int, int); +static int build_reg_mode (operand *, int); + +static struct m68hc11_opcode *find (struct m68hc11_opcode_def *, + operand *, int); +static struct m68hc11_opcode *find_opcode (struct m68hc11_opcode_def *, + operand *, int *); +static void build_jump_insn (struct m68hc11_opcode *, operand *, int, int); +static void build_insn_xg (struct m68hc11_opcode *, operand *, int); +static void build_insn (struct m68hc11_opcode *, operand *, int); +static int relaxable_symbol (symbolS *); + +/* Pseudo op to indicate a relax group. */ +static void s_m68hc11_relax (int); + +/* Pseudo op to control the ELF flags. */ +static void s_m68hc11_mode (int); + +/* Process directives specified via pseudo ops. */ +static void s_m68hc11_parse_pseudo_instruction (int); + +/* Mark the symbols with STO_M68HC12_FAR to indicate the functions + are using 'rtc' for returning. It is necessary to use 'call' + to invoke them. This is also used by the debugger to correctly + find the stack frame. */ +static void s_m68hc11_mark_symbol (int); + +/* Controls whether relative branches can be turned into long branches. + When the relative offset is too large, the insn are changed: + bra -> jmp + bsr -> jsr + bcc -> b!cc +3 + jmp L + dbcc -> db!cc +3 + jmp L + + Setting the flag forbidds this. */ +static short flag_fixed_branches = 0; + +/* Force to use long jumps (absolute) instead of relative branches. */ +static short flag_force_long_jumps = 0; + +/* Change the direct addressing mode into an absolute addressing mode + when the insn does not support direct addressing. + For example, "clr *ZD0" is normally not possible and is changed + into "clr ZDO". */ +static short flag_strict_direct_addressing = 1; + +/* When an opcode has invalid operand, print out the syntax of the opcode + to stderr. */ +static short flag_print_insn_syntax = 0; + +/* Dumps the list of instructions with syntax and then exit: + 1 -> Only dumps the list (sorted by name) + 2 -> Generate an example (or test) that can be compiled. */ +static short flag_print_opcodes = 0; + +/* Opcode hash table. */ +static struct hash_control *m68hc11_hash; + +/* Current cpu (either cpu6811 or cpu6812). This is determined automagically + by 'get_default_target' by looking at default BFD vector. This is overridden + with the -m<cpu> option. */ +static int current_architecture = 0; + +/* Default cpu determined by 'get_default_target'. */ +static const char *default_cpu; + +/* Number of opcodes in the sorted table (filtered by current cpu). */ +static int num_opcodes; + +/* The opcodes sorted by name and filtered by current cpu. */ +static struct m68hc11_opcode *m68hc11_sorted_opcodes; + +/* ELF flags to set in the output file header. */ +static int elf_flags = E_M68HC11_F64; + +/* These are the machine dependent pseudo-ops. These are included so + the assembler can work on the output from the SUN C compiler, which + generates these. */ + +/* 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[] = +{ + /* The following pseudo-ops are supported for MRI compatibility. */ + {"fcb", cons, 1}, + {"fdb", cons, 2}, + {"fqb", cons, 4}, + {"fcc", stringer, 8 + 1}, + {"rmb", s_space, 0}, + + /* Motorola ALIS. */ + {"xrefb", s_ignore, 0}, /* Same as xref */ + + /* Gcc driven relaxation. */ + {"relax", s_m68hc11_relax, 0}, + + /* .mode instruction (ala SH). */ + {"mode", s_m68hc11_mode, 0}, + + /* .far instruction. */ + {"far", s_m68hc11_mark_symbol, STO_M68HC12_FAR}, + + /* .interrupt instruction. */ + {"interrupt", s_m68hc11_mark_symbol, STO_M68HC12_INTERRUPT}, + + /* .nobankwarning instruction. */ + {"nobankwarning", s_m68hc11_parse_pseudo_instruction, E_M68HC11_NO_BANK_WARNING}, + + {0, 0, 0} +}; + +/* Options and initialization. */ + +const char *md_shortopts = "Sm:"; + +struct option md_longopts[] = +{ +#define OPTION_FORCE_LONG_BRANCH (OPTION_MD_BASE) + {"force-long-branches", no_argument, NULL, OPTION_FORCE_LONG_BRANCH}, + {"force-long-branchs", no_argument, NULL, OPTION_FORCE_LONG_BRANCH}, /* Misspelt version kept for backwards compatibility. */ + +#define OPTION_SHORT_BRANCHES (OPTION_MD_BASE + 1) + {"short-branches", no_argument, NULL, OPTION_SHORT_BRANCHES}, + {"short-branchs", no_argument, NULL, OPTION_SHORT_BRANCHES}, /* Misspelt version kept for backwards compatibility. */ + +#define OPTION_STRICT_DIRECT_MODE (OPTION_MD_BASE + 2) + {"strict-direct-mode", no_argument, NULL, OPTION_STRICT_DIRECT_MODE}, + +#define OPTION_PRINT_INSN_SYNTAX (OPTION_MD_BASE + 3) + {"print-insn-syntax", no_argument, NULL, OPTION_PRINT_INSN_SYNTAX}, + +#define OPTION_PRINT_OPCODES (OPTION_MD_BASE + 4) + {"print-opcodes", no_argument, NULL, OPTION_PRINT_OPCODES}, + +#define OPTION_GENERATE_EXAMPLE (OPTION_MD_BASE + 5) + {"generate-example", no_argument, NULL, OPTION_GENERATE_EXAMPLE}, + +#define OPTION_MSHORT (OPTION_MD_BASE + 6) + {"mshort", no_argument, NULL, OPTION_MSHORT}, + +#define OPTION_MLONG (OPTION_MD_BASE + 7) + {"mlong", no_argument, NULL, OPTION_MLONG}, + +#define OPTION_MSHORT_DOUBLE (OPTION_MD_BASE + 8) + {"mshort-double", no_argument, NULL, OPTION_MSHORT_DOUBLE}, + +#define OPTION_MLONG_DOUBLE (OPTION_MD_BASE + 9) + {"mlong-double", no_argument, NULL, OPTION_MLONG_DOUBLE}, + +#define OPTION_XGATE_RAMOFFSET (OPTION_MD_BASE + 10) + {"xgate-ramoffset", no_argument, NULL, OPTION_XGATE_RAMOFFSET}, + + {NULL, no_argument, NULL, 0} +}; +size_t md_longopts_size = sizeof (md_longopts); + +/* Get the target cpu for the assembler. This is based on the configure + options and on the -m68hc11/-m68hc12 option. If no option is specified, + we must get the default. */ +const char * +m68hc11_arch_format (void) +{ + get_default_target (); + if (current_architecture & cpu6811) + return "elf32-m68hc11"; + else + return "elf32-m68hc12"; +} + +enum bfd_architecture +m68hc11_arch (void) +{ + get_default_target (); + if (current_architecture & cpu6811) + return bfd_arch_m68hc11; + else + return bfd_arch_m68hc12; +} + +int +m68hc11_mach (void) +{ + return 0; +} + +/* Listing header selected according to cpu. */ +const char * +m68hc11_listing_header (void) +{ + if (current_architecture & cpu6811) + return "M68HC11 GAS "; + else if (current_architecture & cpuxgate) + return "XGATE GAS "; + else if (current_architecture & cpu9s12x) + return "S12X GAS "; + else + return "M68HC12 GAS "; +} + +void +md_show_usage (FILE *stream) +{ + get_default_target (); + fprintf (stream, _("\ +Motorola 68HC11/68HC12/68HCS12 options:\n\ + -m68hc11 | -m68hc12 |\n\ + -m68hcs12 | -mm9s12x |\n\ + -mm9s12xg specify the processor [default %s]\n\ + -mshort use 16-bit int ABI (default)\n\ + -mlong use 32-bit int ABI\n\ + -mshort-double use 32-bit double ABI\n\ + -mlong-double use 64-bit double ABI (default)\n\ + --force-long-branches always turn relative branches into absolute ones\n\ + -S,--short-branches do not turn relative branches into absolute ones\n\ + when the offset is out of range\n\ + --strict-direct-mode do not turn the direct mode into extended mode\n\ + when the instruction does not support direct mode\n\ + --print-insn-syntax print the syntax of instruction in case of error\n\ + --print-opcodes print the list of instructions with syntax\n\ + --xgate-ramoffset offset ram addresses by 0xc000\n\ + --generate-example generate an example of each instruction\n\ + (used for testing)\n"), default_cpu); + +} + +/* Try to identify the default target based on the BFD library. */ +static void +get_default_target (void) +{ + const bfd_target *target; + bfd abfd; + + if (current_architecture != 0) + return; + + default_cpu = "unknown"; + target = bfd_find_target (0, &abfd); + if (target && target->name) + { + if (strcmp (target->name, "elf32-m68hc12") == 0) + { + current_architecture = cpu6812; + default_cpu = "m68hc12"; + } + else if (strcmp (target->name, "elf32-m68hc11") == 0) + { + current_architecture = cpu6811; + default_cpu = "m68hc11"; + } + else + { + as_bad (_("Default target `%s' is not supported."), target->name); + } + } +} + +void +m68hc11_print_statistics (FILE *file) +{ + int i; + struct m68hc11_opcode_def *opc; + + hash_print_statistics (file, "opcode table", m68hc11_hash); + + opc = m68hc11_opcode_defs; + if (opc == 0 || m68hc11_nb_opcode_defs == 0) + return; + + /* Dump the opcode statistics table. */ + fprintf (file, _("Name # Modes Min ops Max ops Modes mask # Used\n")); + for (i = 0; i < m68hc11_nb_opcode_defs; i++, opc++) + { + fprintf (file, "%-7.7s %5d %7d %7d 0x%08lx %7d\n", + opc->opcode->name, + opc->nb_modes, + opc->min_operands, opc->max_operands, opc->format, opc->used); + } +} + +int +md_parse_option (int c, char *arg) +{ + get_default_target (); + switch (c) + { + /* -S means keep external to 2 bit offset rather than 16 bit one. */ + case OPTION_SHORT_BRANCHES: + case 'S': + flag_fixed_branches = 1; + break; + + case OPTION_FORCE_LONG_BRANCH: + flag_force_long_jumps = 1; + break; + + case OPTION_PRINT_INSN_SYNTAX: + flag_print_insn_syntax = 1; + break; + + case OPTION_PRINT_OPCODES: + flag_print_opcodes = 1; + break; + + case OPTION_STRICT_DIRECT_MODE: + flag_strict_direct_addressing = 0; + break; + + case OPTION_GENERATE_EXAMPLE: + flag_print_opcodes = 2; + break; + + case OPTION_MSHORT: + elf_flags &= ~E_M68HC11_I32; + break; + + case OPTION_MLONG: + elf_flags |= E_M68HC11_I32; + break; + + case OPTION_MSHORT_DOUBLE: + elf_flags &= ~E_M68HC11_F64; + break; + + case OPTION_MLONG_DOUBLE: + elf_flags |= E_M68HC11_F64; + break; + + case OPTION_XGATE_RAMOFFSET: + elf_flags |= E_M68HC11_XGATE_RAMOFFSET; + break; + + case 'm': + if ((strcasecmp (arg, "68hc11") == 0) + || (strcasecmp (arg, "m68hc11") == 0)) + current_architecture = cpu6811; + else if ((strcasecmp (arg, "68hc12") == 0) + || (strcasecmp (arg, "m68hc12") == 0)) + current_architecture = cpu6812; + else if ((strcasecmp (arg, "68hcs12") == 0) + || (strcasecmp (arg, "m68hcs12") == 0)) + current_architecture = cpu6812 | cpu6812s; + else if (strcasecmp (arg, "m9s12x") == 0) + current_architecture = cpu6812 | cpu6812s | cpu9s12x; + else if ((strcasecmp (arg, "m9s12xg") == 0) + || (strcasecmp (arg, "xgate") == 0)) + /* xgate for backwards compatability */ + current_architecture = cpuxgate; + else + as_bad (_("Option `%s' is not recognized."), arg); + break; + + default: + return 0; + } + + return 1; +} + +symbolS * +md_undefined_symbol (char *name ATTRIBUTE_UNUSED) +{ + return 0; +} + +char * +md_atof (int type, char *litP, int *sizeP) +{ + return ieee_md_atof (type, litP, sizeP, TRUE); +} + +valueT +md_section_align (asection *seg, valueT addr) +{ + int align = bfd_get_section_alignment (stdoutput, seg); + return ((addr + (1 << align) - 1) & (-1 << align)); +} + +static int +cmp_opcode (struct m68hc11_opcode *op1, struct m68hc11_opcode *op2) +{ + return strcmp (op1->name, op2->name); +} + +#define IS_CALL_SYMBOL(MODE) \ +(((MODE) & (M6812_OP_PAGE|M6811_OP_IND16)) \ + == ((M6812_OP_PAGE|M6811_OP_IND16))) + +/* Initialize the assembler. Create the opcode hash table + (sorted on the names) with the M6811 opcode table + (from opcode library). */ +void +md_begin (void) +{ + char *prev_name = ""; + struct m68hc11_opcode *opcodes; + struct m68hc11_opcode_def *opc = 0; + int i, j; + + get_default_target (); + + m68hc11_hash = hash_new (); + + /* Get a writable copy of the opcode table and sort it on the names. */ + opcodes = (struct m68hc11_opcode *) xmalloc (m68hc11_num_opcodes * + sizeof (struct + m68hc11_opcode)); + m68hc11_sorted_opcodes = opcodes; + num_opcodes = 0; + for (i = 0; i < m68hc11_num_opcodes; i++) + { + if (m68hc11_opcodes[i].arch & current_architecture) + { + opcodes[num_opcodes] = m68hc11_opcodes[i]; + if (opcodes[num_opcodes].name[0] == 'b' + && opcodes[num_opcodes].format & M6811_OP_JUMP_REL + && !(opcodes[num_opcodes].format & M6811_OP_BITMASK)) + { + num_opcodes++; + opcodes[num_opcodes] = m68hc11_opcodes[i]; + } + num_opcodes++; + for (j = 0; alias_opcodes[j].name != 0; j++) + if (strcmp (m68hc11_opcodes[i].name, alias_opcodes[j].name) == 0) + { + opcodes[num_opcodes] = m68hc11_opcodes[i]; + opcodes[num_opcodes].name = alias_opcodes[j].alias; + num_opcodes++; + break; + } + } + } + qsort (opcodes, num_opcodes, sizeof (struct m68hc11_opcode), + (int (*) (const void*, const void*)) cmp_opcode); + + opc = (struct m68hc11_opcode_def *) + xmalloc (num_opcodes * sizeof (struct m68hc11_opcode_def)); + m68hc11_opcode_defs = opc--; + + /* Insert unique names into hash table. The M6811 instruction set + has several identical opcode names that have different opcodes based + on the operands. This hash table then provides a quick index to + the first opcode with a particular name in the opcode table. */ + for (i = 0; i < num_opcodes; i++, opcodes++) + { + int expect; + + if (strcmp (prev_name, opcodes->name)) + { + prev_name = (char *) opcodes->name; + + opc++; + opc->format = 0; + opc->min_operands = 100; + opc->max_operands = 0; + opc->nb_modes = 0; + opc->opcode = opcodes; + opc->used = 0; + hash_insert (m68hc11_hash, opcodes->name, opc); + } + opc->nb_modes++; + opc->format |= opcodes->format; + + /* See how many operands this opcode needs. */ + expect = 0; + if (opcodes->arch == cpuxgate) + { + if (opcodes->format & (M68XG_OP_IMM3 | M68XG_OP_R | M68XG_OP_REL9 + | M68XG_OP_REL10 )) + expect = 1; + else if (opcodes->format & (M68XG_OP_R_R | M68XG_OP_R_IMM4 + | M68XG_OP_R_IMM8 | M68XG_OP_R_IMM8)) + expect = 2; + else if (opcodes->format & (M68XG_OP_R_R_R | M68XG_OP_R_R_OFFS5 + | M68XG_OP_RD_RB_RI | M68XG_OP_RD_RB_RIp + | M68XG_OP_RD_RB_mRI)) + expect = 3; + } + else + { + if (opcodes->format & M6811_OP_MASK) + expect++; + if (opcodes->format & M6811_OP_BITMASK) + expect++; + if (opcodes->format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16)) + expect++; + if (opcodes->format & (M6812_OP_IND16_P2 | M6812_OP_IDX_P2)) + expect++; + /* Special case for call instruction. */ + if ((opcodes->format & M6812_OP_PAGE) + && !(opcodes->format & M6811_OP_IND16)) + expect++; + } + + if (expect < opc->min_operands) + opc->min_operands = expect; + if (IS_CALL_SYMBOL (opcodes->format)) + expect++; + if (expect > opc->max_operands) + opc->max_operands = expect; + } + opc++; + m68hc11_nb_opcode_defs = opc - m68hc11_opcode_defs; + + if (flag_print_opcodes) + { + print_opcode_list (); + exit (EXIT_SUCCESS); + } +} + +void +m68hc11_init_after_args (void) +{ +} + +/* Builtin help. */ + +/* Return a string that represents the operand format for the instruction. + When example is true, this generates an example of operand. This is used + to give an example and also to generate a test. */ + +static char * +print_opcode_format (struct m68hc11_opcode *opcode, int example) +{ + static char buf[128]; + int format = opcode->format; + char *p; + + p = buf; + buf[0] = 0; + + if (current_architecture == cpuxgate) + { + if (format & M68XG_OP_IMM3) + { + if (example) + sprintf (p, "#%d", rand () & 0x007); + else + strcpy (p, _("imm3")); + p = &p[strlen (p)]; + } + else if (format & M68XG_OP_R) + { + if (example) + sprintf (p, "R%d", rand () & 0x07); + else + strcpy (p, _("RD")); + p = &p[strlen (p)]; + } + else if (format & M68XG_OP_R_R) + { + if (example) + sprintf (p, "R%d,R%d", rand () & 0x07, rand () & 0x07); + else + strcpy (p, _("RD,RS")); + p = &p[strlen (p)]; + } + else if (format & M68XG_OP_R_IMM4) + { + if (example) + sprintf (p, "R%d,#%d", rand () & 0x07, rand () & 0x0f); + else + strcpy (p, _("RI, #imm4")); + p = &p[strlen (p)]; + } + else if (format & M68XG_OP_R_R_R) + { + if (example) + sprintf (p, "R%d,R%d,R%d", rand () & 0x07, rand () & 0x07, rand () & 0x07); + else + strcpy (p, "RD,RS1,RS2"); + p = &p[strlen (p)]; + } + else if (format & M68XG_OP_REL9) + { + if (example) + sprintf (p, "%d", rand () & 0x1FF); + else + strcpy (p, "<rel9>"); + p = &p[strlen (p)]; + } + else if (format & M68XG_OP_REL10) + { + if (example) + sprintf (p, "%d", rand () & 0x3FF); + else + strcpy (p, "<rel10>"); + p = &p[strlen (p)]; + } + else if (format & M68XG_OP_R_R_OFFS5) + { + if (example) + sprintf (p, "R%d, (R%d, #0x%x)", rand () & 0x07, rand () & 0x07, rand () & 0x1f); + else + strcpy (p, _("RD, (RI,#offs5)")); + p = &p[strlen (p)]; + } + else if (format & M68XG_OP_RD_RB_RI) + { + if (example) + sprintf (p, "R%d, (R%d, R%d)", rand () & 0x07, rand () & 0x07, rand () & 0x07); + else + strcpy (p, "RD, (RB, RI)"); + p = &p[strlen (p)]; + } + else if (format & M68XG_OP_RD_RB_RIp) + { + if (example) + sprintf (p, "R%d, (R%d, R%d+)", rand () & 0x07, rand () & 0x07, rand () & 0x07); + else + strcpy (p, "RD, (RB, RI+)"); + p = &p[strlen (p)]; + } + else if (format & M68XG_OP_RD_RB_mRI) + { + if (example) + sprintf (p, "R%d, (R%d, -R%d)", rand () & 0x07, rand () & 0x07, rand () & 0x07); + else + strcpy (p, "RD, (RB, -RI)"); + p = &p[strlen (p)]; + } + else if (format & M68XG_OP_R_IMM8) + { + if (example) + sprintf (p, "R%d, #0x%x", rand () & 0x07, rand () & 0xff); + else + strcpy (p, "RD, #imm8"); + p = &p[strlen (p)]; + } + else if (format & M68XG_OP_R_IMM16) + { + if (example) + sprintf (p, "R%d, #0x%x", rand () & 0x07, rand () & 0xffff); + else + strcpy (p, "RD, #imm16"); + p = &p[strlen (p)]; + } + } + else + { + + if (format & M6811_OP_IMM8) + { + if (example) + sprintf (p, "#%d", rand () & 0x0FF); + else + strcpy (p, _("#<imm8>")); + p = &p[strlen (p)]; + } + + if (format & M6811_OP_IMM16) + { + if (example) + sprintf (p, "#%d", rand () & 0x0FFFF); + else + strcpy (p, _("#<imm16>")); + p = &p[strlen (p)]; + } + + if (format & M6811_OP_IX) + { + if (example) + sprintf (p, "%d,X", rand () & 0x0FF); + else + strcpy (p, _("<imm8>,X")); + p = &p[strlen (p)]; + } + + if (format & M6811_OP_IY) + { + if (example) + sprintf (p, "%d,X", rand () & 0x0FF); + else + strcpy (p, _("<imm8>,X")); + p = &p[strlen (p)]; + } + + if (format & M6812_OP_IDX) + { + if (example) + sprintf (p, "%d,X", rand () & 0x0FF); + else + strcpy (p, "n,r"); + p = &p[strlen (p)]; + } + + if (format & M6812_OP_PAGE) + { + if (example) + sprintf (p, ", %d", rand () & 0x0FF); + else + strcpy (p, ", <page>"); + p = &p[strlen (p)]; + } + + if (format & M6811_OP_DIRECT) + { + if (example) + sprintf (p, "*Z%d", rand () & 0x0FF); + else + strcpy (p, _("*<abs8>")); + p = &p[strlen (p)]; + } + + if (format & M6811_OP_BITMASK) + { + if (buf[0]) + *p++ = ' '; + + if (example) + sprintf (p, "#$%02x", rand () & 0x0FF); + else + strcpy (p, _("#<mask>")); + + p = &p[strlen (p)]; + if (format & M6811_OP_JUMP_REL) + *p++ = ' '; + } + + if (format & M6811_OP_IND16) + { + if (example) + sprintf (p, _("symbol%d"), rand () & 0x0FF); + else + strcpy (p, _("<abs>")); + + p = &p[strlen (p)]; + } + + if (format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16)) + { + if (example) + { + if (format & M6811_OP_BITMASK) + { + sprintf (p, ".+%d", rand () & 0x7F); + } + else + { + sprintf (p, "L%d", rand () & 0x0FF); + } + } + else + strcpy (p, _("<label>")); + } + } + return buf; +} + +/* Prints the list of instructions with the possible operands. */ +static void +print_opcode_list (void) +{ + int i; + char *prev_name = ""; + struct m68hc11_opcode *opcodes; + int example = flag_print_opcodes == 2; + + if (example) + printf (_("# Example of `%s' instructions\n\t.sect .text\n_start:\n"), + default_cpu); + + opcodes = m68hc11_sorted_opcodes; + + /* Walk the list sorted on names (by md_begin). We only report + one instruction per line, and we collect the different operand + formats. */ + for (i = 0; i < num_opcodes; i++, opcodes++) + { + char *fmt = print_opcode_format (opcodes, example); + + if (example) + { + printf ("L%d:\t", i); + printf ("%s %s\n", opcodes->name, fmt); + } + else + { + if (strcmp (prev_name, opcodes->name)) + { + if (i > 0) + printf ("\n"); + + printf ("%-5.5s ", opcodes->name); + prev_name = (char *) opcodes->name; + } + if (fmt[0]) + printf (" [%s]", fmt); + } + } + printf ("\n"); +} + +/* Print the instruction format. This operation is called when some + instruction is not correct. Instruction format is printed as an + error message. */ +static void +print_insn_format (char *name) +{ + struct m68hc11_opcode_def *opc; + struct m68hc11_opcode *opcode; + char buf[128]; + + opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash, name); + if (opc == NULL) + { + as_bad (_("Instruction `%s' is not recognized."), name); + return; + } + opcode = opc->opcode; + + as_bad (_("Instruction formats for `%s':"), name); + do + { + char *fmt; + + fmt = print_opcode_format (opcode, 0); + sprintf (buf, "\t%-5.5s %s", opcode->name, fmt); + + as_bad ("%s", buf); + opcode++; + } + while (strcmp (opcode->name, name) == 0); +} + +/* Analysis of 68HC11 and 68HC12 operands. */ + +/* reg_name_search() finds the register number given its name. + Returns the register number or REG_NONE on failure. */ +static register_id +reg_name_search (char *name) +{ + if (strcasecmp (name, "x") == 0 || strcasecmp (name, "ix") == 0) + return REG_X; + if (strcasecmp (name, "y") == 0 || strcasecmp (name, "iy") == 0) + return REG_Y; + if (strcasecmp (name, "a") == 0) + return REG_A; + if (strcasecmp (name, "b") == 0) + return REG_B; + if (strcasecmp (name, "d") == 0) + return REG_D; + if (strcasecmp (name, "sp") == 0) + return REG_SP; + if (strcasecmp (name, "pc") == 0) + return REG_PC; + if (strcasecmp (name, "ccr") == 0) + return REG_CCR; +/* XGATE */ + if (strcasecmp (name, "r0") == 0) + return REG_R0; + if (strcasecmp (name, "r1") == 0) + return REG_R1; + if (strcasecmp (name, "r2") == 0) + return REG_R2; + if (strcasecmp (name, "r3") == 0) + return REG_R3; + if (strcasecmp (name, "r4") == 0) + return REG_R4; + if (strcasecmp (name, "r5") == 0) + return REG_R5; + if (strcasecmp (name, "r6") == 0) + return REG_R6; + if (strcasecmp (name, "r7") == 0) + return REG_R7; + if (strcasecmp (name, "sp") == 0) + return REG_SP_XG; + if (strcasecmp (name, "pc") == 0) + return REG_PC_XG; + if (strcasecmp (name, "ccr") == 0) + return REG_CCR_XG; + return REG_NONE; +} + +static char * +skip_whites (char *p) +{ + while (*p == ' ' || *p == '\t') + p++; + + return p; +} + +/* Check the string at input_line_pointer + to see if it is a valid register name. */ +static register_id +register_name (void) +{ + register_id reg_number; + char c, *p = input_line_pointer; + + if (!is_name_beginner (*p++)) + return REG_NONE; + + while (is_part_of_name (*p++)) + continue; + + c = *--p; + if (c) + *p++ = 0; + + /* Look to see if it's in the register table. */ + reg_number = reg_name_search (input_line_pointer); + if (reg_number != REG_NONE) + { + if (c) + *--p = c; + + input_line_pointer = p; + return reg_number; + } + if (c) + *--p = c; + + return reg_number; +} +#define M6811_OP_CALL_ADDR 0x00800000 +#define M6811_OP_PAGE_ADDR 0x04000000 + +/* Parse a string of operands and return an array of expressions. + + Operand mode[0] mode[1] exp[0] exp[1] + #n M6811_OP_IMM16 - O_* + *<exp> M6811_OP_DIRECT - O_* + .{+-}<exp> M6811_OP_JUMP_REL - O_* + <exp> M6811_OP_IND16 - O_* + ,r N,r M6812_OP_IDX M6812_OP_REG O_constant O_register + n,-r M6812_PRE_DEC M6812_OP_REG O_constant O_register + n,+r M6812_PRE_INC " " + n,r- M6812_POST_DEC " " + n,r+ M6812_POST_INC " " + A,r B,r D,r M6811_OP_REG M6812_OP_REG O_register O_register + [D,r] M6811_OP_D_IDX M6812_OP_REG O_register O_register + [n,r] M6811_OP_D_IDX_2 M6812_OP_REG O_constant O_register */ +static int +get_operand (operand *oper, int which, long opmode) +{ + char *p = input_line_pointer; + int mode; + register_id reg; + + oper->exp.X_op = O_absent; + oper->reg1 = REG_NONE; + oper->reg2 = REG_NONE; + mode = M6811_OP_NONE; + + p = skip_whites (p); + + if (*p == 0 || *p == '\n' || *p == '\r') + { + input_line_pointer = p; + return 0; + } + + if (*p == '*' && (opmode & (M6811_OP_DIRECT | M6811_OP_IND16))) + { + mode = M6811_OP_DIRECT; + p++; + } + else if (*p == '#') + { + if (!(opmode & (M6811_OP_IMM8 | M6811_OP_IMM16 | M6811_OP_BITMASK))) + { + as_bad (_("Immediate operand is not allowed for operand %d."), + which); + return -1; + } + + mode = M6811_OP_IMM16; + p++; + if (strncmp (p, "%hi", 3) == 0) + { + p += 3; + mode |= M6811_OP_HIGH_ADDR; + } + else if (strncmp (p, "%lo", 3) == 0) + { + p += 3; + mode |= M6811_OP_LOW_ADDR; + } + /* %page modifier is used to obtain only the page number + of the address of a function. */ + else if (strncmp (p, "%page", 5) == 0) + { + p += 5; + mode |= M6811_OP_PAGE_ADDR; + } + + /* %addr modifier is used to obtain the physical address part + of the function (16-bit). For 68HC12 the function will be + mapped in the 16K window at 0x8000 and the value will be + within that window (although the function address may not fit + in 16-bit). See bfd/elf32-m68hc12.c for the translation. */ + else if (strncmp (p, "%addr", 5) == 0) + { + p += 5; + mode |= M6811_OP_CALL_ADDR; + } + } + else if (*p == '.' && (p[1] == '+' || p[1] == '-')) + { + p++; + mode = M6811_OP_JUMP_REL; + } + else if (*p == '[') + { + if (current_architecture & cpu6811) + as_bad (_("Indirect indexed addressing is not valid for 68HC11.")); + + p++; + mode = M6812_OP_D_IDX; + p = skip_whites (p); + } + else if (*p == ',') /* Special handling of ,x and ,y. */ + { + p++; + input_line_pointer = p; + + reg = register_name (); + if (reg != REG_NONE) + { + oper->reg1 = reg; + oper->exp.X_op = O_constant; + oper->exp.X_add_number = 0; + oper->mode = M6812_OP_IDX; + return 1; + } + as_bad (_("Spurious `,' or bad indirect register addressing mode.")); + return -1; + } + /* Handle 68HC12 page specification in 'call foo,%page(bar)'. */ + else if ((opmode & M6812_OP_PAGE) && strncmp (p, "%page", 5) == 0) + { + p += 5; + mode = M6811_OP_PAGE_ADDR | M6812_OP_PAGE | M6811_OP_IND16; + } + input_line_pointer = p; + + if (mode == M6811_OP_NONE || mode == M6812_OP_D_IDX) + reg = register_name (); + else + reg = REG_NONE; + + if (reg != REG_NONE) + { + p = skip_whites (input_line_pointer); + if (*p == ']' && mode == M6812_OP_D_IDX) + { + as_bad + (_("Missing second register or offset for indexed-indirect mode.")); + return -1; + } + + oper->reg1 = reg; + oper->mode = mode | M6812_OP_REG; + if (*p != ',') + { + if (mode == M6812_OP_D_IDX) + { + as_bad (_("Missing second register for indexed-indirect mode.")); + return -1; + } + return 1; + } + + p++; + input_line_pointer = p; + reg = register_name (); + if (reg != REG_NONE) + { + p = skip_whites (input_line_pointer); + if (mode == M6812_OP_D_IDX) + { + if (*p != ']') + { + as_bad (_("Missing `]' to close indexed-indirect mode.")); + return -1; + } + p++; + oper->mode = M6812_OP_D_IDX; + } + input_line_pointer = p; + + oper->reg2 = reg; + return 1; + } + return 1; + } + + /* In MRI mode, isolate the operand because we can't distinguish + operands from comments. */ + if (flag_mri) + { + char c = 0; + + p = skip_whites (p); + while (*p && *p != ' ' && *p != '\t') + p++; + + if (*p) + { + c = *p; + *p = 0; + } + + /* Parse as an expression. */ + expression (&oper->exp); + + if (c) + { + *p = c; + } + } + else + { + expression (&oper->exp); + } + + if (oper->exp.X_op == O_illegal) + { + as_bad (_("Illegal operand.")); + return -1; + } + else if (oper->exp.X_op == O_absent) + { + as_bad (_("Missing operand.")); + return -1; + } + + p = input_line_pointer; + + if (mode == M6811_OP_NONE || mode == M6811_OP_DIRECT + || mode == M6812_OP_D_IDX) + { + p = skip_whites (input_line_pointer); + + if (*p == ',') + { + int possible_mode = M6811_OP_NONE; + char *old_input_line; + + old_input_line = p; + p++; + + /* 68HC12 pre increment or decrement. */ + if (mode == M6811_OP_NONE) + { + if (*p == '-') + { + possible_mode = M6812_PRE_DEC; + p++; + } + else if (*p == '+') + { + possible_mode = M6812_PRE_INC; + p++; + } + p = skip_whites (p); + } + input_line_pointer = p; + reg = register_name (); + + /* Backtrack if we have a valid constant expression and + it does not correspond to the offset of the 68HC12 indexed + addressing mode (as in N,x). */ + if (reg == REG_NONE && mode == M6811_OP_NONE + && possible_mode != M6811_OP_NONE) + { + oper->mode = M6811_OP_IND16 | M6811_OP_JUMP_REL; + input_line_pointer = skip_whites (old_input_line); + return 1; + } + + if (possible_mode != M6811_OP_NONE) + mode = possible_mode; + + if ((current_architecture & cpu6811) + && possible_mode != M6811_OP_NONE) + as_bad (_("Pre-increment mode is not valid for 68HC11")); + /* Backtrack. */ + if (which == 0 && opmode & M6812_OP_IDX_P2 + && reg != REG_X && reg != REG_Y + && reg != REG_PC && reg != REG_SP) + { + reg = REG_NONE; + input_line_pointer = p; + } + + if (reg == REG_NONE && mode != M6811_OP_DIRECT + && !(mode == M6811_OP_NONE && opmode & M6811_OP_IND16)) + { + as_bad (_("Wrong register in register indirect mode.")); + return -1; + } + if (mode == M6812_OP_D_IDX) + { + p = skip_whites (input_line_pointer); + if (*p++ != ']') + { + as_bad (_("Missing `]' to close register indirect operand.")); + return -1; + } + input_line_pointer = p; + oper->reg1 = reg; + oper->mode = M6812_OP_D_IDX_2; + return 1; + } + if (reg != REG_NONE) + { + oper->reg1 = reg; + if (mode == M6811_OP_NONE) + { + p = input_line_pointer; + if (*p == '-') + { + mode = M6812_POST_DEC; + p++; + if (current_architecture & cpu6811) + as_bad + (_("Post-decrement mode is not valid for 68HC11.")); + } + else if (*p == '+') + { + mode = M6812_POST_INC; + p++; + if (current_architecture & cpu6811) + as_bad + (_("Post-increment mode is not valid for 68HC11.")); + } + else + mode = M6812_OP_IDX; + + input_line_pointer = p; + } + else + mode |= M6812_OP_IDX; + + oper->mode = mode; + return 1; + } + input_line_pointer = old_input_line; + } + + if (mode == M6812_OP_D_IDX_2) + { + as_bad (_("Invalid indexed indirect mode.")); + return -1; + } + } + + /* If the mode is not known until now, this is either a label + or an indirect address. */ + if (mode == M6811_OP_NONE) + mode = M6811_OP_IND16 | M6811_OP_JUMP_REL; + + p = input_line_pointer; + while (*p == ' ' || *p == '\t') + p++; + input_line_pointer = p; + oper->mode = mode; + + return 1; +} + +#define M6812_AUTO_INC_DEC (M6812_PRE_INC | M6812_PRE_DEC \ + | M6812_POST_INC | M6812_POST_DEC) + +/* Checks that the number 'num' fits for a given mode. */ +static int +check_range (long num, int mode) +{ + if (current_architecture == cpuxgate) + { + switch (mode) + { + case M68XG_OP_IMM3: + return (num >= 0 && num <= 7) ? 1 : 0; + + case M68XG_OP_R_IMM4: + return (num >= 0 && num <= 15) ? 1 : 0; + + case M68XG_OP_R_R_OFFS5: + return (num >= 0 && num <= 31) ? 1 : 0; + + case M68XG_OP_R_IMM8: + return (num >= 0 && num <= 255) ? 1 : 0; + + case M68XG_OP_R_IMM16: + return (num >= 0 && num <= 65535) ? 1 : 0; + + case M68XG_OP_B_MARKER: + return (num >= -512 && num <= 511) ? 1 : 0; + + case M68XG_OP_BRA_MARKER: + return (num >= -1024 && num <= 1023) ? 1 : 0; + + default: + return 0; + } + } + else + { + /* Auto increment and decrement are ok for [-8..8] without 0. */ + if (mode & M6812_AUTO_INC_DEC) + return (num != 0 && num <= 8 && num >= -8); + + /* The 68HC12 supports 5, 9 and 16-bit offsets. */ + if (mode & (M6812_INDEXED_IND | M6812_INDEXED | M6812_OP_IDX)) + mode = M6811_OP_IND16; + + if (mode & M6812_OP_JUMP_REL16) + mode = M6811_OP_IND16; + + mode &= ~M6811_OP_BRANCH; + switch (mode) + { + case M6811_OP_IX: + case M6811_OP_IY: + case M6811_OP_DIRECT: + return (num >= 0 && num <= 255) ? 1 : 0; + + case M6811_OP_BITMASK: + case M6811_OP_IMM8: + case M6812_OP_PAGE: + return (((num & 0xFFFFFF00) == 0) || ((num & 0xFFFFFF00) == 0xFFFFFF00)) + ? 1 : 0; + + case M6811_OP_JUMP_REL: + return (num >= -128 && num <= 127) ? 1 : 0; + + case M6811_OP_IND16: + case M6811_OP_IND16 | M6812_OP_PAGE: + case M6811_OP_IMM16: + return (((num & 0xFFFF0000) == 0) || ((num & 0xFFFF0000) == 0xFFFF0000)) + ? 1 : 0; + + case M6812_OP_IBCC_MARKER: + case M6812_OP_TBCC_MARKER: + case M6812_OP_DBCC_MARKER: + return (num >= -256 && num <= 255) ? 1 : 0; + + case M6812_OP_TRAP_ID: + return ((num >= 0x30 && num <= 0x39) + || (num >= 0x40 && num <= 0x0ff)) ? 1 : 0; + + default: + return 0; + } + } +} + +/* Gas fixup generation. */ + +/* Put a 1 byte expression described by 'oper'. If this expression contains + unresolved symbols, generate an 8-bit fixup. */ +static void +fixup8 (expressionS *oper, int mode, int opmode) +{ + char *f; + + f = frag_more (1); + + if (oper->X_op == O_constant) + { + if (mode & M6812_OP_TRAP_ID + && !check_range (oper->X_add_number, M6812_OP_TRAP_ID)) + { + static char trap_id_warn_once = 0; + + as_bad (_("Trap id `%ld' is out of range."), oper->X_add_number); + if (trap_id_warn_once == 0) + { + trap_id_warn_once = 1; + as_bad (_("Trap id must be within [0x30..0x39] or [0x40..0xff].")); + } + } + + if (!(mode & M6812_OP_TRAP_ID) + && !check_range (oper->X_add_number, mode)) + { + as_bad (_("Operand out of 8-bit range: `%ld'."), oper->X_add_number); + } + number_to_chars_bigendian (f, oper->X_add_number & 0x0FF, 1); + } + else if (oper->X_op != O_register) + { + if (mode & M6812_OP_TRAP_ID) + as_bad (_("The trap id must be a constant.")); + + if (mode == M6811_OP_JUMP_REL) + { + fix_new_exp (frag_now, f - frag_now->fr_literal, 1, + oper, TRUE, BFD_RELOC_8_PCREL); + } + else + { + fixS *fixp; + int reloc; + + /* Now create an 8-bit fixup. If there was some %hi, %lo + or %page modifier, generate the reloc accordingly. */ + if (opmode & M6811_OP_HIGH_ADDR) + reloc = BFD_RELOC_M68HC11_HI8; + else if (opmode & M6811_OP_LOW_ADDR) + reloc = BFD_RELOC_M68HC11_LO8; + else if (opmode & M6811_OP_PAGE_ADDR) + reloc = BFD_RELOC_M68HC11_PAGE; + else + reloc = BFD_RELOC_8; + + fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 1, + oper, FALSE, reloc); + if (reloc != BFD_RELOC_8) + fixp->fx_no_overflow = 1; + } + number_to_chars_bigendian (f, 0, 1); + } + else + { + as_fatal (_("Operand `%x' not recognized in fixup8."), oper->X_op); + } +} + +/* Put a 2 byte expression described by 'oper'. If this expression contains + unresolved symbols, generate a 16-bit fixup. */ +static void +fixup16 (expressionS *oper, int mode, int opmode ATTRIBUTE_UNUSED) +{ + char *f; + + f = frag_more (2); + + if (oper->X_op == O_constant) + { + if (!check_range (oper->X_add_number, mode)) + { + as_bad (_("Operand out of 16-bit range: `%ld'."), + oper->X_add_number); + } + number_to_chars_bigendian (f, oper->X_add_number & 0x0FFFF, 2); + } + else if (oper->X_op != O_register) + { + fixS *fixp; + int reloc; + + if ((opmode & M6811_OP_CALL_ADDR) && (mode & M6811_OP_IMM16)) + reloc = BFD_RELOC_M68HC11_LO16; + else if (mode & M6812_OP_JUMP_REL16) + reloc = BFD_RELOC_16_PCREL; + else if (mode & M6812_OP_PAGE) + reloc = BFD_RELOC_M68HC11_LO16; + else + reloc = BFD_RELOC_16; + + /* Now create a 16-bit fixup. */ + fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 2, + oper, + reloc == BFD_RELOC_16_PCREL, + reloc); + number_to_chars_bigendian (f, 0, 2); + + if (reloc == BFD_RELOC_M68HC11_LO16) + fixp->fx_no_overflow = 1; + } + else + { + as_fatal (_("Operand `%x' not recognized in fixup16."), oper->X_op); + } +} + +/* Put a 3 byte expression described by 'oper'. If this expression contains + unresolved symbols, generate a 24-bit fixup. */ +static void +fixup24 (expressionS *oper, int mode, int opmode ATTRIBUTE_UNUSED) +{ + char *f; + + f = frag_more (3); + + if (oper->X_op == O_constant) + { + if (!check_range (oper->X_add_number, mode)) + { + as_bad (_("Operand out of 16-bit range: `%ld'."), + oper->X_add_number); + } + number_to_chars_bigendian (f, oper->X_add_number & 0x0FFFFFF, 3); + } + else if (oper->X_op != O_register) + { + /* Now create a 24-bit fixup. */ + fix_new_exp (frag_now, f - frag_now->fr_literal, 3, + oper, FALSE, BFD_RELOC_M68HC11_24); + number_to_chars_bigendian (f, 0, 3); + } + else + { + as_fatal (_("Operand `%x' not recognized in fixup16."), oper->X_op); + } +} + +/* XGATE Put a 1 byte expression described by 'oper'. If this expression + containts unresolved symbols, generate an 8-bit fixup. */ +static void +fixup8_xg (expressionS *oper, int mode, int opmode) +{ + char *f; + + f = frag_more (1); + + if (oper->X_op == O_constant) + { + fixS *fixp; + int reloc; + + if ((opmode & M6811_OP_HIGH_ADDR) || (opmode & M6811_OP_LOW_ADDR)) + { + if (opmode & M6811_OP_HIGH_ADDR) + reloc = BFD_RELOC_M68HC11_HI8; + else + reloc = BFD_RELOC_M68HC11_LO8; + + fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 1, + oper, FALSE, reloc); + fixp->fx_no_overflow = 1; + number_to_chars_bigendian (f, 0, 1); + } + else + { + if (!(check_range (oper->X_add_number, mode))) + as_bad (_("Operand out of 8-bit range: `%ld'."), + oper->X_add_number); + number_to_chars_bigendian (f, oper->X_add_number & 0x0FF, 1); + } + } + else if (oper->X_op != O_register) + { + if (mode == M68XG_OP_REL9) + { + /* Future improvement: + This fixup/reloc isn't adding on constants to symbols. */ + fix_new_exp (frag_now, f - frag_now->fr_literal -1, 2, + oper, TRUE, BFD_RELOC_M68HC12_9_PCREL); + } + else if (mode == M68XG_OP_REL10) + { + /* Future improvement: + This fixup/reloc isn't adding on constants to symbols. */ + fix_new_exp (frag_now, f - frag_now->fr_literal -1, 2, + oper, TRUE, BFD_RELOC_M68HC12_10_PCREL); + } + else + { + fixS *fixp; + int reloc; + + /* Now create an 8-bit fixup. If there was some %hi, %lo + modifier, generate the reloc accordingly. */ + if (opmode & M6811_OP_HIGH_ADDR) + reloc = BFD_RELOC_M68HC11_HI8; + else if (opmode & M6811_OP_LOW_ADDR) + reloc = BFD_RELOC_M68HC11_LO8; + else + reloc = BFD_RELOC_8; + + fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 1, + oper, FALSE, reloc); + if (reloc != BFD_RELOC_8) + fixp->fx_no_overflow = 1; + } + number_to_chars_bigendian (f, 0, 1); + } + else + as_fatal (_("Operand `%x' not recognized in fixup8."), oper->X_op); +} + +/* 68HC11 and 68HC12 code generation. */ + +/* Translate the short branch/bsr instruction into a long branch. */ + +static unsigned char +convert_branch (unsigned char code) +{ + if (IS_OPCODE (code, M6812_BSR)) + return M6812_JSR; + else if (IS_OPCODE (code, M6811_BSR)) + return M6811_JSR; + else if (IS_OPCODE (code, M6811_BRA)) + return (current_architecture & cpu6812) ? M6812_JMP : M6811_JMP; + else + as_fatal (_("Unexpected branch conversion with `%x'"), code); + + /* Keep gcc happy. */ + return M6811_JSR; +} + +/* Start a new insn that contains at least 'size' bytes. Record the + line information of that insn in the dwarf2 debug sections. */ +static char * +m68hc11_new_insn (int size) +{ + char *f; + + f = frag_more (size); + + dwarf2_emit_insn (size); + + return f; +} + +/* Builds a jump instruction (bra, bcc, bsr). */ +static void +build_jump_insn (struct m68hc11_opcode *opcode, operand operands[], + int nb_operands, int jmp_mode) +{ + unsigned char code; + char *f; + unsigned long n; + + /* The relative branch conversion is not supported for + brclr and brset. */ + gas_assert ((opcode->format & M6811_OP_BITMASK) == 0); + gas_assert (nb_operands == 1); + gas_assert (operands[0].reg1 == REG_NONE && operands[0].reg2 == REG_NONE); + + code = opcode->opcode; + + n = operands[0].exp.X_add_number; + + /* Turn into a long branch: + - when force long branch option (and not for jbcc pseudos), + - when jbcc and the constant is out of -128..127 range, + - when branch optimization is allowed and branch out of range. */ + if ((jmp_mode == 0 && flag_force_long_jumps) + || (operands[0].exp.X_op == O_constant + && (!check_range (n, opcode->format) && + (jmp_mode == 1 || flag_fixed_branches == 0)))) + { + fix_new (frag_now, frag_now_fix (), 0, + &abs_symbol, 0, 1, BFD_RELOC_M68HC11_RL_JUMP); + + if (code == M6811_BSR || code == M6811_BRA || code == M6812_BSR) + { + code = convert_branch (code); + + f = m68hc11_new_insn (1); + number_to_chars_bigendian (f, code, 1); + } + else if (current_architecture & cpu6812) + { + /* 68HC12: translate the bcc into a lbcc. */ + f = m68hc11_new_insn (2); + number_to_chars_bigendian (f, M6811_OPCODE_PAGE2, 1); + number_to_chars_bigendian (f + 1, code, 1); + fixup16 (&operands[0].exp, M6812_OP_JUMP_REL16, + M6812_OP_JUMP_REL16); + return; + } + else + { + /* 68HC11: translate the bcc into b!cc +3; jmp <L>. */ + f = m68hc11_new_insn (3); + code ^= 1; + number_to_chars_bigendian (f, code, 1); + number_to_chars_bigendian (f + 1, 3, 1); + number_to_chars_bigendian (f + 2, M6811_JMP, 1); + } + fixup16 (&operands[0].exp, M6811_OP_IND16, M6811_OP_IND16); + return; + } + + /* Branch with a constant that must fit in 8-bits. */ + if (operands[0].exp.X_op == O_constant) + { + if (!check_range (n, opcode->format)) + { + as_bad (_("Operand out of range for a relative branch: `%ld'"), + n); + } + else if (opcode->format & M6812_OP_JUMP_REL16) + { + f = m68hc11_new_insn (4); + number_to_chars_bigendian (f, M6811_OPCODE_PAGE2, 1); + number_to_chars_bigendian (f + 1, code, 1); + number_to_chars_bigendian (f + 2, n & 0x0ffff, 2); + } + else + { + f = m68hc11_new_insn (2); + number_to_chars_bigendian (f, code, 1); + number_to_chars_bigendian (f + 1, n & 0x0FF, 1); + } + } + else if (opcode->format & M6812_OP_JUMP_REL16) + { + fix_new (frag_now, frag_now_fix (), 0, + &abs_symbol, 0, 1, BFD_RELOC_M68HC11_RL_JUMP); + + f = m68hc11_new_insn (2); + number_to_chars_bigendian (f, M6811_OPCODE_PAGE2, 1); + number_to_chars_bigendian (f + 1, code, 1); + fixup16 (&operands[0].exp, M6812_OP_JUMP_REL16, M6812_OP_JUMP_REL16); + } + else + { + char *op; + + fix_new (frag_now, frag_now_fix (), 0, + &abs_symbol, 0, 1, BFD_RELOC_M68HC11_RL_JUMP); + + /* Branch offset must fit in 8-bits, don't do some relax. */ + if (jmp_mode == 0 && flag_fixed_branches) + { + op = m68hc11_new_insn (1); + number_to_chars_bigendian (op, code, 1); + fixup8 (&operands[0].exp, M6811_OP_JUMP_REL, M6811_OP_JUMP_REL); + } + + /* bra/bsr made be changed into jmp/jsr. */ + else if (code == M6811_BSR || code == M6811_BRA || code == M6812_BSR) + { + /* Allocate worst case storage. */ + op = m68hc11_new_insn (3); + number_to_chars_bigendian (op, code, 1); + number_to_chars_bigendian (op + 1, 0, 1); + frag_variant (rs_machine_dependent, 1, 1, + ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF), + operands[0].exp.X_add_symbol, (offsetT) n, + op); + } + else if (current_architecture & cpu6812) + { + op = m68hc11_new_insn (2); + number_to_chars_bigendian (op, code, 1); + number_to_chars_bigendian (op + 1, 0, 1); + frag_var (rs_machine_dependent, 2, 2, + ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, STATE_UNDF), + operands[0].exp.X_add_symbol, (offsetT) n, op); + } + else + { + op = m68hc11_new_insn (2); + number_to_chars_bigendian (op, code, 1); + number_to_chars_bigendian (op + 1, 0, 1); + frag_var (rs_machine_dependent, 3, 3, + ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_UNDF), + operands[0].exp.X_add_symbol, (offsetT) n, op); + } + } +} + +/* Builds a dbne/dbeq/tbne/tbeq instruction. */ +static void +build_dbranch_insn (struct m68hc11_opcode *opcode, operand operands[], + int nb_operands, int jmp_mode) +{ + unsigned char code; + char *f; + unsigned long n; + + /* The relative branch conversion is not supported for + brclr and brset. */ + gas_assert ((opcode->format & M6811_OP_BITMASK) == 0); + gas_assert (nb_operands == 2); + gas_assert (operands[0].reg1 != REG_NONE); + + code = opcode->opcode & 0x0FF; + + f = m68hc11_new_insn (1); + number_to_chars_bigendian (f, code, 1); + + n = operands[1].exp.X_add_number; + code = operands[0].reg1; + + if (operands[0].reg1 == REG_NONE || operands[0].reg1 == REG_CCR + || operands[0].reg1 == REG_PC) + as_bad (_("Invalid register for dbcc/tbcc instruction.")); + + if (opcode->format & M6812_OP_IBCC_MARKER) + code |= 0x80; + else if (opcode->format & M6812_OP_TBCC_MARKER) + code |= 0x40; + + if (!(opcode->format & M6812_OP_EQ_MARKER)) + code |= 0x20; + + /* Turn into a long branch: + - when force long branch option (and not for jbcc pseudos), + - when jdbcc and the constant is out of -256..255 range, + - when branch optimization is allowed and branch out of range. */ + if ((jmp_mode == 0 && flag_force_long_jumps) + || (operands[1].exp.X_op == O_constant + && (!check_range (n, M6812_OP_IBCC_MARKER) && + (jmp_mode == 1 || flag_fixed_branches == 0)))) + { + f = frag_more (2); + code ^= 0x20; + number_to_chars_bigendian (f, code, 1); + number_to_chars_bigendian (f + 1, M6812_JMP, 1); + fixup16 (&operands[0].exp, M6811_OP_IND16, M6811_OP_IND16); + return; + } + + /* Branch with a constant that must fit in 9-bits. */ + if (operands[1].exp.X_op == O_constant) + { + if (!check_range (n, M6812_OP_IBCC_MARKER)) + { + as_bad (_("Operand out of range for a relative branch: `%ld'"), + n); + } + else + { + if ((long) n < 0) + code |= 0x10; + + f = frag_more (2); + number_to_chars_bigendian (f, code, 1); + number_to_chars_bigendian (f + 1, n & 0x0FF, 1); + } + } + else + { + /* Branch offset must fit in 8-bits, don't do some relax. */ + if (jmp_mode == 0 && flag_fixed_branches) + { + fixup8 (&operands[0].exp, M6811_OP_JUMP_REL, M6811_OP_JUMP_REL); + } + + else + { + f = frag_more (2); + number_to_chars_bigendian (f, code, 1); + number_to_chars_bigendian (f + 1, 0, 1); + frag_var (rs_machine_dependent, 3, 3, + ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_UNDF), + operands[1].exp.X_add_symbol, (offsetT) n, f); + } + } +} + +#define OP_EXTENDED (M6811_OP_PAGE2 | M6811_OP_PAGE3 | M6811_OP_PAGE4) + +/* Assemble the post index byte for 68HC12 extended addressing modes. */ + +static int +build_indexed_byte (operand *op, int format ATTRIBUTE_UNUSED, int move_insn) +{ + unsigned char byte = 0; + char *f; + int mode; + long val; + + val = op->exp.X_add_number; + mode = op->mode; + if (mode & M6812_AUTO_INC_DEC) + { + byte = 0x20; + if (mode & (M6812_POST_INC | M6812_POST_DEC)) + byte |= 0x10; + + if (op->exp.X_op == O_constant) + { + if (!check_range (val, mode)) + as_bad (_("Increment/decrement value is out of range: `%ld'."), + val); + + if (mode & (M6812_POST_INC | M6812_PRE_INC)) + byte |= (val - 1) & 0x07; + else + byte |= (8 - ((val) & 7)) | 0x8; + } + + switch (op->reg1) + { + case REG_NONE: + as_fatal (_("Expecting a register.")); + + case REG_X: + byte |= 0; + break; + + case REG_Y: + byte |= 0x40; + break; + + case REG_SP: + byte |= 0x80; + break; + + default: + as_bad (_("Invalid register for post/pre increment.")); + break; + } + + f = frag_more (1); + number_to_chars_bigendian (f, byte, 1); + return 1; + } + + if (mode & (M6812_OP_IDX | M6812_OP_D_IDX_2)) + { + switch (op->reg1) + { + case REG_X: + byte = 0; + break; + + case REG_Y: + byte = 1; + break; + + case REG_SP: + byte = 2; + break; + + case REG_PC: + byte = 3; + break; + + default: + as_bad (_("Invalid register.")); + break; + } + + if (op->exp.X_op == O_constant) + { + if (!check_range (val, M6812_OP_IDX)) + as_bad (_("Offset out of 16-bit range: %ld."), val); + + if (move_insn && !(val >= -16 && val <= 15) + && ((!(mode & M6812_OP_IDX) && !(mode & M6812_OP_D_IDX_2)) + || !(current_architecture & cpu9s12x))) + { + as_bad (_("Offset out of 5-bit range for movw/movb insn: %ld."), + val); + return -1; + } + + if (val >= -16 && val <= 15 && !(mode & M6812_OP_D_IDX_2)) + { + byte = byte << 6; + byte |= val & 0x1f; + f = frag_more (1); + number_to_chars_bigendian (f, byte, 1); + return 1; + } + else if (val >= -256 && val <= 255 && !(mode & M6812_OP_D_IDX_2)) + { + byte = byte << 3; + byte |= 0xe0; + if (val < 0) + byte |= 0x1; + f = frag_more (2); + number_to_chars_bigendian (f, byte, 1); + number_to_chars_bigendian (f + 1, val & 0x0FF, 1); + return 2; + } + else + { + byte = byte << 3; + if (mode & M6812_OP_D_IDX_2) + byte |= 0xe3; + else + byte |= 0xe2; + + f = frag_more (3); + number_to_chars_bigendian (f, byte, 1); + number_to_chars_bigendian (f + 1, val & 0x0FFFF, 2); + return 3; + } + } + + if (mode & M6812_OP_D_IDX_2) + { + byte = (byte << 3) | 0xe3; + f = frag_more (1); + number_to_chars_bigendian (f, byte, 1); + + fixup16 (&op->exp, 0, 0); + } + else if (op->reg1 != REG_PC) + { + symbolS *sym; + offsetT off; + + f = frag_more (1); + number_to_chars_bigendian (f, byte, 1); + sym = op->exp.X_add_symbol; + off = op->exp.X_add_number; + if (op->exp.X_op != O_symbol) + { + sym = make_expr_symbol (&op->exp); + off = 0; + } + + /* movb/movw cannot be relaxed. */ + if (move_insn) + { + if ((mode & M6812_OP_IDX) && (current_architecture & cpu9s12x)) + { + /* Must treat as a 16bit relocate as size of final result is unknown. */ + + byte <<= 3; + byte |= 0xe2; + number_to_chars_bigendian (f, byte, 1); + f = frag_more (2); + fix_new (frag_now, f - frag_now->fr_literal, 2, + sym, off, 0, BFD_RELOC_M68HC12_16B); + return 1; + } + else + { + /* Non-S12X will fail at relocate stage if offset out of range. */ + byte <<= 6; + number_to_chars_bigendian (f, byte, 1); + fix_new (frag_now, f - frag_now->fr_literal, 1, + sym, off, 0, BFD_RELOC_M68HC12_5B); + return 1; + } + } + else + { + number_to_chars_bigendian (f, byte, 1); + frag_var (rs_machine_dependent, 2, 2, + ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_UNDF), + sym, off, f); + } + } + else + { + f = frag_more (1); + + /* movb/movw cannot be relaxed. */ + if (move_insn) + { + byte <<= 6; + number_to_chars_bigendian (f, byte, 1); + fix_new (frag_now, f - frag_now->fr_literal, 1, + op->exp.X_add_symbol, op->exp.X_add_number, 0, BFD_RELOC_M68HC12_5B); + return 1; + } + else + { + number_to_chars_bigendian (f, byte, 1); + frag_var (rs_machine_dependent, 2, 2, + ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_UNDF), + op->exp.X_add_symbol, + op->exp.X_add_number, f); + } + } + return 3; + } + + if (mode & (M6812_OP_REG | M6812_OP_D_IDX)) + { + if (mode & M6812_OP_D_IDX) + { + if (op->reg1 != REG_D) + as_bad (_("Expecting register D for indexed indirect mode.")); + if ((move_insn) && (!(current_architecture & cpu9s12x))) + as_bad (_("Indexed indirect mode is not allowed for movb/movw.")); + + byte = 0xE7; + } + else + { + switch (op->reg1) + { + case REG_A: + byte = 0xE4; + break; + + case REG_B: + byte = 0xE5; + break; + + default: + as_bad (_("Invalid accumulator register.")); + + case REG_D: + byte = 0xE6; + break; + } + } + switch (op->reg2) + { + case REG_X: + break; + + case REG_Y: + byte |= (1 << 3); + break; + + case REG_SP: + byte |= (2 << 3); + break; + + case REG_PC: + byte |= (3 << 3); + break; + + default: + as_bad (_("Invalid indexed register.")); + break; + } + f = frag_more (1); + number_to_chars_bigendian (f, byte, 1); + return 1; + } + + fprintf (stderr, "mode = 0x%x\nop->reg1 = 0x%x\nop->reg2 = 0x%x\n", + mode, op->reg1, op->reg2); + as_fatal (_("Addressing mode not implemented yet.")); + return 0; +} + +/* Assemble the 68HC12 register mode byte. */ +static int +build_reg_mode (operand *op, int format) +{ + unsigned char byte; + char *f; + + if ((format & M6812_OP_SEX_MARKER) + && (op->reg1 != REG_A) && (op->reg1 != REG_B) && (op->reg1 != REG_CCR) + && (!(current_architecture & cpu9s12x))) + as_bad (_("Invalid source register for this instruction, use 'tfr'.")); + else if (op->reg1 == REG_NONE || op->reg1 == REG_PC) + as_bad (_("Invalid source register.")); + + if (format & M6812_OP_SEX_MARKER + && op->reg2 != REG_D + && op->reg2 != REG_X && op->reg2 != REG_Y && op->reg2 != REG_SP) + as_bad (_("Invalid destination register for this instruction, use 'tfr'.")); + else if (op->reg2 == REG_NONE || op->reg2 == REG_PC) + as_bad (_("Invalid destination register.")); + + byte = (op->reg1 << 4) | (op->reg2); + if (format & M6812_OP_EXG_MARKER) + byte |= 0x80; + + if ((format & M6812_OP_SEX_MARKER) + && (op->reg1 == REG_D) && (current_architecture & cpu9s12x)) + byte |= 0x08; + + f = frag_more (1); + number_to_chars_bigendian (f, byte, 1); + return 1; +} + +/* build_insn_xg takes a pointer to the opcode entry in the opcode table, + the array of operand expressions and builds the corresponding instruction. */ + +static void +build_insn_xg (struct m68hc11_opcode *opcode, + operand operands[], + int nb_operands ATTRIBUTE_UNUSED) +{ + char *f; + long format; + + /* Put the page code instruction if there is one. */ + format = opcode->format; + + if (!(operands[0].mode & (M6811_OP_LOW_ADDR | M6811_OP_HIGH_ADDR))) + /* Need to retain those two modes, but clear for others. */ + operands[0].mode = 0; + + if (format & M68XG_OP_R_IMM8) + { + /* These opcodes are byte followed by imm8. */ + f = m68hc11_new_insn (1); + number_to_chars_bigendian (f, opcode->opcode >> 8, 1); + fixup8_xg (&operands[0].exp, format, operands[0].mode); + } + else if (format & M68XG_OP_R_IMM16) + { + fixS *fixp; + /* These opcodes expand into two imm8 instructions. + Emit as low:high as per the Freescale datasheet. + The linker requires them to be adjacent to handle the upper byte. */ + + /* Build low byte. */ + f = m68hc11_new_insn (1); + number_to_chars_bigendian (f, opcode->opcode >> 8, 1); + operands[0].mode = M6811_OP_LOW_ADDR; + f = frag_more (1); + fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 1, + &operands[0].exp, FALSE, BFD_RELOC_M68HC12_LO8XG); + fixp->fx_no_overflow = 1; + number_to_chars_bigendian (f, 0, 1); + + /* Build high byte. */ + f = m68hc11_new_insn (1); + number_to_chars_bigendian (f, (opcode->opcode >> 8) | 0x08, 1); + operands[0].mode = M6811_OP_HIGH_ADDR; + f = frag_more (1); + fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 1, + &operands[0].exp, FALSE, BFD_RELOC_M68HC12_HI8XG); + fixp->fx_no_overflow = 1; + number_to_chars_bigendian (f, 0, 1); + + } + else if (format & M68XG_OP_REL9) + { + f = m68hc11_new_insn (1); + number_to_chars_bigendian (f, opcode->opcode >> 8, 1); /* High byte. */ + fixup8_xg (&operands[0].exp, format, M68XG_OP_REL9); + } + else if (format & M68XG_OP_REL10) + { + f = m68hc11_new_insn (1); + number_to_chars_bigendian (f, opcode->opcode >> 8, 1); /* High byte. */ + fixup8_xg (&operands[0].exp, format, M68XG_OP_REL10); + } + else + { + f = m68hc11_new_insn (2); + number_to_chars_bigendian (f, opcode->opcode, 2); + } + return; +} + +/* build_insn takes a pointer to the opcode entry in the opcode table, + the array of operand expressions and builds the corresponding instruction. + This operation only deals with non relative jumps insn (need special + handling). */ + +static void +build_insn (struct m68hc11_opcode *opcode, + operand operands[], + int nb_operands ATTRIBUTE_UNUSED) +{ + int i; + char *f; + long format; + int move_insn = 0; + + /* Put the page code instruction if there is one. */ + format = opcode->format; + + if (format & M6811_OP_BRANCH) + fix_new (frag_now, frag_now_fix (), 0, + &abs_symbol, 0, 1, BFD_RELOC_M68HC11_RL_JUMP); + + if (format & OP_EXTENDED) + { + int page_code; + + f = m68hc11_new_insn (2); + if (format & M6811_OP_PAGE2) + page_code = M6811_OPCODE_PAGE2; + else if (format & M6811_OP_PAGE3) + page_code = M6811_OPCODE_PAGE3; + else + page_code = M6811_OPCODE_PAGE4; + + number_to_chars_bigendian (f, page_code, 1); + f++; + } + else + f = m68hc11_new_insn (1); + + number_to_chars_bigendian (f, opcode->opcode, 1); + + i = 0; + + /* The 68HC12 movb and movw instructions are special. We have to handle + them in a special way. */ + if (format & (M6812_OP_IND16_P2 | M6812_OP_IDX_P2)) + { + move_insn = 1; + if (format & M6812_OP_IDX) + { + build_indexed_byte (&operands[0], format, 1); + i = 1; + format &= ~M6812_OP_IDX; + } + if (format & M6812_OP_IDX_P2) + { + build_indexed_byte (&operands[1], format, 1); + i = 0; + format &= ~M6812_OP_IDX_P2; + } + } + + if (format & (M6811_OP_DIRECT | M6811_OP_IMM8)) + { + fixup8 (&operands[i].exp, + format & (M6811_OP_DIRECT | M6811_OP_IMM8 | M6812_OP_TRAP_ID), + operands[i].mode); + i++; + } + else if (IS_CALL_SYMBOL (format) && nb_operands == 1) + { + format &= ~M6812_OP_PAGE; + fixup24 (&operands[i].exp, format & M6811_OP_IND16, + operands[i].mode); + i++; + } + else if (format & (M6811_OP_IMM16 | M6811_OP_IND16)) + { + fixup16 (&operands[i].exp, + format & (M6811_OP_IMM16 | M6811_OP_IND16 | M6812_OP_PAGE), + operands[i].mode); + i++; + } + else if (format & (M6811_OP_IX | M6811_OP_IY)) + { + if ((format & M6811_OP_IX) && (operands[0].reg1 != REG_X)) + as_bad (_("Invalid indexed register, expecting register X.")); + if ((format & M6811_OP_IY) && (operands[0].reg1 != REG_Y)) + as_bad (_("Invalid indexed register, expecting register Y.")); + + fixup8 (&operands[0].exp, M6811_OP_IX, operands[0].mode); + i = 1; + } + else if (format & + (M6812_OP_IDX | M6812_OP_IDX_2 | M6812_OP_IDX_1 + | M6812_OP_D_IDX | M6812_OP_D_IDX_2)) + { + build_indexed_byte (&operands[i], format, move_insn); + i++; + } + else if (format & M6812_OP_REG && current_architecture & cpu6812) + { + build_reg_mode (&operands[i], format); + i++; + } + if (format & M6811_OP_BITMASK) + { + fixup8 (&operands[i].exp, M6811_OP_BITMASK, operands[i].mode); + i++; + } + if (format & M6811_OP_JUMP_REL) + { + fixup8 (&operands[i].exp, M6811_OP_JUMP_REL, operands[i].mode); + } + else if (format & M6812_OP_IND16_P2) + { + fixup16 (&operands[1].exp, M6811_OP_IND16, operands[1].mode); + } + if (format & M6812_OP_PAGE) + { + fixup8 (&operands[i].exp, M6812_OP_PAGE, operands[i].mode); + } +} + +/* Opcode identification and operand analysis. */ + +/* find() gets a pointer to an entry in the opcode table. It must look at all + opcodes with the same name and use the operands to choose the correct + opcode. Returns the opcode pointer if there was a match and 0 if none. */ +static struct m68hc11_opcode * +find (struct m68hc11_opcode_def *opc, operand operands[], int nb_operands) +{ + int i, match, pos; + struct m68hc11_opcode *opcode; + struct m68hc11_opcode *op_indirect; + + op_indirect = 0; + opcode = opc->opcode; + + /* Now search the opcode table table for one with operands + that matches what we've got. */ + + if (current_architecture & cpuxgate) + { + /* Many XGATE insns are simple enough that we get an exact match. */ + for (pos = match = 0; match == 0 && pos < opc->nb_modes; pos++, opcode++) + if (opcode->format == operands[nb_operands-1].mode) + return opcode; + + return 0; + } + + /* Non XGATE */ + + /* Now search the opcode table table for one with operands + that matches what we've got. We're only done if the operands matched so + far AND there are no more to check. */ + for (pos = match = 0; match == 0 && pos < opc->nb_modes; pos++, opcode++) + { + int poss_indirect = 0; + long format = opcode->format; + int expect; + + expect = 0; + if (opcode->format & M6811_OP_MASK) + expect++; + if (opcode->format & M6811_OP_BITMASK) + expect++; + if (opcode->format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16)) + expect++; + if (opcode->format & (M6812_OP_IND16_P2 | M6812_OP_IDX_P2)) + expect++; + if ((opcode->format & M6812_OP_PAGE) + && (!IS_CALL_SYMBOL (opcode->format) || nb_operands == 2)) + expect++; + + for (i = 0; expect == nb_operands && i < nb_operands; i++) + { + int mode = operands[i].mode; + + if (mode & M6811_OP_IMM16) + { + if (format & + (M6811_OP_IMM8 | M6811_OP_IMM16 | M6811_OP_BITMASK)) + continue; + break; + } + if (mode == M6811_OP_DIRECT) + { + if (format & M6811_OP_DIRECT) + continue; + + /* If the operand is a page 0 operand, remember a + possible <abs-16> addressing mode. We mark + this and continue to check other operands. */ + if (format & M6811_OP_IND16 + && flag_strict_direct_addressing && op_indirect == 0) + { + poss_indirect = 1; + continue; + } + break; + } + if (mode & M6811_OP_IND16) + { + if (i == 0 && (format & M6811_OP_IND16) != 0) + continue; + if (i != 0 && (format & M6812_OP_PAGE) != 0) + continue; + if (i != 0 && (format & M6812_OP_IND16_P2) != 0) + continue; + if (i == 0 && (format & M6811_OP_BITMASK)) + break; + } + if (mode & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16)) + { + if (format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16)) + continue; + } + if (mode & M6812_OP_REG) + { + if (i == 0 + && (format & M6812_OP_REG) + && (operands[i].reg2 == REG_NONE)) + continue; + if (i == 0 + && (format & M6812_OP_REG) + && (format & M6812_OP_REG_2) + && (operands[i].reg2 != REG_NONE)) + continue; + if (i == 0 + && (format & M6812_OP_IDX) + && (operands[i].reg2 != REG_NONE)) + continue; + if (i == 0 + && (format & M6812_OP_IDX) + && (format & (M6812_OP_IND16_P2 | M6812_OP_IDX_P2))) + continue; + if (i == 1 + && (format & M6812_OP_IDX_P2)) + continue; + break; + } + if (mode & M6812_OP_IDX) + { + if (format & M6811_OP_IX && operands[i].reg1 == REG_X) + continue; + if (format & M6811_OP_IY && operands[i].reg1 == REG_Y) + continue; + if (i == 0 + && format & (M6812_OP_IDX | M6812_OP_IDX_1 | M6812_OP_IDX_2) + && (operands[i].reg1 == REG_X + || operands[i].reg1 == REG_Y + || operands[i].reg1 == REG_SP + || operands[i].reg1 == REG_PC)) + continue; + if (i == 1 && (format & M6812_OP_IDX_P2)) + continue; + } + if (mode & format & (M6812_OP_D_IDX | M6812_OP_D_IDX_2)) + { + if (i == 0) + continue; + } + if (mode & M6812_AUTO_INC_DEC) + { + if (i == 0 + && format & (M6812_OP_IDX | M6812_OP_IDX_1 | + M6812_OP_IDX_2)) + continue; + if (i == 1 && format & M6812_OP_IDX_P2) + continue; + } + break; + } + match = i == nb_operands; + + /* Operands are ok but an operand uses page 0 addressing mode + while the insn supports abs-16 mode. Keep a reference to this + insns in case there is no insn supporting page 0 addressing. */ + if (match && poss_indirect) + { + op_indirect = opcode; + match = 0; + } + if (match) + break; + } + + /* Page 0 addressing is used but not supported by any insn. + If absolute addresses are supported, we use that insn. */ + if (match == 0 && op_indirect) + { + opcode = op_indirect; + match = 1; + } + + return match ? opcode : 0; +} + +/* Find the real opcode and its associated operands. We use a progressive + approach here. On entry, 'opc' points to the first opcode in the + table that matches the opcode name in the source line. We try to + isolate an operand, find a possible match in the opcode table. + We isolate another operand if no match were found. The table 'operands' + is filled while operands are recognized. + + Returns the opcode pointer that matches the opcode name in the + source line and the associated operands. */ +static struct m68hc11_opcode * +find_opcode (struct m68hc11_opcode_def *opc, operand operands[], + int *nb_operands) +{ + struct m68hc11_opcode *opcode; + int i; + + if (opc->max_operands == 0) + { + *nb_operands = 0; + return opc->opcode; + } + + for (i = 0; i < opc->max_operands;) + { + int result; + + result = get_operand (&operands[i], i, opc->format); + if (result <= 0) + return 0; + + /* Special case where the bitmask of the bclr/brclr + instructions is not introduced by #. + Example: bclr 3,x $80. */ + if (i == 1 && (opc->format & M6811_OP_BITMASK) + && (operands[i].mode & M6811_OP_IND16)) + { + operands[i].mode = M6811_OP_IMM16; + } + + i += result; + *nb_operands = i; + if (i >= opc->min_operands) + { + opcode = find (opc, operands, i); + + /* Another special case for 'call foo,page' instructions. + Since we support 'call foo' and 'call foo,page' we must look + if the optional page specification is present otherwise we will + assemble immediately and treat the page spec as garbage. */ + if (opcode && !(opcode->format & M6812_OP_PAGE)) + return opcode; + + if (opcode && *input_line_pointer != ',') + return opcode; + } + + if (*input_line_pointer == ',') + input_line_pointer++; + } + + return 0; +} + +#define M6812_XBCC_MARKER (M6812_OP_TBCC_MARKER \ + | M6812_OP_DBCC_MARKER \ + | M6812_OP_IBCC_MARKER) + +/* Gas line assembler entry point. */ + +/* This is the main entry point for the machine-dependent assembler. str + points to a machine-dependent instruction. This function is supposed to + emit the frags/bytes it assembles to. */ +void +md_assemble (char *str) +{ + struct m68hc11_opcode_def *opc; + struct m68hc11_opcode *opcode; + + struct m68hc11_opcode opcode_local; + unsigned char *op_start, *op_end; + char *save; + char name[20]; + int nlen = 0; + operand operands[M6811_MAX_OPERANDS]; + int nb_operands = 0; + int branch_optimize = 0; + int alias_id = -1; + + /* Drop leading whitespace. */ + while (*str == ' ') + str++; + + /* Find the opcode end and get the opcode in 'name'. The opcode is forced + lower case (the opcode table only has lower case op-codes). */ + for (op_start = op_end = (unsigned char *) str; + *op_end && !is_end_of_line[*op_end] && *op_end != ' '; + op_end++) + { + name[nlen] = TOLOWER (op_start[nlen]); + nlen++; + if (nlen == sizeof (name) - 1) + break; + } + name[nlen] = 0; + + if (nlen == 0) + { + as_bad (_("No instruction or missing opcode.")); + return; + } + + if (current_architecture == cpuxgate) + { + /* Find the opcode definition given its name. */ + opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash, name); + if (opc == NULL) + { + as_bad (_("Opcode `%s' is not recognized."), name); + return; + } + + /* Grab a local copy. */ + opcode_local.name = opc->opcode->name; + /* These will be incomplete where multiple variants exist. */ + opcode_local.opcode = opc->opcode->opcode; + opcode_local.format = opc->opcode->format; + + save = input_line_pointer; + input_line_pointer = (char *) op_end; + + if (opc->format == M68XG_OP_NONE) + { + /* No special handling required. */ + opcode_local.format = M68XG_OP_NONE; + build_insn_xg (opc->opcode, operands, 0); + return; + } + + /* Special handling of TFR. */ + if (strncmp (opc->opcode->name, "tfr",3) == 0) + { + /* There must be two operands with a comma. */ + input_line_pointer = skip_whites (input_line_pointer); + operands[0].reg1 = register_name (); + if (operands[0].reg1 == REG_NONE) + { + as_bad ("Invalid register\n"); + return; + } + input_line_pointer = skip_whites (input_line_pointer); + if (*input_line_pointer != ',') + { + as_bad ("Missing comma.\n"); + return; + } + input_line_pointer++; + input_line_pointer = skip_whites (input_line_pointer); + operands[1].reg1 = register_name (); + if (operands[1].reg1 == REG_NONE) + { + as_bad ("Invalid register\n"); + return; + } + input_line_pointer = skip_whites (input_line_pointer); + if (*input_line_pointer != '\n' && *input_line_pointer) + { + as_bad (_("Garbage at end of instruction: `%s'."), + input_line_pointer); + return; + } + if (operands[1].reg1 == REG_CCR) /* ,CCR */ + opc->opcode->opcode = 0x00f8 | ( operands[0].reg1 << 8); + else if (operands[0].reg1 == REG_CCR) /* CCR, */ + opc->opcode->opcode = 0x00f9 | ( operands[1].reg1 << 8); + else if (operands[1].reg1 == REG_PC) /* ,PC */ + opc->opcode->opcode = 0x00fa | ( operands[0].reg1 << 8); + else + { + as_bad ("Invalid operand to TFR\n"); + return; + } + /* no special handling required */ + opcode_local.format = M68XG_OP_NONE; + opcode_local.opcode = opc->opcode->opcode; + build_insn_xg (&opcode_local, operands, 0); + return; + } + + /* CSEM, SSEM */ + if (opc->format & M68XG_OP_IMM3) + { + /* Either IMM3 or R */ + input_line_pointer = skip_whites (input_line_pointer); + if ((*input_line_pointer == 'R') || (*input_line_pointer == 'r')) + { + operands[0].reg1 = register_name (); + if (operands[0].reg1 == REG_NONE) + { + as_bad ("Invalid register\n"); + return; + } + operands[0].mode = M68XG_OP_R; + /* One opcode has multiple modes, so find right one. */ + opcode = find (opc, operands, 1); + if (opcode) + { + opcode_local.opcode = opcode->opcode + | (operands[0].reg1 << 8); + opcode_local.format = M68XG_OP_NONE; + build_insn_xg (&opcode_local, operands, 1); + } + else + as_bad ("No opcode found\n"); + + return; + } + else + { + if (*input_line_pointer == '#') + input_line_pointer++; + + expression (&operands[0].exp); + if (operands[0].exp.X_op == O_illegal) + { + as_bad (_("Illegal operand.")); + return; + } + else if (operands[0].exp.X_op == O_absent) + { + as_bad (_("Missing operand.")); + return; + } + + if (check_range (operands[0].exp.X_add_number,M68XG_OP_IMM3)) + { + opcode_local.opcode |= (operands[0].exp.X_add_number); + operands[0].mode = M68XG_OP_IMM3; + + opcode = find (opc, operands, 1); + if (opcode) + { + opcode_local.opcode = opcode->opcode; + opcode_local.opcode + |= (operands[0].exp.X_add_number) << 8; + opcode_local.format = M68XG_OP_NONE; + build_insn_xg (&opcode_local, operands, 1); + } + else + as_bad ("No opcode found\n"); + + return; + } + else + { + as_bad ("Number out of range for IMM3\n"); + return; + } + } + } + + /* Special handling of SIF. */ + if (strncmp (opc->opcode->name, "sif",3) == 0) + { + /* Either OP_NONE or OP_RS. */ + if (*input_line_pointer != '\n') + input_line_pointer = skip_whites (input_line_pointer); + + if ((*input_line_pointer == '\n') || (*input_line_pointer == '\r') + || (*input_line_pointer == '\0')) + opc->opcode->opcode = 0x0300; + else + { + operands[0].reg1 = register_name (); + if (operands[0].reg1 == REG_NONE) + { + as_bad ("Invalid register\n"); + return; + } + opcode_local.opcode = 0x00f7 | (operands[0].reg1 << 8); + } + opcode_local.format = M68XG_OP_NONE; + build_insn_xg (&opcode_local, operands, 0); + return; + } + + /* SEX, PAR, JAL plus aliases NEG, TST, COM */ + if (opc->format & M68XG_OP_R) + { + input_line_pointer = skip_whites (input_line_pointer); + operands[0].reg1 = register_name (); + if (operands[0].reg1 == REG_NONE) + { + as_bad ("Invalid register\n"); + return; + } + if ((*input_line_pointer == '\n') || (*input_line_pointer == '\r') + || (*input_line_pointer == '\0')) + { + /* Likely to be OP R. */ + if (opc->format & M68XG_OP_R) + { + operands[0].mode = M68XG_OP_R; + + opcode = find (opc, operands, 1); + if (opcode) + { + if ((strncmp (opc->opcode->name, "com",3) == 0) + || (strncmp (opc->opcode->name, "neg",3) == 0)) + /* Special case for com RD as alias for sub RD,R0,RS */ + /* Special case for neg RD as alias for sub RD,R0,RS */ + opcode_local.opcode = opcode->opcode + | (operands[0].reg1 << 8) | (operands[0].reg1 << 2); + else if (strncmp (opc->opcode->name, "tst",3) == 0) + /* Special case for tst RS alias for sub R0, RS, R0 */ + opcode_local.opcode = opcode->opcode + | (operands[0].reg1 << 5); + else + opcode_local.opcode |= (operands[0].reg1 << 8); + } + opcode_local.format = M68XG_OP_NONE; + build_insn_xg (&opcode_local, operands, 0); + } + else + as_bad ("No valid mode found\n"); + + return; + } + } + + if (opc->format & (M68XG_OP_REL9 | M68XG_OP_REL10)) + { + opcode_local.format = opc->format; + input_line_pointer = skip_whites (input_line_pointer); + expression (&operands[0].exp); + if (operands[0].exp.X_op == O_illegal) + { + as_bad (_("Illegal operand.")); + return; + } + else if (operands[0].exp.X_op == O_absent) + { + as_bad (_("Missing operand.")); + return; + } + opcode_local.opcode = opc->opcode->opcode; + build_insn_xg (&opcode_local, operands, 1); + return; + } + + + /* For other command formats, parse input line and determine the mode + we are using as we go. */ + + input_line_pointer = skip_whites (input_line_pointer); + if ((*input_line_pointer == '\n') || (*input_line_pointer == '\r') + || (*input_line_pointer == '\0')) + return; /* nothing left */ + + if (*input_line_pointer == '#') + { + as_bad ("No register specified before hash\n"); + return; + } + + /* first operand is expected to be a register */ + if ((*input_line_pointer == 'R') || (*input_line_pointer == 'r')) + { + operands[0].reg1 = register_name (); + if (operands[0].reg1 == REG_NONE) + { + as_bad ("Invalid register\n"); + return; + } + } + + input_line_pointer = skip_whites (input_line_pointer); + if (*input_line_pointer != ',') + { + as_bad ("Missing operand\n"); + return; + } + input_line_pointer++; + input_line_pointer = skip_whites (input_line_pointer); + + if (*input_line_pointer == '#') + { + /* Some kind of immediate mode, check if this is possible. */ + if (!(opc->format + & (M68XG_OP_R_IMM8 | M68XG_OP_R_IMM16 | M68XG_OP_R_IMM4))) + as_bad ("Invalid immediate mode for `%s'", opc->opcode->name); + else + { + input_line_pointer++; + input_line_pointer = skip_whites (input_line_pointer); + if (strncmp (input_line_pointer, "%hi", 3) == 0) + { + input_line_pointer += 3; + operands[0].mode = M6811_OP_HIGH_ADDR; + } + else if (strncmp (input_line_pointer, "%lo", 3) == 0) + { + input_line_pointer += 3; + operands[0].mode = M6811_OP_LOW_ADDR; + } + else + operands[0].mode = 0; + + expression (&operands[0].exp); + if (operands[0].exp.X_op == O_illegal) + { + as_bad (_("Illegal operand.")); + return; + } + else if (operands[0].exp.X_op == O_absent) + { + as_bad (_("Missing operand.")); + return; + } + /* ok so far, can only be one mode */ + opcode_local.format = opc->format + & (M68XG_OP_R_IMM8 | M68XG_OP_R_IMM16 | M68XG_OP_R_IMM4); + if (opcode_local.format & M68XG_OP_R_IMM4) + { + operands[0].mode = M68XG_OP_R_IMM4; + /* same opcodes have multiple modes, so find right one */ + opcode = find (opc, operands, 1); + if (opcode) + opcode_local.opcode = opcode->opcode + | (operands[0].reg1 << 8); + + if (operands[0].exp.X_op != O_constant) + as_bad ("Only constants supported at for IMM4 mode\n"); + else + { + if (check_range + (operands[0].exp.X_add_number,M68XG_OP_R_IMM4)) + opcode_local.opcode + |= (operands[0].exp.X_add_number << 4); + else + as_bad ("Number out of range for IMM4\n"); + } + opcode_local.format = M68XG_OP_NONE; + } + else if (opcode_local.format & M68XG_OP_R_IMM16) + { + operands[0].mode = M68XG_OP_R_IMM16; + + opcode = find (opc, operands, 1); + if (opcode) + { + opcode_local.opcode = opcode->opcode + | (operands[0].reg1 << 8); + } + } + else + { + opcode_local.opcode = opc->opcode->opcode + | (operands[0].reg1 << 8); + } + build_insn_xg (&opcode_local, operands, 1); + } + } + else if ((*input_line_pointer == 'R') || (*input_line_pointer == 'r')) + { + /* we've got as far as OP R, R */ + operands[1].reg1 = register_name (); + if (operands[1].reg1 == REG_NONE) + { + as_bad ("Invalid register\n"); + return; + } + if ((*input_line_pointer == '\n') || (*input_line_pointer == '\r') + || (*input_line_pointer == '\0')) + { + /* looks like OP_R_R */ + if (opc->format & M68XG_OP_R_R) + { + operands[0].mode = M68XG_OP_R_R; + /* same opcodes have multiple modes, so find right one */ + opcode = find (opc, operands, 1); + if (opcode) + { + if ((strncmp (opc->opcode->name, "com",3) == 0) + || (strncmp (opc->opcode->name, "mov",3) == 0) + || (strncmp (opc->opcode->name, "neg",3) == 0)) + { + /* Special cases for: + com RD, RS alias for xnor RD,R0,RS + mov RD, RS alias for or RD, R0, RS + neg RD, RS alias for sub RD, R0, RS */ + opcode_local.opcode = opcode->opcode + | (operands[0].reg1 << 8) | (operands[1].reg1 << 2); + } + else if ((strncmp (opc->opcode->name, "cmp",3) == 0) + || (strncmp (opc->opcode->name, "cpc",3) == 0)) + { + /* special cases for: + cmp RS1, RS2 alias for sub R0, RS1, RS2 + cpc RS1, RS2 alias for sbc R0, RS1, RS2 */ + opcode_local.opcode = opcode->opcode + | (operands[0].reg1 << 5) | (operands[1].reg1 << 2); + } + else + { + opcode_local.opcode = opcode->opcode + | (operands[0].reg1 << 8) | (operands[1].reg1 << 5); + } + opcode_local.format = M68XG_OP_NONE; + build_insn_xg (&opcode_local, operands, 1); + } + } + else + { + as_bad ("No valid mode found\n"); + } + } + else + { + /* more data */ + if (*input_line_pointer != ',') + { + as_bad (_("Missing operand.")); + return; + } + input_line_pointer++; + input_line_pointer = skip_whites (input_line_pointer); + if ((*input_line_pointer == 'R') || (*input_line_pointer == 'r')) + { + operands[2].reg1 = register_name (); + if (operands[2].reg1 == REG_NONE) + { + as_bad ("Invalid register\n"); + return; + } + if (opc->format & M68XG_OP_R_R_R) + { + operands[0].mode = M68XG_OP_R_R_R; + + opcode = find (opc, operands, 1); + if (opcode) + { + opcode_local.opcode = opcode->opcode + | (operands[0].reg1 << 8) | (operands[1].reg1 << 5) + | (operands[2].reg1 << 2); + opcode_local.format = M68XG_OP_NONE; + build_insn_xg (&opcode_local, operands, 1); + } + } + else + { + as_bad ("No valid mode found\n"); + } + } + } + } + else if (*input_line_pointer == '(') /* Indexed modes */ + { + input_line_pointer++; + input_line_pointer = skip_whites (input_line_pointer); + if ((*input_line_pointer == 'R') || (*input_line_pointer == 'r')) + { + /* we've got as far as OP R, (R */ + operands[1].reg1 = register_name (); + if (operands[1].reg1 == REG_NONE) + { + as_bad ("Invalid register\n"); + return; + } + + if ((*input_line_pointer == '\n') || (*input_line_pointer == '\r') + || (*input_line_pointer == '\0')) + { + /* Looks like OP_R_R. */ + as_bad (_("Missing operand.")); + return; + } + + input_line_pointer = skip_whites (input_line_pointer); + + if (*input_line_pointer != ',') + { + as_bad (_("Missing operand.")); + return; + } + input_line_pointer++; + input_line_pointer = skip_whites (input_line_pointer); + + if (*input_line_pointer == '#') + { + input_line_pointer++; + input_line_pointer = skip_whites (input_line_pointer); + expression (&operands[0].exp); + if (operands[0].exp.X_op == O_illegal) + { + as_bad (_("Illegal operand.")); + return; + } + else if (operands[0].exp.X_op == O_absent) + { + as_bad (_("Missing operand.")); + return; + } + + input_line_pointer = skip_whites (input_line_pointer); + if (*input_line_pointer != ')') + { + as_bad ("Missing `)' to close register indirect operand."); + return; + } + else + { + input_line_pointer++; + } + + /* Ok so far, can only be one mode. */ + opcode_local.format = M68XG_OP_R_R_OFFS5; + operands[0].mode = M68XG_OP_R_R_OFFS5; + + opcode = find (opc, operands, 1); + if (opcode) + { + opcode_local.opcode = opcode->opcode + | (operands[0].reg1 << 8) | (operands[1].reg1 << 5); + if (operands[0].exp.X_op != O_constant) + { + as_bad + ("Only constants supported for indexed OFFS5 mode\n"); + } + else + { + if (check_range (operands[0].exp.X_add_number, + M68XG_OP_R_R_OFFS5)) + { + opcode_local.opcode + |= (operands[0].exp.X_add_number); + opcode_local.format = M68XG_OP_NONE; + build_insn_xg (&opcode_local, operands, 1); + } + else + { + as_bad ("Number out of range for OFFS5\n"); + } + } + } + } + else + { + operands[0].mode = M68XG_OP_RD_RB_RI; + + if (*input_line_pointer == '-') + { + operands[0].mode = M68XG_OP_RD_RB_mRI; + input_line_pointer++; + } + operands[2].reg1 = register_name (); + if (operands[2].reg1 == REG_NONE) + { + as_bad ("Invalid register\n"); + return; + } + + if (*input_line_pointer == '+') + { + if (opcode_local.format == M68XG_OP_RD_RB_mRI) + { + as_bad (_("Illegal operand.")); + return; + } + operands[0].mode = M68XG_OP_RD_RB_RIp; + input_line_pointer++; + } + + input_line_pointer = skip_whites (input_line_pointer); + if (*input_line_pointer != ')') + { + as_bad + ("Missing `)' to close register indirect operand."); + return; + } + else + { + input_line_pointer++; + } + + opcode = find (opc, operands, 1); + if (opcode) + { + opcode_local.opcode = opcode->opcode + | (operands[0].reg1 << 8) | (operands[1].reg1 << 5) + | (operands[2].reg1 << 2); + opcode_local.format = M68XG_OP_NONE; + build_insn_xg (&opcode_local, operands, 1); + } + else + { + as_bad ("Failed to find opcode for %s %s\n", + opc->opcode->name, (char *)op_end); + } + } + } + } + else + { + as_bad (_("Failed to find a valid mode for `%s'."), + opc->opcode->name); + } + + if (opc->opcode && !flag_mri) + { + char *p = input_line_pointer; + + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') + p++; + + if (*p != '\n' && *p) + as_bad (_("Garbage at end of instruction: `%s'."), p); + } + + input_line_pointer = save; + + /* Opcode is known but does not have valid operands. Print out the + syntax for this opcode. */ + if (opc->opcode == 0) + { + if (flag_print_insn_syntax) + print_insn_format (name); + + as_bad (_("Invalid operand for `%s'"), name); + return; + } + + return; + } + + /* Find the opcode definition given its name. */ + opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash, name); + + /* If it's not recognized, look for 'jbsr' and 'jbxx'. These are + pseudo insns for relative branch. For these branches, we always + optimize them (turned into absolute branches) even if --short-branches + is given. */ + if (opc == NULL && name[0] == 'j' && name[1] == 'b') + { + opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash, &name[1]); + if (opc + && (!(opc->format & M6811_OP_JUMP_REL) + || (opc->format & M6811_OP_BITMASK))) + opc = 0; + if (opc) + branch_optimize = 1; + } + + /* The following test should probably be removed. This does not conform + to Motorola assembler specs. */ + if (opc == NULL && flag_mri) + { + if (*op_end == ' ' || *op_end == '\t') + { + while (*op_end == ' ' || *op_end == '\t') + op_end++; + + if (nlen < 19 + && (*op_end && + (is_end_of_line[op_end[1]] + || op_end[1] == ' ' || op_end[1] == '\t' + || !ISALNUM (op_end[1]))) + && (*op_end == 'a' || *op_end == 'b' + || *op_end == 'A' || *op_end == 'B' + || *op_end == 'd' || *op_end == 'D' + || *op_end == 'x' || *op_end == 'X' + || *op_end == 'y' || *op_end == 'Y')) + { + name[nlen++] = TOLOWER (*op_end++); + name[nlen] = 0; + opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash, + name); + } + } + } + + /* Identify a possible instruction alias. There are some on the + 68HC12 to emulate a few 68HC11 instructions. */ + if (opc == NULL && (current_architecture & cpu6812)) + { + int i; + + for (i = 0; i < m68hc12_num_alias; i++) + if (strcmp (m68hc12_alias[i].name, name) == 0) + { + alias_id = i; + break; + } + } + if (opc == NULL && alias_id < 0) + { + as_bad (_("Opcode `%s' is not recognized."), name); + return; + } + save = input_line_pointer; + input_line_pointer = (char *) op_end; + + if (opc) + { + opc->used++; + opcode = find_opcode (opc, operands, &nb_operands); + } + else + opcode = 0; + + if ((opcode || alias_id >= 0) && !flag_mri) + { + char *p = input_line_pointer; + + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') + p++; + + if (*p != '\n' && *p) + as_bad (_("Garbage at end of instruction: `%s'."), p); + } + + input_line_pointer = save; + + if (alias_id >= 0) + { + char *f = m68hc11_new_insn (m68hc12_alias[alias_id].size); + + number_to_chars_bigendian (f, m68hc12_alias[alias_id].code1, 1); + if (m68hc12_alias[alias_id].size > 1) + number_to_chars_bigendian (f + 1, m68hc12_alias[alias_id].code2, 1); + + return; + } + + /* Opcode is known but does not have valid operands. Print out the + syntax for this opcode. */ + if (opcode == 0) + { + if (flag_print_insn_syntax) + print_insn_format (name); + + if (((strcmp (name, "movb") == 0) || (strcmp (name, "movw") == 0)) + && (current_architecture & cpu9s12x)) + { + char *f; + int movb; + if (strcmp (name, "movb") == 0) + movb = 8; + else + movb = 0; + + /* The existing operand extract code fell over if these additional modes + were enabled in m68hc11-opc.c. So they are commented there and + decoded here instead. */ + + if (operands[1].mode & (M6812_OP_IDX | M6812_OP_IDX_1 + | M6812_OP_IDX_2 | M6812_OP_D_IDX | M6812_OP_D_IDX_2 | M6812_PRE_INC + | M6812_PRE_DEC | M6812_POST_INC | M6812_POST_DEC )) + { + /* first check if valid mode then start building it up */ + if (operands[0].mode & (M6811_OP_IMM8 | M6811_OP_IMM16 + | M6811_OP_IND16 | M6812_OP_IDX | M6812_OP_IDX_1 + | M6812_OP_IDX_2 | M6812_OP_D_IDX | M6812_OP_D_IDX_2)) + { + int opr16a; + if (operands[1].mode & (M6811_OP_IND16)) + opr16a = 3; + else + opr16a = 0; + + f = m68hc11_new_insn (2); + + if (operands[0].mode & (M6811_OP_IMM8 | M6811_OP_IMM16)) + { + number_to_chars_bigendian (f, 0x1800 + movb + opr16a, 2); + build_indexed_byte (&operands[1], operands[1].mode, 1); + if (movb) + fixup8 (&operands[0].exp, M6811_OP_IMM8, + operands[0].mode); + else + fixup16 (&operands[0].exp, M6811_OP_IMM16, + operands[0].mode); + + return; + } + else if (operands[0].mode & M6811_OP_IND16) + { + number_to_chars_bigendian (f, 0x1801 + movb + opr16a, 2); + build_indexed_byte (&operands[1], operands[1].mode, 1); + fixup16 (&operands[0].exp, M6811_OP_IND16, operands[0].mode); + return; + } + else + { + number_to_chars_bigendian (f, 0x1802 + movb + opr16a, 2); + build_indexed_byte (&operands[0], operands[0].mode, 1); + build_indexed_byte (&operands[1], operands[1].mode, 1); + return; + } + } + } + else if (operands[1].mode & M6811_OP_IND16) + { + /* First check if this is valid mode, then start building it up. */ + if (operands[0].mode & (M6811_OP_IMM8 | M6811_OP_IMM16 + | M6811_OP_IND16 | M6812_OP_IDX | M6812_OP_IDX_1 + | M6812_OP_IDX_2 | M6812_OP_D_IDX | M6812_OP_D_IDX_2)) + { + int opr16a; + if (operands[1].mode & (M6811_OP_IND16)) + opr16a = 3; + else + opr16a = 0; + + f = m68hc11_new_insn (2); + + /* The first two cases here should actually be covered by the + normal operand code. */ + if (operands[0].mode & (M6811_OP_IMM8 | M6811_OP_IMM16)) + { + number_to_chars_bigendian (f, 0x1800 + movb + opr16a, 2); + if (movb) + fixup8 (&operands[0].exp, M6811_OP_IMM8, operands[0].mode); + else + fixup16 (&operands[0].exp, M6811_OP_IMM16, operands[0].mode); + + fixup16 (&operands[0].exp, M6811_OP_IND16, operands[0].mode); + return; + } + else if (operands[0].mode & M6811_OP_IND16) + { + number_to_chars_bigendian (f, 0x1801 + movb + opr16a, 2); + build_indexed_byte (&operands[1], operands[1].mode, 1); + fixup16 (&operands[0].exp, M6811_OP_IND16, operands[0].mode); + return; + } + else + { + number_to_chars_bigendian (f, 0x1802 + movb + opr16a, 2); + build_indexed_byte (&operands[0], operands[0].mode, 1); + fixup16 (&operands[1].exp, M6811_OP_IND16, operands[1].mode); + return; + } + } + } + + as_bad (_("Invalid operand for `%s'"), name); + return; + + } + else + { + as_bad (_("Invalid operand for `%s'"), name); + return; + } + } + + /* Treat dbeq/ibeq/tbeq instructions in a special way. The branch is + relative and must be in the range -256..255 (9-bits). */ + if ((opcode->format & M6812_XBCC_MARKER) + && (opcode->format & M6811_OP_JUMP_REL)) + build_dbranch_insn (opcode, operands, nb_operands, branch_optimize); + + /* Relative jumps instructions are taken care of separately. We have to make + sure that the relative branch is within the range -128..127. If it's out + of range, the instructions are changed into absolute instructions. + This is not supported for the brset and brclr instructions. */ + else if ((opcode->format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16)) + && !(opcode->format & M6811_OP_BITMASK)) + build_jump_insn (opcode, operands, nb_operands, branch_optimize); + else + build_insn (opcode, operands, nb_operands); +} + + +/* Pseudo op to control the ELF flags. */ +static void +s_m68hc11_mode (int x ATTRIBUTE_UNUSED) +{ + char *name = input_line_pointer, ch; + + while (!is_end_of_line[(unsigned char) *input_line_pointer]) + input_line_pointer++; + ch = *input_line_pointer; + *input_line_pointer = '\0'; + + if (strcmp (name, "mshort") == 0) + { + elf_flags &= ~E_M68HC11_I32; + } + else if (strcmp (name, "mlong") == 0) + { + elf_flags |= E_M68HC11_I32; + } + else if (strcmp (name, "mshort-double") == 0) + { + elf_flags &= ~E_M68HC11_F64; + } + else if (strcmp (name, "mlong-double") == 0) + { + elf_flags |= E_M68HC11_F64; + } + else + { + as_warn (_("Invalid mode: %s\n"), name); + } + *input_line_pointer = ch; + demand_empty_rest_of_line (); +} + +/* Mark the symbols with STO_M68HC12_FAR to indicate the functions + are using 'rtc' for returning. It is necessary to use 'call' + to invoke them. This is also used by the debugger to correctly + find the stack frame. */ +static void +s_m68hc11_mark_symbol (int mark) +{ + char *name; + int c; + symbolS *symbolP; + asymbol *bfdsym; + elf_symbol_type *elfsym; + + do + { + name = input_line_pointer; + c = get_symbol_end (); + symbolP = symbol_find_or_make (name); + *input_line_pointer = c; + + SKIP_WHITESPACE (); + + bfdsym = symbol_get_bfdsym (symbolP); + elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym); + + gas_assert (elfsym); + + /* Mark the symbol far (using rtc for function return). */ + elfsym->internal_elf_sym.st_other |= mark; + + if (c == ',') + { + input_line_pointer ++; + + SKIP_WHITESPACE (); + + if (*input_line_pointer == '\n') + c = '\n'; + } + } + while (c == ','); + + demand_empty_rest_of_line (); +} + +static void +s_m68hc11_relax (int ignore ATTRIBUTE_UNUSED) +{ + expressionS ex; + + expression (&ex); + + if (ex.X_op != O_symbol || ex.X_add_number != 0) + { + as_bad (_("bad .relax format")); + ignore_rest_of_line (); + return; + } + + fix_new_exp (frag_now, frag_now_fix (), 0, &ex, 1, + BFD_RELOC_M68HC11_RL_GROUP); + + demand_empty_rest_of_line (); +} + + +/* Relocation, relaxation and frag conversions. */ + +/* PC-relative offsets are relative to the start of the + next instruction. That is, the address of the offset, plus its + size, since the offset is always the last part of the insn. */ +long +md_pcrel_from (fixS *fixP) +{ + if (fixP->fx_r_type == BFD_RELOC_M68HC11_RL_JUMP) + return 0; + + return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; +} + +/* If while processing a fixup, a reloc really needs to be created + then it is done here. */ +arelent * +tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp) +{ + arelent *reloc; + + reloc = (arelent *) xmalloc (sizeof (arelent)); + reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; + if (fixp->fx_r_type == 0) + reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16); + else + reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type); + if (reloc->howto == (reloc_howto_type *) NULL) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + _("Relocation %d is not supported by object file format."), + (int) fixp->fx_r_type); + return NULL; + } + + /* Since we use Rel instead of Rela, encode the vtable entry to be + used in the relocation's section offset. */ + if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + reloc->address = fixp->fx_offset; + + reloc->addend = 0; + return reloc; +} + +/* We need a port-specific relaxation function to cope with sym2 - sym1 + relative expressions with both symbols in the same segment (but not + necessarily in the same frag as this insn), for example: + ldab sym2-(sym1-2),pc + sym1: + The offset can be 5, 9 or 16 bits long. */ + +long +m68hc11_relax_frag (segT seg ATTRIBUTE_UNUSED, fragS *fragP, + long stretch ATTRIBUTE_UNUSED) +{ + long growth; + offsetT aim = 0; + symbolS *symbolP; + const relax_typeS *this_type; + const relax_typeS *start_type; + relax_substateT next_state; + relax_substateT this_state; + const relax_typeS *table = TC_GENERIC_RELAX_TABLE; + + /* We only have to cope with frags as prepared by + md_estimate_size_before_relax. The STATE_BITS16 case may geet here + because of the different reasons that it's not relaxable. */ + switch (fragP->fr_subtype) + { + case ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS16): + case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16): + /* When we get to this state, the frag won't grow any more. */ + return 0; + + case ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS5): + case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS5): + case ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS9): + case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9): + if (fragP->fr_symbol == NULL + || S_GET_SEGMENT (fragP->fr_symbol) != absolute_section) + as_fatal (_("internal inconsistency problem in %s: fr_symbol %lx"), + __FUNCTION__, (long) fragP->fr_symbol); + symbolP = fragP->fr_symbol; + if (symbol_resolved_p (symbolP)) + as_fatal (_("internal inconsistency problem in %s: resolved symbol"), + __FUNCTION__); + aim = S_GET_VALUE (symbolP); + break; + + default: + as_fatal (_("internal inconsistency problem in %s: fr_subtype %d"), + __FUNCTION__, fragP->fr_subtype); + } + + /* The rest is stolen from relax_frag. There's no obvious way to + share the code, but fortunately no requirement to keep in sync as + long as fragP->fr_symbol does not have its segment changed. */ + + this_state = fragP->fr_subtype; + start_type = this_type = table + this_state; + + if (aim < 0) + { + /* Look backwards. */ + for (next_state = this_type->rlx_more; next_state;) + if (aim >= this_type->rlx_backward) + next_state = 0; + else + { + /* Grow to next state. */ + this_state = next_state; + this_type = table + this_state; + next_state = this_type->rlx_more; + } + } + else + { + /* Look forwards. */ + for (next_state = this_type->rlx_more; next_state;) + if (aim <= this_type->rlx_forward) + next_state = 0; + else + { + /* Grow to next state. */ + this_state = next_state; + this_type = table + this_state; + next_state = this_type->rlx_more; + } + } + + growth = this_type->rlx_length - start_type->rlx_length; + if (growth != 0) + fragP->fr_subtype = this_state; + return growth; +} + +void +md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, asection *sec ATTRIBUTE_UNUSED, + fragS *fragP) +{ + long value; + long disp; + char *buffer_address = fragP->fr_literal; + + /* Address in object code of the displacement. */ + register int object_address = fragP->fr_fix + fragP->fr_address; + + buffer_address += fragP->fr_fix; + + /* The displacement of the address, from current location. */ + value = S_GET_VALUE (fragP->fr_symbol); + disp = (value + fragP->fr_offset) - object_address; + + switch (fragP->fr_subtype) + { + case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE): + fragP->fr_opcode[1] = disp; + break; + + case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD): + /* This relax is only for bsr and bra. */ + gas_assert (IS_OPCODE (fragP->fr_opcode[0], M6811_BSR) + || IS_OPCODE (fragP->fr_opcode[0], M6811_BRA) + || IS_OPCODE (fragP->fr_opcode[0], M6812_BSR)); + + fragP->fr_opcode[0] = convert_branch (fragP->fr_opcode[0]); + + fix_new (fragP, fragP->fr_fix - 1, 2, + fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_16); + fragP->fr_fix += 1; + break; + + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE): + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, STATE_BYTE): + fragP->fr_opcode[1] = disp; + break; + + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_WORD): + /* Invert branch. */ + fragP->fr_opcode[0] ^= 1; + fragP->fr_opcode[1] = 3; /* Branch offset. */ + buffer_address[0] = M6811_JMP; + fix_new (fragP, fragP->fr_fix + 1, 2, + fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_16); + fragP->fr_fix += 3; + break; + + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, STATE_WORD): + /* Translate branch into a long branch. */ + fragP->fr_opcode[1] = fragP->fr_opcode[0]; + fragP->fr_opcode[0] = M6811_OPCODE_PAGE2; + + fix_new (fragP, fragP->fr_fix, 2, + fragP->fr_symbol, fragP->fr_offset, 1, + BFD_RELOC_16_PCREL); + fragP->fr_fix += 2; + break; + + case ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS5): + if (fragP->fr_symbol != 0 + && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section) + value = disp; + /* fall through */ + + case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS5): + fragP->fr_opcode[0] = fragP->fr_opcode[0] << 6; + fragP->fr_opcode[0] |= value & 0x1f; + break; + + case ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS9): + /* For a PC-relative offset, use the displacement with a -1 correction + to take into account the additional byte of the insn. */ + if (fragP->fr_symbol != 0 + && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section) + value = disp - 1; + /* fall through */ + + case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9): + fragP->fr_opcode[0] = (fragP->fr_opcode[0] << 3); + fragP->fr_opcode[0] |= 0xE0; + fragP->fr_opcode[0] |= (value >> 8) & 1; + fragP->fr_opcode[1] = value; + fragP->fr_fix += 1; + break; + + case ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS16): + case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16): + fragP->fr_opcode[0] = (fragP->fr_opcode[0] << 3); + fragP->fr_opcode[0] |= 0xe2; + if ((fragP->fr_opcode[0] & 0x0ff) == 0x0fa + && fragP->fr_symbol != 0 + && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section) + { + fix_new (fragP, fragP->fr_fix, 2, + fragP->fr_symbol, fragP->fr_offset, + 1, BFD_RELOC_16_PCREL); + } + else + { + fix_new (fragP, fragP->fr_fix, 2, + fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_16); + } + fragP->fr_fix += 2; + break; + + case ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_BYTE): + if (disp < 0) + fragP->fr_opcode[0] |= 0x10; + + fragP->fr_opcode[1] = disp & 0x0FF; + break; + + case ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_WORD): + /* Invert branch. */ + fragP->fr_opcode[0] ^= 0x20; + fragP->fr_opcode[1] = 3; /* Branch offset. */ + buffer_address[0] = M6812_JMP; + fix_new (fragP, fragP->fr_fix + 1, 2, + fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_16); + fragP->fr_fix += 3; + break; + + default: + break; + } +} + +/* On an ELF system, we can't relax a weak symbol. The weak symbol + can be overridden at final link time by a non weak symbol. We can + relax externally visible symbol because there is no shared library + and such symbol can't be overridden (unless they are weak). */ +static int +relaxable_symbol (symbolS *symbol) +{ + return ! S_IS_WEAK (symbol); +} + +/* Force truly undefined symbols to their maximum size, and generally set up + the frag list to be relaxed. */ +int +md_estimate_size_before_relax (fragS *fragP, asection *segment) +{ + if (RELAX_LENGTH (fragP->fr_subtype) == STATE_UNDF) + { + if (S_GET_SEGMENT (fragP->fr_symbol) != segment + || !relaxable_symbol (fragP->fr_symbol) + || (segment != absolute_section + && RELAX_STATE (fragP->fr_subtype) == STATE_INDEXED_OFFSET)) + { + /* Non-relaxable cases. */ + int old_fr_fix; + char *buffer_address; + + old_fr_fix = fragP->fr_fix; + buffer_address = fragP->fr_fix + fragP->fr_literal; + + switch (RELAX_STATE (fragP->fr_subtype)) + { + case STATE_PC_RELATIVE: + + /* This relax is only for bsr and bra. */ + gas_assert (IS_OPCODE (fragP->fr_opcode[0], M6811_BSR) + || IS_OPCODE (fragP->fr_opcode[0], M6811_BRA) + || IS_OPCODE (fragP->fr_opcode[0], M6812_BSR)); + + if (flag_fixed_branches) + as_bad_where (fragP->fr_file, fragP->fr_line, + _("bra or bsr with undefined symbol.")); + + /* The symbol is undefined or in a separate section. + Turn bra into a jmp and bsr into a jsr. The insn + becomes 3 bytes long (instead of 2). A fixup is + necessary for the unresolved symbol address. */ + fragP->fr_opcode[0] = convert_branch (fragP->fr_opcode[0]); + + fix_new (fragP, fragP->fr_fix - 1, 2, fragP->fr_symbol, + fragP->fr_offset, 0, BFD_RELOC_16); + fragP->fr_fix++; + break; + + case STATE_CONDITIONAL_BRANCH: + gas_assert (current_architecture & cpu6811); + + fragP->fr_opcode[0] ^= 1; /* Reverse sense of branch. */ + fragP->fr_opcode[1] = 3; /* Skip next jmp insn (3 bytes). */ + + /* Don't use fr_opcode[2] because this may be + in a different frag. */ + buffer_address[0] = M6811_JMP; + + fragP->fr_fix++; + fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, + fragP->fr_offset, 0, BFD_RELOC_16); + fragP->fr_fix += 2; + break; + + case STATE_INDEXED_OFFSET: + gas_assert (current_architecture & cpu6812); + + if (fragP->fr_symbol + && S_GET_SEGMENT (fragP->fr_symbol) == absolute_section) + { + fragP->fr_subtype = ENCODE_RELAX (STATE_INDEXED_OFFSET, + STATE_BITS5); + /* Return the size of the variable part of the frag. */ + return md_relax_table[fragP->fr_subtype].rlx_length; + } + else + { + /* Switch the indexed operation to 16-bit mode. */ + fragP->fr_opcode[0] = fragP->fr_opcode[0] << 3; + fragP->fr_opcode[0] |= 0xe2; + fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, + fragP->fr_offset, 0, BFD_RELOC_16); + fragP->fr_fix += 2; + } + break; + + case STATE_INDEXED_PCREL: + gas_assert (current_architecture & cpu6812); + + if (fragP->fr_symbol + && S_GET_SEGMENT (fragP->fr_symbol) == absolute_section) + { + fragP->fr_subtype = ENCODE_RELAX (STATE_INDEXED_PCREL, + STATE_BITS5); + /* Return the size of the variable part of the frag. */ + return md_relax_table[fragP->fr_subtype].rlx_length; + } + else + { + fragP->fr_opcode[0] = fragP->fr_opcode[0] << 3; + fragP->fr_opcode[0] |= 0xe2; + fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, + fragP->fr_offset, 1, BFD_RELOC_16_PCREL); + fragP->fr_fix += 2; + } + break; + + case STATE_XBCC_BRANCH: + gas_assert (current_architecture & cpu6812); + + fragP->fr_opcode[0] ^= 0x20; /* Reverse sense of branch. */ + fragP->fr_opcode[1] = 3; /* Skip next jmp insn (3 bytes). */ + + /* Don't use fr_opcode[2] because this may be + in a different frag. */ + buffer_address[0] = M6812_JMP; + + fragP->fr_fix++; + fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, + fragP->fr_offset, 0, BFD_RELOC_16); + fragP->fr_fix += 2; + break; + + case STATE_CONDITIONAL_BRANCH_6812: + gas_assert (current_architecture & cpu6812); + + /* Translate into a lbcc branch. */ + fragP->fr_opcode[1] = fragP->fr_opcode[0]; + fragP->fr_opcode[0] = M6811_OPCODE_PAGE2; + + fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, + fragP->fr_offset, 1, BFD_RELOC_16_PCREL); + fragP->fr_fix += 2; + break; + + default: + as_fatal (_("Subtype %d is not recognized."), fragP->fr_subtype); + } + frag_wane (fragP); + + /* Return the growth in the fixed part of the frag. */ + return fragP->fr_fix - old_fr_fix; + } + + /* Relaxable cases. */ + switch (RELAX_STATE (fragP->fr_subtype)) + { + case STATE_PC_RELATIVE: + /* This relax is only for bsr and bra. */ + gas_assert (IS_OPCODE (fragP->fr_opcode[0], M6811_BSR) + || IS_OPCODE (fragP->fr_opcode[0], M6811_BRA) + || IS_OPCODE (fragP->fr_opcode[0], M6812_BSR)); + + fragP->fr_subtype = ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE); + break; + + case STATE_CONDITIONAL_BRANCH: + gas_assert (current_architecture & cpu6811); + + fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, + STATE_BYTE); + break; + + case STATE_INDEXED_OFFSET: + gas_assert (current_architecture & cpu6812); + + fragP->fr_subtype = ENCODE_RELAX (STATE_INDEXED_OFFSET, + STATE_BITS5); + break; + + case STATE_INDEXED_PCREL: + gas_assert (current_architecture & cpu6812); + + fragP->fr_subtype = ENCODE_RELAX (STATE_INDEXED_PCREL, + STATE_BITS5); + break; + + case STATE_XBCC_BRANCH: + gas_assert (current_architecture & cpu6812); + + fragP->fr_subtype = ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_BYTE); + break; + + case STATE_CONDITIONAL_BRANCH_6812: + gas_assert (current_architecture & cpu6812); + + fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, + STATE_BYTE); + break; + } + } + + if (fragP->fr_subtype >= sizeof (md_relax_table) / sizeof (md_relax_table[0])) + as_fatal (_("Subtype %d is not recognized."), fragP->fr_subtype); + + /* Return the size of the variable part of the frag. */ + return md_relax_table[fragP->fr_subtype].rlx_length; +} + +/* See whether we need to force a relocation into the output file. */ +int +tc_m68hc11_force_relocation (fixS *fixP) +{ + if (fixP->fx_r_type == BFD_RELOC_M68HC11_RL_GROUP) + return 1; + + return generic_force_reloc (fixP); +} + +/* Here we decide which fixups can be adjusted to make them relative + to the beginning of the section instead of the symbol. Basically + we need to make sure that the linker relaxation is done + correctly, so in some cases we force the original symbol to be + used. */ +int +tc_m68hc11_fix_adjustable (fixS *fixP) +{ + switch (fixP->fx_r_type) + { + /* For the linker relaxation to work correctly, these relocs + need to be on the symbol itself. */ + case BFD_RELOC_16: + case BFD_RELOC_M68HC11_RL_JUMP: + case BFD_RELOC_M68HC11_RL_GROUP: + case BFD_RELOC_VTABLE_INHERIT: + case BFD_RELOC_VTABLE_ENTRY: + case BFD_RELOC_32: + + /* The memory bank addressing translation also needs the original + symbol. */ + case BFD_RELOC_M68HC11_LO16: + case BFD_RELOC_M68HC11_PAGE: + case BFD_RELOC_M68HC11_24: + return 0; + + default: + return 1; + } +} + +void +md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) +{ + char *where; + long value = * valP; + + if (fixP->fx_addsy == (symbolS *) NULL) + fixP->fx_done = 1; + + /* We don't actually support subtracting a symbol. */ + if (fixP->fx_subsy != (symbolS *) NULL) + as_bad_where (fixP->fx_file, fixP->fx_line, _("Expression too complex.")); + + /* Patch the instruction with the resolved operand. Elf relocation + info will also be generated to take care of linker/loader fixups. + The 68HC11 addresses only 64Kb, we are only concerned by 8 and 16-bit + relocs. BFD_RELOC_8 is basically used for .page0 access (the linker + will warn for overflows). BFD_RELOC_8_PCREL should not be generated + because it's either resolved or turned out into non-relative insns (see + relax table, bcc, bra, bsr transformations) + + The BFD_RELOC_32 is necessary for the support of --gstabs. */ + where = fixP->fx_frag->fr_literal + fixP->fx_where; + + switch (fixP->fx_r_type) + { + case BFD_RELOC_32: + bfd_putb32 ((bfd_vma) value, (unsigned char *) where); + break; + + case BFD_RELOC_24: + case BFD_RELOC_M68HC11_24: + bfd_putb16 ((bfd_vma) (value & 0x0ffff), (unsigned char *) where); + ((bfd_byte*) where)[2] = ((value >> 16) & 0x0ff); + break; + + case BFD_RELOC_16: + case BFD_RELOC_16_PCREL: + case BFD_RELOC_M68HC11_LO16: + bfd_putb16 ((bfd_vma) value, (unsigned char *) where); + if (value < -65537 || value > 65535) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Value out of 16-bit range.")); + break; + + case BFD_RELOC_M68HC11_HI8: + /* Caution, %hi(<symbol>+%ld) will generate incorrect code if %lo + causes a carry. */ + case BFD_RELOC_M68HC12_HI8XG: + value = value >> 8; + /* Fall through. */ + + case BFD_RELOC_M68HC12_LO8XG: + case BFD_RELOC_M68HC11_LO8: + case BFD_RELOC_8: + case BFD_RELOC_M68HC11_PAGE: + ((bfd_byte *) where)[0] = (bfd_byte) value; + break; + + case BFD_RELOC_8_PCREL: + ((bfd_byte *) where)[0] = (bfd_byte) value; + + if (value < -128 || value > 127) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Value %ld too large for 8-bit PC-relative branch."), + value); + break; + + /* These next two are for XGATE. */ + case BFD_RELOC_M68HC12_9_PCREL: + ((bfd_byte *) where)[0] |= (bfd_byte) ((value >>9) & 0x01); + ((bfd_byte *) where)[1] = (bfd_byte) ((value>>1) & 0xff); + if (value < -512 || value > 511) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Value %ld too large for 9-bit PC-relative branch."), + value); + break; + + case BFD_RELOC_M68HC12_10_PCREL: + ((bfd_byte *) where)[0] |= (bfd_byte) ((value >>9) & 0x03); + ((bfd_byte *) where)[1] = (bfd_byte) ((value>>1) & 0xff); + if (value < -1024 || value > 1023) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Value %ld too large for 10-bit PC-relative branch."), + value); + + break; + + case BFD_RELOC_M68HC11_3B: + if (value <= 0 || value > 8) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Auto increment/decrement offset '%ld' is out of range."), + value); + if (where[0] & 0x8) + value = 8 - value; + else + value--; + + where[0] = where[0] | (value & 0x07); + break; + + case BFD_RELOC_M68HC12_5B: + if (value < -16 || value > 15) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Offset out of 5-bit range for movw/movb insn: %ld"), + value); + if (value >= 0) + where[0] |= value; + else + where[0] |= (0x10 | (16 + value)); + break; + + case BFD_RELOC_M68HC12_9B: + if (value < -256 || value > 255) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Offset out of 9-bit range for movw/movb insn: %ld"), + value); + /* sign bit already in xb postbyte */ + if (value >= 0) + where[1] = value; + else + where[1] = (256 + value); + break; + + case BFD_RELOC_M68HC12_16B: + if (value < -32768 || value > 32767) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Offset out of 16-bit range for movw/movb insn: %ld"), + value); + if (value < 0) + value += 65536; + + where[0] = (value >> 8); + where[1] = (value & 0xff); + break; + + case BFD_RELOC_M68HC11_RL_JUMP: + case BFD_RELOC_M68HC11_RL_GROUP: + case BFD_RELOC_VTABLE_INHERIT: + case BFD_RELOC_VTABLE_ENTRY: + fixP->fx_done = 0; + return; + + default: + as_fatal (_("Line %d: unknown relocation type: 0x%x."), + fixP->fx_line, fixP->fx_r_type); + } +} + +/* Set the ELF specific flags. */ +void +m68hc11_elf_final_processing (void) +{ + if (current_architecture & cpu6812s) + elf_flags |= EF_M68HCS12_MACH; + elf_elfheader (stdoutput)->e_flags &= ~EF_M68HC11_ABI; + elf_elfheader (stdoutput)->e_flags |= elf_flags; +} + +/* Process directives specified via pseudo ops */ +static void +s_m68hc11_parse_pseudo_instruction (int pseudo_insn) +{ + switch (pseudo_insn) + { + case E_M68HC11_NO_BANK_WARNING: + elf_flags |= E_M68HC11_NO_BANK_WARNING; + break; + default: + as_bad (_("Invalid directive")); + } +} |