/* Disassemble h8300 instructions. Copyright 1993, 1994, 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2010 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 "dis-asm.h" #include "opintl.h" #include "libiberty.h" struct h8_instruction { 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++) ; if (i & 1) { fprintf (stderr, "Internal error, h8_disassemble_init.\n"); abort (); } 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 = (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 l; unsigned char data[MAX_CODE_NIBBLES]; 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 (l = 2; status == 0 && l < sizeof (data) / 2; l += 2) status = info->read_memory_func (addr + l, data + l, 2, info); /* Find the exact opcode/arg combo. */ for (qi = h8_instructions; qi->opcode->name; qi++) { const struct h8_opcode *q = qi->opcode; const op_type *nib = q->data.nib; unsigned int len = 0; while (1) { op_type looking_for = *nib; int thisnib = data[len / 2]; int opnr; 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) { 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) { 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) { int i = len / 2; cst[opnr] = ((data[i] << 24) | (data[i + 1] << 16) | (data[i + 2] << 8) | (data[i + 3])); cstlen[opnr] = 32; } else if ((looking_for & SIZE) == L_24) { int i = len / 2; cst[opnr] = (data[i] << 16) | (data[i + 1] << 8) | (data[i + 2]); cstlen[opnr] = 24; } else if (looking_for & IGNORE) { ; } 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 if (looking_for == (op_type) E) { 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; } else /* xgettext:c-format */ outfn (stream, _("Don't understand 0x%x \n"), looking_for); } len++; nib++; } 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); }