/* Disassemble MN10300 instructions. Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2005, 2007, 2012 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. */ #include "sysdep.h" #include #include "opcode/mn10300.h" #include "dis-asm.h" #include "opintl.h" #define HAVE_AM33_2 (info->mach == AM33_2) #define HAVE_AM33 (info->mach == AM33 || HAVE_AM33_2) #define HAVE_AM30 (info->mach == AM30) static void disassemble (bfd_vma memaddr, struct disassemble_info *info, unsigned long insn, unsigned int size) { struct mn10300_opcode *op = (struct mn10300_opcode *) mn10300_opcodes; const struct mn10300_operand *operand; bfd_byte buffer[4]; unsigned long extension = 0; int status, match = 0; /* Find the opcode. */ while (op->name) { int mysize, extra_shift; if (op->format == FMT_S0) mysize = 1; else if (op->format == FMT_S1 || op->format == FMT_D0) mysize = 2; else if (op->format == FMT_S2 || op->format == FMT_D1) mysize = 3; else if (op->format == FMT_S4) mysize = 5; else if (op->format == FMT_D2) mysize = 4; else if (op->format == FMT_D3) mysize = 5; else if (op->format == FMT_D4) mysize = 6; else if (op->format == FMT_D6) mysize = 3; else if (op->format == FMT_D7 || op->format == FMT_D10) mysize = 4; else if (op->format == FMT_D8) mysize = 6; else if (op->format == FMT_D9) mysize = 7; else mysize = 7; if ((op->mask & insn) == op->opcode && size == (unsigned int) mysize && (op->machine == 0 || (op->machine == AM33_2 && HAVE_AM33_2) || (op->machine == AM33 && HAVE_AM33) || (op->machine == AM30 && HAVE_AM30))) { const unsigned char *opindex_ptr; unsigned int nocomma; int paren = 0; if (op->format == FMT_D1 || op->format == FMT_S1) extra_shift = 8; else if (op->format == FMT_D2 || op->format == FMT_D4 || op->format == FMT_S2 || op->format == FMT_S4 || op->format == FMT_S6 || op->format == FMT_D5) extra_shift = 16; else if (op->format == FMT_D7 || op->format == FMT_D8 || op->format == FMT_D9) extra_shift = 8; else extra_shift = 0; if (size == 1 || size == 2) extension = 0; else if (size == 3 && (op->format == FMT_D1 || op->opcode == 0xdf0000 || op->opcode == 0xde0000)) extension = 0; else if (size == 3 && op->format == FMT_D6) extension = 0; else if (size == 3) { insn &= 0xff0000; status = (*info->read_memory_func) (memaddr + 1, buffer, 2, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } insn |= bfd_getl16 (buffer); extension = 0; } else if (size == 4 && (op->opcode == 0xfaf80000 || op->opcode == 0xfaf00000 || op->opcode == 0xfaf40000)) extension = 0; else if (size == 4 && (op->format == FMT_D7 || op->format == FMT_D10)) extension = 0; else if (size == 4) { insn &= 0xffff0000; status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } insn |= bfd_getl16 (buffer); extension = 0; } else if (size == 5 && op->opcode == 0xdc000000) { unsigned long temp = 0; status = (*info->read_memory_func) (memaddr + 1, buffer, 4, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } temp |= bfd_getl32 (buffer); insn &= 0xff000000; insn |= (temp & 0xffffff00) >> 8; extension = temp & 0xff; } else if (size == 5 && op->format == FMT_D3) { status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } insn &= 0xffff0000; insn |= bfd_getl16 (buffer); status = (*info->read_memory_func) (memaddr + 4, buffer, 1, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } extension = *(unsigned char *) buffer; } else if (size == 5) { unsigned long temp = 0; status = (*info->read_memory_func) (memaddr + 1, buffer, 2, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } temp |= bfd_getl16 (buffer); insn &= 0xff0000ff; insn |= temp << 8; status = (*info->read_memory_func) (memaddr + 4, buffer, 1, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } extension = *(unsigned char *) buffer; } else if (size == 6 && op->format == FMT_D8) { insn &= 0xffffff00; status = (*info->read_memory_func) (memaddr + 5, buffer, 1, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } insn |= *(unsigned char *) buffer; status = (*info->read_memory_func) (memaddr + 3, buffer, 2, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } extension = bfd_getl16 (buffer); } else if (size == 6) { unsigned long temp = 0; status = (*info->read_memory_func) (memaddr + 2, buffer, 4, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } temp |= bfd_getl32 (buffer); insn &= 0xffff0000; insn |= (temp >> 16) & 0xffff; extension = temp & 0xffff; } else if (size == 7 && op->format == FMT_D9) { insn &= 0xffffff00; status = (*info->read_memory_func) (memaddr + 3, buffer, 4, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } extension = bfd_getl32 (buffer); insn |= (extension & 0xff000000) >> 24; extension &= 0xffffff; } else if (size == 7 && op->opcode == 0xdd000000) { unsigned long temp = 0; status = (*info->read_memory_func) (memaddr + 1, buffer, 4, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } temp |= bfd_getl32 (buffer); insn &= 0xff000000; insn |= (temp >> 8) & 0xffffff; extension = (temp & 0xff) << 16; status = (*info->read_memory_func) (memaddr + 5, buffer, 2, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } extension |= bfd_getb16 (buffer); } else if (size == 7) { unsigned long temp = 0; status = (*info->read_memory_func) (memaddr + 2, buffer, 4, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } temp |= bfd_getl32 (buffer); insn &= 0xffff0000; insn |= (temp >> 16) & 0xffff; extension = (temp & 0xffff) << 8; status = (*info->read_memory_func) (memaddr + 6, buffer, 1, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return; } extension |= *(unsigned char *) buffer; } match = 1; (*info->fprintf_func) (info->stream, "%s\t", op->name); /* Now print the operands. */ for (opindex_ptr = op->operands, nocomma = 1; *opindex_ptr != 0; opindex_ptr++) { unsigned long value; operand = &mn10300_operands[*opindex_ptr]; /* If this operand is a PLUS (autoincrement), then do not emit a comma before emitting the plus. */ if ((operand->flags & MN10300_OPERAND_PLUS) != 0) nocomma = 1; if ((operand->flags & MN10300_OPERAND_SPLIT) != 0) { unsigned long temp; value = insn & ((1 << operand->bits) - 1); value <<= (32 - operand->bits); temp = extension >> operand->shift; temp &= ((1 << (32 - operand->bits)) - 1); value |= temp; value = ((value ^ (((unsigned long) 1) << 31)) - (((unsigned long) 1) << 31)); } else if ((operand->flags & MN10300_OPERAND_24BIT) != 0) { unsigned long temp; value = insn & ((1 << operand->bits) - 1); value <<= (24 - operand->bits); temp = extension >> operand->shift; temp &= ((1 << (24 - operand->bits)) - 1); value |= temp; if ((operand->flags & MN10300_OPERAND_SIGNED) != 0) value = ((value & 0xffffff) ^ 0x800000) - 0x800000; } else if ((operand->flags & (MN10300_OPERAND_FSREG | MN10300_OPERAND_FDREG))) { /* See m10300-opc.c just before #define FSM0 for an explanation of these variables. Note that FMT-implied shifts are not taken into account for FP registers. */ unsigned long mask_low, mask_high; int shl_low, shr_high, shl_high; switch (operand->bits) { case 5: /* Handle regular FP registers. */ if (operand->shift >= 0) { /* This is an `m' register. */ shl_low = operand->shift; shl_high = 8 + (8 & shl_low) + (shl_low & 4) / 4; } else { /* This is an `n' register. */ shl_low = -operand->shift; shl_high = shl_low / 4; } mask_low = 0x0f; mask_high = 0x10; shr_high = 4; break; case 3: /* Handle accumulators. */ shl_low = -operand->shift; shl_high = 0; mask_low = 0x03; mask_high = 0x04; shr_high = 2; break; default: abort (); } value = ((((insn >> shl_high) << shr_high) & mask_high) | ((insn >> shl_low) & mask_low)); } else if ((operand->flags & MN10300_OPERAND_EXTENDED) != 0) value = ((extension >> (operand->shift)) & ((1 << operand->bits) - 1)); else value = ((insn >> (operand->shift)) & ((1 << operand->bits) - 1)); if ((operand->flags & MN10300_OPERAND_SIGNED) != 0 /* These are properly extended by the code above. */ && ((operand->flags & MN10300_OPERAND_24BIT) == 0)) value = ((value ^ (((unsigned long) 1) << (operand->bits - 1))) - (((unsigned long) 1) << (operand->bits - 1))); if (!nocomma && (!paren || ((operand->flags & MN10300_OPERAND_PAREN) == 0))) (*info->fprintf_func) (info->stream, ","); nocomma = 0; if ((operand->flags & MN10300_OPERAND_DREG) != 0) { value = ((insn >> (operand->shift + extra_shift)) & ((1 << operand->bits) - 1)); (*info->fprintf_func) (info->stream, "d%d", (int) value); } else if ((operand->flags & MN10300_OPERAND_AREG) != 0) { value = ((insn >> (operand->shift + extra_shift)) & ((1 << operand->bits) - 1)); (*info->fprintf_func) (info->stream, "a%d", (int) value); } else if ((operand->flags & MN10300_OPERAND_SP) != 0) (*info->fprintf_func) (info->stream, "sp"); else if ((operand->flags & MN10300_OPERAND_PSW) != 0) (*info->fprintf_func) (info->stream, "psw"); else if ((operand->flags & MN10300_OPERAND_MDR) != 0) (*info->fprintf_func) (info->stream, "mdr"); else if ((operand->flags & MN10300_OPERAND_RREG) != 0) { value = ((insn >> (operand->shift + extra_shift)) & ((1 << operand->bits) - 1)); if (value < 8) (*info->fprintf_func) (info->stream, "r%d", (int) value); else if (value < 12) (*info->fprintf_func) (info->stream, "a%d", (int) value - 8); else (*info->fprintf_func) (info->stream, "d%d", (int) value - 12); } else if ((operand->flags & MN10300_OPERAND_XRREG) != 0) { value = ((insn >> (operand->shift + extra_shift)) & ((1 << operand->bits) - 1)); if (value == 0) (*info->fprintf_func) (info->stream, "sp"); else (*info->fprintf_func) (info->stream, "xr%d", (int) value); } else if ((operand->flags & MN10300_OPERAND_FSREG) != 0) (*info->fprintf_func) (info->stream, "fs%d", (int) value); else if ((operand->flags & MN10300_OPERAND_FDREG) != 0) (*info->fprintf_func) (info->stream, "fd%d", (int) value); else if ((operand->flags & MN10300_OPERAND_FPCR) != 0) (*info->fprintf_func) (info->stream, "fpcr"); else if ((operand->flags & MN10300_OPERAND_USP) != 0) (*info->fprintf_func) (info->stream, "usp"); else if ((operand->flags & MN10300_OPERAND_SSP) != 0) (*info->fprintf_func) (info->stream, "ssp"); else if ((operand->flags & MN10300_OPERAND_MSP) != 0) (*info->fprintf_func) (info->stream, "msp"); else if ((operand->flags & MN10300_OPERAND_PC) != 0) (*info->fprintf_func) (info->stream, "pc"); else if ((operand->flags & MN10300_OPERAND_EPSW) != 0) (*info->fprintf_func) (info->stream, "epsw"); else if ((operand->flags & MN10300_OPERAND_PLUS) != 0) (*info->fprintf_func) (info->stream, "+"); else if ((operand->flags & MN10300_OPERAND_PAREN) != 0) { if (paren) (*info->fprintf_func) (info->stream, ")"); else { (*info->fprintf_func) (info->stream, "("); nocomma = 1; } paren = !paren; } else if ((operand->flags & MN10300_OPERAND_PCREL) != 0) (*info->print_address_func) ((long) value + memaddr, info); else if ((operand->flags & MN10300_OPERAND_MEMADDR) != 0) (*info->print_address_func) (value, info); else if ((operand->flags & MN10300_OPERAND_REG_LIST) != 0) { int comma = 0; (*info->fprintf_func) (info->stream, "["); if (value & 0x80) { (*info->fprintf_func) (info->stream, "d2"); comma = 1; } if (value & 0x40) { if (comma) (*info->fprintf_func) (info->stream, ","); (*info->fprintf_func) (info->stream, "d3"); comma = 1; } if (value & 0x20) { if (comma) (*info->fprintf_func) (info->stream, ","); (*info->fprintf_func) (info->stream, "a2"); comma = 1; } if (value & 0x10) { if (comma) (*info->fprintf_func) (info->stream, ","); (*info->fprintf_func) (info->stream, "a3"); comma = 1; } if (value & 0x08) { if (comma) (*info->fprintf_func) (info->stream, ","); (*info->fprintf_func) (info->stream, "other"); comma = 1; } if (value & 0x04) { if (comma) (*info->fprintf_func) (info->stream, ","); (*info->fprintf_func) (info->stream, "exreg0"); comma = 1; } if (value & 0x02) { if (comma) (*info->fprintf_func) (info->stream, ","); (*info->fprintf_func) (info->stream, "exreg1"); comma = 1; } if (value & 0x01) { if (comma) (*info->fprintf_func) (info->stream, ","); (*info->fprintf_func) (info->stream, "exother"); comma = 1; } (*info->fprintf_func) (info->stream, "]"); } else (*info->fprintf_func) (info->stream, "%ld", (long) value); } /* All done. */ break; } op++; } if (!match) /* xgettext:c-format */ (*info->fprintf_func) (info->stream, _("unknown\t0x%04lx"), insn); } int print_insn_mn10300 (bfd_vma memaddr, struct disassemble_info *info) { int status; bfd_byte buffer[4]; unsigned long insn; unsigned int consume; /* First figure out how big the opcode is. */ status = (*info->read_memory_func) (memaddr, buffer, 1, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return -1; } insn = *(unsigned char *) buffer; /* These are one byte insns. */ if ((insn & 0xf3) == 0x00 || (insn & 0xf0) == 0x10 || (insn & 0xfc) == 0x3c || (insn & 0xf3) == 0x41 || (insn & 0xf3) == 0x40 || (insn & 0xfc) == 0x50 || (insn & 0xfc) == 0x54 || (insn & 0xf0) == 0x60 || (insn & 0xf0) == 0x70 || ((insn & 0xf0) == 0x80 && (insn & 0x0c) >> 2 != (insn & 0x03)) || ((insn & 0xf0) == 0x90 && (insn & 0x0c) >> 2 != (insn & 0x03)) || ((insn & 0xf0) == 0xa0 && (insn & 0x0c) >> 2 != (insn & 0x03)) || ((insn & 0xf0) == 0xb0 && (insn & 0x0c) >> 2 != (insn & 0x03)) || (insn & 0xff) == 0xcb || (insn & 0xfc) == 0xd0 || (insn & 0xfc) == 0xd4 || (insn & 0xfc) == 0xd8 || (insn & 0xf0) == 0xe0 || (insn & 0xff) == 0xff) { consume = 1; } /* These are two byte insns. */ else if ((insn & 0xf0) == 0x80 || (insn & 0xf0) == 0x90 || (insn & 0xf0) == 0xa0 || (insn & 0xf0) == 0xb0 || (insn & 0xfc) == 0x20 || (insn & 0xfc) == 0x28 || (insn & 0xf3) == 0x43 || (insn & 0xf3) == 0x42 || (insn & 0xfc) == 0x58 || (insn & 0xfc) == 0x5c || ((insn & 0xf0) == 0xc0 && (insn & 0xff) != 0xcb && (insn & 0xff) != 0xcc && (insn & 0xff) != 0xcd) || (insn & 0xff) == 0xf0 || (insn & 0xff) == 0xf1 || (insn & 0xff) == 0xf2 || (insn & 0xff) == 0xf3 || (insn & 0xff) == 0xf4 || (insn & 0xff) == 0xf5 || (insn & 0xff) == 0xf6) { status = (*info->read_memory_func) (memaddr, buffer, 2, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return -1; } insn = bfd_getb16 (buffer); consume = 2; } /* These are three byte insns. */ else if ((insn & 0xff) == 0xf8 || (insn & 0xff) == 0xcc || (insn & 0xff) == 0xf9 || (insn & 0xf3) == 0x01 || (insn & 0xf3) == 0x02 || (insn & 0xf3) == 0x03 || (insn & 0xfc) == 0x24 || (insn & 0xfc) == 0x2c || (insn & 0xfc) == 0x30 || (insn & 0xfc) == 0x34 || (insn & 0xfc) == 0x38 || (insn & 0xff) == 0xde || (insn & 0xff) == 0xdf || (insn & 0xff) == 0xf9 || (insn & 0xff) == 0xcc) { status = (*info->read_memory_func) (memaddr, buffer, 2, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return -1; } insn = bfd_getb16 (buffer); insn <<= 8; status = (*info->read_memory_func) (memaddr + 2, buffer, 1, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return -1; } insn |= *(unsigned char *) buffer; consume = 3; } /* These are four byte insns. */ else if ((insn & 0xff) == 0xfa || (insn & 0xff) == 0xf7 || (insn & 0xff) == 0xfb) { status = (*info->read_memory_func) (memaddr, buffer, 4, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return -1; } insn = bfd_getb32 (buffer); consume = 4; } /* These are five byte insns. */ else if ((insn & 0xff) == 0xcd || (insn & 0xff) == 0xdc) { status = (*info->read_memory_func) (memaddr, buffer, 4, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return -1; } insn = bfd_getb32 (buffer); consume = 5; } /* These are six byte insns. */ else if ((insn & 0xff) == 0xfd || (insn & 0xff) == 0xfc) { status = (*info->read_memory_func) (memaddr, buffer, 4, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return -1; } insn = bfd_getb32 (buffer); consume = 6; } /* Else its a seven byte insns (in theory). */ else { status = (*info->read_memory_func) (memaddr, buffer, 4, info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return -1; } insn = bfd_getb32 (buffer); consume = 7; /* Handle the 5-byte extended instruction codes. */ if ((insn & 0xfff80000) == 0xfe800000) consume = 5; } disassemble (memaddr, info, insn, consume); return consume; }