832a580781
This patch also had some problems. Calculation of maxlen was wrong, and the insn arg loop needed rearranging to work with a correct length. * disassemble.h (opcodes_assert): Declare. (OPCODES_ASSERT): Define. * disassemble.c: Don't include assert.h. Include opintl.h. (opcodes_assert): New function. * h8300-dis.c (bfd_h8_disassemble_init): Use OPCODES_ASSERT. (bfd_h8_disassemble): Reduce size of data array. Correctly calculate maxlen. Omit insn decoding when insn length exceeds maxlen. Exit from nibble loop when looking for E, before accessing next data byte. Move processing of E outside loop. Replace tests of maxlen in loop with assertions.
746 lines
18 KiB
C
746 lines
18 KiB
C
/* Disassemble h8300 instructions.
|
|
Copyright (C) 1993-2020 Free Software Foundation, Inc.
|
|
|
|
This file is part of the GNU opcodes library.
|
|
|
|
This library 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 3, or (at your option)
|
|
any later version.
|
|
|
|
It 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 this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
#define DEFINE_TABLE
|
|
|
|
#include "sysdep.h"
|
|
#define h8_opcodes h8ops
|
|
#include "opcode/h8300.h"
|
|
#include "disassemble.h"
|
|
#include "opintl.h"
|
|
#include "libiberty.h"
|
|
|
|
struct h8_instruction
|
|
{
|
|
unsigned int length;
|
|
const struct h8_opcode *opcode;
|
|
};
|
|
|
|
struct h8_instruction *h8_instructions;
|
|
|
|
/* Run through the opcodes and sort them into order to make them easy
|
|
to disassemble. */
|
|
|
|
static void
|
|
bfd_h8_disassemble_init (void)
|
|
{
|
|
unsigned int i;
|
|
unsigned int nopcodes;
|
|
const struct h8_opcode *p;
|
|
struct h8_instruction *pi;
|
|
|
|
nopcodes = sizeof (h8_opcodes) / sizeof (struct h8_opcode);
|
|
|
|
h8_instructions = xmalloc (nopcodes * sizeof (struct h8_instruction));
|
|
|
|
for (p = h8_opcodes, pi = h8_instructions; p->name; p++, pi++)
|
|
{
|
|
/* Just make sure there are an even number of nibbles in it, and
|
|
that the count is the same as the length. */
|
|
for (i = 0; p->data.nib[i] != (op_type) E; i++)
|
|
;
|
|
OPCODES_ASSERT (!(i & 1));
|
|
|
|
pi->length = i / 2;
|
|
pi->opcode = p;
|
|
}
|
|
|
|
/* Add entry for the NULL vector terminator. */
|
|
pi->length = 0;
|
|
pi->opcode = p;
|
|
}
|
|
|
|
static void
|
|
extract_immediate (FILE *stream,
|
|
op_type looking_for,
|
|
int thisnib,
|
|
unsigned char *data,
|
|
int *cst,
|
|
int *len,
|
|
const struct h8_opcode *q)
|
|
{
|
|
switch (looking_for & SIZE)
|
|
{
|
|
case L_2:
|
|
*len = 2;
|
|
*cst = thisnib & 3;
|
|
|
|
/* DISP2 special treatment. */
|
|
if ((looking_for & MODE) == DISP)
|
|
{
|
|
if (OP_KIND (q->how) == O_MOVAB
|
|
|| OP_KIND (q->how) == O_MOVAW
|
|
|| OP_KIND (q->how) == O_MOVAL)
|
|
{
|
|
/* Handling for mova insn. */
|
|
switch (q->args.nib[0] & MODE)
|
|
{
|
|
case INDEXB:
|
|
default:
|
|
break;
|
|
case INDEXW:
|
|
*cst *= 2;
|
|
break;
|
|
case INDEXL:
|
|
*cst *= 4;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Handling for non-mova insn. */
|
|
switch (OP_SIZE (q->how))
|
|
{
|
|
default: break;
|
|
case SW:
|
|
*cst *= 2;
|
|
break;
|
|
case SL:
|
|
*cst *= 4;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case L_8:
|
|
*len = 8;
|
|
*cst = data[0];
|
|
break;
|
|
case L_16:
|
|
case L_16U:
|
|
*len = 16;
|
|
*cst = (data[0] << 8) + data [1];
|
|
#if 0
|
|
if ((looking_for & SIZE) == L_16)
|
|
*cst = (short) *cst; /* Sign extend. */
|
|
#endif
|
|
break;
|
|
case L_32:
|
|
*len = 32;
|
|
*cst = (((unsigned) data[0] << 24) + (data[1] << 16)
|
|
+ (data[2] << 8) + data[3]);
|
|
break;
|
|
default:
|
|
*len = 0;
|
|
*cst = 0;
|
|
fprintf (stream, "DISP bad size\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const char *regnames[] =
|
|
{
|
|
"r0h", "r1h", "r2h", "r3h", "r4h", "r5h", "r6h", "r7h",
|
|
"r0l", "r1l", "r2l", "r3l", "r4l", "r5l", "r6l", "r7l"
|
|
};
|
|
static const char *wregnames[] =
|
|
{
|
|
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
|
"e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7"
|
|
};
|
|
static const char *lregnames[] =
|
|
{
|
|
"er0", "er1", "er2", "er3", "er4", "er5", "er6", "er7",
|
|
"er0", "er1", "er2", "er3", "er4", "er5", "er6", "er7"
|
|
};
|
|
static const char *cregnames[] =
|
|
{
|
|
"ccr", "exr", "mach", "macl", "", "", "vbr", "sbr"
|
|
};
|
|
|
|
static void
|
|
print_one_arg (disassemble_info *info,
|
|
bfd_vma addr,
|
|
op_type x,
|
|
int cst,
|
|
int cstlen,
|
|
int rdisp_n,
|
|
int rn,
|
|
const char **pregnames,
|
|
int len)
|
|
{
|
|
void * stream = info->stream;
|
|
fprintf_ftype outfn = info->fprintf_func;
|
|
|
|
if ((x & SIZE) == L_3 || (x & SIZE) == L_3NZ)
|
|
outfn (stream, "#0x%x", (unsigned) cst);
|
|
else if ((x & MODE) == IMM)
|
|
outfn (stream, "#0x%x", (unsigned) cst);
|
|
else if ((x & MODE) == DBIT || (x & MODE) == KBIT)
|
|
outfn (stream, "#%d", (unsigned) cst);
|
|
else if ((x & MODE) == CONST_2)
|
|
outfn (stream, "#2");
|
|
else if ((x & MODE) == CONST_4)
|
|
outfn (stream, "#4");
|
|
else if ((x & MODE) == CONST_8)
|
|
outfn (stream, "#8");
|
|
else if ((x & MODE) == CONST_16)
|
|
outfn (stream, "#16");
|
|
else if ((x & MODE) == REG)
|
|
{
|
|
switch (x & SIZE)
|
|
{
|
|
case L_8:
|
|
outfn (stream, "%s", regnames[rn]);
|
|
break;
|
|
case L_16:
|
|
case L_16U:
|
|
outfn (stream, "%s", wregnames[rn]);
|
|
break;
|
|
case L_P:
|
|
case L_32:
|
|
outfn (stream, "%s", lregnames[rn]);
|
|
break;
|
|
}
|
|
}
|
|
else if ((x & MODE) == LOWREG)
|
|
{
|
|
switch (x & SIZE)
|
|
{
|
|
case L_8:
|
|
/* Always take low half of reg. */
|
|
outfn (stream, "%s.b", regnames[rn < 8 ? rn + 8 : rn]);
|
|
break;
|
|
case L_16:
|
|
case L_16U:
|
|
/* Always take low half of reg. */
|
|
outfn (stream, "%s.w", wregnames[rn < 8 ? rn : rn - 8]);
|
|
break;
|
|
case L_P:
|
|
case L_32:
|
|
outfn (stream, "%s.l", lregnames[rn]);
|
|
break;
|
|
}
|
|
}
|
|
else if ((x & MODE) == POSTINC)
|
|
outfn (stream, "@%s+", pregnames[rn]);
|
|
|
|
else if ((x & MODE) == POSTDEC)
|
|
outfn (stream, "@%s-", pregnames[rn]);
|
|
|
|
else if ((x & MODE) == PREINC)
|
|
outfn (stream, "@+%s", pregnames[rn]);
|
|
|
|
else if ((x & MODE) == PREDEC)
|
|
outfn (stream, "@-%s", pregnames[rn]);
|
|
|
|
else if ((x & MODE) == IND)
|
|
outfn (stream, "@%s", pregnames[rn]);
|
|
|
|
else if ((x & MODE) == ABS || (x & ABSJMP))
|
|
outfn (stream, "@0x%x:%d", (unsigned) cst, cstlen);
|
|
|
|
else if ((x & MODE) == MEMIND)
|
|
outfn (stream, "@@%d (0x%x)", cst, cst);
|
|
|
|
else if ((x & MODE) == VECIND)
|
|
{
|
|
/* FIXME Multiplier should be 2 or 4, depending on processor mode,
|
|
by which is meant "normal" vs. "middle", "advanced", "maximum". */
|
|
|
|
int offset = (cst + 0x80) * 4;
|
|
outfn (stream, "@@%d (0x%x)", offset, offset);
|
|
}
|
|
else if ((x & MODE) == PCREL)
|
|
{
|
|
if ((x & SIZE) == L_16 ||
|
|
(x & SIZE) == L_16U)
|
|
{
|
|
outfn (stream, ".%s%d (0x%lx)",
|
|
(short) cst > 0 ? "+" : "",
|
|
(short) cst,
|
|
(long)(addr + (short) cst + len));
|
|
}
|
|
else
|
|
{
|
|
outfn (stream, ".%s%d (0x%lx)",
|
|
(char) cst > 0 ? "+" : "",
|
|
(char) cst,
|
|
(long)(addr + (char) cst + len));
|
|
}
|
|
}
|
|
else if ((x & MODE) == DISP)
|
|
outfn (stream, "@(0x%x:%d,%s)", cst, cstlen, pregnames[rdisp_n]);
|
|
|
|
else if ((x & MODE) == INDEXB)
|
|
/* Always take low half of reg. */
|
|
outfn (stream, "@(0x%x:%d,%s.b)", cst, cstlen,
|
|
regnames[rdisp_n < 8 ? rdisp_n + 8 : rdisp_n]);
|
|
|
|
else if ((x & MODE) == INDEXW)
|
|
/* Always take low half of reg. */
|
|
outfn (stream, "@(0x%x:%d,%s.w)", cst, cstlen,
|
|
wregnames[rdisp_n < 8 ? rdisp_n : rdisp_n - 8]);
|
|
|
|
else if ((x & MODE) == INDEXL)
|
|
outfn (stream, "@(0x%x:%d,%s.l)", cst, cstlen, lregnames[rdisp_n]);
|
|
|
|
else if (x & CTRL)
|
|
outfn (stream, "%s", cregnames[rn]);
|
|
|
|
else if ((x & MODE) == CCR)
|
|
outfn (stream, "ccr");
|
|
|
|
else if ((x & MODE) == EXR)
|
|
outfn (stream, "exr");
|
|
|
|
else if ((x & MODE) == MACREG)
|
|
outfn (stream, "mac%c", cst ? 'l' : 'h');
|
|
|
|
else
|
|
/* xgettext:c-format */
|
|
outfn (stream, _("Hmmmm 0x%x"), x);
|
|
}
|
|
|
|
static unsigned int
|
|
bfd_h8_disassemble (bfd_vma addr, disassemble_info *info, int mach)
|
|
{
|
|
/* Find the first entry in the table for this opcode. */
|
|
int regno[3] = { 0, 0, 0 };
|
|
int dispregno[3] = { 0, 0, 0 };
|
|
int cst[3] = { 0, 0, 0 };
|
|
int cstlen[3] = { 0, 0, 0 };
|
|
static bfd_boolean init = 0;
|
|
const struct h8_instruction *qi;
|
|
char const **pregnames = mach != 0 ? lregnames : wregnames;
|
|
int status;
|
|
unsigned int maxlen;
|
|
unsigned char data[MAX_CODE_NIBBLES / 2];
|
|
void *stream = info->stream;
|
|
fprintf_ftype outfn = info->fprintf_func;
|
|
|
|
if (!init)
|
|
{
|
|
bfd_h8_disassemble_init ();
|
|
init = 1;
|
|
}
|
|
|
|
status = info->read_memory_func (addr, data, 2, info);
|
|
if (status != 0)
|
|
{
|
|
info->memory_error_func (status, addr, info);
|
|
return -1;
|
|
}
|
|
|
|
for (maxlen = 2; maxlen < sizeof (data); maxlen += 2)
|
|
{
|
|
status = info->read_memory_func (addr + maxlen, data + maxlen, 2, info);
|
|
if (status != 0)
|
|
break;
|
|
}
|
|
|
|
/* Find the exact opcode/arg combo. */
|
|
for (qi = h8_instructions; qi->opcode->name; qi++)
|
|
{
|
|
const struct h8_opcode *q;
|
|
const op_type *nib;
|
|
unsigned int len;
|
|
op_type looking_for;
|
|
|
|
if (qi->length > maxlen)
|
|
continue;
|
|
|
|
q = qi->opcode;
|
|
nib = q->data.nib;
|
|
len = 0;
|
|
while ((looking_for = *nib) != (op_type) E)
|
|
{
|
|
int thisnib;
|
|
int opnr;
|
|
|
|
OPCODES_ASSERT (len / 2 < maxlen);
|
|
thisnib = data[len / 2];
|
|
thisnib = (len & 1) ? (thisnib & 0xf) : ((thisnib / 16) & 0xf);
|
|
opnr = ((looking_for & OP3) == OP3 ? 2
|
|
: (looking_for & DST) == DST ? 1 : 0);
|
|
|
|
if (looking_for < 16 && looking_for >= 0)
|
|
{
|
|
if (looking_for != thisnib)
|
|
goto fail;
|
|
}
|
|
else
|
|
{
|
|
if ((int) looking_for & (int) B31)
|
|
{
|
|
if (!((thisnib & 0x8) != 0))
|
|
goto fail;
|
|
|
|
looking_for = (op_type) ((int) looking_for & ~(int) B31);
|
|
thisnib &= 0x7;
|
|
}
|
|
else if ((int) looking_for & (int) B30)
|
|
{
|
|
if (!((thisnib & 0x8) == 0))
|
|
goto fail;
|
|
|
|
looking_for = (op_type) ((int) looking_for & ~(int) B30);
|
|
}
|
|
|
|
if ((int) looking_for & (int) B21)
|
|
{
|
|
if (!((thisnib & 0x4) != 0))
|
|
goto fail;
|
|
|
|
looking_for = (op_type) ((int) looking_for & ~(int) B21);
|
|
thisnib &= 0xb;
|
|
}
|
|
else if ((int) looking_for & (int) B20)
|
|
{
|
|
if (!((thisnib & 0x4) == 0))
|
|
goto fail;
|
|
|
|
looking_for = (op_type) ((int) looking_for & ~(int) B20);
|
|
}
|
|
if ((int) looking_for & (int) B11)
|
|
{
|
|
if (!((thisnib & 0x2) != 0))
|
|
goto fail;
|
|
|
|
looking_for = (op_type) ((int) looking_for & ~(int) B11);
|
|
thisnib &= 0xd;
|
|
}
|
|
else if ((int) looking_for & (int) B10)
|
|
{
|
|
if (!((thisnib & 0x2) == 0))
|
|
goto fail;
|
|
|
|
looking_for = (op_type) ((int) looking_for & ~(int) B10);
|
|
}
|
|
|
|
if ((int) looking_for & (int) B01)
|
|
{
|
|
if (!((thisnib & 0x1) != 0))
|
|
goto fail;
|
|
|
|
looking_for = (op_type) ((int) looking_for & ~(int) B01);
|
|
thisnib &= 0xe;
|
|
}
|
|
else if ((int) looking_for & (int) B00)
|
|
{
|
|
if (!((thisnib & 0x1) == 0))
|
|
goto fail;
|
|
|
|
looking_for = (op_type) ((int) looking_for & ~(int) B00);
|
|
}
|
|
|
|
if (looking_for & IGNORE)
|
|
{
|
|
/* Hitachi has declared that IGNORE must be zero. */
|
|
if (thisnib != 0)
|
|
goto fail;
|
|
}
|
|
else if ((looking_for & MODE) == DATA)
|
|
{
|
|
; /* Skip embedded data. */
|
|
}
|
|
else if ((looking_for & MODE) == DBIT)
|
|
{
|
|
/* Exclude adds/subs by looking at bit 0 and 2, and
|
|
make sure the operand size, either w or l,
|
|
matches by looking at bit 1. */
|
|
if ((looking_for & 7) != (thisnib & 7))
|
|
goto fail;
|
|
|
|
cst[opnr] = (thisnib & 0x8) ? 2 : 1;
|
|
}
|
|
else if ((looking_for & MODE) == DISP
|
|
|| (looking_for & MODE) == ABS
|
|
|| (looking_for & MODE) == PCREL
|
|
|| (looking_for & MODE) == INDEXB
|
|
|| (looking_for & MODE) == INDEXW
|
|
|| (looking_for & MODE) == INDEXL)
|
|
{
|
|
int extra;
|
|
switch (looking_for & SIZE)
|
|
{
|
|
case L_16:
|
|
case L_16U:
|
|
extra = 1;
|
|
break;
|
|
case L_32:
|
|
extra = 3;
|
|
break;
|
|
default:
|
|
extra = 0;
|
|
break;
|
|
}
|
|
OPCODES_ASSERT (len / 2 + extra < maxlen);
|
|
extract_immediate (stream, looking_for, thisnib,
|
|
data + len / 2, cst + opnr,
|
|
cstlen + opnr, q);
|
|
/* Even address == bra, odd == bra/s. */
|
|
if (q->how == O (O_BRAS, SB))
|
|
cst[opnr] -= 1;
|
|
}
|
|
else if ((looking_for & MODE) == REG
|
|
|| (looking_for & MODE) == LOWREG
|
|
|| (looking_for & MODE) == IND
|
|
|| (looking_for & MODE) == PREINC
|
|
|| (looking_for & MODE) == POSTINC
|
|
|| (looking_for & MODE) == PREDEC
|
|
|| (looking_for & MODE) == POSTDEC)
|
|
{
|
|
regno[opnr] = thisnib;
|
|
}
|
|
else if (looking_for & CTRL) /* Control Register. */
|
|
{
|
|
thisnib &= 7;
|
|
if (((looking_for & MODE) == CCR && (thisnib != C_CCR))
|
|
|| ((looking_for & MODE) == EXR && (thisnib != C_EXR))
|
|
|| ((looking_for & MODE) == MACH && (thisnib != C_MACH))
|
|
|| ((looking_for & MODE) == MACL && (thisnib != C_MACL))
|
|
|| ((looking_for & MODE) == VBR && (thisnib != C_VBR))
|
|
|| ((looking_for & MODE) == SBR && (thisnib != C_SBR)))
|
|
goto fail;
|
|
if (((looking_for & MODE) == CCR_EXR
|
|
&& (thisnib != C_CCR && thisnib != C_EXR))
|
|
|| ((looking_for & MODE) == VBR_SBR
|
|
&& (thisnib != C_VBR && thisnib != C_SBR))
|
|
|| ((looking_for & MODE) == MACREG
|
|
&& (thisnib != C_MACH && thisnib != C_MACL)))
|
|
goto fail;
|
|
if (((looking_for & MODE) == CC_EX_VB_SB
|
|
&& (thisnib != C_CCR && thisnib != C_EXR
|
|
&& thisnib != C_VBR && thisnib != C_SBR)))
|
|
goto fail;
|
|
|
|
regno[opnr] = thisnib;
|
|
}
|
|
else if ((looking_for & SIZE) == L_5)
|
|
{
|
|
cst[opnr] = data[len / 2] & 31;
|
|
cstlen[opnr] = 5;
|
|
}
|
|
else if ((looking_for & SIZE) == L_4)
|
|
{
|
|
cst[opnr] = thisnib;
|
|
cstlen[opnr] = 4;
|
|
}
|
|
else if ((looking_for & SIZE) == L_16
|
|
|| (looking_for & SIZE) == L_16U)
|
|
{
|
|
OPCODES_ASSERT (len / 2 + 1 < maxlen);
|
|
cst[opnr] = (data[len / 2]) * 256 + data[(len + 2) / 2];
|
|
cstlen[opnr] = 16;
|
|
}
|
|
else if ((looking_for & MODE) == MEMIND)
|
|
{
|
|
cst[opnr] = data[1];
|
|
}
|
|
else if ((looking_for & MODE) == VECIND)
|
|
{
|
|
cst[opnr] = data[1] & 0x7f;
|
|
}
|
|
else if ((looking_for & SIZE) == L_32)
|
|
{
|
|
unsigned int i = len / 2;
|
|
|
|
OPCODES_ASSERT (i + 3 < maxlen);
|
|
cst[opnr] = (((unsigned) data[i] << 24)
|
|
| (data[i + 1] << 16)
|
|
| (data[i + 2] << 8)
|
|
| (data[i + 3]));
|
|
|
|
cstlen[opnr] = 32;
|
|
}
|
|
else if ((looking_for & SIZE) == L_24)
|
|
{
|
|
unsigned int i = len / 2;
|
|
|
|
OPCODES_ASSERT (i + 2 < maxlen);
|
|
cst[opnr] =
|
|
(data[i] << 16) | (data[i + 1] << 8) | (data[i + 2]);
|
|
cstlen[opnr] = 24;
|
|
}
|
|
else if (looking_for & DISPREG)
|
|
{
|
|
dispregno[opnr] = thisnib & 7;
|
|
}
|
|
else if ((looking_for & MODE) == KBIT)
|
|
{
|
|
switch (thisnib)
|
|
{
|
|
case 9:
|
|
cst[opnr] = 4;
|
|
break;
|
|
case 8:
|
|
cst[opnr] = 2;
|
|
break;
|
|
case 0:
|
|
cst[opnr] = 1;
|
|
break;
|
|
default:
|
|
goto fail;
|
|
}
|
|
}
|
|
else if ((looking_for & SIZE) == L_8)
|
|
{
|
|
cstlen[opnr] = 8;
|
|
cst[opnr] = data[len / 2];
|
|
}
|
|
else if ((looking_for & SIZE) == L_3
|
|
|| (looking_for & SIZE) == L_3NZ)
|
|
{
|
|
cst[opnr] = thisnib & 0x7;
|
|
if (cst[opnr] == 0 && (looking_for & SIZE) == L_3NZ)
|
|
goto fail;
|
|
}
|
|
else if ((looking_for & SIZE) == L_2)
|
|
{
|
|
cstlen[opnr] = 2;
|
|
cst[opnr] = thisnib & 0x3;
|
|
}
|
|
else if ((looking_for & MODE) == MACREG)
|
|
{
|
|
cst[opnr] = (thisnib == 3);
|
|
}
|
|
else
|
|
/* xgettext:c-format */
|
|
outfn (stream, _("Don't understand 0x%x \n"), looking_for);
|
|
}
|
|
|
|
len++;
|
|
nib++;
|
|
}
|
|
|
|
outfn (stream, "%s\t", q->name);
|
|
|
|
/* Gross. Disgusting. */
|
|
if (strcmp (q->name, "ldm.l") == 0)
|
|
{
|
|
int count, high;
|
|
|
|
count = (data[1] / 16) & 0x3;
|
|
high = regno[1];
|
|
|
|
outfn (stream, "@sp+,er%d-er%d", high - count, high);
|
|
return qi->length;
|
|
}
|
|
|
|
if (strcmp (q->name, "stm.l") == 0)
|
|
{
|
|
int count, low;
|
|
|
|
count = (data[1] / 16) & 0x3;
|
|
low = regno[0];
|
|
|
|
outfn (stream, "er%d-er%d,@-sp", low, low + count);
|
|
return qi->length;
|
|
}
|
|
if (strcmp (q->name, "rte/l") == 0
|
|
|| strcmp (q->name, "rts/l") == 0)
|
|
{
|
|
if (regno[0] == 0)
|
|
outfn (stream, "er%d", regno[1]);
|
|
else
|
|
outfn (stream, "er%d-er%d", regno[1] - regno[0],
|
|
regno[1]);
|
|
return qi->length;
|
|
}
|
|
if (CONST_STRNEQ (q->name, "mova"))
|
|
{
|
|
const op_type *args = q->args.nib;
|
|
|
|
if (args[1] == (op_type) E)
|
|
{
|
|
/* Short form. */
|
|
print_one_arg (info, addr, args[0], cst[0],
|
|
cstlen[0], dispregno[0], regno[0],
|
|
pregnames, qi->length);
|
|
outfn (stream, ",er%d", dispregno[0]);
|
|
}
|
|
else
|
|
{
|
|
outfn (stream, "@(0x%x:%d,", cst[0], cstlen[0]);
|
|
print_one_arg (info, addr, args[1], cst[1],
|
|
cstlen[1], dispregno[1], regno[1],
|
|
pregnames, qi->length);
|
|
outfn (stream, ".%c),",
|
|
(args[0] & MODE) == INDEXB ? 'b' : 'w');
|
|
print_one_arg (info, addr, args[2], cst[2],
|
|
cstlen[2], dispregno[2], regno[2],
|
|
pregnames, qi->length);
|
|
}
|
|
return qi->length;
|
|
}
|
|
/* Fill in the args. */
|
|
{
|
|
const op_type *args = q->args.nib;
|
|
int hadone = 0;
|
|
int nargs;
|
|
|
|
/* Special case handling for the adds and subs instructions
|
|
since in H8 mode thay can only take the r0-r7 registers
|
|
but in other (higher) modes they can take the er0-er7
|
|
registers as well. */
|
|
if (strcmp (qi->opcode->name, "adds") == 0
|
|
|| strcmp (qi->opcode->name, "subs") == 0)
|
|
{
|
|
outfn (stream, "#%d,%s", cst[0], pregnames[regno[1] & 0x7]);
|
|
return qi->length;
|
|
}
|
|
|
|
for (nargs = 0;
|
|
nargs < 3 && args[nargs] != (op_type) E;
|
|
nargs++)
|
|
{
|
|
int x = args[nargs];
|
|
|
|
if (hadone)
|
|
outfn (stream, ",");
|
|
|
|
print_one_arg (info, addr, x,
|
|
cst[nargs], cstlen[nargs],
|
|
dispregno[nargs], regno[nargs],
|
|
pregnames, qi->length);
|
|
|
|
hadone = 1;
|
|
}
|
|
}
|
|
return qi->length;
|
|
|
|
fail:
|
|
;
|
|
}
|
|
|
|
/* Fell off the end. */
|
|
outfn (stream, ".word\tH'%x,H'%x", data[0], data[1]);
|
|
return 2;
|
|
}
|
|
|
|
int
|
|
print_insn_h8300 (bfd_vma addr, disassemble_info *info)
|
|
{
|
|
return bfd_h8_disassemble (addr, info, 0);
|
|
}
|
|
|
|
int
|
|
print_insn_h8300h (bfd_vma addr, disassemble_info *info)
|
|
{
|
|
return bfd_h8_disassemble (addr, info, 1);
|
|
}
|
|
|
|
int
|
|
print_insn_h8300s (bfd_vma addr, disassemble_info *info)
|
|
{
|
|
return bfd_h8_disassemble (addr, info, 2);
|
|
}
|