/* Single instruction disassembler for the Visium. Copyright (C) 2002-2020 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 "disassemble.h" #include "opcode/visium.h" #include <string.h> #include <stdlib.h> #include <stdio.h> #include <ctype.h> #include <setjmp.h> /* Maximum length of an instruction. */ #define MAXLEN 4 struct private { /* Points to first byte not fetched. */ bfd_byte *max_fetched; bfd_byte the_buffer[MAXLEN]; bfd_vma insn_start; jmp_buf bailout; }; /* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive) to ADDR (exclusive) are valid. Returns 1 for success, longjmps on error. */ #define FETCH_DATA(info, addr) \ ((addr) <= ((struct private *)(info->private_data))->max_fetched \ ? 1 : fetch_data ((info), (addr))) static int fetch_data (struct disassemble_info *info, bfd_byte * addr); static int fetch_data (struct disassemble_info *info, bfd_byte *addr) { int status; struct private *priv = (struct private *) info->private_data; bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer); status = (*info->read_memory_func) (start, priv->max_fetched, addr - priv->max_fetched, info); if (status != 0) { (*info->memory_error_func) (status, start, info); longjmp (priv->bailout, 1); } else priv->max_fetched = addr; return 1; } static char *size_names[] = { "?", "b", "w", "?", "l", "?", "?", "?" }; static char *cc_names[] = { "fa", "eq", "cs", "os", "ns", "ne", "cc", "oc", "nc", "ge", "gt", "hi", "le", "ls", "lt", "tr" }; /* Disassemble non-storage relative instructions. */ static int disassem_class0 (disassemble_info *info, unsigned int ins) { int opcode = (ins >> 21) & 0x000f; if (ins & CLASS0_UNUSED_MASK) goto illegal_opcode; switch (opcode) { case 0: /* BRR instruction. */ { unsigned cbf = (ins >> 27) & 0x000f; int displacement = ((ins & 0xffff) ^ 0x8000) - 0x8000; if (ins == 0) (*info->fprintf_func) (info->stream, "nop"); else (*info->fprintf_func) (info->stream, "brr %s,%+d", cc_names[cbf], displacement); } break; case 1: /* Illegal opcode. */ goto illegal_opcode; break; case 2: /* Illegal opcode. */ goto illegal_opcode; break; case 3: /* Illegal opcode. */ goto illegal_opcode; break; case 4: /* Illegal opcode. */ goto illegal_opcode; break; case 5: /* Illegal opcode. */ goto illegal_opcode; break; case 6: /* Illegal opcode. */ goto illegal_opcode; break; case 7: /* Illegal opcode. */ goto illegal_opcode; break; case 8: /* Illegal opcode. */ goto illegal_opcode; break; case 9: /* Illegal opcode. */ goto illegal_opcode; break; case 10: /* Illegal opcode. */ goto illegal_opcode; break; case 11: /* Illegal opcode. */ goto illegal_opcode; break; case 12: /* Illegal opcode. */ goto illegal_opcode; break; case 13: /* Illegal opcode. */ goto illegal_opcode; break; case 14: /* Illegal opcode. */ goto illegal_opcode; break; case 15: /* Illegal opcode. */ goto illegal_opcode; break; } return 0; illegal_opcode: return -1; } /* Disassemble non-storage register class instructions. */ static int disassem_class1 (disassemble_info *info, unsigned int ins) { int opcode = (ins >> 21) & 0xf; int source_a = (ins >> 16) & 0x1f; int source_b = (ins >> 4) & 0x1f; int indx = (ins >> 10) & 0x1f; int size = ins & 0x7; if (ins & CLASS1_UNUSED_MASK) goto illegal_opcode; switch (opcode) { case 0: /* Stop. */ (*info->fprintf_func) (info->stream, "stop %d,r%d", indx, source_a); break; case 1: /* BMI - Block Move Indirect. */ if (ins != BMI) goto illegal_opcode; (*info->fprintf_func) (info->stream, "bmi r1,r2,r3"); break; case 2: /* Illegal opcode. */ goto illegal_opcode; break; case 3: /* BMD - Block Move Direct. */ if (ins != BMD) goto illegal_opcode; (*info->fprintf_func) (info->stream, "bmd r1,r2,r3"); break; case 4: /* DSI - Disable Interrupts. */ if (ins != DSI) goto illegal_opcode; (*info->fprintf_func) (info->stream, "dsi"); break; case 5: /* ENI - Enable Interrupts. */ if (ins != ENI) goto illegal_opcode; (*info->fprintf_func) (info->stream, "eni"); break; case 6: /* Illegal opcode (was EUT). */ goto illegal_opcode; break; case 7: /* RFI - Return from Interrupt. */ if (ins != RFI) goto illegal_opcode; (*info->fprintf_func) (info->stream, "rfi"); break; case 8: /* Illegal opcode. */ goto illegal_opcode; break; case 9: /* Illegal opcode. */ goto illegal_opcode; break; case 10: /* Illegal opcode. */ goto illegal_opcode; break; case 11: /* Illegal opcode. */ goto illegal_opcode; break; case 12: /* Illegal opcode. */ goto illegal_opcode; break; case 13: goto illegal_opcode; break; case 14: goto illegal_opcode; break; case 15: if (ins & EAM_SELECT_MASK) { /* Extension arithmetic module write */ int fp_ins = (ins >> 27) & 0xf; if (size != 4) goto illegal_opcode; if (ins & FP_SELECT_MASK) { /* Which floating point instructions don't need a fsrcB register. */ const int no_fsrcb[16] = { 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0 }; if (no_fsrcb[fp_ins] && source_b) goto illegal_opcode; /* Check that none of the floating register register numbers is higher than 15. (If this is fload, then srcA is a general register. */ if (ins & ((1 << 14) | (1 << 8)) || (fp_ins && ins & (1 << 20))) goto illegal_opcode; switch (fp_ins) { case 0: (*info->fprintf_func) (info->stream, "fload f%d,r%d", indx, source_a); break; case 1: (*info->fprintf_func) (info->stream, "fadd f%d,f%d,f%d", indx, source_a, source_b); break; case 2: (*info->fprintf_func) (info->stream, "fsub f%d,f%d,f%d", indx, source_a, source_b); break; case 3: (*info->fprintf_func) (info->stream, "fmult f%d,f%d,f%d", indx, source_a, source_b); break; case 4: (*info->fprintf_func) (info->stream, "fdiv f%d,f%d,f%d", indx, source_a, source_b); break; case 5: (*info->fprintf_func) (info->stream, "fsqrt f%d,f%d", indx, source_a); break; case 6: (*info->fprintf_func) (info->stream, "fneg f%d,f%d", indx, source_a); break; case 7: (*info->fprintf_func) (info->stream, "fabs f%d,f%d", indx, source_a); break; case 8: (*info->fprintf_func) (info->stream, "ftoi f%d,f%d", indx, source_a); break; case 9: (*info->fprintf_func) (info->stream, "itof f%d,f%d", indx, source_a); break; case 12: (*info->fprintf_func) (info->stream, "fmove f%d,f%d", indx, source_a); break; default: (*info->fprintf_func) (info->stream, "fpinst %d,f%d,f%d,f%d", fp_ins, indx, source_a, source_b); break; } } else { /* Which EAM operations do not need a srcB register. */ const int no_srcb[32] = { 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (no_srcb[indx] && source_b) goto illegal_opcode; if (fp_ins) goto illegal_opcode; switch (indx) { case 0: (*info->fprintf_func) (info->stream, "mults r%d,r%d", source_a, source_b); break; case 1: (*info->fprintf_func) (info->stream, "multu r%d,r%d", source_a, source_b); break; case 2: (*info->fprintf_func) (info->stream, "divs r%d", source_a); break; case 3: (*info->fprintf_func) (info->stream, "divu r%d", source_a); break; case 4: (*info->fprintf_func) (info->stream, "writemd r%d,r%d", source_a, source_b); break; case 5: (*info->fprintf_func) (info->stream, "writemdc r%d", source_a); break; case 6: (*info->fprintf_func) (info->stream, "divds r%d", source_a); break; case 7: (*info->fprintf_func) (info->stream, "divdu r%d", source_a); break; case 9: (*info->fprintf_func) (info->stream, "asrd r%d", source_a); break; case 10: (*info->fprintf_func) (info->stream, "lsrd r%d", source_a); break; case 11: (*info->fprintf_func) (info->stream, "asld r%d", source_a); break; default: (*info->fprintf_func) (info->stream, "eamwrite %d,r%d,r%d", indx, source_a, source_b); break; } } } else { /* WRITE - write to memory. */ (*info->fprintf_func) (info->stream, "write.%s %d(r%d),r%d", size_names[size], indx, source_a, source_b); } break; } return 0; illegal_opcode: return -1; } /* Disassemble storage immediate class instructions. */ static int disassem_class2 (disassemble_info *info, unsigned int ins) { int opcode = (ins >> 21) & 0xf; int source_a = (ins >> 16) & 0x1f; unsigned immediate = ins & 0x0000ffff; if (ins & CC_MASK) goto illegal_opcode; switch (opcode) { case 0: /* ADDI instruction. */ (*info->fprintf_func) (info->stream, "addi r%d,%d", source_a, immediate); break; case 1: /* Illegal opcode. */ goto illegal_opcode; break; case 2: /* SUBI instruction. */ (*info->fprintf_func) (info->stream, "subi r%d,%d", source_a, immediate); break; case 3: /* Illegal opcode. */ goto illegal_opcode; break; case 4: /* MOVIL instruction. */ (*info->fprintf_func) (info->stream, "movil r%d,0x%04X", source_a, immediate); break; case 5: /* MOVIU instruction. */ (*info->fprintf_func) (info->stream, "moviu r%d,0x%04X", source_a, immediate); break; case 6: /* MOVIQ instruction. */ (*info->fprintf_func) (info->stream, "moviq r%d,%u", source_a, immediate); break; case 7: /* Illegal opcode. */ goto illegal_opcode; break; case 8: /* WRTL instruction. */ if (source_a != 0) goto illegal_opcode; (*info->fprintf_func) (info->stream, "wrtl 0x%04X", immediate); break; case 9: /* WRTU instruction. */ if (source_a != 0) goto illegal_opcode; (*info->fprintf_func) (info->stream, "wrtu 0x%04X", immediate); break; case 10: /* Illegal opcode. */ goto illegal_opcode; break; case 11: /* Illegal opcode. */ goto illegal_opcode; break; case 12: /* Illegal opcode. */ goto illegal_opcode; break; case 13: /* Illegal opcode. */ goto illegal_opcode; break; case 14: /* Illegal opcode. */ goto illegal_opcode; break; case 15: /* Illegal opcode. */ goto illegal_opcode; break; } return 0; illegal_opcode: return -1; } /* Disassemble storage register class instructions. */ static int disassem_class3 (disassemble_info *info, unsigned int ins) { int opcode = (ins >> 21) & 0xf; int source_b = (ins >> 4) & 0x1f; int source_a = (ins >> 16) & 0x1f; int size = ins & 0x7; int dest = (ins >> 10) & 0x1f; /* Those instructions that don't have a srcB register. */ const int no_srcb[16] = { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 }; /* These are instructions which can take an immediate srcB value. */ const int srcb_immed[16] = { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1 }; /* User opcodes should not provide a non-zero srcB register when none is required. Only a BRA or floating point instruction should have a non-zero condition code field. Only a WRITE or EAMWRITE (opcode 15) should select an EAM or floating point operation. Note that FP_SELECT_MASK is the same bit (bit 3) as the interrupt bit which distinguishes SYS1 from BRA and SYS2 from RFLAG. */ if ((no_srcb[opcode] && source_b) || (!srcb_immed[opcode] && ins & CLASS3_SOURCEB_IMMED) || (opcode != 12 && opcode != 15 && ins & CC_MASK) || (opcode != 15 && ins & (EAM_SELECT_MASK | FP_SELECT_MASK))) goto illegal_opcode; switch (opcode) { case 0: /* ADD instruction. */ (*info->fprintf_func) (info->stream, "add.%s r%d,r%d,r%d", size_names[size], dest, source_a, source_b); break; case 1: /* ADC instruction. */ (*info->fprintf_func) (info->stream, "adc.%s r%d,r%d,r%d", size_names[size], dest, source_a, source_b); break; case 2: /* SUB instruction. */ if (dest == 0) (*info->fprintf_func) (info->stream, "cmp.%s r%d,r%d", size_names[size], source_a, source_b); else (*info->fprintf_func) (info->stream, "sub.%s r%d,r%d,r%d", size_names[size], dest, source_a, source_b); break; case 3: /* SUBC instruction. */ if (dest == 0) (*info->fprintf_func) (info->stream, "cmpc.%s r%d,r%d", size_names[size], source_a, source_b); else (*info->fprintf_func) (info->stream, "subc.%s r%d,r%d,r%d", size_names[size], dest, source_a, source_b); break; case 4: /* EXTW instruction. */ if (size == 1) goto illegal_opcode; (*info->fprintf_func) (info->stream, "extw.%s r%d,r%d", size_names[size], dest, source_a); break; case 5: /* ASR instruction. */ if (ins & CLASS3_SOURCEB_IMMED) (*info->fprintf_func) (info->stream, "asr.%s r%d,r%d,%d", size_names[size], dest, source_a, source_b); else (*info->fprintf_func) (info->stream, "asr.%s r%d,r%d,r%d", size_names[size], dest, source_a, source_b); break; case 6: /* LSR instruction. */ if (ins & CLASS3_SOURCEB_IMMED) (*info->fprintf_func) (info->stream, "lsr.%s r%d,r%d,%d", size_names[size], dest, source_a, source_b); else (*info->fprintf_func) (info->stream, "lsr.%s r%d,r%d,r%d", size_names[size], dest, source_a, source_b); break; case 7: /* ASL instruction. */ if (ins & CLASS3_SOURCEB_IMMED) (*info->fprintf_func) (info->stream, "asl.%s r%d,r%d,%d", size_names[size], dest, source_a, source_b); else (*info->fprintf_func) (info->stream, "asl.%s r%d,r%d,r%d", size_names[size], dest, source_a, source_b); break; case 8: /* XOR instruction. */ (*info->fprintf_func) (info->stream, "xor.%s r%d,r%d,r%d", size_names[size], dest, source_a, source_b); break; case 9: /* OR instruction. */ if (source_b == 0) (*info->fprintf_func) (info->stream, "move.%s r%d,r%d", size_names[size], dest, source_a); else (*info->fprintf_func) (info->stream, "or.%s r%d,r%d,r%d", size_names[size], dest, source_a, source_b); break; case 10: /* AND instruction. */ (*info->fprintf_func) (info->stream, "and.%s r%d,r%d,r%d", size_names[size], dest, source_a, source_b); break; case 11: /* NOT instruction. */ (*info->fprintf_func) (info->stream, "not.%s r%d,r%d", size_names[size], dest, source_a); break; case 12: /* BRA instruction. */ { unsigned cbf = (ins >> 27) & 0x000f; if (size != 4) goto illegal_opcode; (*info->fprintf_func) (info->stream, "bra %s,r%d,r%d", cc_names[cbf], source_a, dest); } break; case 13: /* RFLAG instruction. */ if (source_a || size != 4) goto illegal_opcode; (*info->fprintf_func) (info->stream, "rflag r%d", dest); break; case 14: /* EXTB instruction. */ (*info->fprintf_func) (info->stream, "extb.%s r%d,r%d", size_names[size], dest, source_a); break; case 15: if (!(ins & CLASS3_SOURCEB_IMMED)) goto illegal_opcode; if (ins & EAM_SELECT_MASK) { /* Extension arithmetic module read. */ int fp_ins = (ins >> 27) & 0xf; if (size != 4) goto illegal_opcode; if (ins & FP_SELECT_MASK) { /* Check fsrcA <= 15 and fsrcB <= 15. */ if (ins & ((1 << 20) | (1 << 8))) goto illegal_opcode; switch (fp_ins) { case 0: if (source_b) goto illegal_opcode; (*info->fprintf_func) (info->stream, "fstore r%d,f%d", dest, source_a); break; case 10: (*info->fprintf_func) (info->stream, "fcmp r%d,f%d,f%d", dest, source_a, source_b); break; case 11: (*info->fprintf_func) (info->stream, "fcmpe r%d,f%d,f%d", dest, source_a, source_b); break; default: (*info->fprintf_func) (info->stream, "fpuread %d,r%d,f%d,f%d", fp_ins, dest, source_a, source_b); break; } } else { if (fp_ins || source_a) goto illegal_opcode; switch (source_b) { case 0: (*info->fprintf_func) (info->stream, "readmda r%d", dest); break; case 1: (*info->fprintf_func) (info->stream, "readmdb r%d", dest); break; case 2: (*info->fprintf_func) (info->stream, "readmdc r%d", dest); break; default: (*info->fprintf_func) (info->stream, "eamread r%d,%d", dest, source_b); break; } } } else { if (ins & FP_SELECT_MASK) goto illegal_opcode; /* READ instruction. */ (*info->fprintf_func) (info->stream, "read.%s r%d,%d(r%d)", size_names[size], dest, source_b, source_a); } break; } return 0; illegal_opcode: return -1; } /* Print the visium instruction at address addr in debugged memory, on info->stream. Return length of the instruction, in bytes. */ int print_insn_visium (bfd_vma addr, disassemble_info *info) { unsigned ins; unsigned p1, p2; int ans; int i; /* Stuff copied from m68k-dis.c. */ struct private priv; bfd_byte *buffer = priv.the_buffer; info->private_data = (PTR) & priv; priv.max_fetched = priv.the_buffer; priv.insn_start = addr; if (setjmp (priv.bailout) != 0) { /* Error return. */ return -1; } /* We do return this info. */ info->insn_info_valid = 1; /* Assume non branch insn. */ info->insn_type = dis_nonbranch; /* Assume no delay. */ info->branch_delay_insns = 0; /* Assume no target known. */ info->target = 0; /* Get 32-bit instruction word. */ FETCH_DATA (info, buffer + 4); ins = (unsigned) buffer[0] << 24; ins |= buffer[1] << 16; ins |= buffer[2] << 8; ins |= buffer[3]; ans = 0; p1 = buffer[0] ^ buffer[1] ^ buffer[2] ^ buffer[3]; p2 = 0; for (i = 0; i < 8; i++) { p2 += p1 & 1; p1 >>= 1; } /* Decode the instruction. */ if (p2 & 1) ans = -1; else { switch ((ins >> 25) & 0x3) { case 0: ans = disassem_class0 (info, ins); break; case 1: ans = disassem_class1 (info, ins); break; case 2: ans = disassem_class2 (info, ins); break; case 3: ans = disassem_class3 (info, ins); break; } } if (ans != 0) (*info->fprintf_func) (info->stream, "err"); /* Return number of bytes consumed (always 4 for the Visium). */ return 4; }