/* Print DEC PDP-11 instructions. Copyright 2001, 2002 Free Software Foundation, Inc. This file 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/pdp11.h" #define AFTER_INSTRUCTION "\t" #define OPERAND_SEPARATOR ", " #define JUMP 0x1000 /* flag that this operand is used in a jump */ #define FPRINTF (*info->fprintf_func) #define F info->stream /* sign-extend a 16-bit number in an int */ #define SIGN_BITS (8 * sizeof (int) - 16) #define sign_extend(x) (((x) << SIGN_BITS) >> SIGN_BITS) static int read_word PARAMS ((bfd_vma memaddr, int *word, disassemble_info *info)); static void print_signed_octal PARAMS ((int n, disassemble_info *info)); static void print_reg PARAMS ((int reg, disassemble_info *info)); static void print_freg PARAMS ((int freg, disassemble_info *info)); static int print_operand PARAMS ((bfd_vma *memaddr, int code, disassemble_info *info)); static int print_foperand PARAMS ((bfd_vma *memaddr, int code, disassemble_info *info)); int print_insn_pdp11 PARAMS ((bfd_vma memaddr, disassemble_info *info)); static int read_word (memaddr, word, info) bfd_vma memaddr; int *word; disassemble_info *info; { int status; bfd_byte x[2]; status = (*info->read_memory_func) (memaddr, x, 2, info); if (status != 0) return -1; *word = x[1] << 8 | x[0]; return 0; } static void print_signed_octal (n, info) int n; disassemble_info *info; { if (n < 0) FPRINTF (F, "-%o", -n); else FPRINTF (F, "%o", n); } static void print_reg (reg, info) int reg; disassemble_info *info; { /* mask off the addressing mode, if any */ reg &= 7; switch (reg) { case 0: case 1: case 2: case 3: case 4: case 5: FPRINTF (F, "r%d", reg); break; case 6: FPRINTF (F, "sp"); break; case 7: FPRINTF (F, "pc"); break; default: ; /* error */ } } static void print_freg (freg, info) int freg; disassemble_info *info; { FPRINTF (F, "fr%d", freg); } static int print_operand (memaddr, code, info) bfd_vma *memaddr; int code; disassemble_info *info; { int mode = (code >> 3) & 7; int reg = code & 7; int disp; switch (mode) { case 0: print_reg (reg, info); break; case 1: FPRINTF (F, "("); print_reg (reg, info); FPRINTF (F, ")"); break; case 2: if (reg == 7) { int data; if (read_word (*memaddr, &data, info) < 0) return -1; FPRINTF (F, "$"); print_signed_octal (sign_extend (data), info); *memaddr += 2; } else { FPRINTF (F, "("); print_reg (reg, info); FPRINTF (F, ")+"); } break; case 3: if (reg == 7) { int address; if (read_word (*memaddr, &address, info) < 0) return -1; FPRINTF (F, "*$%o", address); *memaddr += 2; } else { FPRINTF (F, "*("); print_reg (reg, info); FPRINTF (F, ")+"); } break; case 4: FPRINTF (F, "-("); print_reg (reg, info); FPRINTF (F, ")"); break; case 5: FPRINTF (F, "*-("); print_reg (reg, info); FPRINTF (F, ")"); break; case 6: case 7: if (read_word (*memaddr, &disp, info) < 0) return -1; *memaddr += 2; if (reg == 7) { bfd_vma address = *memaddr + sign_extend (disp); if (mode == 7) FPRINTF (F, "*"); if (!(code & JUMP)) FPRINTF (F, "$"); (*info->print_address_func) (address, info); } else { if (mode == 7) FPRINTF (F, "*"); print_signed_octal (sign_extend (disp), info); FPRINTF (F, "("); print_reg (reg, info); FPRINTF (F, ")"); } break; } return 0; } static int print_foperand (memaddr, code, info) bfd_vma *memaddr; int code; disassemble_info *info; { int mode = (code >> 3) & 7; int reg = code & 7; if (mode == 0) print_freg (reg, info); else return print_operand (memaddr, code, info); return 0; } /* Print the PDP-11 instruction at address MEMADDR in debugged memory, on INFO->STREAM. Returns length of the instruction, in bytes. */ int print_insn_pdp11 (memaddr, info) bfd_vma memaddr; disassemble_info *info; { bfd_vma start_memaddr = memaddr; int opcode; int src, dst; int i; info->bytes_per_line = 6; info->bytes_per_chunk = 2; info->display_endian = BFD_ENDIAN_LITTLE; if (read_word (memaddr, &opcode, info) != 0) return -1; memaddr += 2; src = (opcode >> 6) & 0x3f; dst = opcode & 0x3f; for (i = 0; i < pdp11_num_opcodes; i++) { #define OP pdp11_opcodes[i] if ((opcode & OP.mask) == OP.opcode) switch (OP.type) { case PDP11_OPCODE_NO_OPS: FPRINTF (F, OP.name); goto done; case PDP11_OPCODE_REG: FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); print_reg (dst, info); goto done; case PDP11_OPCODE_OP: FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); if (strcmp (OP.name, "jmp") == 0) dst |= JUMP; if (print_operand (&memaddr, dst, info) < 0) return -1; goto done; case PDP11_OPCODE_FOP: FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); if (strcmp (OP.name, "jmp") == 0) dst |= JUMP; if (print_foperand (&memaddr, dst, info) < 0) return -1; goto done; case PDP11_OPCODE_REG_OP: FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); print_reg (src, info); FPRINTF (F, OPERAND_SEPARATOR); if (strcmp (OP.name, "jsr") == 0) dst |= JUMP; if (print_operand (&memaddr, dst, info) < 0) return -1; goto done; case PDP11_OPCODE_REG_OP_REV: FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); if (print_operand (&memaddr, dst, info) < 0) return -1; FPRINTF (F, OPERAND_SEPARATOR); print_reg (src, info); goto done; case PDP11_OPCODE_AC_FOP: { int ac = (opcode & 0xe0) >> 6; FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); print_freg (ac, info); FPRINTF (F, OPERAND_SEPARATOR); if (print_foperand (&memaddr, dst, info) < 0) return -1; goto done; } case PDP11_OPCODE_FOP_AC: { int ac = (opcode & 0xe0) >> 6; FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); if (print_foperand (&memaddr, dst, info) < 0) return -1; FPRINTF (F, OPERAND_SEPARATOR); print_freg (ac, info); goto done; } case PDP11_OPCODE_AC_OP: { int ac = (opcode & 0xe0) >> 6; FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); print_freg (ac, info); FPRINTF (F, OPERAND_SEPARATOR); if (print_operand (&memaddr, dst, info) < 0) return -1; goto done; } case PDP11_OPCODE_OP_AC: { int ac = (opcode & 0xe0) >> 6; FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); if (print_operand (&memaddr, dst, info) < 0) return -1; FPRINTF (F, OPERAND_SEPARATOR); print_freg (ac, info); goto done; } case PDP11_OPCODE_OP_OP: FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); if (print_operand (&memaddr, src, info) < 0) return -1; FPRINTF (F, OPERAND_SEPARATOR); if (print_operand (&memaddr, dst, info) < 0) return -1; goto done; case PDP11_OPCODE_DISPL: { int displ = (opcode & 0xff) << 8; bfd_vma address = memaddr + (sign_extend (displ) >> 7); FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); (*info->print_address_func) (address, info); goto done; } case PDP11_OPCODE_REG_DISPL: { int displ = (opcode & 0x3f) << 10; bfd_vma address = memaddr - (displ >> 9); FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); print_reg (src, info); FPRINTF (F, OPERAND_SEPARATOR); (*info->print_address_func) (address, info); goto done; } case PDP11_OPCODE_IMM8: { int code = opcode & 0xff; FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); FPRINTF (F, "%o", code); goto done; } case PDP11_OPCODE_IMM6: { int code = opcode & 0x3f; FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); FPRINTF (F, "%o", code); goto done; } case PDP11_OPCODE_IMM3: { int code = opcode & 7; FPRINTF (F, OP.name); FPRINTF (F, AFTER_INSTRUCTION); FPRINTF (F, "%o", code); goto done; } case PDP11_OPCODE_ILLEGAL: { FPRINTF (F, ".word"); FPRINTF (F, AFTER_INSTRUCTION); FPRINTF (F, "%o", opcode); goto done; } default: /* TODO: is this a proper way of signalling an error? */ FPRINTF (F, ""); return -1; } #undef OP } done: return memaddr - start_memaddr; }