From 20dbd90026b26847400b62ed2ce70f6f11a17fca Mon Sep 17 00:00:00 2001 From: Cyd Date: Wed, 15 Jul 2026 07:01:52 -0500 Subject: [PATCH] Tier 0 emulator: wire-command replay harness (emu_replay.py) Feeds the real captured VelociRender wire (dpl3-revive/patha/*.raw.bin, from the soft-renderer work) to the firmware's own command handlers in emu860 -- the path to building a scene and rendering a frame with the board's own code. - Parses captures via vrboard's Assembler (skips the .BTL boot, frames action+payload). cap7 = a clean small scene (17 zones / 22 instances / 24 geometry / 1 view). - Calls each command's handler directly with the payload pointer in r16 -- verified valid: create's prologue reads [r16+0]=type, [r16+4]=handle, i.e. the raw wire layout (no CCB wrapping needed). Bypasses the un-emulated transputer. - Confirmed WIRE-action -> handler map by function identity (create/flush/ dcs_link/list_add/draw_scene/statistics/set_texmap_texels). Two blockers identified for a full frame (documented in the memory + README): 1. The dispatch jump table is indexed by an INTERNAL command code, not the wire action (velocirender_input remaps wire->internal first, e.g. draw=9 ->12, flush=3->30); the high-frequency per-frame commands (0x09, 0x1d artics, 0x2a) still need that remap reversed. 2. init(0) must run first to set up the allocator/name-table/scene-root -- cold create() faults without it. Co-Authored-By: Claude Fable 5 --- emulator/firmware-decomp/emu_replay.py | 104 +++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 emulator/firmware-decomp/emu_replay.py diff --git a/emulator/firmware-decomp/emu_replay.py b/emulator/firmware-decomp/emu_replay.py new file mode 100644 index 0000000..e39fc43 --- /dev/null +++ b/emulator/firmware-decomp/emu_replay.py @@ -0,0 +1,104 @@ +"""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 + +# Confirmed WIRE-action -> handler map (vaddr). IMPORTANT: the firmware's +# dispatch jump table (file 0x47cb8) is indexed by an INTERNAL command code, NOT +# the wire action -- velocirender_input remaps wire->internal first (e.g. wire +# draw_scene=9 -> internal index 12; flush=3 -> 30; statistics=15 -> 31; while +# create/list_add/dcs_link are identity). So we can't index that table by the +# capture's action. These are matched by function identity instead (create's +# prologue verified reads type/handle; statistics verified returns; draw_scene, +# flush, list_add, dcs_link identified in the dispatch analysis). +# TODO: reverse the wire->internal remap in velocirender_input to complete the +# map for the high-frequency per-frame commands (0x09 draw, 0x1d artics, 0x2a). +WIRE_HANDLERS = { + A.create: 0xf040c180, + A.flush: 0xf040cfd8, + A.dcs_link: 0xf040dc70, + A.list_add: 0xf040d4d0, + A.draw_scene: 0xf040e340, + A.statistics: 0xf040e940, + A.set_texmap_texels: 0xf040a030, + # A.init handler + the 0x1d/0x2a artic handlers: need the remap (see TODO). +} + +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('