darling-gdb/gas/config/tc-m68hc11.c
2000-07-17 21:37:02 +00:00

2806 lines
70 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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 *));
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));
/* 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;
}
/* Check 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;
}
/* 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 */
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);
#if 0
fix_new_exp (frag_now, f - frag_now->fr_literal, 2,
&op->exp, false, BFD_RELOC_16);
#endif
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:
#if 0
bfd_putb8 ((bfd_vma) value, (unsigned char *) where);
#endif
((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,
_("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;
}