diff options
Diffstat (limited to 'gas/config/tc-m68hc11.c')
-rw-r--r-- | gas/config/tc-m68hc11.c | 2831 |
1 files changed, 2831 insertions, 0 deletions
diff --git a/gas/config/tc-m68hc11.c b/gas/config/tc-m68hc11.c new file mode 100644 index 0000000..e773420 --- /dev/null +++ b/gas/config/tc-m68hc11.c @@ -0,0 +1,2831 @@ +/* tc-m68hc11.c -- Assembler code for the Motorola 68HC11 & 68HC12. + Copyright (C) 1999, 2000 Free Software Foundation. + 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 <stdio.h> +#include <ctype.h> +#include "as.h" +#include "subsegs.h" +#include "opcode/m68hc11.h" +#include "dwarf2dbg.h" + +struct dwarf2_line_info debug_line; + +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 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, 1, 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 +} 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 *name)); +static register_id register_name PARAMS (()); +static int check_range PARAMS ((long num, int mode)); + +static void print_opcode_list PARAMS ((void)); + +static void get_default_target PARAMS ((void)); +static void print_insn_format PARAMS ((char *name)); +static int get_operand PARAMS ((operand * op, int first, long opmode)); +static void fixup8 PARAMS ((expressionS * oper, int mode, int opmode)); +static void fixup16 PARAMS ((expressionS * oper, int mode, int opmode)); +static struct m68hc11_opcode *find_opcode +PARAMS ( + (struct m68hc11_opcode_def * opc, operand operands[], + int *nb_operands)); +static void build_jump_insn +PARAMS ( + (struct m68hc11_opcode * opcode, operand operands[], int nb_operands, + int optimize)); + +static void build_insn PARAMS ((struct m68hc11_opcode * opcode, + operand operands[], int nb_operands)); + +/* 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<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; + +/* 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}, + {"file", dwarf2_directive_file, 0}, + {"loc", dwarf2_directive_loc, 0}, + + {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; +} + + +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 bits offset rather than 16 bits 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, _("#<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 & 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 () +{ + 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 (name) + 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, 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 (name) + 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; + + return REG_NONE; +} + +static char * +skip_whites (p) + char *p; +{ + while (*p == ' ' || *p == '\t') + p++; + + return p; +} + +/* register_name() checks the string at input_line_pointer + to see if it is a valid register name. */ +static register_id +register_name () +{ + 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; +} + +/* get_operands parses a string of operands and returns + 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_IDX_2 M6812_OP_REG O_register O_register + [n,r] M6811_OP_IDX_1 M6812_OP_REG O_constant O_register + +*/ + +static int +get_operand (oper, which, opmode) + 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; + } + } + 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_IDX_2; + 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; + } + input_line_pointer = p; + + if (mode == M6811_OP_NONE || mode == M6812_OP_IDX_2) + reg = register_name (); + else + reg = REG_NONE; + + if (reg != REG_NONE) + { + p = skip_whites (input_line_pointer); + if (*p == ']' && mode == M6812_OP_IDX_2) + { + 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_IDX_2) + { + 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_IDX_2) + { + if (*p != ']') + { + as_bad (_("Missing `]' to close indexed-indirect mode.")); + return -1; + } + p++; + } + 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_IDX_2) + { + p = skip_whites (input_line_pointer); + + if (*p == ',') + { + p++; + + /* 68HC12 pre increment or decrement. */ + if (mode == M6811_OP_NONE) + { + if (*p == '-') + { + mode = M6812_PRE_DEC; + p++; + if (current_architecture & cpu6811) + as_bad (_("Pre-decrement mode is not valid for 68HC11")); + } + else if (*p == '+') + { + mode = M6812_PRE_INC; + p++; + if (current_architecture & cpu6811) + as_bad (_("Pre-increment mode is not valid for 68HC11")); + } + p = skip_whites (p); + } + input_line_pointer = p; + reg = register_name (); + + /* 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_IDX_2) + { + p = skip_whites (input_line_pointer); + if (*p++ != ']') + { + as_bad (_("Missing `]' to close register indirect operand.")); + return -1; + } + input_line_pointer = p; + } + 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; + } + } + + 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 (num, mode) + long num; + int mode; +{ + /* 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-bits 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; + + 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: + 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_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 (oper, mode, opmode) + 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) + { + fixS *fixp; + + fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 1, + oper, true, BFD_RELOC_8_PCREL); + fixp->fx_pcrel_adjust = 1; + } + else + { + /* Now create an 8-bit fixup. If there was some %hi or %lo + modifier, generate the reloc accordingly. */ + fix_new_exp (frag_now, f - frag_now->fr_literal, 1, + oper, false, + ((opmode & M6811_OP_HIGH_ADDR) + ? BFD_RELOC_M68HC11_HI8 + : ((opmode & M6811_OP_LOW_ADDR) + ? BFD_RELOC_M68HC11_LO8 : BFD_RELOC_8))); + } + number_to_chars_bigendian (f, 0, 1); + } + else + { + as_fatal (_("Operand `%x' not recognized in fixup8."), oper->X_op); + } +} + +/* Put a 2 bytes expression described by 'oper'. If this expression contains + unresolved symbols, generate a 16-bit fixup. */ +static void +fixup16 (oper, mode, opmode) + 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; + + /* Now create a 16-bit fixup. */ + fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 2, + oper, + (mode & M6812_OP_JUMP_REL16 ? true : false), + (mode & M6812_OP_JUMP_REL16 + ? BFD_RELOC_16_PCREL : BFD_RELOC_16)); + number_to_chars_bigendian (f, 0, 2); + if (mode & M6812_OP_JUMP_REL16) + fixp->fx_pcrel_adjust = 2; + } + else + { + as_fatal (_("Operand `%x' not recognized in fixup16."), oper->X_op); + } +} + + +/* 68HC11 and 68HC12 code generation. */ + +/* Translate the short branch/bsr instruction into a long branch. */ +static unsigned char +convert_branch (code) + 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 (size) + int size; +{ + char* f; + + f = frag_more (size); + + /* Emit line number information in dwarf2 debug sections. */ + if (debug_type == DEBUG_DWARF2) + { + bfd_vma addr; + + dwarf2_where (&debug_line); + addr = frag_now->fr_address + frag_now_fix () - size; + dwarf2_gen_line_info (addr, &debug_line); + } + return f; +} + +/* Builds a jump instruction (bra, bcc, bsr). */ +static void +build_jump_insn (opcode, operands, nb_operands, jmp_mode) + struct m68hc11_opcode *opcode; + operand operands[]; + int nb_operands; + int jmp_mode; +{ + unsigned char code; + int insn_size; + char *f; + unsigned long n; + + /* The relative branch convertion is not supported for + brclr and brset. */ + assert ((opcode->format & M6811_OP_BITMASK) == 0); + assert (nb_operands == 1); + assert (operands[0].reg1 == REG_NONE && operands[0].reg2 == REG_NONE); + + code = opcode->opcode; + insn_size = 1; + + 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_branchs == 0)))) + { + 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) + { + 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 *opcode; + + /* Branch offset must fit in 8-bits, don't do some relax. */ + if (jmp_mode == 0 && flag_fixed_branchs) + { + opcode = m68hc11_new_insn (1); + number_to_chars_bigendian (opcode, 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) + { + opcode = m68hc11_new_insn (2); + number_to_chars_bigendian (opcode, code, 1); + number_to_chars_bigendian (opcode + 1, 0, 1); + frag_var (rs_machine_dependent, 1, 1, + ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF), + operands[0].exp.X_add_symbol, (offsetT) n, opcode); + } + else if (current_architecture & cpu6812) + { + opcode = m68hc11_new_insn (2); + number_to_chars_bigendian (opcode, code, 1); + number_to_chars_bigendian (opcode + 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, opcode); + } + else + { + opcode = m68hc11_new_insn (2); + number_to_chars_bigendian (opcode, code, 1); + number_to_chars_bigendian (opcode + 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, opcode); + } + } +} + +/* Builds a dbne/dbeq/tbne/tbeq instruction. */ +static void +build_dbranch_insn (opcode, operands, nb_operands, jmp_mode) + struct m68hc11_opcode *opcode; + operand operands[]; + int nb_operands; + int jmp_mode; +{ + unsigned char code; + int insn_size; + char *f; + unsigned long n; + + /* The relative branch convertion is not supported for + brclr and brset. */ + assert ((opcode->format & M6811_OP_BITMASK) == 0); + assert (nb_operands == 2); + assert (operands[0].reg1 != REG_NONE); + + code = opcode->opcode & 0x0FF; + insn_size = 1; + + 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_branchs == 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_branchs) + { + 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 (op, format, move_insn) + 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) + { + 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)) + { + as_bad (_("Offset out of 5-bit range for movw/movb insn.")); + return -1; + } + + if (val >= -16 && val <= 15 && !(mode & M6812_OP_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_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_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; + } + } + f = frag_more (1); + number_to_chars_bigendian (f, byte, 1); + /* + fix_new_exp (frag_now, f - frag_now->fr_literal, 2, + &op->exp, false, BFD_RELOC_16); */ + frag_var (rs_machine_dependent, 2, 2, + ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_UNDF), + op->exp.X_add_symbol, val, f); + return 3; + } + + if (mode & M6812_OP_REG) + { + if (mode & M6812_OP_IDX_2) + { + if (op->reg1 != REG_D) + as_bad (_("Expecting register D for indexed indirect mode.")); + if (move_insn) + 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; + } + + as_fatal (_("Addressing mode not implemented yet.")); + return 0; +} + +/* Assemble the 68HC12 register mode byte. */ +static int +build_reg_mode (op, format) + 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) + 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; + + f = frag_more (1); + number_to_chars_bigendian (f, byte, 1); + return 1; +} + +/* build_insn takes a pointer to the opcode entry in the opcode table, + the array of operand expressions and builds the correspding instruction. + This operation only deals with non relative jumps insn (need special + handling). */ +static void +build_insn (opcode, operands, nb_operands) + struct m68hc11_opcode *opcode; + operand operands[]; + int nb_operands ATTRIBUTE_UNUSED; +{ + int i; + char *f; + int insn_size = 1; + long format; + int move_insn = 0; + + /* Put the page code instruction if there is one. */ + format = opcode->format; + 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++; + insn_size = 2; + } + 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) + { + insn_size += build_indexed_byte (&operands[0], format, 1); + i = 1; + format &= ~M6812_OP_IDX; + } + if (format & M6812_OP_IDX_P2) + { + insn_size += build_indexed_byte (&operands[1], format, 1); + i = 0; + format &= ~M6812_OP_IDX_P2; + } + } + + if (format & (M6811_OP_DIRECT | M6811_OP_IMM8)) + { + insn_size++; + fixup8 (&operands[i].exp, + format & (M6811_OP_DIRECT | M6811_OP_IMM8 | M6812_OP_TRAP_ID), + operands[i].mode); + i++; + } + else if (format & (M6811_OP_IMM16 | M6811_OP_IND16)) + { + insn_size += 2; + fixup16 (&operands[i].exp, format & (M6811_OP_IMM16 | M6811_OP_IND16), + 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.")); + + insn_size++; + 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)) + { + insn_size += build_indexed_byte (&operands[i], format, move_insn); + i++; + } + else if (format & M6812_OP_REG && current_architecture & cpu6812) + { + insn_size += build_reg_mode (&operands[i], format); + i++; + } + if (format & M6811_OP_BITMASK) + { + insn_size++; + fixup8 (&operands[i].exp, M6811_OP_BITMASK, operands[i].mode); + i++; + } + if (format & M6811_OP_JUMP_REL) + { + insn_size++; + fixup8 (&operands[i].exp, M6811_OP_JUMP_REL, operands[i].mode); + i++; + } + else if (format & M6812_OP_IND16_P2) + { + insn_size += 2; + fixup16 (&operands[1].exp, M6811_OP_IND16, operands[1].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 (opc, operands, nb_operands) + 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. 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++; + + 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_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_D_IDX) + 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 & 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; + } + + if (!match) + { + return (0); + } + + return opcode; +} + + +/* 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 (opc, operands, nb_operands) + 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); + if (opcode) + { + 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 (str) + char *str; +{ + struct m68hc11_opcode_def *opc; + struct m68hc11_opcode *opcode; + + unsigned char *op_start, *save; + unsigned char *op_end; + char name[20]; + int nlen = 0; + operand operands[M6811_MAX_OPERANDS]; + int nb_operands; + 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 && nlen < 20 && !is_end_of_line[*op_end] && *op_end != ' '; + op_end++) + { + name[nlen] = tolower (op_start[nlen]); + nlen++; + } + name[nlen] = 0; + + if (nlen == 0) + { + as_bad (_("No instruction or missing opcode.")); + 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 branchs, we always + optimize them (turned into absolute branchs) even if --short-branchs + 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 is 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 fiew 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 = 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); + + 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); + + /* 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); +} + + +/* Relocation, relaxation and frag conversions. */ + +long +md_pcrel_from_section (fixp, sec) + fixS *fixp; + segT sec; +{ + int adjust; + if (fixp->fx_addsy != (symbolS *) NULL + && (!S_IS_DEFINED (fixp->fx_addsy) + || (S_GET_SEGMENT (fixp->fx_addsy) != sec))) + return 0; + + adjust = fixp->fx_pcrel_adjust; + return fixp->fx_frag->fr_address + fixp->fx_where + adjust; +} + +/* If while processing a fixup, a reloc really needs to be created + then it is done here. */ +arelent * +tc_gen_reloc (section, fixp) + asection *section; + 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; + } + + if (!fixp->fx_pcrel) + reloc->addend = fixp->fx_addnumber; + else + reloc->addend = (section->vma + + (fixp->fx_pcrel_adjust == 64 + ? -1 : fixp->fx_pcrel_adjust) + + fixp->fx_addnumber + + md_pcrel_from_section (fixp, section)); + return reloc; +} + +void +md_convert_frag (abfd, sec, fragP) + bfd *abfd ATTRIBUTE_UNUSED; + asection *sec ATTRIBUTE_UNUSED; + fragS *fragP; +{ + fixS *fixp; + 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. */ + disp = fragP->fr_symbol ? S_GET_VALUE (fragP->fr_symbol) : 0; + disp = (disp + fragP->fr_offset) - object_address; + disp += symbol_get_frag (fragP->fr_symbol)->fr_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. */ + 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; + + fixp = fix_new (fragP, fragP->fr_fix, 2, + fragP->fr_symbol, fragP->fr_offset, 1, + BFD_RELOC_16_PCREL); + fixp->fx_pcrel_adjust = 2; + fragP->fr_fix += 2; + break; + + case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS5): + fragP->fr_opcode[0] = fragP->fr_opcode[0] << 5; + fragP->fr_opcode[0] |= disp & 0x1f; + break; + + case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9): + fragP->fr_opcode[0] = (fragP->fr_opcode[0] << 3); + fragP->fr_opcode[0] |= 0xE0; + fix_new (fragP, fragP->fr_fix + 1, 1, + fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_8); + fragP->fr_fix += 1; + break; + + case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16): + 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 += 1; + 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; + } +} + +/* Force truly undefined symbols to their maximum size, and generally set up + the frag list to be relaxed. */ +int +md_estimate_size_before_relax (fragP, segment) + fragS *fragP; + asection *segment; +{ + int old_fr_fix; + char *buffer_address = fragP->fr_fix + fragP->fr_literal; + + old_fr_fix = fragP->fr_fix; + + switch (fragP->fr_subtype) + { + case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF): + + /* This relax is only for bsr and bra. */ + 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)); + + /* A relaxable case. */ + if (S_GET_SEGMENT (fragP->fr_symbol) == segment) + { + fragP->fr_subtype = ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE); + } + else + { + if (flag_fixed_branchs) + 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]); + + fragP->fr_fix++; + fix_new (fragP, old_fr_fix - 1, 2, fragP->fr_symbol, + fragP->fr_offset, 0, BFD_RELOC_16); + frag_wane (fragP); + } + break; + + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_UNDF): + assert (current_architecture & cpu6811); + + if (S_GET_SEGMENT (fragP->fr_symbol) == segment) + { + fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, + STATE_BYTE); + } + else + { + 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; + frag_wane (fragP); + } + break; + + case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_UNDF): + assert (current_architecture & cpu6812); + + if (S_GET_SEGMENT (fragP->fr_symbol) == segment) + { + fragP->fr_subtype = ENCODE_RELAX (STATE_INDEXED_OFFSET, + STATE_BITS5); + } + else + { + /* Switch the indexed operation to 16-bit mode. */ + if ((fragP->fr_opcode[1] & 0x21) == 0x20) + fragP->fr_opcode[1] = (fragP->fr_opcode[1] >> 3) | 0xc0 | 0x02; + + fragP->fr_fix++; + fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, + fragP->fr_offset, 0, BFD_RELOC_16); + fragP->fr_fix += 2; + frag_wane (fragP); + } + break; + + case ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_UNDF): + assert (current_architecture & cpu6812); + + if (S_GET_SEGMENT (fragP->fr_symbol) == segment) + { + fragP->fr_subtype = ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_BYTE); + } + else + { + 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; + frag_wane (fragP); + } + break; + + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, STATE_UNDF): + assert (current_architecture & cpu6812); + + if (S_GET_SEGMENT (fragP->fr_symbol) == segment) + { + fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, + STATE_BYTE); + } + else + { + /* 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, 0, BFD_RELOC_16_PCREL); + fragP->fr_fix += 2; + frag_wane (fragP); + } + break; + + default: + as_fatal (_("Subtype %d is not recognized."), fragP->fr_subtype); + } + + return (fragP->fr_fix - old_fr_fix); +} + +int +md_apply_fix (fixp, valuep) + fixS *fixp; + valueT *valuep; +{ + char *where; + long value; + int op_type; + + if (fixp->fx_addsy == (symbolS *) NULL) + { + value = *valuep; + fixp->fx_done = 1; + } + else if (fixp->fx_pcrel) + { + value = *valuep; + } + else + { + value = fixp->fx_offset; + if (fixp->fx_subsy != (symbolS *) NULL) + { + if (S_GET_SEGMENT (fixp->fx_subsy) == absolute_section) + { + value -= S_GET_VALUE (fixp->fx_subsy); + } + else + { + /* We don't actually support subtracting a symbol. */ + as_bad_where (fixp->fx_file, fixp->fx_line, + _("Expression too complex.")); + } + } + } + + op_type = fixp->fx_r_type; + + /* 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_16: + case BFD_RELOC_16_PCREL: + 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: + value = value >> 8; + /* Fall through */ + + case BFD_RELOC_M68HC11_LO8: + case BFD_RELOC_8: + /*bfd_putb8 ((bfd_vma) value, (unsigned char *) where); */ + ((bfd_byte *) where)[0] = (bfd_byte) value; + break; + + case BFD_RELOC_8_PCREL: + /*bfd_putb8 ((bfd_vma) value, (unsigned char *) where); */ + ((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; + + 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; + + default: + as_fatal (_("Line %d: unknown relocation type: 0x%x."), + fixp->fx_line, fixp->fx_r_type); + } + return 0; +} + +int +m68hc11_cleanup () +{ + return 1; +} + +void +m68hc11_end_of_source () +{ + segT saved_seg; + subsegT saved_subseg; + segT debug_info; + char* p; + long total_size = 0; + + if (debug_type != DEBUG_DWARF2) + return; + + dwarf2_finish (); + + saved_seg = now_seg; + saved_subseg = now_subseg; + + debug_info = subseg_new (".debug_info", 0); + bfd_set_section_flags (stdoutput, debug_info, SEC_READONLY); + subseg_set (debug_info, 0); + p = frag_more (10); + total_size = 12; + +# define STUFF(val,size) md_number_to_chars (p, val, size); p += size; + STUFF (total_size, 4); /* Length of compilation unit. */ + STUFF (2, 2); /* Dwarf version */ + STUFF (0, 4); + STUFF (2, 1); /* Pointer size */ + STUFF (1, 1); /* Compile unit */ + STUFF (0, 4); + + now_subseg = saved_subseg; + now_seg = saved_seg; +} |