| /* Print instructions for the Motorola 88000, for GDB and GNU Binutils. |
| Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1993, 1998, 2000, 2001 |
| Free Software Foundation, Inc. |
| Contributed by Data General Corporation, November 1989. |
| Partially derived from an earlier printcmd.c. |
| |
| This file is part of GDB and the GNU Binutils. |
| |
| This program 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 2 of the License, or |
| (at your option) any later version. |
| |
| This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
| |
| #include "sysdep.h" |
| #include "dis-asm.h" |
| #include "opcode/m88k.h" |
| #include "opintl.h" |
| |
| INSTAB *hashtable[HASHVAL] = {0}; |
| |
| static int |
| m88kdis PARAMS ((bfd_vma, unsigned long, struct disassemble_info *)); |
| |
| static void |
| printop PARAMS ((struct disassemble_info *, OPSPEC *, unsigned long, bfd_vma, int)); |
| |
| static void |
| init_disasm PARAMS ((void)); |
| |
| static void |
| install PARAMS ((INSTAB *instptr)); |
| |
| |
| /* Disassemble an M88000 instruction at `memaddr'. */ |
| |
| int |
| print_insn_m88k (memaddr, info) |
| bfd_vma memaddr; |
| struct disassemble_info *info; |
| { |
| bfd_byte buffer[4]; |
| int status; |
| |
| /* Instruction addresses may have low two bits set. Clear them. */ |
| memaddr &=~ (bfd_vma) 3; |
| |
| status = (*info->read_memory_func) (memaddr, buffer, 4, info); |
| if (status != 0) |
| { |
| (*info->memory_error_func) (status, memaddr, info); |
| return -1; |
| } |
| |
| return m88kdis (memaddr, bfd_getb32 (buffer), info); |
| } |
| |
| /* |
| * Disassemble the instruction in `instruction'. |
| * `pc' should be the address of this instruction, it will be used to |
| * print the target address if this is a relative jump or call the |
| * disassembled instruction is written to `info'. |
| * |
| * The function returns the length of this instruction in bytes. |
| */ |
| |
| static int |
| m88kdis (pc, instruction, info) |
| bfd_vma pc; |
| unsigned long instruction; |
| struct disassemble_info *info; |
| { |
| static int ihashtab_initialized = 0; |
| unsigned int opcode; |
| INSTAB *entry_ptr; |
| int opmask; |
| unsigned int class; |
| |
| if (! ihashtab_initialized) |
| init_disasm (); |
| |
| /* Create the appropriate mask to isolate the opcode. */ |
| opmask = DEFMASK; |
| class = instruction & DEFMASK; |
| if ((class >= SFU0) && (class <= SFU7)) |
| { |
| if (instruction < SFU1) |
| opmask = CTRLMASK; |
| else |
| opmask = SFUMASK; |
| } |
| else if (class == RRR) |
| opmask = RRRMASK; |
| else if (class == RRI10) |
| opmask = RRI10MASK; |
| |
| /* Isolate the opcode. */ |
| opcode = instruction & opmask; |
| |
| /* Search the hash table with the isolated opcode. */ |
| for (entry_ptr = hashtable[opcode % HASHVAL]; |
| (entry_ptr != NULL) && (entry_ptr->opcode != opcode); |
| entry_ptr = entry_ptr->next) |
| ; |
| |
| if (entry_ptr == NULL) |
| (*info->fprintf_func) (info->stream, "word\t%08x", instruction); |
| else |
| { |
| (*info->fprintf_func) (info->stream, "%s", entry_ptr->mnemonic); |
| printop (info, &(entry_ptr->op1), instruction, pc, 1); |
| printop (info, &(entry_ptr->op2), instruction, pc, 0); |
| printop (info, &(entry_ptr->op3), instruction, pc, 0); |
| } |
| |
| return 4; |
| } |
| |
| /* |
| * Decode an Operand of an instruction. |
| * |
| * This function formats and writes an operand of an instruction to |
| * info based on the operand specification. When the `first' flag is |
| * set this is the first operand of an instruction. Undefined operand |
| * types cause a <dis error> message. |
| * |
| * Parameters: |
| * disassemble_info where the operand may be printed |
| * OPSPEC *opptr pointer to an operand specification |
| * UINT inst instruction from which operand is extracted |
| * UINT pc pc of instruction; used for pc-relative disp. |
| * int first flag which if nonzero indicates the first |
| * operand of an instruction |
| * |
| * The operand specified is extracted from the instruction and is |
| * written to buf in the format specified. The operand is preceded by |
| * a comma if it is not the first operand of an instruction and it is |
| * not a register indirect form. Registers are preceded by 'r' and |
| * hex values by '0x'. |
| */ |
| |
| static void |
| printop (info, opptr, inst, pc, first) |
| struct disassemble_info *info; |
| OPSPEC *opptr; |
| unsigned long inst; |
| bfd_vma pc; |
| int first; |
| { |
| int extracted_field; |
| char *cond_mask_sym; |
| |
| if (opptr->width == 0) |
| return; |
| |
| if (! first) |
| { |
| switch (opptr->type) |
| { |
| case REGSC: |
| case CONT: |
| break; |
| default: |
| (*info->fprintf_func) (info->stream, ","); |
| break; |
| } |
| } |
| |
| switch (opptr->type) |
| { |
| case CRREG: |
| (*info->fprintf_func) (info->stream, "cr%d", |
| UEXT (inst, opptr->offset, opptr->width)); |
| break; |
| |
| case FCRREG: |
| (*info->fprintf_func) (info->stream, "fcr%d", |
| UEXT (inst, opptr->offset, opptr->width)); |
| break; |
| |
| case REGSC: |
| (*info->fprintf_func) (info->stream, "[r%d]", |
| UEXT (inst, opptr->offset, opptr->width)); |
| break; |
| |
| case REG: |
| (*info->fprintf_func) (info->stream, "r%d", |
| UEXT (inst, opptr->offset, opptr->width)); |
| break; |
| |
| case XREG: |
| (*info->fprintf_func) (info->stream, "x%d", |
| UEXT (inst, opptr->offset, opptr->width)); |
| break; |
| |
| case HEX: |
| extracted_field = UEXT (inst, opptr->offset, opptr->width); |
| if (extracted_field == 0) |
| (*info->fprintf_func) (info->stream, "0"); |
| else |
| (*info->fprintf_func) (info->stream, "0x%02x", extracted_field); |
| break; |
| |
| case DEC: |
| extracted_field = UEXT (inst, opptr->offset, opptr->width); |
| (*info->fprintf_func) (info->stream, "%d", extracted_field); |
| break; |
| |
| case CONDMASK: |
| extracted_field = UEXT (inst, opptr->offset, opptr->width); |
| switch (extracted_field & 0x0f) |
| { |
| case 0x1: cond_mask_sym = "gt0"; break; |
| case 0x2: cond_mask_sym = "eq0"; break; |
| case 0x3: cond_mask_sym = "ge0"; break; |
| case 0xc: cond_mask_sym = "lt0"; break; |
| case 0xd: cond_mask_sym = "ne0"; break; |
| case 0xe: cond_mask_sym = "le0"; break; |
| default: cond_mask_sym = NULL; break; |
| } |
| if (cond_mask_sym != NULL) |
| (*info->fprintf_func) (info->stream, "%s", cond_mask_sym); |
| else |
| (*info->fprintf_func) (info->stream, "%x", extracted_field); |
| break; |
| |
| case PCREL: |
| (*info->print_address_func) |
| (pc + (4 * (SEXT (inst, opptr->offset, opptr->width))), |
| info); |
| break; |
| |
| case CONT: |
| (*info->fprintf_func) (info->stream, "%d,r%d", |
| UEXT (inst, opptr->offset, 5), |
| UEXT (inst, (opptr->offset) + 5, 5)); |
| break; |
| |
| case BF: |
| (*info->fprintf_func) (info->stream, "%d<%d>", |
| UEXT (inst, (opptr->offset) + 5, 5), |
| UEXT (inst, opptr->offset, 5)); |
| break; |
| |
| default: |
| /* xgettext:c-format */ |
| (*info->fprintf_func) (info->stream, _("# <dis error: %08x>"), inst); |
| } |
| } |
| |
| /* |
| * Initialize the disassembler instruction table. |
| * |
| * Initialize the hash table and instruction table for the |
| * disassembler. This should be called once before the first call to |
| * disasm(). |
| */ |
| |
| static void |
| init_disasm () |
| { |
| int i, size; |
| |
| for (i = 0; i < HASHVAL; i++) |
| hashtable[i] = NULL; |
| |
| size = sizeof (instructions) / sizeof (INSTAB); |
| for (i = 0; i < size; i++) |
| install (&instructions[i]); |
| } |
| |
| /* |
| * Insert an instruction into the disassembler table by hashing the |
| * opcode and inserting it into the linked list for that hash value. |
| */ |
| |
| static void |
| install (instptr) |
| INSTAB *instptr; |
| { |
| unsigned int i; |
| |
| i = (instptr->opcode) % HASHVAL; |
| instptr->next = hashtable[i]; |
| hashtable[i] = instptr; |
| } |