import java.util.HashMap; public class Decode { private Decode() { } /* The op codes (first 6 bits) for various instructions. */ public static final int OP_RTYPE = 0x00; public static final int OP_ADDI = 0x08; public static final int OP_ADDIU = 0x09; public static final int OP_ANDI = 0x0C; public static final int OP_ORI = 0x0D; public static final int OP_XORI = 0x0E; public static final int OP_LHI = 0x19; public static final int OP_LLO = 0x18; public static final int OP_SLTI = 0x0A; public static final int OP_SLTIU = 0x0B; public static final int OP_BEQ = 0x04; public static final int OP_BNE = 0x05; public static final int OP_BGTZ = 0x07; public static final int OP_BLEZ = 0x06; public static final int OP_J = 0x02; public static final int OP_JAL = 0x03; public static final int OP_LB = 0x20; public static final int OP_LBU = 0x24; public static final int OP_LH = 0x21; public static final int OP_LHU = 0x25; public static final int OP_LW = 0x23; public static final int OP_SB = 0x28; public static final int OP_SH = 0x29; public static final int OP_SW = 0x2B; public static final int OP_TRAP = 0x1A; /* The function codes (last 6 bits) for various R-type instructions. */ public static final int FUNC_ADD = 0x20; public static final int FUNC_ADDU = 0x21; public static final int FUNC_AND = 0x24; public static final int FUNC_DIV = 0x1A; public static final int FUNC_DIVU = 0x1B; public static final int FUNC_MULT = 0x18; public static final int FUNC_MULTU = 0x19; public static final int FUNC_JALR = 0x09; public static final int FUNC_JR = 0x08; public static final int FUNC_MFHI = 0x10; public static final int FUNC_MFLO = 0x12; public static final int FUNC_MTHI = 0x11; public static final int FUNC_MTLO = 0x13; public static final int FUNC_NOR = 0x27; public static final int FUNC_OR = 0x25; public static final int FUNC_SLL = 0x00; public static final int FUNC_SLLV = 0x04; public static final int FUNC_SLT = 0x2A; public static final int FUNC_SLTU = 0x29; public static final int FUNC_SRA = 0x03; public static final int FUNC_SRAV = 0x07; public static final int FUNC_SRL = 0x02; public static final int FUNC_SRLV = 0x06; public static final int FUNC_SUB = 0x22; public static final int FUNC_SUBU = 0x23; public static final int FUNC_XOR = 0x26; /* Return values to indicate where data values can be located from * an instruction. */ /** Indicates that the data is not relevant to the instruction. */ public static final int DATA_NONE = 0; /** Indicates that the data can be found in the register named in * bits 21 to 25 of the instruction. */ public static final int DATA_REGS = 1; /** Indicates that the data can be found in the register named in * bits 16 to 20 of the instruction. */ public static final int DATA_REGT = 2; /** Indicates that the data can be found in the register named in * bits 11 to 15 of the instruction. */ public static final int DATA_REGD = 3; /** Indicates that the data can be found in the unsigned immediate * named in bits 6 to 10 of the instruction. */ public static final int DATA_SHIFT = 4; /** Indicates that the data can be found in the lower 16 bits * of the instruction, after performing a sign extension. */ public static final int DATA_IMM16_EXTEND = 5; /** Indicates that the data can be found in the lower 16 bits * of the instruction, using no sign extension. */ public static final int DATA_IMM16_UNSIGN = 6; /** Indicates that the data can be found in the lower 26 bits * of the instruction, lefted shifted 2 bits, using no sign extension. */ public static final int DATA_IMM26 = 7; /** Indicates that the data can be found in the PC, masking out * all but the upper four bits. */ public static final int DATA_PC4 = 8; /** Indicates that the data can be found in the HI:LO register pair. */ public static final int DATA_HILO = 9; /** Indicates that the data can be found register $ra. */ public static final int DATA_RA = 10; /** Indicates in a syntax template that a memory reference should be used. */ public static final int DATA_ADDR = 11; /** Returns information about how to determine the first data value * to be used by the instruction. Return value will be one of * DATA_REGS, DATA_SHIFT, DATA_PC4, DATA_IMM16_UNSIGN, and DATA_NONE. */ public static int getSourceA(int instr) { SyntaxType type = getSyntaxType(instr); return type == null ? DATA_NONE : type.srcA; } /** Returns information about how to determine the second data value * to be used by the instruction. Return value will be one of * DATA_REGT, DATA_IMM16_EXTEND, DATA_IMM16_UNSIGN, DATA_IMM26, and DATA_NONE. */ public static int getSourceB(int instr) { SyntaxType type = getSyntaxType(instr); return type == null ? DATA_NONE : type.srcB; } /** Returns information about how to determine the where to place * the result of the instruction. Return value will be one of * DATA_REGT, DATA_REGD, DATA_RA, and DATA_NONE. */ public static int getDestination(int instr) { SyntaxType type = getSyntaxType(instr); return type == null ? DATA_NONE : type.dest; } /** Returns true if the instruction loads data from memory. */ public static boolean isLoad(int instr) { return getSyntaxType(instr) == SYN_LOAD; } /** Returns true if the instruction stores data to memory. */ public static boolean isStore(int instr) { return getSyntaxType(instr) == SYN_STORE; } /** Returns the instruction address to which the instruction will * branch, if the branch is taken. For non-branch, non-jump instructions, * this will simply be PC+1. The PC should be specified without the * last two zeroes. */ public static int getNextInstructionAddress(int pc, int instr) { switch((instr >> 26) & 0x3F) { case Decode.OP_BEQ: case Decode.OP_BNE: case Decode.OP_BGTZ: case Decode.OP_BLEZ: return pc + 1 + (instr << 16 >> 16); case Decode.OP_J: case Decode.OP_JAL: return ((pc + 1) & 0xF0000000) | ((instr & 0x03FFFFFF) << 2); default: return pc + 1; } } /** Returns the order of arguments required for the instruction. */ public static int[] getSyntaxTemplate(int mask) { SyntaxType type = getSyntaxType(mask); return type == null ? null : type.template; } /** Returns a string representation of the operation. */ public static String getOperationName(int instr) { switch((instr >> 26) & 0x3F) { case OP_RTYPE: switch(instr & 0x3F) { case FUNC_ADD: return "add"; case FUNC_ADDU: return "addu"; case FUNC_AND: return "and"; case FUNC_DIV: return "div"; case FUNC_DIVU: return "divu"; case FUNC_MULT: return "mult"; case FUNC_MULTU: return "multu"; case FUNC_JALR: return "jalr"; case FUNC_JR: return "jr"; case FUNC_MFHI: return "mfhi"; case FUNC_MFLO: return "mflo"; case FUNC_MTHI: return "mthi"; case FUNC_MTLO: return "mtlo"; case FUNC_NOR: return "nor"; case FUNC_OR: return "or"; case FUNC_SLL: return "sll"; case FUNC_SLLV: return "sllv"; case FUNC_SLT: return "slt"; case FUNC_SLTU: return "sltu"; case FUNC_SRA: return "sra"; case FUNC_SRAV: return "srav"; case FUNC_SRL: return "srl"; case FUNC_SRLV: return "srlv"; case FUNC_SUB: return "sub"; case FUNC_SUBU: return "subu"; case FUNC_XOR: return "xor"; default: return NAME_UNDEFINED; } case OP_ADDI: return "addi"; case OP_ADDIU: return "addiu"; case OP_ANDI: return "andi"; case OP_ORI: return "ori"; case OP_XORI: return "xori"; case OP_LHI: return "lhi"; case OP_LLO: return "llo"; case OP_SLTI: return "slti"; case OP_SLTIU: return "sltiu"; case OP_BEQ: return "beq"; case OP_BNE: return "bne"; case OP_BGTZ: return "bgtz"; case OP_BLEZ: return "blez"; case OP_J: return "j"; case OP_JAL: return "jal"; case OP_LB: return "lb"; case OP_LBU: return "lbu"; case OP_LH: return "lh"; case OP_LHU: return "lhu"; case OP_LW: return "lw"; case OP_SB: return "sb"; case OP_SH: return "sh"; case OP_SW: return "sw"; case OP_TRAP: return "trap"; default: return NAME_UNDEFINED; } } /** Returns a String representation of the specified instruction. */ public static String disassemble(int instr) { String op = getOperationName(instr); if(op.equals(NAME_UNDEFINED)) return op; int[] template = getSyntaxTemplate(instr); if(template == null) return op + " ??"; StringBuilder ret = new StringBuilder(op); for(int i = 0; i < template.length; i++) { ret.append(i == 0 ? " " : ", "); switch(template[i]) { case Decode.DATA_REGS: ret.append(getRegisterName((instr >> 21) & 0x1F)); break; case Decode.DATA_REGT: ret.append(getRegisterName((instr >> 16) & 0x1F)); break; case Decode.DATA_REGD: ret.append(getRegisterName((instr >> 11) & 0x1F)); break; case Decode.DATA_SHIFT: ret.append(String.valueOf((instr >> 6) & 0x1F)); break; case Decode.DATA_IMM16_EXTEND: ret.append(String.valueOf(instr << 16 >> 16)); break; case Decode.DATA_IMM16_UNSIGN: ret.append(String.valueOf(instr & 0xFFFF)); break; case Decode.DATA_IMM26: ret.append(String.valueOf(instr & 0x03FFFFFF)); break; case Decode.DATA_ADDR: int offs = instr << 16 >> 16; int reg = (instr >> 21) & 0x1F; if(offs != 0) ret.append(String.valueOf(offs)); if(reg != 0) ret.append("(" + getRegisterName(reg) + ")"); break; default: ret.append("??"); } } return ret.toString(); } /** Returns the name of the specified register. */ public static String getRegisterName(int num) { return num >= 0 && num < REG_NAMES.length ? REG_NAMES[num] : NAME_UNDEFINED; } /** Returns the register number corresponding to the register name. */ public static int parseRegisterName(String regName) { Integer ret = REG_IDS.get(regName.toLowerCase()); if(ret != null) return ret.intValue(); if(!regName.startsWith("$")) return -1; try { int reg = Integer.parseInt(regName.substring(1)); return reg >= 0 && reg < 32 ? reg : -1; } catch(NumberFormatException e) { return -1; } } /** Returned by getOperationName when instruction op code not recognized. */ public static final String NAME_UNDEFINED = "???"; private static class SyntaxType { private int[] template; private int dest; private int srcA; private int srcB; private SyntaxType(int[] template, int srcA, int srcB, int dest) { this.template = template; this.srcA = srcA; this.srcB = srcB; this.dest = dest; } } /* Categories of instruction syntax. */ private static final SyntaxType SYN_ARITHLOG = new SyntaxType( new int[] { DATA_REGD, DATA_REGS, DATA_REGT }, DATA_REGS, DATA_REGT, DATA_REGD); private static final SyntaxType SYN_DIVMULT = new SyntaxType( new int[] { DATA_REGS, DATA_REGT }, DATA_REGS, DATA_REGT, DATA_HILO); private static final SyntaxType SYN_SHIFT = new SyntaxType( new int[] { DATA_REGD, DATA_REGT, DATA_SHIFT }, DATA_SHIFT, DATA_REGT, DATA_REGD); private static final SyntaxType SYN_SHIFTV = new SyntaxType( new int[] { DATA_REGD, DATA_REGT, DATA_REGS }, DATA_REGS, DATA_REGT, DATA_REGD); private static final SyntaxType SYN_JUMPR = new SyntaxType( new int[] { DATA_REGS }, DATA_REGS, DATA_NONE, DATA_NONE); private static final SyntaxType SYN_JALR = new SyntaxType( new int[] { DATA_REGS }, DATA_REGS, DATA_NONE, DATA_RA); private static final SyntaxType SYN_MOVEFROM = new SyntaxType( new int[] { DATA_REGD }, DATA_NONE, DATA_NONE, DATA_REGD); private static final SyntaxType SYN_MOVETO = new SyntaxType( new int[] { DATA_REGS }, DATA_REGS, DATA_NONE, DATA_NONE); private static final SyntaxType SYN_ARITHLOGI = new SyntaxType( new int[] { DATA_REGT, DATA_REGS, DATA_IMM16_EXTEND }, DATA_REGS, DATA_IMM16_EXTEND, DATA_REGT); private static final SyntaxType SYN_ARITHLOGIU = new SyntaxType( new int[] { DATA_REGT, DATA_REGS, DATA_IMM16_UNSIGN }, DATA_REGS, DATA_IMM16_UNSIGN, DATA_REGT); private static final SyntaxType SYN_LOADI = new SyntaxType( new int[] { DATA_REGT, DATA_IMM16_UNSIGN }, DATA_IMM16_UNSIGN, DATA_REGT, DATA_REGT); private static final SyntaxType SYN_BRANCH = new SyntaxType( new int[] { DATA_REGS, DATA_REGT, DATA_IMM16_EXTEND }, DATA_REGS, DATA_REGT, DATA_NONE); private static final SyntaxType SYN_BRANCHZ = new SyntaxType( new int[] { DATA_REGS, DATA_IMM16_EXTEND }, DATA_REGS, DATA_NONE, DATA_NONE); private static final SyntaxType SYN_LOAD = new SyntaxType( new int[] { DATA_REGT, DATA_ADDR }, DATA_REGS, DATA_IMM16_EXTEND, DATA_REGT); private static final SyntaxType SYN_STORE = new SyntaxType( new int[] { DATA_REGT, DATA_ADDR }, DATA_REGS, DATA_IMM16_EXTEND, DATA_NONE); private static final SyntaxType SYN_JUMP = new SyntaxType( new int[] { DATA_IMM26 }, DATA_PC4, DATA_IMM26, DATA_NONE); private static final SyntaxType SYN_JAL = new SyntaxType( new int[] { DATA_IMM26 }, DATA_PC4, DATA_IMM26, DATA_RA); private static final SyntaxType SYN_TRAP = new SyntaxType( new int[] { DATA_IMM26 }, DATA_NONE, DATA_IMM26, DATA_NONE); private static String[] REG_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", "$fp", "$ra" }; private static HashMap REG_IDS = new HashMap(); static { for(int i = 0; i < REG_NAMES.length; i++) { REG_IDS.put(REG_NAMES[i], Integer.valueOf(i)); } } /** Returns the syntax type corresponding to the specified instruction. */ private static SyntaxType getSyntaxType(int instr) { switch((instr >> 26) & 0x3F) { case OP_RTYPE: switch(instr & 0x3F) { case FUNC_ADD: return SYN_ARITHLOG; case FUNC_ADDU: return SYN_ARITHLOG; case FUNC_AND: return SYN_ARITHLOG; case FUNC_DIV: return SYN_DIVMULT; case FUNC_DIVU: return SYN_DIVMULT; case FUNC_MULT: return SYN_DIVMULT; case FUNC_MULTU: return SYN_DIVMULT; case FUNC_JALR: return SYN_JALR; case FUNC_JR: return SYN_JUMPR; case FUNC_MFHI: return SYN_MOVEFROM; case FUNC_MFLO: return SYN_MOVEFROM; case FUNC_MTHI: return SYN_MOVETO; case FUNC_MTLO: return SYN_MOVETO; case FUNC_NOR: return SYN_ARITHLOG; case FUNC_OR: return SYN_ARITHLOG; case FUNC_SLL: return SYN_SHIFT; case FUNC_SLLV: return SYN_SHIFTV; case FUNC_SLT: return SYN_ARITHLOG; case FUNC_SLTU: return SYN_ARITHLOG; case FUNC_SRA: return SYN_SHIFT; case FUNC_SRAV: return SYN_SHIFTV; case FUNC_SRL: return SYN_SHIFT; case FUNC_SRLV: return SYN_SHIFTV; case FUNC_SUB: return SYN_ARITHLOG; case FUNC_SUBU: return SYN_ARITHLOG; case FUNC_XOR: return SYN_ARITHLOG; default: return null; } case OP_ADDI: return SYN_ARITHLOGI; case OP_ADDIU: return SYN_ARITHLOGI; case OP_ANDI: return SYN_ARITHLOGIU; case OP_ORI: return SYN_ARITHLOGIU; case OP_XORI: return SYN_ARITHLOGIU; case OP_LHI: return SYN_LOADI; case OP_LLO: return SYN_LOADI; case OP_SLTI: return SYN_ARITHLOGI; case OP_SLTIU: return SYN_ARITHLOGI; case OP_BEQ: return SYN_BRANCH; case OP_BNE: return SYN_BRANCH; case OP_BGTZ: return SYN_BRANCHZ; case OP_BLEZ: return SYN_BRANCHZ; case OP_J: return SYN_JUMP; case OP_JAL: return SYN_JAL; case OP_LB: return SYN_LOAD; case OP_LBU: return SYN_LOAD; case OP_LH: return SYN_LOAD; case OP_LHU: return SYN_LOAD; case OP_LW: return SYN_LOAD; case OP_SB: return SYN_STORE; case OP_SH: return SYN_STORE; case OP_SW: return SYN_STORE; case OP_TRAP: return SYN_TRAP; default: return null; } } }