Reorganise m68k instruction decoding and improve handling of MAC/EMAC

This commit is contained in:
Nick Clifton 2004-05-24 14:33:22 +00:00
parent dfdffa2c3c
commit be8c092bb0
11 changed files with 10607 additions and 10404 deletions

View File

@ -1,3 +1,12 @@
2004-05-24 Peter Barada <peter@the-baradas.com>
* config/m68k-parse.y(operand): Allow for MAC/EMAC mask
addressing on MIT style operands.
* config/m68k-parse.y(yylex): Allow '-&' for predecrement
w/mask addressing.
* config/tc-m68k.c(install_operand): Comment 'G' and 'H' type
operands.
2004-05-23 Alan Modra <amodra@bigpond.net.au>
* expr.c (operand, operator): Don't reject '++' and '--'.

View File

@ -1,5 +1,5 @@
/* m68k.y -- bison grammar for m68k operand parsing
Copyright 1995, 1996, 1997, 1998, 2001 Free Software Foundation, Inc.
Copyright 1995, 1996, 1997, 1998, 2001, 2004 Free Software Foundation, Inc.
Written by Ken Raeburn and Ian Lance Taylor, Cygnus Support
This file is part of GAS, the GNU Assembler.
@ -35,7 +35,7 @@
etc), as well as gratuitously global symbol names If other parser
generators (bison, byacc, etc) produce additional global names that
conflict at link time, then those parser generators need to be
fixed instead of adding those names to this list. */
fixed instead of adding those names to this list. */
#define yymaxdepth m68k_maxdepth
#define yyparse m68k_parse
@ -122,7 +122,10 @@ operand:
{
op->trailing_ampersand = $2;
}
| mit_operand
| mit_operand optional_ampersand
{
op->trailing_ampersand = $2;
}
;
/* A trailing ampersand(for MAC/EMAC mask addressing). */
@ -793,8 +796,8 @@ yylex ()
case '-':
/* A '-' can only appear in -(ar), rn-rn, or ar@-. If it
appears anywhere else, it must be a unary minus on an
expression. */
if (str[1] == '\0')
expression, unless it it trailed by a '&'(see mac load insn). */
if (str[1] == '\0' || (str[1] == '&' && str[2] == '\0'))
return *str++;
s = str + 1;
if (*s == '(')

View File

@ -3476,11 +3476,11 @@ install_operand (mode, val)
case 'f':
the_ins.opcode[0] |= ((val & 0x3) << 0);
break;
case 'G':
case 'G': /* EMAC accumulator in a EMAC load instruction. */
the_ins.opcode[0] |= ((~val & 0x1) << 7);
the_ins.opcode[1] |= ((val & 0x2) << (4 - 1));
break;
case 'H':
case 'H': /* EMAC accumulator in a EMAC non-load instruction. */
the_ins.opcode[0] |= ((val & 0x1) << 7);
the_ins.opcode[1] |= ((val & 0x2) << (4 - 1));
break;

View File

@ -1,3 +1,8 @@
2004-05-24 Peter Barada <peter@the-baradas.com>
* gas/m68k/mcf-emac.d: Provide correct disassembler results.
* gas/m68k/mcf-mac.d: Provide correct disassembler results.
2004-05-20 H.J. Lu <hongjiu.lu@intel.com>
* gas/ia64/dv-srlz.d: Fix a typo.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,7 @@
2004-05-24 Peter Barada <peter@the-baradas.com>
* m68k.h: Add 'size' to m68k_opcode.
2004-05-05 Peter Barada <peter@the-baradas.com>
* m68k.h: Switch from ColdFire chip name to core variant.

View File

@ -73,6 +73,9 @@ struct m68k_opcode
{
/* The opcode name. */
const char *name;
/* The pseudo-size of the instruction(in bytes). Used to determine
number of bytes necessary to disassemble the instruction. */
unsigned int size;
/* The opcode itself. */
unsigned long opcode;
/* The mask used by the disassembler. */

View File

@ -1,3 +1,20 @@
2004-05-24 Peter Barada <peter@the-baradas.com>
* m68k-dis.c(print_insn_m68k): Strip body of diassembly out
into new match_insn_m68k function. Loop over canidate
matches and select first that completely matches.
* m68k-dis.c(print_insn_arg): Fix 'g' case to only extract 1 bit.
* m68k-dis.c(print_insn_arg): Call new function m68k_valid_ea
to verify addressing for MAC/EMAC.
* m68k-dis.c(print_insn_arg): Use reg_half_names for MAC/EMAC
reigster halves since 'fpu' and 'spl' look misleading.
* m68k-dis.c(fetch_arg): Fix 'G', 'H', 'I', 'f', 'M', 'N' cases.
* m68k-opc.c: Rearragne mac/emac cases to use longest for
first, tighten up match masks.
* m68k-opc.c: Add 'size' field to struct m68k_opcode. Produce
'size' from special case code in print_insn_m68k to
determine decode size of insns.
2004-05-19 Alan Modra <amodra@bigpond.net.au>
* ppc-opc.c (insert_fxm): Enable two operand mfcr when -many as

View File

@ -46,18 +46,31 @@ static int
print_insn_arg PARAMS ((const char *, unsigned char *, unsigned char *,
bfd_vma, disassemble_info *));
const char * const fpcr_names[] = {
static bfd_boolean m68k_valid_ea (char code, int val);
const char * const fpcr_names[] =
{
"", "%fpiar", "%fpsr", "%fpiar/%fpsr", "%fpcr",
"%fpiar/%fpcr", "%fpsr/%fpcr", "%fpiar/%fpsr/%fpcr"
};
static char *const reg_names[] = {
static char *const reg_names[] =
{
"%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
"%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%fp", "%sp",
"%ps", "%pc"
};
/* Sign-extend an (unsigned char). */
/* Name of register halves for MAC/EMAC.
Seperate from reg_names since 'spu', 'fpl' look weird. */
static char *const reg_half_names[] =
{
"%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
"%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",
"%ps", "%pc"
};
/* Sign-extend an (unsigned char). */
#if __STDC__ == 1
#define COERCE_SIGNED_CHAR(ch) ((signed char) (ch))
#else
@ -169,6 +182,147 @@ dummy_print_address (vma, info)
{
}
/* Try to match the current instruction to best and if so, return the
number of bytes consumed from the instruction stream, else zero. */
static int
match_insn_m68k (bfd_vma memaddr, disassemble_info * info,
const struct m68k_opcode * best, struct private * priv)
{
unsigned char *save_p;
unsigned char *p;
const char *d;
bfd_byte *buffer = priv->the_buffer;
fprintf_ftype save_printer = info->fprintf_func;
void (* save_print_address) (bfd_vma, struct disassemble_info *)
= info->print_address_func;
/* Point at first word of argument data,
and at descriptor for first argument. */
p = buffer + 2;
/* Figure out how long the fixed-size portion of the instruction is.
The only place this is stored in the opcode table is
in the arguments--look for arguments which specify fields in the 2nd
or 3rd words of the instruction. */
for (d = best->args; *d; d += 2)
{
/* I don't think it is necessary to be checking d[0] here;
I suspect all this could be moved to the case statement below. */
if (d[0] == '#')
{
if (d[1] == 'l' && p - buffer < 6)
p = buffer + 6;
else if (p - buffer < 4 && d[1] != 'C' && d[1] != '8')
p = buffer + 4;
}
if ((d[0] == 'L' || d[0] == 'l') && d[1] == 'w' && p - buffer < 4)
p = buffer + 4;
switch (d[1])
{
case '1':
case '2':
case '3':
case '7':
case '8':
case '9':
case 'i':
if (p - buffer < 4)
p = buffer + 4;
break;
case '4':
case '5':
case '6':
if (p - buffer < 6)
p = buffer + 6;
break;
default:
break;
}
}
/* pflusha is an exceptions. It takes no arguments but is two words
long. Recognize it by looking at the lower 16 bits of the mask. */
if (p - buffer < 4 && (best->match & 0xFFFF) != 0)
p = buffer + 4;
/* lpstop is another exception. It takes a one word argument but is
three words long. */
if (p - buffer < 6
&& (best->match & 0xffff) == 0xffff
&& best->args[0] == '#'
&& best->args[1] == 'w')
{
/* Copy the one word argument into the usual location for a one
word argument, to simplify printing it. We can get away with
this because we know exactly what the second word is, and we
aren't going to print anything based on it. */
p = buffer + 6;
FETCH_DATA (info, p);
buffer[2] = buffer[4];
buffer[3] = buffer[5];
}
FETCH_DATA (info, p);
d = best->args;
save_p = p;
info->print_address_func = dummy_print_address;
info->fprintf_func = (fprintf_ftype) dummy_printer;
/* We scan the operands twice. The first time we don't print anything,
but look for errors. */
for (; *d; d += 2)
{
int eaten = print_insn_arg (d, buffer, p, memaddr + (p - buffer), info);
if (eaten >= 0)
p += eaten;
else if (eaten == -1)
{
info->fprintf_func = save_printer;
info->print_address_func = save_print_address;
return 0;
}
else
{
info->fprintf_func (info->stream,
/* xgettext:c-format */
_("<internal error in opcode table: %s %s>\n"),
best->name, best->args);
info->fprintf_func = save_printer;
info->print_address_func = save_print_address;
return 2;
}
}
p = save_p;
info->fprintf_func = save_printer;
info->print_address_func = save_print_address;
d = best->args;
info->fprintf_func (info->stream, "%s", best->name);
if (*d)
info->fprintf_func (info->stream, " ");
while (*d)
{
p += print_insn_arg (d, buffer, p, memaddr + (p - buffer), info);
d += 2;
if (*d && *(d - 2) != 'I' && *d != 'k')
info->fprintf_func (info->stream, ",");
}
return p - buffer;
}
/* Print the m68k instruction at address MEMADDR in debugged memory,
on INFO->STREAM. Returns length of the instruction, in bytes. */
@ -177,38 +331,32 @@ print_insn_m68k (memaddr, info)
bfd_vma memaddr;
disassemble_info *info;
{
register int i;
register unsigned char *p;
unsigned char *save_p;
register const char *d;
register unsigned long bestmask;
const struct m68k_opcode *best;
int i;
const char *d;
unsigned int arch_mask;
struct private priv;
bfd_byte *buffer = priv.the_buffer;
fprintf_ftype save_printer = info->fprintf_func;
void (*save_print_address) PARAMS ((bfd_vma, struct disassemble_info *))
= info->print_address_func;
int major_opcode;
static int numopcodes[16];
static const struct m68k_opcode **opcodes[16];
int val;
if (!opcodes[0])
{
/* Speed up the matching by sorting the opcode table on the upper
four bits of the opcode. */
/* Speed up the matching by sorting the opcode
table on the upper four bits of the opcode. */
const struct m68k_opcode **opc_pointer[16];
/* First count how many opcodes are in each of the sixteen buckets. */
for (i = 0; i < m68k_numopcodes; i++)
numopcodes[(m68k_opcodes[i].opcode >> 28) & 15]++;
/* Then create a sorted table of pointers that point into the
unsorted table. */
opc_pointer[0] = ((const struct m68k_opcode **)
xmalloc (sizeof (struct m68k_opcode *)
* m68k_numopcodes));
/* Then create a sorted table of pointers
that point into the unsorted table. */
opc_pointer[0] = xmalloc (sizeof (struct m68k_opcode *)
* m68k_numopcodes);
opcodes[0] = opc_pointer[0];
for (i = 1; i < 16; i++)
{
opc_pointer[i] = opc_pointer[i - 1] + numopcodes[i - 1];
@ -217,22 +365,21 @@ print_insn_m68k (memaddr, info)
for (i = 0; i < m68k_numopcodes; i++)
*opc_pointer[(m68k_opcodes[i].opcode >> 28) & 15]++ = &m68k_opcodes[i];
}
info->private_data = (PTR) &priv;
/* Tell objdump to use two bytes per chunk and six bytes per line for
displaying raw data. */
/* Tell objdump to use two bytes per chunk
and six bytes per line for displaying raw data. */
info->bytes_per_chunk = 2;
info->bytes_per_line = 6;
info->display_endian = BFD_ENDIAN_BIG;
priv.max_fetched = priv.the_buffer;
priv.insn_start = memaddr;
if (setjmp (priv.bailout) != 0)
/* Error return. */
return -1;
best = NULL;
switch (info->mach)
{
default:
@ -286,9 +433,9 @@ print_insn_m68k (memaddr, info)
break;
}
bestmask = 0;
FETCH_DATA (info, buffer + 2);
major_opcode = (buffer[0] >> 4) & 15;
for (i = 0; i < numopcodes[major_opcode]; i++)
{
const struct m68k_opcode *opc = opcodes[major_opcode][i];
@ -315,22 +462,20 @@ print_insn_m68k (memaddr, info)
/* Don't use for printout the variants of most floating
point coprocessor instructions which use the same
register number in two places, as above. */
register number in two places, as above. */
if (*d == '\0')
for (d = opc->args; *d; d += 2)
if (d[1] == 't')
break;
/* Don't match fmovel with more than one register; wait for
fmoveml. */
/* Don't match fmovel with more than one register;
wait for fmoveml. */
if (*d == '\0')
{
for (d = opc->args; *d; d += 2)
{
if (d[0] == 's' && d[1] == '8')
{
int val;
val = fetch_arg (buffer, d[1], 3, info);
if ((val & (val - 1)) != 0)
break;
@ -338,158 +483,35 @@ print_insn_m68k (memaddr, info)
}
}
if (*d == '\0' && match > bestmask)
{
best = opc;
bestmask = match;
}
if (*d == '\0')
if ((val = match_insn_m68k (memaddr, info, opc, & priv)))
return val;
}
}
if (best == NULL)
goto invalid;
/* Point at first word of argument data,
and at descriptor for first argument. */
p = buffer + 2;
/* Figure out how long the fixed-size portion of the instruction is.
The only place this is stored in the opcode table is
in the arguments--look for arguments which specify fields in the 2nd
or 3rd words of the instruction. */
for (d = best->args; *d; d += 2)
{
/* I don't think it is necessary to be checking d[0] here; I suspect
all this could be moved to the case statement below. */
if (d[0] == '#')
{
if (d[1] == 'l' && p - buffer < 6)
p = buffer + 6;
else if (p - buffer < 4 && d[1] != 'C' && d[1] != '8')
p = buffer + 4;
}
if ((d[0] == 'L' || d[0] == 'l') && d[1] == 'w' && p - buffer < 4)
p = buffer + 4;
switch (d[1])
{
case '1':
case '2':
case '3':
case '7':
case '8':
case '9':
case 'i':
if (p - buffer < 4)
p = buffer + 4;
break;
case '4':
case '5':
case '6':
if (p - buffer < 6)
p = buffer + 6;
break;
default:
break;
}
}
/* pflusha is an exceptions. It takes no arguments but is two words
long. Recognize it by looking at the lower 16 bits of the mask. */
if (p - buffer < 4 && (best->match & 0xFFFF) != 0)
p = buffer + 4;
/* lpstop is another exception. It takes a one word argument but is
three words long. */
if (p - buffer < 6
&& (best->match & 0xffff) == 0xffff
&& best->args[0] == '#'
&& best->args[1] == 'w')
{
/* Copy the one word argument into the usual location for a one
word argument, to simplify printing it. We can get away with
this because we know exactly what the second word is, and we
aren't going to print anything based on it. */
p = buffer + 6;
FETCH_DATA (info, p);
buffer[2] = buffer[4];
buffer[3] = buffer[5];
}
FETCH_DATA (info, p);
d = best->args;
/* We scan the operands twice. The first time we don't print anything,
but look for errors. */
save_p = p;
info->print_address_func = dummy_print_address;
info->fprintf_func = (fprintf_ftype) dummy_printer;
for (; *d; d += 2)
{
int eaten = print_insn_arg (d, buffer, p, memaddr + (p - buffer), info);
if (eaten >= 0)
p += eaten;
else if (eaten == -1)
goto invalid;
else
{
(*info->fprintf_func) (info->stream,
/* xgettext:c-format */
_("<internal error in opcode table: %s %s>\n"),
best->name,
best->args);
goto invalid;
}
}
p = save_p;
info->fprintf_func = save_printer;
info->print_address_func = save_print_address;
d = best->args;
(*info->fprintf_func) (info->stream, "%s", best->name);
if (*d)
(*info->fprintf_func) (info->stream, " ");
while (*d)
{
p += print_insn_arg (d, buffer, p, memaddr + (p - buffer), info);
d += 2;
if (*d && *(d - 2) != 'I' && *d != 'k')
(*info->fprintf_func) (info->stream, ",");
}
return p - buffer;
invalid:
/* Handle undefined instructions. */
info->fprintf_func = save_printer;
info->print_address_func = save_print_address;
(*info->fprintf_func) (info->stream, "0%o",
(buffer[0] << 8) + buffer[1]);
info->fprintf_func (info->stream, "0%o", (buffer[0] << 8) + buffer[1]);
return 2;
}
/* Returns number of bytes "eaten" by the operand, or
return -1 if an invalid operand was found, or -2 if
an opcode tabe error was found. */
an opcode tabe error was found. */
static int
print_insn_arg (d, buffer, p0, addr, info)
const char *d;
unsigned char *buffer;
unsigned char *p0;
bfd_vma addr; /* PC for this arg to be relative to */
bfd_vma addr; /* PC for this arg to be relative to. */
disassemble_info *info;
{
register int val = 0;
register int place = d[1];
register unsigned char *p = p0;
int val = 0;
int place = d[1];
unsigned char *p = p0;
int regno;
register const char *regname;
register unsigned char *p1;
const char *regname;
unsigned char *p1;
double flval;
int flt_p;
bfd_signed_vma disp;
@ -497,7 +519,7 @@ print_insn_arg (d, buffer, p0, addr, info)
switch (*d)
{
case 'c': /* cache identifier */
case 'c': /* Cache identifier. */
{
static char *const cacheFieldName[] = { "nc", "dc", "ic", "bc" };
val = fetch_arg (buffer, place, 2, info);
@ -505,7 +527,7 @@ print_insn_arg (d, buffer, p0, addr, info)
break;
}
case 'a': /* address register indirect only. Cf. case '+'. */
case 'a': /* Address register indirect only. Cf. case '+'. */
{
(*info->fprintf_func)
(info->stream,
@ -514,7 +536,7 @@ print_insn_arg (d, buffer, p0, addr, info)
break;
}
case '_': /* 32-bit absolute address for move16. */
case '_': /* 32-bit absolute address for move16. */
{
uval = NEXTULONG (p);
(*info->print_address_func) (uval, info);
@ -749,7 +771,7 @@ print_insn_arg (d, buffer, p0, addr, info)
break;
case 'g':
val = fetch_arg(buffer, place, 2, info);
val = fetch_arg(buffer, place, 1, info);
(*info->fprintf_func) (info->stream, "%%accext%s", val==0 ? "01" : "23");
break;
@ -759,6 +781,8 @@ print_insn_arg (d, buffer, p0, addr, info)
(*info->fprintf_func) (info->stream, "<<");
else if (val == 3)
(*info->fprintf_func) (info->stream, ">>");
else
return -1;
break;
case 'I':
@ -801,6 +825,10 @@ print_insn_arg (d, buffer, p0, addr, info)
else
val = fetch_arg (buffer, 's', 6, info);
/* If the <ea> is invalid for *d, then reject this match. */
if (m68k_valid_ea (*d, val) == FALSE)
return -1;
/* Get register number assuming address register. */
regno = (val & 7) + 8;
regname = reg_names[regno];
@ -915,7 +943,7 @@ print_insn_arg (d, buffer, p0, addr, info)
{
val = fetch_arg (buffer, place, 1, info);
if (val)
(*info->fprintf_func) (info->stream, "&");
info->fprintf_func (info->stream, "&");
}
break;
@ -1084,7 +1112,7 @@ print_insn_arg (d, buffer, p0, addr, info)
reg &= 0xf;
}
(*info->fprintf_func) (info->stream, "%s%s",
reg_names[reg],
reg_half_names[reg],
is_upper ? "u" : "l");
}
break;
@ -1096,6 +1124,24 @@ print_insn_arg (d, buffer, p0, addr, info)
return p - p0;
}
/* Check if an EA is valid for a particular code. This is required
for the EMAC instructions since the type of source address determines
if it is a EMAC-load instruciton if the EA is mode 2-5, otherwise it
is a non-load EMAC instruction and the bits mean register Ry. */
static bfd_boolean
m68k_valid_ea (char code, int val)
{
int mode;
mode = (val >> 3) & 7;
if (code == '4')
if (!(mode >= 2 && mode <= 5))
return FALSE;
return TRUE;
}
/* Fetch BITS bits from a position in the instruction specified by CODE.
CODE is a "place to put an argument", or 'x' for a destination
that is a general address (mode and register).
@ -1108,7 +1154,8 @@ fetch_arg (buffer, code, bits, info)
int bits;
disassemble_info *info;
{
register int val = 0;
int val = 0;
switch (code)
{
case '/': /* MAC/EMAC mask bit. */
@ -1116,11 +1163,11 @@ fetch_arg (buffer, code, bits, info)
break;
case 'G': /* EMAC ACC load. */
val = ((buffer[3] >> 3) & 0x2) | ((~buffer[2] >> 7) & 0x1);
val = ((buffer[3] >> 3) & 0x2) | ((~buffer[1] >> 7) & 0x1);
break;
case 'H': /* EMAC ACC !load. */
val = ((buffer[3] >> 3) & 0x2) | ((buffer[2] >> 7) & 0x1);
val = ((buffer[3] >> 3) & 0x2) | ((buffer[1] >> 7) & 0x1);
break;
case ']': /* EMAC ACCEXT bit. */
@ -1128,7 +1175,7 @@ fetch_arg (buffer, code, bits, info)
break;
case 'I': /* MAC/EMAC scale factor. */
val = buffer[0] >> 1;
val = buffer[2] >> 1;
break;
case 'F': /* EMAC ACCx. */
@ -1136,7 +1183,7 @@ fetch_arg (buffer, code, bits, info)
break;
case 'f':
val = buffer[0];
val = buffer[1];
break;
case 's':
@ -1235,11 +1282,11 @@ fetch_arg (buffer, code, bits, info)
break;
case 'M':
val = buffer[1] | (buffer[3] & 0x40 ? 0x10 : 0);
val = (buffer[1] & 0xf) | (buffer[3] & 0x40 ? 0x10 : 0);
break;
case 'N':
val = buffer[3] | (buffer[3] & 0x40 ? 0x10 : 0);
val = (buffer[3] & 0xf) | (buffer[3] & 0x40 ? 0x10 : 0);
break;
case 'h':
@ -1286,7 +1333,7 @@ print_indexed (basereg, p, addr, info)
bfd_vma addr;
disassemble_info *info;
{
register int word;
int word;
static char *const scales[] = { "", ":2", ":4", ":8" };
bfd_vma base_disp;
bfd_vma outer_disp;

File diff suppressed because it is too large Load Diff