Files
TeslaRel410/emulator/firmware-decomp/run_to_draw.py
T
CydandClaude Opus 4.8 753564aef2 Capture-embedded firmware extracted: cap7 carries the EXACT build the game ran
VR_COMMS.C boot_860() shows the host boots the render board by streaming the
whole .MNG over the wire: args860 ("i860 50MHz and kicking"), a 40-byte
code860 packet holding the 7-word header {csize,dsize,bsize,cstart,dstart,
bstart,entry}, then the code/data/bss segments. cap7's preamble is therefore
a complete firmware image -- and a DIFFERENT BUILD than sda4/RPLIVE/VREND.MNG
(text 0x31440 vs 0x39ec0), which explains the residual global-address
mismatches (version skew) when replaying cap7 against the sda4 build.

The wire header also confirms the link layout from the protocol itself:
cstart=0xf0400000, dstart=0x00001000, bstart=0x0001f4a0, entry=0xf0400000.
Note the streamed "bss" content is NOT zeros (10685/14272 nonzero bytes).

extract_capfw.py reassembles the image (capfw7.mng + capfw7.bss, committed);
it boots in emu860 to 0xf040062c then needs op 0x0d (new ctrl-reg variant).
run_to_draw.py = marathon runner with board-stream dump.

Next: re-derive the hook addresses (main/dN_receive/allocator/...) for the
capture build and replay cap7 against its own firmware, version-matched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 11:43:02 -05:00

56 lines
2.1 KiB
Python

"""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('<BII', 1 if k == 'w' else 0, a, v))
print(f"board stream -> {out} ({len(cpu.board_log)} entries)")