/* 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)
+ Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ Free Software Foundation, Inc.
+ Written by Stephane Carrez (stcarrez@nerim.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)
+ 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,
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. */
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
-#include <stdio.h>
-#include <ctype.h>
#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[] = "#*";
#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_INDEXED_PCREL (4)
+#define STATE_XBCC_BRANCH (5)
+#define STATE_CONDITIONAL_BRANCH_6812 (6)
#define STATE_BYTE (0)
#define STATE_BITS5 (0)
{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)},
};
/* Local functions. */
-static register_id reg_name_search PARAMS ((char *));
-static register_id register_name PARAMS ((void));
-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 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 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 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 (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);
+
+/* 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:
jmp L
Setting the flag forbidds this. */
-static short flag_fixed_branchs = 0;
+static short flag_fixed_branches = 0;
/* Force to use long jumps (absolute) instead of relative branches. */
static short flag_force_long_jumps = 0;
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
+ by 'get_default_target' by looking at default BFD vector. This is overridden
with the -m<cpu> option. */
static int current_architecture = 0;
/* 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. */
/* The following pseudo-ops are supported for MRI compatibility. */
{"fcb", cons, 1},
{"fdb", cons, 2},
- {"fcc", stringer, 1},
+ {"fcc", stringer, 8 + 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 */
+ /* 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},
+
{0, 0, 0}
};
\f
/* Options and initialization. */
-CONST char *md_shortopts = "Sm:";
+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},
+ {"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_BRANCHS (OPTION_MD_BASE + 1)
- {"short-branchs", no_argument, NULL, OPTION_SHORT_BRANCHS},
+#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_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},
+
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);
options and on the -m68hc11/-m68hc12 option. If no option is specified,
we must get the default. */
const char *
-m68hc11_arch_format ()
+m68hc11_arch_format (void)
{
get_default_target ();
if (current_architecture & cpu6811)
}
enum bfd_architecture
-m68hc11_arch ()
+m68hc11_arch (void)
{
get_default_target ();
if (current_architecture & cpu6811)
}
int
-m68hc11_mach ()
+m68hc11_mach (void)
{
return 0;
}
/* Listing header selected according to cpu. */
const char *
-m68hc11_listing_header ()
+m68hc11_listing_header (void)
{
if (current_architecture & cpu6811)
return "M68HC11 GAS ";
}
void
-md_show_usage (stream)
- FILE *stream;
+md_show_usage (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\
+Motorola 68HC11/68HC12/68HCS12 options:\n\
+ -m68hc11 | -m68hc12 |\n\
+ -m68hcs12 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\
/* Try to identify the default target based on the BFD library. */
static void
-get_default_target ()
+get_default_target (void)
{
const bfd_target *target;
bfd abfd;
}
void
-m68hc11_print_statistics (file)
- FILE *file;
+m68hc11_print_statistics (FILE *file)
{
int i;
struct m68hc11_opcode_def *opc;
}
int
-md_parse_option (c, arg)
- int c;
- char *arg;
+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_BRANCHS:
+ case OPTION_SHORT_BRANCHES:
case 'S':
- flag_fixed_branchs = 1;
+ flag_fixed_branches = 1;
break;
case OPTION_FORCE_LONG_BRANCH:
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 'm':
if (strcasecmp (arg, "68hc11") == 0)
current_architecture = cpu6811;
else if (strcasecmp (arg, "68hc12") == 0)
current_architecture = cpu6812;
+ else if (strcasecmp (arg, "68hcs12") == 0)
+ current_architecture = cpu6812 | cpu6812s;
else
as_bad (_("Option `%s' is not recognized."), arg);
break;
}
\f
symbolS *
-md_undefined_symbol (name)
- char *name ATTRIBUTE_UNUSED;
+md_undefined_symbol (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;
+md_atof (int 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;
+ return ieee_md_atof (type, litP, sizeP, TRUE);
}
valueT
-md_section_align (seg, addr)
- asection *seg;
- valueT addr;
+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 (op1, op2)
- struct m68hc11_opcode *op1;
- struct m68hc11_opcode *op2;
+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 ()
+md_begin (void)
{
char *prev_name = "";
struct m68hc11_opcode *opcodes;
}
}
}
- qsort (opcodes, num_opcodes, sizeof (struct m68hc11_opcode), cmp_opcode);
+ 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));
opc->nb_modes = 0;
opc->opcode = opcodes;
opc->used = 0;
- hash_insert (m68hc11_hash, opcodes->name, (char *) opc);
+ hash_insert (m68hc11_hash, opcodes->name, opc);
}
opc->nb_modes++;
opc->format |= opcodes->format;
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;
}
}
void
-m68hc11_init_after_args ()
+m68hc11_init_after_args (void)
{
}
\f
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;
+print_opcode_format (struct m68hc11_opcode *opcode, int example)
{
static char buf[128];
int format = opcode->format;
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)
/* Prints the list of instructions with the possible operands. */
static void
-print_opcode_list ()
+print_opcode_list (void)
{
int i;
char *prev_name = "";
instruction is not correct. Instruction format is printed as an
error message. */
static void
-print_insn_format (name)
- char *name;
+print_insn_format (char *name)
{
struct m68hc11_opcode_def *opc;
struct m68hc11_opcode *opcode;
{
char *fmt;
- fmt = print_opcode_format (opcode, 0, 0);
+ fmt = print_opcode_format (opcode, 0);
sprintf (buf, "\t%-5.5s %s", opcode->name, fmt);
as_bad ("%s", buf);
/* 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;
+reg_name_search (char *name)
{
if (strcasecmp (name, "x") == 0 || strcasecmp (name, "ix") == 0)
return REG_X;
}
static char *
-skip_whites (p)
- char *p;
+skip_whites (char *p)
{
while (*p == ' ' || *p == '\t')
p++;
/* Check the string at input_line_pointer
to see if it is a valid register name. */
static register_id
-register_name ()
+register_name (void)
{
register_id reg_number;
char c, *p = input_line_pointer;
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_IDX_2 M6812_OP_REG O_register O_register
- [n,r] M6811_OP_IDX_1 M6812_OP_REG O_constant O_register */
+ 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 (oper, which, opmode)
- operand *oper;
- int which;
- long opmode;
+get_operand (operand *oper, int which, long opmode)
{
char *p = input_line_pointer;
int mode;
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] == '-'))
{
as_bad (_("Indirect indexed addressing is not valid for 68HC11."));
p++;
- mode = M6812_OP_IDX_2;
+ mode = M6812_OP_D_IDX;
p = skip_whites (p);
}
else if (*p == ',') /* Special handling of ,x and ,y. */
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_IDX_2)
+ 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_IDX_2)
+ if (*p == ']' && mode == M6812_OP_D_IDX)
{
as_bad
(_("Missing second register or offset for indexed-indirect mode."));
oper->mode = mode | M6812_OP_REG;
if (*p != ',')
{
- if (mode == M6812_OP_IDX_2)
+ if (mode == M6812_OP_D_IDX)
{
as_bad (_("Missing second register for indexed-indirect mode."));
return -1;
if (reg != REG_NONE)
{
p = skip_whites (input_line_pointer);
- if (mode == M6812_OP_IDX_2)
+ if (mode == M6812_OP_D_IDX)
{
if (*p != ']')
{
return -1;
}
p++;
+ oper->mode = M6812_OP_D_IDX;
}
input_line_pointer = p;
p = input_line_pointer;
if (mode == M6811_OP_NONE || mode == M6811_OP_DIRECT
- || mode == M6812_OP_IDX_2)
+ || mode == M6812_OP_D_IDX)
{
p = skip_whites (input_line_pointer);
{
int possible_mode = M6811_OP_NONE;
char *old_input_line;
+
+ old_input_line = p;
p++;
/* 68HC12 pre increment or decrement. */
}
p = skip_whites (p);
}
- old_input_line = input_line_pointer;
input_line_pointer = p;
reg = register_name ();
as_bad (_("Wrong register in register indirect mode."));
return -1;
}
- if (mode == M6812_OP_IDX_2)
+ if (mode == M6812_OP_D_IDX)
{
p = skip_whites (input_line_pointer);
if (*p++ != ']')
return -1;
}
input_line_pointer = p;
+ oper->reg1 = reg;
+ oper->mode = M6812_OP_D_IDX_2;
+ return 1;
}
if (reg != REG_NONE)
{
oper->mode = mode;
return 1;
}
+ input_line_pointer = old_input_line;
}
if (mode == M6812_OP_D_IDX_2)
/* Checks that the number 'num' fits for a given mode. */
static int
-check_range (num, mode)
- long num;
- int mode;
+check_range (long num, int mode)
{
/* Auto increment and decrement are ok for [-8..8] without 0. */
if (mode & M6812_AUTO_INC_DEC)
if (mode & M6812_OP_JUMP_REL16)
mode = M6811_OP_IND16;
+ mode &= ~M6811_OP_BRANCH;
switch (mode)
{
case M6811_OP_IX:
case M6811_OP_BITMASK:
case M6811_OP_IMM8:
+ case M6812_OP_PAGE:
return (((num & 0xFFFFFF00) == 0) || ((num & 0xFFFFFF00) == 0xFFFFFF00))
? 1 : 0;
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;
/* 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;
+fixup8 (expressionS *oper, int mode, int opmode)
{
char *f;
fixS *fixp;
fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 1,
- oper, true, BFD_RELOC_8_PCREL);
+ 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)));
+ 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);
}
/* Put a 2 byte 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;
+fixup16 (expressionS *oper, int mode, int opmode ATTRIBUTE_UNUSED)
{
char *f;
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,
- (mode & M6812_OP_JUMP_REL16 ? true : false),
- (mode & M6812_OP_JUMP_REL16
- ? BFD_RELOC_16_PCREL : BFD_RELOC_16));
+ reloc == BFD_RELOC_16_PCREL,
+ reloc);
number_to_chars_bigendian (f, 0, 2);
- if (mode & M6812_OP_JUMP_REL16)
+ if (reloc == BFD_RELOC_16_PCREL)
fixp->fx_pcrel_adjust = 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)
+ {
+ fixS *fixp;
+
+ /* Now create a 24-bit fixup. */
+ fixp = 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
{
/* Translate the short branch/bsr instruction into a long branch. */
static unsigned char
-convert_branch (code)
- unsigned char code;
+convert_branch (unsigned char code)
{
if (IS_OPCODE (code, M6812_BSR))
return M6812_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;
+m68hc11_new_insn (int size)
{
char *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;
+build_jump_insn (struct m68hc11_opcode *opcode, operand operands[],
+ int nb_operands, int jmp_mode)
{
unsigned char code;
char *f;
unsigned long n;
+ fragS *frag;
+ int where;
- /* The relative branch convertion is not supported for
+ /* The relative branch conversion 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);
+ 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;
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))))
+ (jmp_mode == 1 || flag_fixed_branches == 0))))
{
+ frag = frag_now;
+ where = frag_now_fix ();
+
+ 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);
}
else if (opcode->format & M6812_OP_JUMP_REL16)
{
+ frag = frag_now;
+ where = frag_now_fix ();
+
+ 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);
{
char *opcode;
+ frag = frag_now;
+ where = frag_now_fix ();
+
+ 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_branchs)
+ if (jmp_mode == 0 && flag_fixed_branches)
{
opcode = m68hc11_new_insn (1);
number_to_chars_bigendian (opcode, code, 1);
/* bra/bsr made be changed into jmp/jsr. */
else if (code == M6811_BSR || code == M6811_BRA || code == M6812_BSR)
{
- opcode = m68hc11_new_insn (2);
+ /* Allocate worst case storage. */
+ opcode = m68hc11_new_insn (3);
number_to_chars_bigendian (opcode, code, 1);
number_to_chars_bigendian (opcode + 1, 0, 1);
- frag_var (rs_machine_dependent, 2, 1,
- ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF),
- operands[0].exp.X_add_symbol, (offsetT) n, opcode);
+ frag_variant (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)
{
/* 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;
+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 convertion is not supported for
+ /* The relative branch conversion is not supported for
brclr and brset. */
- assert ((opcode->format & M6811_OP_BITMASK) == 0);
- assert (nb_operands == 2);
- assert (operands[0].reg1 != REG_NONE);
+ gas_assert ((opcode->format & M6811_OP_BITMASK) == 0);
+ gas_assert (nb_operands == 2);
+ gas_assert (operands[0].reg1 != REG_NONE);
code = opcode->opcode & 0x0FF;
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))))
+ (jmp_mode == 1 || flag_fixed_branches == 0))))
{
f = frag_more (2);
code ^= 0x20;
else
{
/* Branch offset must fit in 8-bits, don't do some relax. */
- if (jmp_mode == 0 && flag_fixed_branchs)
+ if (jmp_mode == 0 && flag_fixed_branches)
{
fixup8 (&operands[0].exp, M6811_OP_JUMP_REL, M6811_OP_JUMP_REL);
}
/* 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;
+build_indexed_byte (operand *op, int format ATTRIBUTE_UNUSED, int move_insn)
{
unsigned char byte = 0;
char *f;
return 1;
}
- if (mode & M6812_OP_IDX)
+ if (mode & (M6812_OP_IDX | M6812_OP_D_IDX_2))
{
switch (op->reg1)
{
return -1;
}
- if (val >= -16 && val <= 15 && !(mode & M6812_OP_IDX_2))
+ if (val >= -16 && val <= 15 && !(mode & M6812_OP_D_IDX_2))
{
byte = byte << 6;
byte |= val & 0x1f;
number_to_chars_bigendian (f, byte, 1);
return 1;
}
- else if (val >= -256 && val <= 255 && !(mode & M6812_OP_IDX_2))
+ else if (val >= -256 && val <= 255 && !(mode & M6812_OP_D_IDX_2))
{
byte = byte << 3;
byte |= 0xe0;
else
{
byte = byte << 3;
- if (mode & M6812_OP_IDX_2)
+ if (mode & M6812_OP_D_IDX_2)
byte |= 0xe3;
else
byte |= 0xe2;
return 3;
}
}
- if (op->reg1 != REG_PC)
+ 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)
{
- byte = (byte << 3) | 0xe2;
+ symbolS *sym;
+ offsetT off;
+
f = frag_more (1);
number_to_chars_bigendian (f, byte, 1);
-
- f = frag_more (2);
- fix_new_exp (frag_now, f - frag_now->fr_literal, 2,
- &op->exp, false, BFD_RELOC_16);
- number_to_chars_bigendian (f, 0, 2);
+ 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)
+ {
+ 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);
- number_to_chars_bigendian (f, byte, 1);
- frag_var (rs_machine_dependent, 2, 2,
- ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_UNDF),
- op->exp.X_add_symbol,
- op->exp.X_add_number, f);
+ /* 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)
+ if (mode & (M6812_OP_REG | M6812_OP_D_IDX))
{
- if (mode & M6812_OP_IDX_2)
+ if (mode & M6812_OP_D_IDX)
{
if (op->reg1 != REG_D)
as_bad (_("Expecting register D for indexed indirect mode."));
/* Assemble the 68HC12 register mode byte. */
static int
-build_reg_mode (op, format)
- operand *op;
- int format;
+build_reg_mode (operand *op, int format)
{
unsigned char byte;
char *f;
}
/* build_insn takes a pointer to the opcode entry in the opcode table,
- the array of operand expressions and builds the correspding instruction.
+ 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 (opcode, operands, nb_operands)
- struct m68hc11_opcode *opcode;
- operand operands[];
- int nb_operands ATTRIBUTE_UNUSED;
+build_insn (struct m68hc11_opcode *opcode, operand operands[],
+ int nb_operands ATTRIBUTE_UNUSED)
{
int i;
char *f;
/* 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;
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),
+ fixup16 (&operands[i].exp,
+ format & (M6811_OP_IMM16 | M6811_OP_IND16 | M6812_OP_PAGE),
operands[i].mode);
i++;
}
i = 1;
}
else if (format &
- (M6812_OP_IDX | M6812_OP_IDX_2 | M6812_OP_IDX_1 | M6812_OP_D_IDX))
+ (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++;
{
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);
+ }
}
\f
/* Opcode identification and operand analysis. */
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;
+find (struct m68hc11_opcode_def *opc, operand operands[], int nb_operands)
{
int i, match, pos;
struct m68hc11_opcode *opcode;
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++)
{
{
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))
&& (format & M6812_OP_IDX)
&& (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)))
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
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;
+find_opcode (struct m68hc11_opcode_def *opc, operand operands[],
+ int *nb_operands)
{
struct m68hc11_opcode *opcode;
int i;
if (i >= opc->min_operands)
{
opcode = find (opc, operands, i);
- if (opcode)
+
+ /* 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;
}
points to a machine-dependent instruction. This function is supposed to
emit the frags/bytes it assembles to. */
void
-md_assemble (str)
- char *str;
+md_assemble (char *str)
{
struct m68hc11_opcode_def *opc;
struct m68hc11_opcode *opcode;
- unsigned char *op_start, *save;
- unsigned char *op_end;
+ unsigned char *op_start, *op_end;
+ char *save;
char name[20];
int nlen = 0;
operand operands[M6811_MAX_OPERANDS];
- int nb_operands;
+ int nb_operands = 0;
int branch_optimize = 0;
int alias_id = -1;
/* 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);
+ 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]);
+ name[nlen] = TOLOWER (op_start[nlen]);
nlen++;
}
name[nlen] = 0;
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
+ 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')
{
&& (*op_end &&
(is_end_of_line[op_end[1]]
|| op_end[1] == ' ' || op_end[1] == '\t'
- || !isalnum (op_end[1])))
+ || !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++] = TOLOWER (*op_end++);
name[nlen] = 0;
opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash,
name);
return;
}
save = input_line_pointer;
- input_line_pointer = op_end;
+ input_line_pointer = (char *) op_end;
if (opc)
{
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);
+ 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
else
build_insn (opcode, operands, nb_operands);
}
+
+\f
+/* 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 ();
+}
+
\f
/* 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_section (fixp, sec)
- fixS *fixp;
- segT sec;
+md_pcrel_from (fixS *fixP)
{
- int adjust;
- if (fixp->fx_addsy != (symbolS *) NULL
- && (!S_IS_DEFINED (fixp->fx_addsy)
- || (S_GET_SEGMENT (fixp->fx_addsy) != sec)))
+ if (fixP->fx_r_type == BFD_RELOC_M68HC11_RL_JUMP)
return 0;
- adjust = fixp->fx_pcrel_adjust;
- return fixp->fx_frag->fr_address + fixp->fx_where + adjust;
+ 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 (section, fixp)
- asection *section;
- fixS *fixp;
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
{
arelent *reloc;
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));
+ /* 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 (abfd, sec, fragP)
- bfd *abfd ATTRIBUTE_UNUSED;
- asection *sec ATTRIBUTE_UNUSED;
- fragS *fragP;
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, asection *sec ATTRIBUTE_UNUSED,
+ fragS *fragP)
{
fixS *fixp;
long value;
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)
+ 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_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;
- if ((fragP->fr_opcode[0] & 0x0ff) == 0x0c0)
- fragP->fr_opcode[0] |= disp & 0x1f;
- else
- fragP->fr_opcode[0] |= value & 0x1f;
+ 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;
- fix_new (fragP, fragP->fr_fix, 1,
- fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_8);
+ 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)
+ if ((fragP->fr_opcode[0] & 0x0ff) == 0x0fa
+ && fragP->fr_symbol != 0
+ && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
{
fixp = fix_new (fragP, fragP->fr_fix, 2,
fragP->fr_symbol, fragP->fr_offset,
1, BFD_RELOC_16_PCREL);
- fixp->fx_pcrel_adjust = 2;
}
else
{
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 (symbol)
- symbolS *symbol;
+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 (fragP, segment)
- fragS *fragP;
- asection *segment;
+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))
+ || !relaxable_symbol (fragP->fr_symbol)
+ || (segment != absolute_section
+ && RELAX_STATE (fragP->fr_subtype) == STATE_INDEXED_OFFSET))
{
/* Non-relaxable cases. */
int old_fr_fix;
case STATE_PC_RELATIVE:
/* This relax is only for bsr and bra. */
- assert (IS_OPCODE (fragP->fr_opcode[0], M6811_BSR)
+ 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_branchs)
+ if (flag_fixed_branches)
as_bad_where (fragP->fr_file, fragP->fr_line,
_("bra or bsr with undefined symbol."));
break;
case STATE_CONDITIONAL_BRANCH:
- assert (current_architecture & cpu6811);
+ 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). */
break;
case STATE_INDEXED_OFFSET:
- assert (current_architecture & cpu6812);
+ 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;
- /* Switch the indexed operation to 16-bit mode. */
- fragP->fr_opcode[0] = fragP->fr_opcode[0] << 3;
- fragP->fr_opcode[0] |= 0xe2;
- fragP->fr_fix++;
- fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
- fragP->fr_offset, 0, BFD_RELOC_16);
- fragP->fr_fix++;
+ 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
+ {
+ fixS* fixp;
+
+ fragP->fr_opcode[0] = fragP->fr_opcode[0] << 3;
+ fragP->fr_opcode[0] |= 0xe2;
+ fixp = 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:
- assert (current_architecture & cpu6812);
+ 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). */
break;
case STATE_CONDITIONAL_BRANCH_6812:
- assert (current_architecture & cpu6812);
+ 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, 0, BFD_RELOC_16_PCREL);
+ fragP->fr_offset, 1, BFD_RELOC_16_PCREL);
fragP->fr_fix += 2;
break;
{
case STATE_PC_RELATIVE:
/* This relax is only for bsr and bra. */
- assert (IS_OPCODE (fragP->fr_opcode[0], M6811_BSR)
+ 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));
break;
case STATE_CONDITIONAL_BRANCH:
- assert (current_architecture & cpu6811);
+ gas_assert (current_architecture & cpu6811);
fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH,
STATE_BYTE);
break;
case STATE_INDEXED_OFFSET:
- assert (current_architecture & cpu6812);
+ 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:
- assert (current_architecture & cpu6812);
+ gas_assert (current_architecture & cpu6812);
fragP->fr_subtype = ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_BYTE);
break;
case STATE_CONDITIONAL_BRANCH_6812:
- assert (current_architecture & cpu6812);
+ gas_assert (current_architecture & cpu6812);
fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812,
STATE_BYTE);
return md_relax_table[fragP->fr_subtype].rlx_length;
}
+/* See whether we need to force a relocation into the output file. */
int
-md_apply_fix (fixp, valuep)
- fixS *fixp;
- valueT *valuep;
+tc_m68hc11_force_relocation (fixS *fixP)
{
- char *where;
- long value;
- int op_type;
+ if (fixP->fx_r_type == BFD_RELOC_M68HC11_RL_GROUP)
+ return 1;
- if (fixp->fx_addsy == (symbolS *) NULL)
- {
- value = *valuep;
- fixp->fx_done = 1;
- }
- else if (fixp->fx_pcrel)
- {
- value = *valuep;
- }
- else
+ 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)
{
- 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."));
- }
- }
+ /* 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;
+ int op_type;
+
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ fixP->fx_done = 1;
- op_type = fixp->fx_r_type;
+ /* 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."));
+
+ 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.
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;
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
- switch (fixp->fx_r_type)
+ 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,
+ as_bad_where (fixP->fx_file, fixP->fx_line,
_("Value out of 16-bit range."));
break;
case BFD_RELOC_M68HC11_LO8:
case BFD_RELOC_8:
-#if 0
- bfd_putb8 ((bfd_vma) value, (unsigned char *) where);
-#endif
+ case BFD_RELOC_M68HC11_PAGE:
((bfd_byte *) where)[0] = (bfd_byte) value;
break;
case BFD_RELOC_8_PCREL:
-#if 0
- bfd_putb8 ((bfd_vma) value, (unsigned char *) where);
-#endif
((bfd_byte *) where)[0] = (bfd_byte) value;
if (value < -128 || value > 127)
- as_bad_where (fixp->fx_file, fixp->fx_line,
+ 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,
+ as_bad_where (fixP->fx_file, fixP->fx_line,
_("Auto increment/decrement offset '%ld' is out of range."),
value);
if (where[0] & 0x8)
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_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);
+ fixP->fx_line, fixP->fx_r_type);
}
+}
- return 0;
+/* 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;
}