"""Replay a captured VelociRender wire through the REAL firmware in emu860. Feeds each captured (action, payload) command to the firmware's own handler (payload pointer in r16, per the PGI calling convention), building the scene up in the emulated i860's memory, then runs draw_scene -- capturing the IGC coefficient stream the firmware emits (the Tier-1 handoff). Bypasses the transputer/CCB/interrupt path (see HARDWARE-ARCHITECTURE.md): we call the handlers directly, which is valid because they read the raw wire payload. python emu_replay.py [capture.raw.bin] [--verbose] """ import sys, os, struct HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) sys.path.insert(0, r'C:\VWE\TeslaRel410\dpl3-revive\patha') import emu860 from vrboard import Assembler, A, BOOT_ACTIONS MNG = r'C:\VWE\TeslaRel410\sda4\RPLIVE\VREND.MNG' def s26(v): return v - 0x4000000 if v & 0x2000000 else v # From VR_REMOT.C remote_velocirender(): the wire `action` (== the vr_action enum, # which matches vrboard's) drives a switch. init(0) is special -> velocirender_init # + deferred do_init(); every other action indexes a jump table by (action-1). INIT_FN = 0xf040e300 # velocirender_init(param_data) [action 0] DO_INIT = 0xf040afb0 # do_init() -- deferred, before first real command # vr_action -> handler vaddr. Handler addresses matched by function identity from # the case blocks (create verified: reads [p+0]=type,[p+4]=handle; draw_scene and # statistics verified). The switch jump-table indexing is a compiler idiom we # don't need since remote_velocirender's source gives handler-per-action directly. WIRE_HANDLERS = { A.create: 0xf040c180, A.delete: 0xf040c3b8, A.flush: 0xf040cfd8, A.dcs_link: 0xf040dc70, A.list_add: 0xf040d4d0, A.draw_scene: 0xf040e340, A.statistics: 0xf040e940, A.set_geom_verts: 0xf040a9a8, A.set_texmap_texels: 0xf040a030, } def extract_handlers(cpu): return dict(WIRE_HANDLERS) def parse_capture(path): data = open(path, 'rb').read() start = None for i in range(0, len(data) - 8): w = struct.unpack_from(' {'FAULT' if cpu.stopmsg else 'ok'} (steps={cpu.steps})") if cpu.stopmsg: report(idx, 'do_init'); break h = handlers.get(act) if h is None: done[str(ANAME.get(act, act)) + '?'] = done.get(str(ANAME.get(act, act)) + '?', 0) + 1 continue cpu.call(h, args=(ppl,), maxsteps=8_000_000) done[ANAME.get(act, act)] = done.get(ANAME.get(act, act), 0) + 1 if cpu.stopmsg: report(idx, f"action {ANAME.get(act,act)} (built {sum(done.values())} cmds)"); break if verbose and act == A.draw_scene: print(f"[cmd {idx}] draw_scene ok IGC writes: {len(cpu.igc)}") print("\nreplayed:", done) print(f"IGC/board writes captured: {len(cpu.board_log)}") if __name__ == '__main__': main()