diff --git a/emulator/firmware-decomp/capfw7.bss b/emulator/firmware-decomp/capfw7.bss new file mode 100644 index 0000000..6c4b173 Binary files /dev/null and b/emulator/firmware-decomp/capfw7.bss differ diff --git a/emulator/firmware-decomp/capfw7.mng b/emulator/firmware-decomp/capfw7.mng new file mode 100644 index 0000000..705519c Binary files /dev/null and b/emulator/firmware-decomp/capfw7.mng differ diff --git a/emulator/firmware-decomp/extract_capfw.py b/emulator/firmware-decomp/extract_capfw.py new file mode 100644 index 0000000..7ce7f6d --- /dev/null +++ b/emulator/firmware-decomp/extract_capfw.py @@ -0,0 +1,52 @@ +"""Extract the firmware image embedded in a wire capture's 860-boot preamble. + +The dPL3 host boots the render board by STREAMING the whole .MNG over the wire +(VR_COMMS.C boot_860): args860("i860 50MHz and kicking"), one 40-byte code860 +packet = the 7-word header {csize,dsize,bsize,cstart,dstart,bstart,entry}, +then code860/data860/bss860 segment packets. So every capture carries the +EXACT firmware build the game ran -- run THAT for a version-matched replay +(sda4/RPLIVE/VREND.MNG is a different build: text 0x39ec0 vs cap7's 0x31440). + +Writes .mng (12-byte header + text + data, emu860.load_mng format) and +.bss (the streamed bss-segment content -- NOT all zeros; load at bstart). + +Usage: python extract_capfw.py +""" +import sys, os, struct +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(0, r'C:\VWE\TeslaRel410\dpl3-revive\patha') +from emu_replay import parse_capture +from vrboard import A + +cap = sys.argv[1] if len(sys.argv) > 1 else r'C:\VWE\TeslaRel410\dpl3-revive\patha\cap7.raw.bin' +out = sys.argv[2] if len(sys.argv) > 2 else os.path.join( + os.path.dirname(os.path.abspath(__file__)), 'capfw7') + +cmds = parse_capture(cap) +code, data, bss, hdr = [], [], [], None +for a, pl in cmds: + if a == A.code860: + if hdr is None and len(pl) == 40: + hdr = struct.unpack_from('<7I', pl, 0) + continue + code.append(pl) + elif a == A.data860: + data.append(pl) + elif a == A.bss860: + bss.append(pl) + +csize, dsize, bsize, cstart, dstart, bstart, entry = hdr +text, dat, bs = b''.join(code), b''.join(data), b''.join(bss) +assert len(text) == csize and len(dat) == dsize and len(bs) == bsize, \ + f"stream/header mismatch: {len(text):#x}/{csize:#x} {len(dat):#x}/{dsize:#x} {len(bs):#x}/{bsize:#x}" + +with open(out + '.mng', 'wb') as f: + f.write(struct.pack(' {out}.mng (+ {out}.bss, {sum(1 for b in bs if b)} nonzero bytes)") diff --git a/emulator/firmware-decomp/run_to_draw.py b/emulator/firmware-decomp/run_to_draw.py new file mode 100644 index 0000000..d0fc14f --- /dev/null +++ b/emulator/firmware-decomp/run_to_draw.py @@ -0,0 +1,55 @@ +"""Marathon runner: authentic main() + cap7 through the first draw_scene frames. + +Writes progress to stdout and dumps the captured IGC/board stream + a state +summary when the command budget is reached (or on fault). +""" +import sys, os, time, struct +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import emu860, dis860, emu_main + +CAP = sys.argv[1] if len(sys.argv) > 1 else r'C:\VWE\TeslaRel410\dpl3-revive\patha\cap7.raw.bin' +MAXC = int(sys.argv[2]) if len(sys.argv) > 2 else 1600 +TIME_BUDGET = float(sys.argv[3]) if len(sys.argv) > 3 else 3300.0 + +emu860.Mem.log = lambda self, *a, **k: None +r = emu_main.MainRunner(CAP, max_cmds=MAXC) +cpu = r.cpu +cpu.wr(1, cpu.RET_SENTINEL); cpu.wr(2, 0xc0000); cpu.wr(16, 0); cpu.wr(17, 0) +cpu.pc = emu_main.MAIN + +t0 = time.time(); last = t0 +result = 'time' +while True: + pc = cpu.pc + if pc == cpu.RET_SENTINEL: + result = 'main-returned'; break + if pc < 0x100000: + result = f'derail->{pc:#x}'; break + h = r.hooks.get(pc) + if h: + if h(cpu) == 'done': + result = 'queue-done'; break + continue + if not cpu.step(): + result = f'STOP {cpu.stopmsg}'; break + now = time.time() + if now - last > 60: + last = now + print(f" .. t={now-t0:6.0f}s steps={cpu.steps:,} cmds={r.qi} " + f"igc={len(cpu.igc)} board={len(cpu.board_log)}", flush=True) + if now - t0 > TIME_BUDGET: + result = 'time-budget'; break + +print(f"\nRESULT: {result}") +print(f"steps={cpu.steps:,} cmds={r.qi}/{len(r.queue)} done={dict(r.done)}") +print(f"replies={r.replies[:30]}") +print(f"IGC writes={len(cpu.igc)} board-log={len(cpu.board_log)}") +if cpu.stopmsg or result.startswith('derail'): + for tpc, tw in cpu.tail[-16:]: + m, ops = dis860.decode(tw, tpc) + print(f" {tpc:#010x}: {tw:08x} {m} {ops}") +out = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'igc_stream.bin') +with open(out, 'wb') as f: + for k, a, v in cpu.board_log: + f.write(struct.pack(' {out} ({len(cpu.board_log)} entries)")