Files
goldeneye-pc-port/tools_pc/disasm.py
T
James Dansereau 8482ba3ed1 PC port: Phase 1.5 — root-cause D32 Part B (D33): ROM big-endian
structured fields + x86-64 loop-stride bug; fix designed, handoff updated

- Finding D33 in docs/PCPortResearch.md: the .z64 stores multi-byte
  structured fields in big-endian byte order (bit-packed streams raw);
  per-field load-time transform rule (u32 bswap32 / u16 bswap16 / u8
  identity) validated over all 173 animation entries; full blob layout
  mapped; exact reproduction of the gdb-observed garbage values.
- Second root cause: expand_ani_table_entries iterates s32** (+8 bytes/iter
  on x86-64 vs +4 on N64), rebasing only even-indexed entries (verified in
  compiled binary). Fix design: romdataFixupAnimationData() in port/, one
  call between romCopy and expand, s32* loop stride fix.
- docs/HANDOFF.md rewritten with the implementation task; AGENTS.md phase
  status updated. tools_pc/disasm.py (BE ROM research tool) added.
2026-08-21 20:18:38 -04:00

131 lines
5.9 KiB
Python

#!/usr/bin/env python3
"""Minimal MIPS disassembler for the BE-encoded GE ROM.
Usage: disasm.py <ram_addr_hex> [n_instructions]
Maps game-segment RAM (base 0x7F000000) to file offset 0x34B28.
Also handles lib segment (RAM base = _startSegmentEnd - 0x10000000, ROM 0x1050).
"""
import struct, sys
ROM = open('data/ge007.ntsc-final.z64', 'rb').read()
GAME_RAM_BASE = 0x7F000000
GAME_ROM_OFF = 0x34B28
LIB_ROM_OFF = 0x1050
LIB_RAM_BASE = 0x70000450 # from boot.s comment: code at ROM 0x1050 -> RAM 0x70000450
REGS = ["$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","$t10","$t11","$t12","$t13","$gp","$sp",
"$fp","$ra"]
RTYPE = {0x00:"sll",0x02:"srl",0x04:"sra",0x06:"sllv",0x08:"srav",0x0A:"sralv",
0x0C:"and",0x0E:"or",0x0F:"xor",0x10:"nor",0x12:"slt",0x14:"sltu",
0x18:"add",0x1A:"addu",0x1C:"sub",0x1E:"subu",
0x20:"mult",0x21:"multu",0x22:"div",0x23:"divu",
0x24:"madd",0x25:"maddu",0x26:"msub",0x27:"msubu"}
COP0 = {0x00:"mfc0",0x01:"mfc1",0x02:"mfc2",0x10:"mtc0",0x11:"mtc1",0x12:"mtc2",
0x40:"bc0t",0x44:"bc0f"}
def disasm_word(w):
op = w >> 26
rs = (w >> 21) & 31
rt = (w >> 16) & 31
rd = (w >> 11) & 31
sa = (w >> 6) & 31
fn = w & 63
imm = w & 0xFFFF
def s16(v): return v - 0x10000 if v >= 0x8000 else v
if op == 0x00:
if fn in RTYPE:
if fn in (0x00,0x02,0x04,0x06,0x08,0x0A):
return "%s %s,%s,%d" % (RTYPE[fn], REGS[rd], REGS[rt], sa)
if fn in (0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27):
return "%s %s,%s" % (RTYPE[fn], REGS[rs], REGS[rt])
return "%s %s,%s,%s" % (RTYPE[fn], REGS[rd], REGS[rs], REGS[rt])
if fn == 0x19: # tgei etc rare
return "tgeiu? %s,%s,0x%04x" % (REGS[rd], REGS[rs], imm)
return ".word 0x%08x" % w
if op == 0x01:
c = (w >> 21) & 3
if c == 0:
if fn in COP0:
if fn in (0x00,0x10):
return "%s %s,0x%02x" % (COP0[fn], REGS[rt], rs)
return "%s %s,0x%02x" % (COP0[fn], REGS[rs], rt)
if fn == 0x00: return "mfc0? %s" % REGS[rt]
return ".word 0x%08x" % w
if c == 1: # cop1
if fn in (0x00,0x01,0x02,0x03):
names = ["mfc1","cfc1","mtc1","ctc1"]
return "%s %s,%s" % (names[fn], REGS[rt], REGS[rs])
if fn == 0x46: return "bc1t +0x%04x" % s16(imm)
if fn == 0x47: return "bc1f +0x%04x" % s16(imm)
if fn == 0x4E: return "bc1t? (cond)"
if fn == 0x4F: return "bc1f? (cond)"
# fp ops
fops = {0x10:"abs.s",0x11:"add.s",0x12:"div.s",0x13:"cmp.s",0x14:"cvt.s.w",
0x15:"cvt.w.s",0x16:"max.s",0x17:"min.s",0x18:"mov.s",0x19:"neg.s",
0x1A:"recip.s",0x1B:"rsq.s",0x1C:"sqrt.s",0x20:"abs.d",0x21:"add.d",
0x22:"div.d",0x23:"cmp.d",0x26:"max.d",0x27:"min.d",0x28:"mov.d",
0x29:"neg.d",0x41:"and.b",0x42:"or.b",0x45:"cvt.s.w"}
if fn in fops:
return "%s %f%d,%f%d,%f%d" % (fops[fn], rd, rs, rt, 0)
return ".word 0x%08x" % w
if c == 2:
return "cop2? .word 0x%08x" % w
return ".word 0x%08x" % w
if op == 0x02: return "jalr %s%s" % (REGS[rs], ", "+REGS[rt] if rt else "")
if op == 0x03: return "syscall"
if op == 0x04: return "beq %s,%s,+0x%04x" % (REGS[rs], REGS[rt], s16(imm))
if op == 0x05: return "bne %s,%s,+0x%04x" % (REGS[rs], REGS[rt], s16(imm))
if op == 0x06: return "blez %s,+0x%04x" % (REGS[rs], s16(imm))
if op == 0x07: return "bgtz %s,+0x%04x" % (REGS[rs], s16(imm))
if op == 0x08: return "j 0x%08x" % ((w & 0x3FFFFFF) << 2)
if op == 0x09: return "jal 0x%08x" % ((w & 0x3FFFFFF) << 2)
if op == 0x0A: return "addi %s,%s,0x%04x" % (REGS[rt], REGS[rs], s16(imm))
if op == 0x0B: return "addiu %s,%s,0x%04x" % (REGS[rt], REGS[rs], s16(imm))
if op == 0x0C: return "andi %s,%s,0x%04x" % (REGS[rt], REGS[rs], imm)
if op == 0x0D: return "slti %s,%s,0x%04x" % (REGS[rt], REGS[rs], s16(imm))
if op == 0x0E: return "sltiu %s,%s,0x%04x" % (REGS[rt], REGS[rs], imm)
if op == 0x0F: return "ori %s,%s,0x%04x" % (REGS[rt], REGS[rs], imm)
if op == 0x10: return "xori %s,%s,0x%04x" % (REGS[rt], REGS[rs], imm)
if op == 0x11: return "lui %s,0x%04x" % (REGS[rt], imm)
if op in range(0x20, 0x3C):
name = {0x20:"lb",0x21:"lhu",0x22:"lw",0x23:"lbu",0x24:"lwl",0x25:"lwr",
0x28:"sb",0x29:"sh",0x2A:"sw",0x2B:"swl",0x2D:"swr",0x2F:"ld"}.get(op, ".word")
if op in (0x28,0x29,0x2A):
return "%s 0x%04x(%s)" % (name, s16(imm), REGS[rs])
if op == 0x2F:
return "ld %s,0x%04x(%s)" % (REGS[rt], s16(imm), REGS[rs])
return "%s %s,0x%04x(%s)" % (name, REGS[rt], s16(imm), REGS[rs])
if op == 0x3C: return "lui %s,0x%04x" % (REGS[rt], imm)
if op == 0x3D: return "ld %s,0x%04x(%s)" % (REGS[rt], s16(imm), REGS[rs])
return ".word 0x%08x" % w
def ram_to_file(ram):
if GAME_RAM_BASE <= ram < GAME_RAM_BASE + 0x200000:
return GAME_ROM_OFF + (ram - GAME_RAM_BASE)
if LIB_RAM_BASE <= ram < LIB_RAM_BASE + 0x40000:
return LIB_ROM_OFF + (ram - LIB_RAM_BASE)
# bss/data in lib segment: RAM 0x8002xxxx etc — not in ROM; skip
return None
def main():
if len(sys.argv) < 2:
print(__doc__); sys.exit(1)
ram = int(sys.argv[1], 16)
n = int(sys.argv[2]) if len(sys.argv) > 2 else 40
off = ram_to_file(ram)
if off is None or off < 0 or off + 4*n > len(ROM):
print("cannot map RAM %x" % ram); sys.exit(1)
for i in range(n):
w = struct.unpack_from('>I', ROM, off + i*4)[0]
addr = ram + i*4
print(" %08x: %08x %s" % (addr, w, disasm_word(w)))
if __name__ == '__main__':
main()