-
-
Save parasyte/f8fa1de4e7070048846a to your computer and use it in GitHub Desktop.
NEC VR43xx disassembler (MIPS R4300i; N64)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## MIPS R4300i disassembler | |
## Specifically: NEC VR43xx | |
## TODO: Split this file into a generic "mips.py" and specific "vr43xx.py" | |
## Even better! Split it into MIPS-I, MIPS-II, MIPS-III... | |
## TODO: New line after "branch and link" instructions | |
## TODO: Turn addi/addiu/ori into li | |
## TODO: any instruction with destination $zero = nop (MUST NOT HAVE SIDE-EFFECTS; ll/lld have side-effects) | |
## TODO: compact multiple instructions. eg. lui/ori -> la, mul/mflo -> mult ... | |
## TODO: swap operands for mtcz instruction; destination should be on the left! FTM!! | |
## TODO: Make pseudo-ops configurable (on/off) | |
def fmt_r(inst): | |
""" | |
Instruction format R (Register) | |
opcode (6) | rs (5) | rt (5) | rd (5) | sh (5) | funct (6) | |
""" | |
opcode = (inst >> 26) & 0x0000003F | |
rs = (inst >> 21) & 0x0000001F | |
rt = (inst >> 16) & 0x0000001F | |
rd = (inst >> 11) & 0x0000001F | |
sh = (inst >> 6) & 0x0000001F | |
funct = (inst >> 0) & 0x0000003F | |
return (opcode, rs, rt, rd, sh, funct) | |
def fmt_i(inst): | |
""" | |
Instruction format I (Immediate) | |
opcode (6) | rs (5) | rt (5) | imm (16) | |
""" | |
opcode = (inst >> 26) & 0x0000003F | |
rs = (inst >> 21) & 0x0000001F | |
rt = (inst >> 16) & 0x0000001F | |
imm = (inst >> 0) & 0x0000FFFF | |
return (opcode, rs, rt, imm) | |
def fmt_j(inst): | |
""" | |
Instruction format J (Jump) | |
opcode (6) | addr (26) | |
""" | |
opcode = (inst >> 26) & 0x0000003F | |
addr = (inst >> 0) & 0x03FFFFFF | |
return (opcode, addr) | |
def sign_ext(imm): | |
return -(0x10000 - imm) if (imm & 0x8000) else imm | |
def mask32(imm): | |
return imm & 0xFFFFFFFF | |
def branch_target(pc, imm): | |
return mask32(pc + (sign_ext(imm) << 2)) | |
def jump_target(pc, addr): | |
return (pc & 0xF0000000) | (addr << 2) | |
""" | |
General Purpose Registers LUT | |
""" | |
gpr = ( | |
"$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", | |
) | |
""" | |
CoProcessor Registers LUT | |
""" | |
cpr = ( | |
## cp0 (System CoProcessor) | |
( | |
"Index", "Random", "EntryLo0", "EntryLo1", | |
"Context", "PageMask", "Wired", None, | |
"BadVAddr", "Count", "EntryHi", "Compare", | |
"Status", "Cause", "EPC", "PRId", | |
"Config", "LLAddr", "WatchLo", "WatchHi", | |
"XContext", None, None, None, | |
None, None, "PErr", "CacheErr", | |
"TagLo", "TagHi", "ErrorEPC", None, | |
), | |
## cp1 (FPU) | |
( | |
"fpr0", "fpr1", "fpr2", "fpr3", "fpr4", "fpr5", "fpr6", "fpr7", | |
"fpr8", "fpr9", "fpr10", "fpr11", "fpr12", "fpr13", "fpr14", "fpr15", | |
"fpr16", "fpr17", "fpr18", "fpr19", "fpr20", "fpr21", "fpr22", "fpr23", | |
"fpr24", "fpr25", "fpr26", "fpr27", "fpr28", "fpr29", "fpr30", "fpr31", | |
), | |
## cp2 (N/A) | |
) | |
""" | |
CoProcessor Control Registers LUT | |
""" | |
cpcr = ( | |
## cp1 (FPU) | |
( | |
"fcr0", None, None, None, None, None, None, None, | |
None, None, None, None, None, None, None, None, | |
None, None, None, None, None, None, None, None, | |
None, None, None, None, None, None, None, "fcsr", | |
), | |
## cp2 (N/A) | |
) | |
def dw(inst): | |
""" | |
Fall-back decoder function | |
Returns instruction as a data word | |
""" | |
return ".dw 0x{inst:08X}".format( | |
inst=inst | |
) | |
""" | |
Opcode decoder functions | |
""" | |
def o_cp(pc, inst): | |
(opcode, rs, rt, rd, sh, funct) = fmt_r(inst) | |
cp = (opcode & 3) | |
assert cp <= 1 | |
if rs == 8: | |
(opcode, rs, rt, imm) = fmt_i(inst) | |
return cp_bc(pc, cp, rs, rt, imm) | |
if rs & 0x10: | |
return cp_functs[cp][funct](rs, rt, rd, sh, funct) | |
return cp_opcodes[rs](cp, rs, rt, rd, sh, funct) | |
def o_regimm(pc, inst): | |
(opcode, rs, rt, imm) = fmt_i(inst) | |
return branches[rt](pc, rs, rt, imm) | |
def o_special(pc, inst): | |
(opcode, rs, rt, rd, sh, funct) = fmt_r(inst) | |
return functs[funct](rs, rt, rd, sh, funct) | |
def o_addi(pc, inst): | |
(opcode, rs, rt, imm) = fmt_i(inst) | |
mnemonics = ( | |
"addi", # 001000, daddi 011000 | |
"addiu", # 001001, daddiu 011001 | |
"slti", # 001010 | |
"sltiu", # 001011 | |
"andi", # 001100 | |
"ori", # 001101 | |
"xori", # 001110 | |
) | |
if (opcode in ( 0x08, 0x09, 0x0D )) and (imm == 0): | |
if rt == rs: | |
return "nop" | |
return "move {rt:s}, {rs:s}".format( | |
rt=gpr[rt], | |
rs=gpr[rs] | |
) | |
return "{mnemonic: <6s} {rt:s}, {rs:s}, 0x{imm:04X}".format( | |
mnemonic=("d" if (opcode & 0x10) else "") + mnemonics[opcode & 7], | |
rt=gpr[rt], | |
rs=gpr[rs], | |
imm=imm | |
) | |
def o_b(pc, inst): | |
(opcode, rs, rt, imm) = fmt_i(inst) | |
mnemonics = ( | |
"beq", # 000100, beql 010100 | |
"bne", # 000101, bnel 010101 | |
"blez", # 000110, blezl 010110 | |
"bgtz", # 000111, bgtzl 010111 | |
) | |
target = branch_target(pc, imm) | |
if (opcode == 4) and (rs == 0) and (rt == 0): | |
return "b 0x{target:08X}".format( | |
target=target | |
) | |
if (rs == 0 or rt == 0): | |
return "{mnemonic: <7s} {rs:s}, 0x{target:08X}".format( | |
mnemonic=mnemonics[opcode & 3] + | |
("z" if (opcode & 3) < 2 else "") + | |
("l" if (opcode >> 4) else ""), | |
rs=gpr[rt if (rs == 0) else rs], | |
target=target | |
) | |
return "{mnemonic: <7s} {rs:s}, {rt:s}, 0x{target:08X}".format( | |
mnemonic=mnemonics[opcode & 3] + ("l" if (opcode >> 4) else ""), | |
rs=gpr[rs], | |
rt=gpr[rt], | |
target=target, | |
) | |
def o_cache(pc, inst): | |
(opcode, rs, rt, imm) = fmt_i(inst) | |
assert (rt & 2) == 0 | |
assert (rt >> 2) < 7 | |
assert rt != 0x0C | |
operations = ( | |
"Index_Invalidate", # 00000 | |
"Index_Write_Back_Invalidate", # 00001 | |
"Index_Load_Tag_I", # 00100 | |
"Index_Load_Tag_D", # 00101 | |
"Index_Store_Tag_I", # 01000 | |
"Index_Store_Tag_D", # 01001 | |
None, # 01100 | |
"Create_Dirty_Exclusive", # 01101 | |
"Hit_Invalidate_I", # 10000 | |
"Hit_Invalidate_D", # 10001 | |
"Fill", # 10100 | |
"Hit_Write_Back_Invalidate", # 10101 | |
"Hit_Write_Back_I", # 11000 | |
"Hit_Write_Back_D", # 11001 | |
) | |
return "cache {op:s}, 0x{offset:04X}({base:s})".format( | |
op=operations[(rt >> 1) | (rt & 1)], | |
offset=imm, | |
base=gpr[rs] | |
) | |
def o_j(pc, inst): | |
(opcode, addr) = fmt_j(inst) | |
return "{mnemonic: <3s} 0x{target:08X}".format( | |
mnemonic="j" + ("al" if (opcode & 1) else ""), | |
target=jump_target(pc, addr) | |
) | |
def o_lui(pc, inst): | |
(opcode, rs, rt, imm) = fmt_i(inst) | |
assert rs == 0 | |
return "lui {rt:s}, 0x{imm:04X}".format( | |
rt=gpr[rt], | |
imm=imm | |
) | |
def o_loadstore(pc, inst): | |
(opcode, rs, rt, imm) = fmt_i(inst) | |
mnemonics = ( | |
"lb", # 100000 | |
"lh", # 100001 | |
"lwl", # 100010 | |
"lw", # 100011 | |
"lbu", # 100100 | |
"lhu", # 100101 | |
"lwr", # 100110 | |
"lwu", # 100111 | |
"sb", # 101000 | |
"sh", # 101001 | |
"swl", # 101010 | |
"sw", # 101011 | |
"sdl", # 101100 | |
"sdr", # 101101 | |
"swr", # 101110 | |
) | |
mnemonics_ll = ( | |
"ll", # 110000 | |
"lld", # 110100 | |
"sc", # 111000 | |
"scd", # 111100 | |
) | |
if opcode & 0x10: | |
if opcode & 3: | |
if opcode & 4: | |
mnemonic = ("sd" if (opcode & 8) else "ld") | |
else: | |
mnemonic = ("ldr" if (opcode & 1) else "ldl") | |
else: | |
mnemonic = mnemonics_ll[(opcode >> 2) & 3] | |
else: | |
mnemonic = mnemonics[opcode & 0x0F] | |
return "{mnemonic: <3s} {rt:s}, 0x{offset:04X}({base:s})".format( | |
mnemonic=mnemonic, | |
rt=gpr[rt], | |
offset=imm, | |
base=gpr[rs] | |
) | |
def o_loadstore_cp(pc, inst): | |
(opcode, rs, rt, imm) = fmt_i(inst) | |
return "{mnemonic:s} {rt:s}, 0x{offset:04X}({base:s})".format( | |
mnemonic=("s" if (opcode & 8) else "l") + ("d" if (opcode & 4) else "w") + "c1", | |
rt=cpr[1][rt], | |
offset=imm, | |
base=gpr[rs] | |
) | |
""" | |
Opcodes LUT | |
Use 'opcode' to look up decoder function | |
""" | |
opcodes = ( | |
o_special, # 000000 | |
o_regimm, # 000001 | |
o_j, # 000010, j | |
o_j, # 000011, jal | |
o_b, # 000100, beq | |
o_b, # 000101, bne | |
o_b, # 000110, blez | |
o_b, # 000111, bgtz | |
o_addi, # 001000, addi | |
o_addi, # 001001, addiu | |
o_addi, # 001010, slti | |
o_addi, # 001011, sltiu | |
o_addi, # 001100, andi | |
o_addi, # 001101, ori | |
o_addi, # 001110, xori | |
o_lui, # 001111 | |
o_cp, # 010000, cp0 | |
o_cp, # 010001, cp1 | |
None, # 010010, cp2 N/A | |
None, # 010011, cp3 N/A | |
o_b, # 010100, beql | |
o_b, # 010101, bnel | |
o_b, # 010110, blezl | |
o_b, # 010111, bgtzl | |
o_addi, # 011000, daddi | |
o_addi, # 011001, daddiu | |
o_loadstore, # 011010, ldl | |
o_loadstore, # 011011, ldr | |
None, # 011100 | |
None, # 011101 | |
None, # 011110 | |
None, # 011111 | |
o_loadstore, # 100000, lb | |
o_loadstore, # 100001, lh | |
o_loadstore, # 100010, lwl | |
o_loadstore, # 100011, lw | |
o_loadstore, # 100100, lbu | |
o_loadstore, # 100101, lhu | |
o_loadstore, # 100110, lwr | |
o_loadstore, # 100111, lwu | |
o_loadstore, # 101000, sb | |
o_loadstore, # 101001, sh | |
o_loadstore, # 101010, swl | |
o_loadstore, # 101011, sw | |
o_loadstore, # 101100, sdl | |
o_loadstore, # 101101, sdr | |
o_loadstore, # 101110, swr | |
o_cache, # 101111 | |
o_loadstore, # 110000, ll | |
o_loadstore_cp, # 110001, lwc1 | |
None, # 110010, lwc2 N/A | |
None, # 110011 | |
o_loadstore, # 110100, lld | |
o_loadstore_cp, # 110101, ldc1 | |
None, # 110110, ldc2 N/A | |
o_loadstore, # 110111, ld | |
o_loadstore, # 111000, sc | |
o_loadstore_cp, # 111001, swc1 | |
None, # 111010, swc2 N/A | |
None, # 111011 | |
o_loadstore, # 111100 scd | |
o_loadstore_cp, # 111101, sdc1 | |
None, # 111110, sdc2 N/A | |
o_loadstore, # 111111, sd | |
) | |
""" | |
CoProcessor Opcode decoder functions | |
""" | |
def cp_bc(pc, cp, rs, rt, imm): | |
assert rt <= 3 | |
return "{mnemonic: <5s} 0x{target:08X}".format( | |
mnemonic="bc" + str(cp) + ("t" if (rt & 1) else "f") + ("l" if (rt & 2) else ""), | |
target=branch_target(pc, imm) | |
) | |
def cp_cc(cp, rs, rt, rd, sh, funct): | |
assert sh == 0 | |
assert funct == 0 | |
assert cp >= 1 | |
assert cpcr[cp - 1][rd] is not None | |
return "{mnemonic: <5s} {rt:s}, {rd:s}".format( | |
mnemonic=("ctc" if (rs & 4) else "cfc") + str(cp), | |
rt=gpr[rt], | |
rd=cpcr[cp - 1][rd] | |
) | |
def cp_mfc(cp, rs, rt, rd, sh, funct): | |
assert sh == 0 | |
assert funct == 0 | |
assert cpr[cp][rd] is not None | |
return "{mnemonic: <5s} {rt:s}, {rd:s}".format( | |
mnemonic=("d" if (rs & 1) else "") + "mfc" + str(cp), | |
rt=gpr[rt], | |
rd=cpr[cp][rd] | |
) | |
""" | |
cp_mtc() swaps the register order (opposed to the MIPS spec) | |
It makes more sense to have this instruction left-handed, like all others. | |
""" | |
def cp_mtc(cp, rs, rt, rd, sh, funct): | |
assert sh == 0 | |
assert funct == 0 | |
assert cpr[cp][rd] is not None | |
return "{mnemonic: <5s} {rd:s}, {rt:s}".format( | |
mnemonic=("d" if (rs & 1) else "") + "mtc" + str(cp), | |
rd=cpr[cp][rd], | |
rt=gpr[rt] | |
) | |
""" | |
CoProcessor Opcodes LUT | |
Use 'rs' to lookup secondary decoder function | |
""" | |
cp_opcodes = ( | |
cp_mfc, # 00000, mfc | |
cp_mfc, # 00001, dmfc | |
cp_cc, # 00010, cfc | |
None, # 00011 | |
cp_mtc, # 00100, mtc | |
cp_mtc, # 00101, dmtc | |
cp_cc, # 00110, ctc | |
None, # 00111 | |
cp_bc, # 01000 | |
) | |
""" | |
CoProcessor 0 Function decoder functions | |
""" | |
def cp0(rs, rt, rd, sh, funct): | |
assert rs == 0x10 | |
assert rt == 0 | |
assert rd == 0 | |
assert sh == 0 | |
mnemonics = { | |
'1' : "tlbr", | |
'2' : "tlbwi", | |
'6' : "tlbwr", | |
'8' : "tlbp", | |
'24' : "eret" | |
} | |
return mnemonics[str(funct)] | |
""" | |
CoProcessor 1 format LUT | |
""" | |
cp1_fmt = ( | |
"s", # 10000 | |
"d", # 10001 | |
None, # 10010 | |
None, # 10011 | |
"w", # 10100 | |
"l", # 10101 | |
) | |
""" | |
CoProcessor 1 decoder functions | |
""" | |
def cp1_add(fmt, ft, fs, fd, funct): | |
assert fmt in ( 0x10, 0x11 ) | |
mnemonics = ( | |
"add.", # 000000 | |
"sub.", # 000001 | |
"mul.", # 000010 | |
"div.", # 000011 | |
) | |
return "{mnemonic:s} {fd:s}, {fs:s}, {ft:s}".format( | |
mnemonic=mnemonics[funct] + cp1_fmt[fmt & 1], | |
fd=cpr[1][fd], | |
fs=cpr[1][fs], | |
ft=cpr[1][ft] | |
) | |
def cp1_c(fmt, ft, fs, fd, funct): | |
assert fmt in ( 0x10, 0x11 ) | |
assert fd == 0 | |
conds = ( | |
"f", # 0000, False | |
"un", # 0001, Unordered | |
"eq", # 0010, Equal | |
"ueq", # 0011, Unordered or Equal | |
"olt", # 0100, Ordered or Less Than | |
"ult", # 0101, Unordered or Less Than | |
"ole", # 0110, Ordered or Less or Equal | |
"ule", # 0111, Unordered or Less Or Equal | |
"sf", # 1000, Signaling False | |
"ngle", # 1001, Not Greater or Less or Equal | |
"seq", # 1010, Signaling Equal | |
"ngl", # 1011, Not Greater or Less | |
"lt", # 1100, Less Than | |
"nge", # 1101, Not Greater or Equal | |
"le", # 1110, Less or Equal | |
"ngt", # 1111, Not Greater Than | |
) | |
return "{mnemonic: <8s} {fs:s}, {ft:s}".format( | |
mnemonic="c." + conds[funct & 0x0F] + "." + cp1_fmt[fmt & 1], | |
fs=cpr[1][fs], | |
ft=cpr[1][ft] | |
) | |
def cp1_cvt(fmt, ft, fs, fd, funct): | |
assert fmt in ( 0x10, 0x11, 0x14, 0x15 ) | |
assert ft == 0 | |
mnemonics = ( | |
"round.", # 001000 | |
"trunc.", # 001001 | |
"ceil.", # 001010 | |
"floor.", # 001011 | |
) | |
if funct & 0x20: | |
if (funct & 7) >= 2: | |
assert fmt in ( 0x10, 0x11 ) | |
assert (funct & 7) in ( 0, 1, 4, 5 ) | |
mnemonic = "cvt." + cp1_fmt[funct & 7] | |
else: | |
assert fmt in ( 0x10, 0x11 ) | |
mnemonic = mnemonics[funct & 3] + ("w." if (funct & 4) else "l.") + cp1_fmt[fmt & 1] | |
return "{mnemonic: <9} {fd:s}, {fs:s}".format( | |
mnemonic=mnemonic, | |
fd=cpr[1][fd], | |
fs=cpr[1][fs] | |
) | |
def cp1_mov(fmt, ft, fs, fd, funct): | |
assert fmt in ( 0x10, 0x11 ) | |
assert ft == 0 | |
mnemonics = ( | |
"sqrt.", # 000100 | |
"abs.", # 000101 | |
"mov.", # 000110 | |
"neg.", # 000111 | |
) | |
return "{mnemonic:s} {fd:s}, {fs:s}".format( | |
mnemonic=mnemonics[funct & 3] + cp1_fmt[fmt & 1], | |
fd=cpr[1][fd], | |
fs=cpr[1][fs] | |
) | |
""" | |
CoProcessor Functions LUT | |
Use 'funct' to lookup function decoder function | |
""" | |
cp_functs = ( | |
## CoProcessor 0 | |
( | |
None, # 000000 | |
cp0, # 000001, tlbr | |
cp0, # 000010, tlbwi | |
None, # 000011 | |
None, # 000100 | |
None, # 000101 | |
cp0, # 000110, tlbwr | |
None, # 000111 | |
cp0, # 001000, tlbp | |
None, # 001001 | |
None, # 001010 | |
None, # 001011 | |
None, # 001100 | |
None, # 001101 | |
None, # 001110 | |
None, # 001111 | |
None, # 010000 | |
None, # 010001 | |
None, # 010010 | |
None, # 010011 | |
None, # 010100 | |
None, # 010101 | |
None, # 010110 | |
None, # 010111 | |
cp0, # 011000, eret | |
), | |
## CoProcessor 1 | |
( | |
cp1_add, # 000000, add | |
cp1_add, # 000001, sub | |
cp1_add, # 000010, mul | |
cp1_add, # 000011, div | |
cp1_mov, # 000100, sqrt | |
cp1_mov, # 000101, abs | |
cp1_mov, # 000110, mov | |
cp1_mov, # 000111, neg | |
cp1_cvt, # 001000, round.l | |
cp1_cvt, # 001001, trunc.l | |
cp1_cvt, # 001010, ceil.l | |
cp1_cvt, # 001011, floor.l | |
cp1_cvt, # 001100, round.w | |
cp1_cvt, # 001101, trunc.w | |
cp1_cvt, # 001110, ceil.w | |
cp1_cvt, # 001111, floor.w | |
None, # 010000 | |
None, # 010001 | |
None, # 010010 | |
None, # 010011 | |
None, # 010100 | |
None, # 010101 | |
None, # 010110 | |
None, # 010111 | |
None, # 011000 | |
None, # 011001 | |
None, # 011010 | |
None, # 011011 | |
None, # 011100 | |
None, # 011101 | |
None, # 011110 | |
None, # 011111 | |
cp1_cvt, # 100000, cvt.s | |
cp1_cvt, # 100001, cvt.d | |
None, # 100010 | |
None, # 100011 | |
cp1_cvt, # 100100, cvt.w | |
cp1_cvt, # 100101, cvt.l | |
None, # 100110 | |
None, # 100111 | |
None, # 101000 | |
None, # 101001 | |
None, # 101010 | |
None, # 101011 | |
None, # 101100 | |
None, # 101101 | |
None, # 101110 | |
None, # 101111 | |
cp1_c, # 110000, c.f | |
cp1_c, # 110001, c.un | |
cp1_c, # 110010, c.eq | |
cp1_c, # 110011, c.ueq | |
cp1_c, # 110100, c.olt | |
cp1_c, # 110101, c.ult | |
cp1_c, # 110110, c.ole | |
cp1_c, # 110111, c.ule | |
cp1_c, # 111000, c.sf | |
cp1_c, # 111001, c.ngle | |
cp1_c, # 111010, c.seq | |
cp1_c, # 111011, c.ngl | |
cp1_c, # 111100, c.lt | |
cp1_c, # 111101, c.gne | |
cp1_c, # 111110, c.le | |
cp1_c, # 111111, c.ngt | |
), | |
) | |
""" | |
Special Function decoder functions | |
""" | |
def f_add(rs, rt, rd, sh, funct): | |
assert sh == 0 | |
mnemonics = ( | |
"add", # 100000, dadd 101100 | |
"addu", # 100001, daddu 101101 | |
"sub", # 100010, dsub 101110 | |
"subu", # 100011, dsubu 101111 | |
"and", # 100100 | |
"or", # 100101 | |
"xor", # 100110 | |
"nor", # 100111 | |
) | |
if (funct in ( 0x20, 0x21, 0x25 )) and ((rt == 0) or (rs == 0)): | |
if rd == (rs if (rt == 0) else rt): | |
return "nop" | |
return "move {rd:s}, {rs:s}".format( | |
rd=gpr[rd], | |
rs=gpr[rs if (rt == 0) else rt] | |
) | |
if funct & 8: | |
mnemonic = "d" + mnemonics[funct & 3] | |
else: | |
mnemonic = mnemonics[funct & 7] | |
return "{mnemonic: <5s} {rd:s}, {rs:s}, {rt:s}".format( | |
mnemonic=mnemonic, | |
rd=gpr[rd], | |
rs=gpr[rs], | |
rt=gpr[rt] | |
) | |
def f_jalr(rs, rt, rd, sh, funct): | |
assert rt == 0 | |
assert sh == 0 | |
if rd == 31: | |
return "jalr {rs:s}".format( | |
rs=gpr[rs] | |
) | |
return "jalr {rd:s}, {rs:s}".format( | |
rd=gpr[rd], | |
rs=gpr[rs] | |
) | |
def f_jr(rs, rt, rd, sh, funct): | |
assert rt == 0 | |
assert rd == 0 | |
assert sh == 0 | |
return "jr {rs:s}".format( | |
rs=gpr[rs] | |
) | |
def f_mfhilo(rs, rt, rd, sh, funct): | |
assert rs == 0 | |
assert rt == 0 | |
assert sh == 0 | |
return "{mnemonic:s} {rd:s}".format( | |
mnemonic="mf" + ("lo" if (funct & 2) else "hi"), | |
rd=gpr[rd] | |
) | |
def f_mthilo(rs, rt, rd, sh, funct): | |
assert rt == 0 | |
assert rd == 0 | |
assert sh == 0 | |
return "{mnemonic:s} {rd:s}".format( | |
mnemonic="mt" + ("lo" if (funct & 2) else "hi"), | |
rd=gpr[rs] | |
) | |
def f_mult(rs, rt, rd, sh, funct): | |
assert rd == 0 | |
assert sh == 0 | |
return "{mnemonic: <6s} {rs:s}, {rt:s}".format( | |
mnemonic=("d" if (funct & 4) else "") + ("div" if (funct & 2) else "mult") + ("u" if (funct & 1) else ""), | |
rs=gpr[rs], | |
rt=gpr[rt] | |
) | |
def f_sll(rs, rt, rd, sh, funct): | |
assert rs == 0 | |
if (rt == 0) and (rd == 0) and (sh == 0) and (funct == 0): | |
return "nop" | |
mnemonics = ( | |
"sll", # 000000, dsll 111000, dsll32 111100 | |
None, # 000001 | |
"srl", # 000010, dsrl 111010, dsrl32 111110 | |
"sra", # 000011, dsra 111011, dsra32 111111 | |
) | |
if funct & 4: | |
sh += 32 | |
return "{mnemonic: <6s} {rd:s}, {rt:s}, {sh:d}".format( | |
mnemonic=("d" if (funct & 0x10) else "") + mnemonics[funct & 3] + ("32" if (funct & 4) else ""), | |
rd=gpr[rd], | |
rt=gpr[rt], | |
sh=sh | |
) | |
def f_sllv(rs, rt, rd, sh, funct): | |
assert sh == 0 | |
mnemonics = ( | |
"sllv", # 000100, dsllv 010100 | |
None, # 000101 | |
"srlv", # 000110, dsrlv 010110 | |
"srav", # 000111, dsrav 010111 | |
) | |
if funct & 8: | |
mnemonic = "slt" + ("u" if (funct & 1) else "") | |
else: | |
mnemonic = ("d" if (funct & 0x10) else "") + mnemonics[funct & 3] | |
return "{mnemonic: <5s} {rd:s}, {rt:s}, {rs:s}".format( | |
mnemonic=mnemonic, | |
rd=gpr[rd], | |
rt=gpr[rt], | |
rs=gpr[rs] | |
) | |
def f_sync(rs, rt, rd, sh, funct): | |
assert rs == 0 | |
assert rt == 0 | |
assert rd == 0 | |
assert sh == 0 | |
return "sync" | |
def f_syscall(rs, rt, rd, sh, funct): | |
return "{mnemonic: <7s} 0x{code:05X}".format( | |
mnemonic=("break" if (funct & 1) else "syscall"), | |
code=(rs << 15) | (rt << 10) | (rd << 5) | sh | |
) | |
def f_trap(rs, rt, rd, sh, funct): | |
mnemonics = ( | |
"tgeu", # 110001 | |
"tlt", # 110010 | |
"tltu", # 110011 | |
"teq", # 110100 | |
"tge", # 110101 | |
"tne", # 110110 | |
) | |
return "{mnemonic: <4s} {rs:s}, {rt:s}, 0x{code:03X}".format( | |
mnemonic=mnemonics[funct - 1], | |
rs=gpr[rs], | |
rt=gpr[rt], | |
code=(rd << 5) | sh | |
) | |
""" | |
Special Functions LUT | |
Use 'funct' portion of Format R instruction to look up function decoder function | |
""" | |
functs = ( | |
f_sll, # 000000, sll | |
None, # 000001 | |
f_sll, # 000010, srl | |
f_sll, # 000011, sra | |
f_sllv, # 000100, sllv | |
None, # 000101 | |
f_sllv, # 000110, srlv | |
f_sllv, # 000111, srav | |
f_jr, # 001000 | |
f_jalr, # 001001 | |
None, # 001010 | |
None, # 001011 | |
f_syscall, # 001100, syscall | |
f_syscall, # 001101, break | |
None, # 001110 | |
f_sync, # 001111 | |
f_mfhilo, # 010000, mfhi | |
f_mthilo, # 010001, mthi | |
f_mfhilo, # 010010, mflo | |
f_mthilo, # 010011, mtlo | |
f_sllv, # 010100, dsllv | |
None, # 010101 | |
f_sllv, # 010110, dsrlv | |
f_sllv, # 010111, dsrav | |
f_mult, # 011000, mult | |
f_mult, # 011001, multu | |
f_mult, # 011010, div | |
f_mult, # 011011, divu | |
f_mult, # 011100, dmult | |
f_mult, # 011101, dmultu | |
f_mult, # 011110, ddiv | |
f_mult, # 011111, ddivu | |
f_add, # 100000, add | |
f_add, # 100001, addu | |
f_add, # 100010, sub | |
f_add, # 100011, subu | |
f_add, # 100100, and | |
f_add, # 100101, or | |
f_add, # 100110, xor | |
f_add, # 100111, nor | |
None, # 101000 | |
None, # 101001 | |
f_sllv, # 101010, slt | |
f_sllv, # 101011, sltu | |
f_add, # 101100, dadd | |
f_add, # 101101, daddu | |
f_add, # 101110, dsub | |
f_add, # 101111, dsubu | |
None, # 110000 | |
f_trap, # 110001, tgeu | |
f_trap, # 110010, tlt | |
f_trap, # 110011, tltu | |
f_trap, # 110100, teq | |
f_trap, # 110101, tge | |
f_trap, # 110110, tne | |
None, # 110111 | |
f_sll, # 111000, dsll | |
None, # 111001 | |
f_sll, # 111010, dsrl | |
f_sll, # 111011, dsra | |
f_sll, # 111100, dsll32 | |
None, # 111101 | |
f_sll, # 111110, dsrl32 | |
f_sll, # 111111, dsra32 | |
) | |
""" | |
Branch/Trap decoder functions | |
""" | |
def b_bz(pc, rs, rt, imm): | |
if (rt == 0x11) and (rs == 0): | |
return "bal 0x{target:08X}".format( | |
target=branch_target(pc, imm) | |
) | |
return "{mnemonic: <7s} {rs:s}, 0x{target:08X}".format( | |
mnemonic="b" + ("gez" if (rt & 1) else "ltz") + ("al" if (rt & 0x10) else "") + ("l" if (rt & 2) else ""), | |
rs=gpr[rs], | |
target=branch_target(pc, imm) | |
) | |
def b_trap(pc, rs, rt, imm): | |
mnemonics = ( | |
"tgei", # 01000 | |
"tgeiu", # 01001 | |
"tlti", # 01010 | |
"tltiu", # 01011 | |
"teqi", # 01100 | |
None, # 01101 | |
"tnei", # 01110 | |
) | |
return "{mnemonic: <5s} {rs:s}, 0x{imm:04X}".format( | |
mnemonic=mnemonics[rt & 7], | |
rs=gpr[rs], | |
imm=imm | |
) | |
""" | |
Branch/Trap LUT | |
Use 'rt' portion of Format I instruction to look up secondary decoder function | |
""" | |
branches = ( | |
b_bz, # 00000, bltz | |
b_bz, # 00001, bgez | |
b_bz, # 00010, bltzl | |
b_bz, # 00011, bgezl | |
None, # 00100 | |
None, # 00101 | |
None, # 00110 | |
None, # 00111 | |
b_trap, # 01000, tgei | |
b_trap, # 01001, tgeiu | |
b_trap, # 01010, tlti | |
b_trap, # 01011, tltiu | |
b_trap, # 01100, teqi | |
None, # 01101 | |
b_trap, # 01110, tnei | |
None, # 01111 | |
b_bz, # 10000, bltzal | |
b_bz, # 10001, bgezal | |
b_bz, # 10010, bltzall | |
b_bz, # 10011, bgezall | |
) | |
if __name__ == "__main__": | |
import struct | |
import sys | |
if len(sys.argv) <= 1: | |
print("Usage: python {:s} <file> [address]".format(sys.argv[0])) | |
sys.exit(1) | |
fp = open(sys.argv[1], mode="rb") | |
data = fp.read() | |
fp.close() | |
data = struct.unpack(">" + ("L" * int(len(data) / 4)), data) | |
if len(sys.argv) >= 3: | |
pc = int(sys.argv[2], 16) | |
else: | |
pc = 0x80000000 | |
jr = 0 | |
for inst in data: | |
(opcode, rs, rt, rd, sh, funct) = fmt_r(inst) | |
## Print address and data | |
print("{:08X}: {:02X} {:02X} {:02X} {:02X} ".format( | |
pc, | |
(inst >> 24) & 0xFF, | |
(inst >> 16) & 0xFF, | |
(inst >> 8) & 0xFF, | |
(inst >> 0) & 0xFF | |
), end="") | |
## Print instruction | |
try: | |
print(opcodes[opcode](pc + 4, inst)) | |
except: | |
print(dw(inst)) | |
def is_jr(): | |
return ( | |
(opcode == 0) and | |
(rt == 0) and | |
(rd == 0) and | |
(sh == 0) and | |
(funct == 8) | |
) | |
def is_jalr(): | |
return ( | |
(opcode == 0) and | |
(rt == 0) and | |
(sh == 0) and | |
(funct == 9) | |
) | |
def is_eret(): | |
return ( | |
(opcode == 0x10) and | |
(rs == 0x10) and | |
(rt == 0) and | |
(rd == 0) and | |
(sh == 0) and | |
(funct == 0x18) | |
) | |
## Keep track of jr/jalr instructions | |
if is_jr() or is_jalr(): | |
jr = pc | |
## eret instructions do not have a delay slot | |
if is_eret(): | |
jr = pc - 4 | |
## And print a blank line if the previous instruction was jr/jalr/eret | |
if (pc - 4) == jr: | |
print() | |
pc += 4 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment