/* tc-m68hc11.c -- Assembler code for the Motorola 68HC11 & 68HC12. Copyright 1999, 2000, 2001 Free Software Foundation, Inc. Written by Stephane Carrez (stcarrez@worldnet.fr) This file is part of GAS, the GNU Assembler. GAS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GAS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GAS; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "as.h" #include "safe-ctype.h" #include "subsegs.h" #include "opcode/m68hc11.h" #include "dwarf2dbg.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_XBCC_BRANCH (4) #define STATE_CONDITIONAL_BRANCH_6812 (5) #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 . 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 and bra . 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 dbeq/ibeq/tbeq r,: 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 on 68HC12. These insns are translated into lbcc . */ {(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 } 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} }; /* Local functions. */ static register_id reg_name_search PARAMS ((char *)); static register_id register_name PARAMS ((void)); static int cmp_opcode PARAMS ((struct m68hc11_opcode *, struct m68hc11_opcode *)); static char *print_opcode_format PARAMS ((struct m68hc11_opcode *, int)); static char *skip_whites PARAMS ((char *)); static int check_range PARAMS ((long, int)); static void print_opcode_list PARAMS ((void)); static void get_default_target PARAMS ((void)); static void print_insn_format PARAMS ((char *)); static int get_operand PARAMS ((operand *, int, long)); static void fixup8 PARAMS ((expressionS *, int, int)); static void fixup16 PARAMS ((expressionS *, int, int)); static unsigned char convert_branch PARAMS ((unsigned char)); static char *m68hc11_new_insn PARAMS ((int)); static void build_dbranch_insn PARAMS ((struct m68hc11_opcode *, operand *, int, int)); static int build_indexed_byte PARAMS ((operand *, int, int)); static int build_reg_mode PARAMS ((operand *, int)); static struct m68hc11_opcode *find PARAMS ((struct m68hc11_opcode_def *, operand *, int)); static struct m68hc11_opcode *find_opcode PARAMS ((struct m68hc11_opcode_def *, operand *, int *)); static void build_jump_insn PARAMS ((struct m68hc11_opcode *, operand *, int, int)); static void build_insn PARAMS ((struct m68hc11_opcode *, operand *, int)); static int relaxable_symbol PARAMS ((symbolS *)); /* 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_branchs = 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 overriden with the -m 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; /* 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}, {"fcc", stringer, 1}, {"rmb", s_space, 0}, /* Dwarf2 support for Gcc. */ {"file", dwarf2_directive_file, 0}, {"loc", dwarf2_directive_loc, 0}, /* Motorola ALIS. */ {"xrefb", s_ignore, 0}, /* Same as xref */ {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-branchs", no_argument, NULL, OPTION_FORCE_LONG_BRANCH}, #define OPTION_SHORT_BRANCHS (OPTION_MD_BASE + 1) {"short-branchs", no_argument, NULL, OPTION_SHORT_BRANCHS}, #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}, {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 () { get_default_target (); if (current_architecture & cpu6811) return "elf32-m68hc11"; else return "elf32-m68hc12"; } enum bfd_architecture m68hc11_arch () { get_default_target (); if (current_architecture & cpu6811) return bfd_arch_m68hc11; else return bfd_arch_m68hc12; } int m68hc11_mach () { return 0; } /* Listing header selected according to cpu. */ const char * m68hc11_listing_header () { if (current_architecture & cpu6811) return "M68HC11 GAS "; else return "M68HC12 GAS "; } void md_show_usage (stream) FILE *stream; { get_default_target (); fprintf (stream, _("\ Motorola 68HC11/68HC12 options:\n\ -m68hc11 | -m68hc12 specify the processor [default %s]\n\ --force-long-branchs always turn relative branchs into absolute ones\n\ -S,--short-branchs do not turn relative branchs 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\ --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 () { 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 *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 (c, arg) 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_BRANCHS: case 'S': flag_fixed_branchs = 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 'm': if (strcasecmp (arg, "68hc11") == 0) current_architecture = cpu6811; else if (strcasecmp (arg, "68hc12") == 0) current_architecture = cpu6812; else as_bad (_("Option `%s' is not recognized."), arg); break; default: return 0; } return 1; } symbolS * md_undefined_symbol (name) char *name ATTRIBUTE_UNUSED; { return 0; } /* Equal to MAX_PRECISION in atof-ieee.c. */ #define MAX_LITTLENUMS 6 /* Turn a string in input_line_pointer into a floating point constant of type TYPE, and store the appropriate bytes in *LITP. The number of LITTLENUMS emitted is stored in *SIZEP. An error message is returned, or NULL on OK. */ char * md_atof (type, litP, sizeP) char type; char *litP; int *sizeP; { int prec; LITTLENUM_TYPE words[MAX_LITTLENUMS]; LITTLENUM_TYPE *wordP; char *t; switch (type) { case 'f': case 'F': case 's': case 'S': prec = 2; break; case 'd': case 'D': case 'r': case 'R': prec = 4; break; case 'x': case 'X': prec = 6; break; case 'p': case 'P': prec = 6; break; default: *sizeP = 0; return _("Bad call to MD_ATOF()"); } t = atof_ieee (input_line_pointer, type, words); if (t) input_line_pointer = t; *sizeP = prec * sizeof (LITTLENUM_TYPE); for (wordP = words; prec--;) { md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE)); litP += sizeof (LITTLENUM_TYPE); } return 0; } valueT md_section_align (seg, addr) asection *seg; valueT addr; { int align = bfd_get_section_alignment (stdoutput, seg); return ((addr + (1 << align) - 1) & (-1 << align)); } static int cmp_opcode (op1, op2) struct m68hc11_opcode *op1; struct m68hc11_opcode *op2; { return strcmp (op1->name, op2->name); } /* Initialize the assembler. Create the opcode hash table (sorted on the names) with the M6811 opcode table (from opcode library). */ void md_begin () { 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), 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, (char *) opc); } opc->nb_modes++; opc->format |= opcodes->format; /* See how many operands this opcode needs. */ expect = 0; 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++; if (expect < opc->min_operands) opc->min_operands = 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 () { } /* 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 (opcode, example) struct m68hc11_opcode *opcode; int example; { static char buf[128]; int format = opcode->format; char *p; p = buf; buf[0] = 0; if (format & M6811_OP_IMM8) { if (example) sprintf (p, "#%d", rand () & 0x0FF); else strcpy (p, _("#")); p = &p[strlen (p)]; } if (format & M6811_OP_IMM16) { if (example) sprintf (p, "#%d", rand () & 0x0FFFF); else strcpy (p, _("#")); p = &p[strlen (p)]; } if (format & M6811_OP_IX) { if (example) sprintf (p, "%d,X", rand () & 0x0FF); else strcpy (p, _(",X")); p = &p[strlen (p)]; } if (format & M6811_OP_IY) { if (example) sprintf (p, "%d,X", rand () & 0x0FF); else strcpy (p, _(",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 & M6811_OP_DIRECT) { if (example) sprintf (p, "*Z%d", rand () & 0x0FF); else strcpy (p, _("*")); p = &p[strlen (p)]; } if (format & M6811_OP_BITMASK) { if (buf[0]) *p++ = ' '; if (example) sprintf (p, "#$%02x", rand () & 0x0FF); else strcpy (p, _("#")); 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, _("")); 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, _("