/* +----------------------------------------------------------------+ */ /* | Copyright (c) 1994 Stanford University. | */ /* | All Rights Reserved. | */ /* | | */ /* | This software is distributed with *ABSOLUTELY NO SUPPORT* | */ /* | and *NO WARRANTY*. Use or reproduction of this code for | */ /* | commerical gains is strictly prohibited. Otherwise, you | */ /* | are given permission to use or modify this code as long | */ /* | as you do not remove this notice. | */ /* +----------------------------------------------------------------+ */ /* --------------------------------------------------- */ /* | Copyright (c) 1986 MIPS Computer Systems, Inc. | */ /* | All Rights Reserved. | */ /* --------------------------------------------------- */ /* * Copyright 1985 by MIPS Computer Systems, Inc. */ /* TORCH disassembler */ /* derived from MIPS instruction dissassembler by PGL October 91 */ #include #include #include "mipsdis.h" #define false 0 #define true 1 typedef int boolean; char *strcat(); #define ZERO 0 #define COMPILER_NAMES tdis_reg_names[0] #define HARDWARE_NAMES tdis_reg_names[1] #define ASSEMBLER_NAMES tdis_reg_names[2] #define DIS_REG_NAMES(x) tdis_reg_names[x] union extension_byte { unsigned char byte; struct { /* extension byte bits in little-endian order */ #if (defined __MIPSEB || defined _MIPSEB || defined MIPSEB) unsigned reserved:1; unsigned dyn_nop:1; unsigned rs_boost:2; unsigned rt_boost:2; unsigned rd_boost:2; #elif (defined __MIPSEL || defined _MIPSEL || defined MIPSEL) unsigned rd_boost:2; unsigned rt_boost:2; unsigned rs_boost:2; unsigned dyn_nop:1; unsigned reserved:1; #endif /*MIPSEB*/ } e; }; #define blez_op_T blez_op #define blez_op_N (blez_op | 0x10) #define bgtz_op_T bgtz_op #define bgtz_op_N (bgtz_op | 0x10) #define beq_op_T beq_op #define beq_op_N (beq_op | 0x10) #define bne_op_T bne_op #define bne_op_N (bne_op | 0x10) #define bc_op_T bc_op #define bc_op_N (bc_op | 0x02) static char *op_name[64] = { /* 0 */ "special", "regimm","j", "jal", "beq", "bne", "blez", "bgtz", /* 8 */ "addi", "addiu","slti", "sltiu","andi", "ori", "xori", "lui", /*16 */ "cop0", "cop1", "cop2", "cop3", "beql","bnel","blezl","bgtzl", /*24 */ "op60", "op64", "op68", "op6c", "op70", "op74", "op78", "op7c", /*32 */ "lb", "lh", "lwl", "lw", "lbu", "lhu", "lwr", "ld", /*40 */ "sb", "sh", "swl", "sw", "opb0", "opb4", "swr", "sd", /*48 */ "lwc0", "lwc1", "lwc2", "lwc3", "ldc0", "ldc1", "ldc2", "ldc3", /*56 */ "swc0", "swc1", "swc2", "swc3", "sdc0", "sdc1", "sdc2", "sdc3" }; static char *spec_name[64] = { /* 0*/ "sll", "spec01","srl", "sra", "sllv", "spec05","srlv","srav", /* 8*/ "jr", "jalr", "spec12","spec13","syscall","break","vcall","spec17", /*16*/ "mfhi", "mthi", "mflo", "mtlo", "spec24","spec25","spec26","spec27", /*24*/ "mult", "multu","div", "divu", "spec34","spec35","spec36","spec37", /*32*/ "add", "addu", "sub", "subu", "and", "or", "xor", "nor", /*40*/ "spec50","spec51","slt","sltu", "spec54","spec55","spec56","spec57", /*48*/ "spec60","spec61","spec62","spec63","spec64","spec65","spec66","spec67", /*56*/ "spec70","spec71","spec72","spec73","spec74","spec75","spec76","spec77" }; static char *bcond_name[32] = { "bltz", "bgez", "bltzl", "bgezl", "bcond04", "bcond05", "bcond06", "bcond07", "tgei", "tgeiu", "tlti", "tltiu", "teqi", "bcond0d", "tnei", "bcond0f", "bltzal", "bgezal", "bltzall", "bgezall", "bcond14", "bcond15", "bcond16", "bcond17", "bcond18", "bcond19", "bcond1a", "bcond1b", "bcond1c", "bcond1d", "bcond1e", "bcond1f" }; static char *cop1_name[64] = { /* 0 */ "add", "sub", "mul", "div", "sqrt", "abs", "mov", "neg", /* 8 */ "fop08","fop09","fop0a","fop0b","fop0c","fop0d","fop0e","fop0f", /*16 */ "fop10","fop11","fop12","fop13","fop14","fop15","fop16","fop17", /*24 */ "fop18","fop19","fop1a","fop1b","fop1c","fop1d","fop1e","fop1f", /*32 */ "cvt.s","cvt.d","cvt.e","fop23","cvt.w","fop25","fop26","fop27", /*40 */ "fop28","fop29","fop2a","fop2b","fop2c","fop2d","fop2e","fop2f", /*48 */ "c.f", "c.un","c.eq","c.ueq","c.olt","c.ult","c.ole","c.ule", /*56 */ "c.sf","c.ngle","c.seq","c.ngl","c.lt","c.nge","c.le","c.ngt" }; static char *fmt_name[16] = { "s", "d", "e", "q", "w", "fmt5", "fmt6", "fmt7", "fmt8", "fmt9", "fmta", "fmtb", "fmtc", "fmtd", "fmte", "fmtf" }; /* public */ /* Three sets of commonly used register names */ const char *tdis_reg_names[3][64] = { { /* compiler names */ "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", "s8", "ra", "zero.B","at.B","v0.B", "v1.B", "a0.B", "a1.B", "a2.B", "a3.B", "t0.B", "t1.B", "t2.B", "t3.B", "t4.B", "t5.B", "t6.B", "t7.B", "s0.B", "s1.B", "s2.B", "s3.B", "s4.B", "s5.B", "s6.B", "s7.B", "t8.B", "t9.B", "k0.B", "k1.B", "gp.B", "sp.B", "s8.B", "ra.B" }, { /* hardware names */ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "gp", "sp", "r30", "r31", "r0.B", "r1.B", "r2.B", "r3.B", "r4.B", "r5.B", "r6.B", "r7.B", "r8.B", "r9.B", "r10.B","r11.B","r12.B","r13.B","r14.B","r15.B", "r16.B","r17.B","r18.B","r19.B","r20.B","r21.B","r22.B","r23.B", "r24.B","r25.B","r26.B","r27.B","gp.B", "sp.B", "r30.B","r31.B" }, { /* assembler names */ "$0", "$at", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", "$24", "$25", "$26", "$27", "$gp", "$sp", "$30", "$31", "$0.B", "$at.B","$2.B", "$3.B", "$4.B", "$5.B", "$6.B", "$7.B", "$8.B", "$9.B", "$10.B","$11.B","$12.B","$13.B","$14.B","$15.B", "$16.B","$17.B","$18.B","$19.B","$20.B","$21.B","$22.B","$23.B", "$24.B","$25.B","$26.B","$27.B","$gp.B","$sp.B","$30.B","$31.B" } }; static char *c0_opname[64] = { "c0op0","tlbr","tlbwi","c0op3","c0op4","c0op5","tlbwr","c0op7", "tlbp","c0op9","c0op10","c0op11","c0op12","c0op13","c0op14","c0op15", "rfe","c0op17","c0op18","c0op19","c0op20","c0op21","c0op22","c0op23", "c0op24","c0op25","c0op26","c0op27","c0op28","c0op29","c0op30","c0op31", "c0op32","c0op33","c0op34","c0op35","c0op36","c0op37","c0op38","c0op39", "c0op40","c0op41","c0op42","c0op43","c0op44","c0op45","c0op46","c0op47", "c0op48","c0op49","c0op50","c0op51","c0op52","c0op53","c0op54","c0op55", "c0op56","c0op57","c0op58","c0op59","c0op60","c0op61","c0op62","c0op63" }; static char *c0_reg[32] = { "index","random","tlblo","c0r3","context","c0r5","c0r6","c0r7", "badvaddr","c0r9","tlbhi","epcn","sr", "cause","epc", "c0r15", "c0r16","c0r17","c0r18","c0r19","c0r20","c0r21","c0r22","c0r23", "c0r24","c0r25","c0r26","c0r27","c0r28","c0r29","c0r30","c0r31" }; /* Remember the options set by dis_init */ //#define ADDR_DEFAULT "%#010x:\t" #define ADDR_DEFAULT 0 //#define VALUE_DEFAULT "%#010x\t" #define VALUE_DEFAULT 0 #define NAME_DEFAULT COMPILER_NAMES static struct { char *addr_format; char *value_format; const char **reg_names; int print_jal_targets; } save = { ADDR_DEFAULT, VALUE_DEFAULT, NAME_DEFAULT, true }; /* Update regmask to reflect the use of this general-purpose (not fp) register, and return its name */ static const char * register_name(ireg, boosted, regmask) unsigned ireg, *regmask; int boosted; { if (!boosted) *regmask |= (1 << ireg); return save.reg_names[boosted ? ireg + 32 : ireg]; } /* public -- see dissassembler.h */ int tdisasm(buffer, address, iword, ext, regmask, symbol_value, ls_register) char *buffer; unsigned char ext; /* extension byte */ unsigned address, iword, *regmask, *symbol_value, *ls_register; { int return_value = 0; char *bufptr = buffer; boolean do_b_displacement = false; boolean do_loadstore = false; union mips_instruction i; union extension_byte e; *bufptr = 0; i.word = iword; e.byte = ext; *regmask = *symbol_value = *ls_register = 0; /* Put out the address and hex value of the instruction, leaving bufptr set at the end */ if (save.addr_format) { sprintf(bufptr, save.addr_format, address); bufptr += strlen(bufptr); } if (save.value_format) { sprintf(bufptr, save.value_format, iword); bufptr += strlen(bufptr); } /* check for dynamic NOP */ if (e.e.dyn_nop) { strcat(bufptr, "D."); bufptr += 2; } /* decode the instruction */ switch (i.j_format.opcode) { case spec_op: if (i.word == 0x20) { strcat(bufptr, "nop"); if (e.e.rd_boost) strcat(bufptr, ".B"); bufptr += strlen(bufptr); break; } else if (i.r_format.func == addu_op && i.r_format.rt == ZERO) { sprintf(bufptr, "move%s\t%s,%s", e.e.rd_boost ? ".B" : "", register_name(i.r_format.rd, e.e.rd_boost, regmask), register_name(i.r_format.rs, e.e.rs_boost, regmask)); bufptr += strlen(bufptr); break; } strcat(bufptr, spec_name[i.r_format.func]); if (e.e.rd_boost) strcat(bufptr, ".B"); bufptr += strlen(bufptr); switch (i.r_format.func) { case sll_op: case srl_op: case sra_op: sprintf(bufptr, "\t%s,%s,%d", register_name(i.r_format.rd, e.e.rd_boost, regmask), register_name(i.r_format.rt, e.e.rt_boost, regmask), i.r_format.re); break; case sllv_op: case srlv_op: case srav_op: sprintf(bufptr, "\t%s,%s,%s", register_name(i.r_format.rd, e.e.rd_boost, regmask), register_name(i.r_format.rt, e.e.rt_boost, regmask), register_name(i.r_format.rs, e.e.rs_boost, regmask)); break; case mfhi_op: case mflo_op: sprintf(bufptr, "\t%s", register_name(i.r_format.rd, e.e.rd_boost, regmask)); break; case jalr_op: return_value = 3; sprintf(bufptr, "\t%s,%s", register_name(i.r_format.rd, e.e.rd_boost, regmask), register_name(i.r_format.rs, e.e.rs_boost, regmask)); *ls_register = i.r_format.rs; break; case jr_op: return_value = 3; *ls_register = i.r_format.rs; /* fall through */ case mtlo_op: case mthi_op: sprintf(bufptr, "\t%s", register_name(i.r_format.rs, e.e.rs_boost, regmask)); break; case mult_op: case multu_op: case div_op: case divu_op: sprintf(bufptr, "\t%s,%s", register_name(i.r_format.rs, e.e.rs_boost, regmask), register_name(i.r_format.rt, e.e.rt_boost, regmask)); break; case syscall_op: break; case break_op: case vcall_op: { char *format = "\t%d"; unsigned op2 = i.r_format.rd * 32 + i.r_format.re; if (op2) format = "\t%d,%d"; sprintf(bufptr, format, i.r_format.rs*32+i.r_format.rt, op2); } break; default: sprintf(bufptr, "\t%s,%s,%s", register_name(i.r_format.rd, e.e.rd_boost, regmask), register_name(i.r_format.rs, e.e.rs_boost, regmask), register_name(i.r_format.rt, e.e.rt_boost, regmask)); break; }; break; case bcond_op: sprintf(bufptr, "%s%s\t%s,", bcond_name[i.i_format.rt], e.e.rd_boost ? ".B" : "", register_name(i.i_format.rs, e.e.rs_boost, regmask)); do_b_displacement = true; break; case blez_op_T: case bgtz_op_T: case blez_op_N: case bgtz_op_N: sprintf(bufptr, "%s%s\t%s,", op_name[i.i_format.opcode], e.e.rd_boost ? ".B" : "", register_name(i.i_format.rs, e.e.rs_boost, regmask)); do_b_displacement = true; break; case beq_op_T: case beq_op_N: if (i.i_format.rs == ZERO && i.i_format.rt == ZERO) { strcat(bufptr, "b"); if (i.j_format.opcode == beq_op_T) strcat(bufptr, ".T"); else strcat(bufptr, ".N"); if (e.e.rd_boost) strcat(bufptr, ".B\t"); else strcat(bufptr, "\t"); do_b_displacement = true; break; } /* fall through */ case bne_op_T: case bne_op_N: sprintf(bufptr, "%s%s\t%s,%s,", op_name[i.i_format.opcode], e.e.rd_boost ? ".B" : "", register_name(i.i_format.rs, e.e.rs_boost, regmask), register_name(i.i_format.rt, e.e.rt_boost, regmask)); do_b_displacement = true; break; case cop0_op: case cop1_op: case cop2_op: case cop3_op: { unsigned which_cop = i.j_format.opcode - cop0_op; char *f_or_r = "rf"; switch (i.r_format.rs) { case bc_op_T: sprintf(bufptr, "bc%d%c.T%s\t", which_cop, "ft"[i.r_format.rt], e.e.rd_boost ? ".B" : ""); do_b_displacement = true; break; case bc_op_N: sprintf(bufptr, "bc%d%c.N%s\t", which_cop, "ft"[i.r_format.rt], e.e.rd_boost ? ".B" : ""); do_b_displacement = true; break; case mtc_op: if (which_cop == 0) sprintf(bufptr, "mtc%d%s\t%s,%s", which_cop, e.e.rd_boost ? ".B" : "", register_name(i.r_format.rt, e.e.rt_boost,regmask), c0_reg[i.f_format.rd]); else sprintf(bufptr, "mtc%d%s\t%s,%c%d", which_cop, e.e.rd_boost ? ".B" : "", register_name(i.r_format.rt, e.e.rt_boost,regmask), f_or_r[(which_cop == 1)], i.f_format.rd); break; case mfc_op: if (which_cop == 0) sprintf(bufptr, "mfc%d%s\t%s,%s", which_cop, e.e.rd_boost ? ".B" : "", register_name(i.r_format.rt, e.e.rt_boost,regmask), c0_reg[i.f_format.rd]); else sprintf(bufptr, "mfc%d%s\t%s,%c%d", which_cop, e.e.rd_boost ? ".B" : "", register_name(i.r_format.rt, e.e.rt_boost,regmask), f_or_r[(which_cop == 1)], i.f_format.rd); break; case cfc_op: sprintf(bufptr, "cfc%d%s\t%s,%c%d", which_cop, e.e.rd_boost ? ".B" : "", register_name(i.r_format.rt, e.e.rt_boost, regmask), f_or_r[(which_cop == 1)], i.f_format.rd); break; case ctc_op: sprintf(bufptr, "ctc%d%s\t%s,%c%d", which_cop, e.e.rd_boost ? ".B" : "", register_name(i.r_format.rt, e.e.rt_boost, regmask), f_or_r[(which_cop == 1)], i.f_format.rd); break; default: if (which_cop != 1) sprintf(bufptr, "c0%s\t%s", e.e.rd_boost ? ".B" : "", c0_opname[i.f_format.func]); else { sprintf(bufptr, "%s.%s%s\t", cop1_name[i.f_format.func], fmt_name[i.f_format.fmt], e.e.rd_boost ? ".B" : ""); bufptr += strlen(bufptr); switch (i.f_format.func) { case fsqrt_op: case fabs_op: case fmov_op: case fcvts_op: case fcvtd_op: case fcvte_op: case fcvtw_op: sprintf(bufptr, "f%d,f%d", i.f_format.re, i.f_format.rd); break; case fcmp_op+0x0: case fcmp_op+0x1: case fcmp_op+0x2: case fcmp_op+0x3: case fcmp_op+0x4: case fcmp_op+0x5: case fcmp_op+0x6: case fcmp_op+0x7: case fcmp_op+0x8: case fcmp_op+0x9: case fcmp_op+0xa: case fcmp_op+0xb: case fcmp_op+0xc: case fcmp_op+0xd: case fcmp_op+0xe: case fcmp_op+0xf: sprintf(bufptr, "f%d,f%d", i.f_format.rd, i.f_format.rt); break; default: sprintf(bufptr, "f%d,f%d,f%d", i.f_format.re, i.f_format.rd, i.f_format.rt); break; } /* switch on func */ } break; /* End of default */ } /* switch on rs */ } break; /* End of cop0, cop1, cop2, cop3 */ case jal_op: case j_op: sprintf(bufptr, "%s%s\t", op_name[i.j_format.opcode], e.e.rd_boost ? ".B" : ""); *symbol_value = (address & 0xF0000000) | i.j_format.target << 2; if (save.print_jal_targets) { bufptr += strlen(bufptr); sprintf(bufptr, "%#x", *symbol_value); } return_value = 1; break; case swc1_op: case sdc1_op: case lwc1_op: case ldc1_op: sprintf(bufptr, "%s%s\tf%d,", op_name[i.i_format.opcode], e.e.rd_boost ? ".B" : "", i.i_format.rt); do_loadstore = true; break; case lb_op: case lh_op: case lw_op: case ld_op: case lbu_op: case lhu_op: case sb_op: case sh_op: case sw_op: case sd_op: case lwl_op: case lwr_op: sprintf(bufptr, "%s%s\t%s,", op_name[i.i_format.opcode], e.e.rd_boost ? ".B" : "", register_name(i.i_format.rt, e.e.rt_boost, regmask)); do_loadstore = true; break; case ori_op: case xori_op: if (i.u_format.rs == ZERO) { sprintf(bufptr, "li%s\t%s,%d", e.e.rd_boost ? ".B" : "", register_name(i.u_format.rt, e.e.rt_boost, regmask), i.u_format.uimmediate); break; } /* fall through */ case andi_op: sprintf(bufptr, "%s%s\t%s,%s,%#x", op_name[i.u_format.opcode], e.e.rd_boost ? ".B" : "", register_name(i.u_format.rt, e.e.rt_boost, regmask), register_name(i.u_format.rs, e.e.rs_boost, regmask), i.u_format.uimmediate); break; case lui_op: sprintf(bufptr, "%s%s\t%s,%#x", op_name[i.u_format.opcode], e.e.rd_boost ? ".B" : "", register_name(i.u_format.rt, e.e.rt_boost, regmask), i.u_format.uimmediate); break; case addi_op: case addiu_op: if (i.i_format.rs == ZERO) { short sign_extender = i.i_format.simmediate; sprintf(bufptr, "li%s\t%s,%d", e.e.rd_boost ? ".B" : "", register_name(i.i_format.rt, e.e.rt_boost, regmask), sign_extender); break; } /* fall through */ default: { short sign_extender = i.i_format.simmediate; sprintf(bufptr, "%s%s\t%s,%s,%d", op_name[i.i_format.opcode], e.e.rd_boost ? ".B" : "", register_name(i.i_format.rt, e.e.rt_boost, regmask), register_name(i.i_format.rs, e.e.rs_boost, regmask), sign_extender); } break; } /* Some instructions require more than just registers */ if (do_loadstore) { short sign_extender = i.i_format.simmediate; *symbol_value = sign_extender; *ls_register = i.i_format.rs; bufptr += strlen(bufptr); sprintf(bufptr, "%d(%s)", sign_extender, register_name(i.i_format.rs, e.e.rs_boost, regmask)); return_value = -1; } else if (do_b_displacement) { short sign_extender = i.i_format.simmediate; bufptr += strlen(bufptr); *symbol_value = 4 + address + ((((long)sign_extender & 0xFFFD0000) | (0x3FFFF & (sign_extender << 2)))); sprintf(bufptr, "%#x", *symbol_value); return_value = 2; } return return_value; }