123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- /*
- * very basic mips disassembler for MIPS32/MIPS64 Release 2, only for picodrive
- * Copyright (C) 2019 kub
- *
- * This work is licensed under the terms of MAME license.
- * See COPYING file in the top-level directory.
- */
- // unimplemented insns: SYNC, CACHE, PREF,
- // MOV[FT], LWC*/LDC*, SWC*/SDC*, and all of COP* (fpu, mmu, irq, exc, ...)
- // unimplemented variants of insns: EHB, SSNOP (both SLL zero), JALR.HB, JR.HB
- // however, it's certainly good enough for anything picodrive DRC throws at it.
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <string.h>
- #include "dismips.h"
- static char *const register_names[32] = {
- "$zero",
- "$at",
- "$v0",
- "$v1",
- "$a0",
- "$a1",
- "$a2",
- "$a3",
- "$t0",
- "$t1",
- "$t2",
- "$t3",
- "$t4",
- "$t5",
- "$t6",
- "$t7",
- "$s0",
- "$s1",
- "$s2",
- "$s3",
- "$s4",
- "$s5",
- "$s6",
- "$s7",
- "$t8",
- "$t9",
- "$k0",
- "$k1",
- "$gp",
- "$sp",
- "$fp",
- "$ra"
- };
- enum insn_type {
- REG_DST, REG_ST, REG_TD, // 3, 2, or 1 regs
- REG_DS, REG_DT, REG_D, REG_S,
- S_IMM_DT, // 2 regs with shift amount
- F_IMM_TS, // 2 regs with bitfield spec
- B_IMM_S, B_IMM_TS, // pc-relative branches with 1 or 2 regs
- T_IMM_S, // trap insns with immediate
- J_IMM, // region-relative jump
- A_IMM_TS, // arithmetic immediate with 2 regs
- L_IMM_T, L_IMM_TS, // logical immediate with 1 or 2 regs
- M_IMM_TS, M_IMM_S, // memory indexed with 2 regs
- SB_CODE, // code parameter (syscall, break, sdbbp)
- SR_BIT = 0x80 // shift right with R-bit
- };
- struct insn {
- unsigned char op;
- enum insn_type type;
- char *name;
- };
- // ATTN: these array MUST be sorted by op (decode relies on it)
- // instructions with opcode SPECIAL (R-type)
- #define OP_SPECIAL 0x00
- static const struct insn special_insns[] = {
- {0x00, S_IMM_DT, "sll"},
- // {0x01, , "movf\0movt"},
- {0x02, S_IMM_DT|SR_BIT, "srl\0rotr"},
- {0x03, S_IMM_DT, "sra"},
- {0x04, REG_DST, "sllv"},
- {0x06, REG_DST|SR_BIT, "srlv\0rotrv"},
- {0x07, REG_DST, "srav"},
- {0x08, REG_S, "jr"},
- {0x09, REG_DS, "jalr"},
- {0x0a, REG_DST, "movz"},
- {0x0b, REG_DST, "movn"},
- {0x0c, SB_CODE, "syscall"},
- {0x0d, SB_CODE, "break"},
- // {0x0f, , "sync"},
- {0x10, REG_D, "mfhi"},
- {0x11, REG_S, "mthi"},
- {0x12, REG_D, "mflo"},
- {0x13, REG_S, "mtlo"},
- {0x14, REG_DST, "dsllv"},
- {0x16, REG_DST|SR_BIT, "dsrlv\0drotrv"},
- {0x17, REG_DST, "dsrav"},
- {0x18, REG_ST, "mult"},
- {0x19, REG_ST, "multu"},
- {0x1A, REG_ST, "div"},
- {0x1B, REG_ST, "divu"},
- {0x1C, REG_ST, "dmult"},
- {0x1D, REG_ST, "dmultu"},
- {0x1E, REG_ST, "ddiv"},
- {0x1F, REG_ST, "ddivu"},
- {0x20, REG_DST, "add"},
- {0x21, REG_DST, "addu"},
- {0x22, REG_DST, "sub"},
- {0x23, REG_DST, "subu"},
- {0x24, REG_DST, "and"},
- {0x25, REG_DST, "or"},
- {0x26, REG_DST, "xor"},
- {0x27, REG_DST, "nor"},
- {0x2A, REG_DST, "slt"},
- {0x2B, REG_DST, "sltu"},
- {0x2C, REG_DST, "dadd"},
- {0x2D, REG_DST, "daddu"},
- {0x2E, REG_DST, "dsub"},
- {0x2F, REG_DST, "dsubu"},
- {0x30, REG_ST, "tge" },
- {0x31, REG_ST, "tgeu" },
- {0x32, REG_ST, "tlt" },
- {0x33, REG_ST, "tltu" },
- {0x34, REG_ST, "teq" },
- {0x36, REG_ST, "tne" },
- {0x38, S_IMM_DT, "dsll"},
- {0x3A, S_IMM_DT|SR_BIT, "dsrl\0drotrv"},
- {0x3B, S_IMM_DT, "dsra"},
- {0x3C, S_IMM_DT, "dsll32"},
- {0x3E, S_IMM_DT|SR_BIT, "dsrl32\0drotr32"},
- {0x3F, S_IMM_DT, "dsra32"},
- };
- // instructions with opcode SPECIAL2 (R-type)
- #define OP_SPECIAL2 0x1C
- static const struct insn special2_insns[] = {
- {0x00, REG_ST, "madd" },
- {0x01, REG_ST, "maddu" },
- {0x02, REG_ST, "mul" },
- {0x04, REG_ST, "msub" },
- {0x05, REG_ST, "msubu" },
- {0x20, REG_DS, "clz" },
- {0x21, REG_DS, "clo" },
- {0x24, REG_DS, "dclz" },
- {0x25, REG_DS, "dclo" },
- {0x37, SB_CODE, "sdbbp" },
- };
- // instructions with opcode SPECIAL3 (R-type)
- #define OP_SPECIAL3 0x1F
- static const struct insn special3_insns[] = {
- {0x00, F_IMM_TS, "ext" },
- {0x01, F_IMM_TS, "dextm" },
- {0x02, F_IMM_TS, "dextu" },
- {0x03, F_IMM_TS, "dext" },
- {0x04, F_IMM_TS, "ins" },
- {0x05, F_IMM_TS, "dinsm" },
- {0x06, F_IMM_TS, "dinsu" },
- {0x07, F_IMM_TS, "dins" },
- {0x3b, REG_TD, "rdhwr" },
- };
- // instruction with opcode SPECIAL3 and function *BSHFL
- #define FN_BSHFL 0x20
- static const struct insn bshfl_insns[] = {
- {0x02, REG_DT, "wsbh" },
- {0x10, REG_DT, "seb" },
- {0x18, REG_DT, "seh" },
- };
- #define FN_DBSHFL 0x24
- static const struct insn dbshfl_insns[] = {
- {0x02, REG_DT, "dsbh" },
- {0x05, REG_DT, "dshd" },
- };
- // instructions with opcode REGIMM (I-type)
- #define OP_REGIMM 0x01
- static const struct insn regimm_insns[] = {
- {0x00, B_IMM_S, "bltz"},
- {0x01, B_IMM_S, "bgez"},
- {0x02, B_IMM_S, "bltzl"},
- {0x03, B_IMM_S, "bgezl"},
- {0x08, T_IMM_S, "tgei"},
- {0x09, T_IMM_S, "tgeiu"},
- {0x0a, T_IMM_S, "tlti"},
- {0x0b, T_IMM_S, "tltiu"},
- {0x0c, T_IMM_S, "teqi"},
- {0x0e, T_IMM_S, "tnei"},
- {0x10, B_IMM_S, "bltzal"},
- {0x11, B_IMM_S, "bgezal"},
- {0x12, B_IMM_S, "bltzall"},
- {0x13, B_IMM_S, "bgezall"},
- {0x1f, M_IMM_S, "synci" },
- };
- // instructions with other opcodes (I-type)
- static const struct insn immediate_insns[] = {
- {0x02, J_IMM, "j"},
- {0x03, J_IMM, "jal"},
- {0x04, B_IMM_TS, "beq"},
- {0x05, B_IMM_TS, "bne"},
- {0x06, B_IMM_S, "blez"},
- {0x07, B_IMM_S, "bgtz"},
- {0x08, A_IMM_TS, "addi"},
- {0x09, A_IMM_TS, "addiu"},
- {0x0A, A_IMM_TS, "slti"},
- {0x0B, A_IMM_TS, "sltiu"},
- {0x0C, L_IMM_TS, "andi"},
- {0x0D, L_IMM_TS, "ori"},
- {0x0E, L_IMM_TS, "xori"},
- {0x0F, L_IMM_T, "lui"},
- {0x14, B_IMM_TS, "beql"},
- {0x15, B_IMM_TS, "bnel"},
- {0x16, B_IMM_S, "blezl"},
- {0x17, B_IMM_S, "bgtzl"},
- {0x18, A_IMM_TS, "daddi"},
- {0x19, A_IMM_TS, "daddiu"},
- {0x1A, M_IMM_TS, "ldl"},
- {0x1B, M_IMM_TS, "ldr"},
- {0x20, M_IMM_TS, "lb"},
- {0x21, M_IMM_TS, "lh"},
- {0x22, M_IMM_TS, "lwl"},
- {0x23, M_IMM_TS, "lw"},
- {0x24, M_IMM_TS, "lbu"},
- {0x25, M_IMM_TS, "lhu"},
- {0x26, M_IMM_TS, "lwr"},
- {0x27, M_IMM_TS, "lwu"},
- {0x28, M_IMM_TS, "sb"},
- {0x29, M_IMM_TS, "sh"},
- {0x2A, M_IMM_TS, "swl"},
- {0x2B, M_IMM_TS, "sw"},
- {0x2C, M_IMM_TS, "sdl"},
- {0x2D, M_IMM_TS, "sdr"},
- {0x2E, M_IMM_TS, "swr"},
- // {0x2F, , "cache"},
- {0x30, M_IMM_TS, "ll"},
- // {0x31, , "lwc1"},
- // {0x32, , "lwc2"},
- // {0x33, , "pref"},
- {0x34, M_IMM_TS, "lld"},
- // {0x35, , "ldc1"},
- // {0x36, , "ldc2"},
- {0x37, M_IMM_TS, "ld"},
- {0x38, M_IMM_TS, "sc"},
- // {0x39, , "swc1"},
- // {0x3A, , "swc2"},
- {0x3C, M_IMM_TS, "scd"},
- // {0x3D, , "sdc1"},
- // {0x3E, , "sdc2"},
- {0x3F, M_IMM_TS, "sd"},
- };
- #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*a))
- // find instruction description for insn
- static const struct insn *decode_insn(uint32_t insn)
- {
- uint32_t op = insn >> 26;
- const struct insn *pi;
- int l = 0, r = 0;
- if (op == OP_SPECIAL) {
- op = insn & 0x3f;
- pi = special_insns;
- r = ARRAY_SIZE(special_insns)-1;
- } else if (op == OP_SPECIAL2) {
- op = insn & 0x3f;
- pi = special2_insns;
- r = ARRAY_SIZE(special2_insns)-1;
- } else if (op == OP_SPECIAL3) {
- op = insn & 0x3f;
- if (op == FN_BSHFL) {
- op = (insn >> 6) & 0x1f;
- pi = bshfl_insns;
- r = ARRAY_SIZE(bshfl_insns)-1;
- } else if (op == FN_DBSHFL) {
- op = (insn >> 6) & 0x1f;
- pi = dbshfl_insns;
- r = ARRAY_SIZE(dbshfl_insns)-1;
- } else {
- pi = special3_insns;
- r = ARRAY_SIZE(special3_insns)-1;
- }
- } else if (op == OP_REGIMM) {
- op = (insn>>16) & 0x1f;
- pi = regimm_insns;
- r = ARRAY_SIZE(regimm_insns)-1;
- } else {
- pi = immediate_insns;
- r = ARRAY_SIZE(immediate_insns)-1;
- }
- while (l <= r) {
- int m = (l+r) / 2;
- if (pi[m].op == op)
- return pi+m;
- else if (pi[m].op < op)
- l = m+1;
- else
- r = m-1;
- }
- return NULL;
- }
- // calculate target for pc-relative branches
- static unsigned long b_target(unsigned long pc, uint32_t insn)
- {
- return pc + 4 + (int16_t)insn * 4;
- }
- // calculate target for region-relative branches
- static unsigned long j_target(unsigned long pc, uint32_t insn)
- {
- return (pc & ~0x0fffffffL) | ((insn & 0x03ffffff) << 2);
- }
- // main disassembler function
- int dismips(uintptr_t pc, uint32_t insn, char *buf, size_t buflen, unsigned long *sym)
- {
- const struct insn *pi = decode_insn(insn);
- char *rs = register_names[(insn >> 21) & 0x1f];
- char *rt = register_names[(insn >> 16) & 0x1f];
- char *rd = register_names[(insn >> 11) & 0x1f];
- int sa = (insn >> 6) & 0x1f, sb = (insn >> 11) & 0x1f;
- int imm = (int16_t) insn;
- *sym = 0;
- if (pi == NULL) {
- snprintf(buf, buflen, "0x%08lx", (unsigned long)insn);
- return 0;
- }
- switch (pi->type & ~SR_BIT) {
- case REG_DST:
- if ((insn & 0x3f) == 0x25 /*OR*/ && (insn & 0x1f0000) == 0 /*zero*/)
- snprintf(buf, buflen, "move %s, %s", rd, rs);
- else if ((pi->type & SR_BIT) && (insn & (1<<6)))
- snprintf(buf, buflen, "%s %s, %s, %s", pi->name+strlen(pi->name)+1, rd, rs, rt);
- else
- snprintf(buf, buflen, "%s %s, %s, %s", pi->name, rd, rs, rt);
- break;
- case REG_ST:
- if ((insn & 0x38) == 0x30 /*T..*/)
- snprintf(buf, buflen, "%s %s, %s (code %d)", pi->name, rs, rt, (int)(insn>>6) & 0x3ff);
- else
- snprintf(buf, buflen, "%s %s, %s", pi->name, rs, rt);
- break;
- case REG_TD:
- snprintf(buf, buflen, "%s %s, %s", pi->name, rt, rd);
- break;
- case REG_DS:
- snprintf(buf, buflen, "%s %s, %s", pi->name, rd, rs);
- break;
- case REG_DT:
- snprintf(buf, buflen, "%s %s, %s", pi->name, rd, rt);
- break;
- case REG_D:
- snprintf(buf, buflen, "%s %s", pi->name, rd);
- break;
- case REG_S:
- snprintf(buf, buflen, "%s %s", pi->name, rs);
- break;
- case S_IMM_DT:
- if (insn == 0x00000000)
- snprintf(buf, buflen, "nop");
- else if ((pi->type & SR_BIT) && (insn & (1<<21)))
- snprintf(buf, buflen, "%s %s, %s, %d", pi->name+strlen(pi->name)+1, rd, rt, sa);
- else
- snprintf(buf, buflen, "%s %s, %s, %d", pi->name, rd, rt, sa);
- break;
- //dext: pos,size-1 dextm: pos,size-33 dextu: pos-32,size-1
- //dins: pos,pos+size-1 dinsm: pos,pos+size-33 dinsu: pos-32,pos+size-33
- case F_IMM_TS:
- if (insn & 0x01) sb+=32; // ...m
- if (insn & 0x02) sa+=32; // ...u
- if (insn & 0x04) sb-=sa; // ins
- snprintf(buf, buflen, "%s %s, %s, %d, %d", pi->name, rt, rs, sa, sb+1);
- break;
- case B_IMM_S:
- *sym = b_target(pc, insn);
- snprintf(buf, buflen, "%s %s, 0x%lx", pi->name, rs, *sym);
- break;
- case B_IMM_TS:
- *sym = b_target(pc, insn);
- snprintf(buf, buflen, "%s %s, %s, 0x%lx", pi->name, rs, rt, *sym);
- break;
- case J_IMM:
- *sym = j_target(pc, insn);
- snprintf(buf, buflen, "%s 0x%lx", pi->name, *sym);
- break;
- case A_IMM_TS:
- if (abs(imm) < 1000)
- snprintf(buf, buflen, "%s %s, %s, %d", pi->name, rt, rs, imm);
- else
- snprintf(buf, buflen, "%s %s, %s, 0x%x", pi->name, rt, rs, imm);
- break;
- case L_IMM_T:
- snprintf(buf, buflen, "%s %s, 0x%x", pi->name, rt, (uint16_t)imm);
- break;
- case L_IMM_TS:
- if ((insn >> 26) == 0x34 /*ORI*/ && (insn & 0x03e00000) == 0 /*zero*/)
- snprintf(buf, buflen, "li %s, 0x%x", rt, (uint16_t)imm);
- else
- snprintf(buf, buflen, "%s %s, %s, 0x%x", pi->name, rt, rs, (uint16_t)imm);
- break;
- case M_IMM_TS:
- snprintf(buf, buflen, "%s %s, %d(%s)", pi->name, rt, imm, rs);
- break;
- case M_IMM_S:
- snprintf(buf, buflen, "%s %d(%s)", pi->name, imm, rs);
- break;
- case T_IMM_S:
- snprintf(buf, buflen, "%s %s, %d", pi->name, rs, imm);
- break;
- case SB_CODE:
- snprintf(buf, buflen, "%s %ld", pi->name, (unsigned long)(insn>>6) & 0xfffff);
- break;
- }
- return 1;
- }
|