/* Disassemble MN10300 instructions. Copyright (C) 1996-2022 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 "disassemble.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_DREG) != 0 || (operand->flags & MN10300_OPERAND_AREG) != 0 || (operand->flags & MN10300_OPERAND_RREG) != 0 || (operand->flags & MN10300_OPERAND_XRREG) != 0) value = ((insn >> (operand->shift + extra_shift)) & ((1 << operand->bits) - 1)); else 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) (*info->fprintf_func) (info->stream, "d%d", (int) value); else if ((operand->flags & MN10300_OPERAND_AREG) != 0) (*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) { 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) { 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; }