"""Run the render firmware's own main() and feed it real wire captures through dN_receive. This is the AUTHENTIC path: the firmware's main() does its own startup (newBytes pools, dN_mynode/dN_nodes) and enters remote_velocirender()'s while(1) { dN_receive(); dispatch; } loop. We hook only the transputer-LINK primitives (they'd block on real hardware) and the page-allocation core (whose VM page tables the monitor builds -- Tier-2 = run VRENDMON on a T425); everything else -- init, do_init, dispatch, handlers, allocator wrappers -- is the firmware's own code. Two builds are mapped: sda4 -- sda4/RPLIVE/VREND.MNG (text 0x39ec0), our archive copy. capfw7 -- capfw7.mng (text 0x31440), extracted from cap7's own wire-boot preamble (extract_capfw.py): the EXACT build the game ran, with an EXTENDED action set (42 actions vs the SDK enum's 24). Usage: python emu_main.py [capture.raw.bin] [--fw sda4|capfw7] [--max-cmds N] [--verbose] """ 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') import emu860 from emu_replay import parse_capture from vrboard import A _HERE = os.path.dirname(os.path.abspath(__file__)) MAPS = { 'sda4': dict( fw=r'C:\VWE\TeslaRel410\sda4\RPLIVE\VREND.MNG', main=0xf0403f10, bla=0xf04285f0, mynode=0xf0402570, nodes=0xf0402610, receive=0xf0402450, vinit=0xf040e300, doinit=0xf040afb0, reply=0xf040ae90, sendprim=0xf0402370, putchar=0xf04030c8, alloc=0xf04350b8, v2p=0xf0434d90, lockacq=0xf042bdf0, lockrel=0xf042be48, # create's table routes types 2-5 through the dispatch tail at `chain` # (r31=0-on-entry semantics); new_node's init table at chain_tbl. chain=0xf040bd0c, chain_tbl=0xece8, chain_tail=0xf040bd1c, seeds=[ (0x1000, 0), # _processorId (monitor-written slot) (0x1ecc0, 0), (0x1ecc4, 0), # sbrk break ptrs -> lazy-init (0x14fc4, 0x00300000), # shared uncached blocks (locks at +0x18) (0x14fc8, 0x00301000), (0x14fcc, 0x00302000), ], ), 'capfw7': dict( fw=os.path.join(_HERE, 'capfw7.mng'), main=0xf0403f80, bla=0xf0420c00, mynode=0xf04025e0, nodes=0xf0402680, receive=0xf04024c0, vinit=0xf040c5d8, doinit=0xf0409440, reply=0xf0409320, sendprim=None, putchar=None, alloc=0xf042c628, v2p=0xf042c300, lockacq=0xf0423360, lockrel=0xf04233b8, chain=None, chain_tbl=None, chain_tail=None, # intdiv hook retired 2026-07-15: the exact MAME-validated pipeline model # (PFAM/PFMAM families, FLAGM->MPIPE, sp/rp bit swap, fdest bypass) makes # the firmware's own Newton-Raphson divide compute correctly. intdiv=None, igcwait=0xf0421510, # post-flush IGC drain poll (ld.l 0(r16)) seeds=[ (0x1000, 0), # _processorId ], ), } ANAME = {int(a): a.name for a in A} class MainRunner: def __init__(self, cap, fw='capfw7', verbose=False, max_cmds=None): self.map = MAPS[fw] self.cpu = emu860.I860(trace=0) self.cpu.load_mng(self.map['fw']) self.cpu.map_control(); self.cpu.map_board() for addr, val in self.map['seeds']: self.cpu.mem.w32(addr, val) # The 860-boot preamble (args860/code860/data860/bss860/hspcode) is # consumed by the MONITOR on real hardware, not the app -- the capfw7 # app's dispatch rejects those actions (prints the message and dies). # We are the monitor and the image is already loaded: skip them. BOOT = {A.hspcode, A.code860, A.data860, A.bss860, A.args860} cmds = [(a, p) for a, p in parse_capture(cap) if a not in BOOT] if max_cmds: cmds = cmds[:max_cmds] self.queue = cmds self.qi = 0 self.verbose = verbose self.done = {} self.replies = [] self.console = [] m = self.map self.hooks = { m['bla']: self.h_ret, m['mynode']: lambda c: self.h_writeback(c, 2), m['nodes']: lambda c: self.h_writeback(c, 3), m['receive']: self.h_receive, m['reply']: self.h_reply, m['alloc']: self.h_allocpages, m['v2p']: lambda c: self.h_ret(c, c.rd(16) & ~0xfff), m['lockacq']: self.h_ret, m['lockrel']: self.h_ret, } if m['sendprim']: self.hooks[m['sendprim']] = self.h_ret if m['putchar']: self.hooks[m['putchar']] = self.h_console if m['chain']: self.hooks[m['chain']] = self.h_chainfix if m.get('intdiv'): self.hooks[m['intdiv']] = self.h_intdiv if m.get('igcwait'): self.hooks[m['igcwait']] = self.h_igcwait self.heap = [0x08010000, 0x0c010000] # bump allocator over the DRAM banks def h_igcwait(self, cpu): # Post-flush drain wait: the firmware polls [r16] until the IGC clears # it after consuming the region queue. We are the IGC: consume instantly. cpu.mem.w32(cpu.rd(16), 0) cpu.step() return True def h_intdiv(self, cpu): # Runtime integer divide (r22 = r22/r23, result via ftrunc+fxfr): the # compiled Newton-Raphson relies on exact dual-op pipeline timing our # functional FP model doesn't reproduce yet -- compute exactly instead. a = cpu.rd(22); b = cpu.rd(23) if a & 0x80000000: a -= 1 << 32 if b & 0x80000000: b -= 1 << 32 q = abs(a) // abs(b) if b else 0 if (a < 0) != (b < 0): q = -q cpu.wr(22, q & 0xffffffff) cpu.pc = cpu.rd(1) return True # ---- hook helpers: emulate a called function returning ---- def h_ret(self, cpu, ret=None): if ret is not None: cpu.wr(16, ret) cpu.pc = cpu.rd(1) return True def h_writeback(self, cpu, val): cpu.mem.w32(cpu.rd(16), val) return self.h_ret(cpu) def h_reply(self, cpu): self.replies.append(cpu.rd(16)) return self.h_ret(cpu) def h_console(self, cpu): self.console.append(cpu.rd(16) & 0xff) return self.h_ret(cpu) def h_allocpages(self, cpu): sel = cpu.rd(16) bank = 1 if sel == 1 else 0 size = (cpu.rd(17) & 0xffff) << 12 ptr = self.heap[bank] self.heap[bank] += size return self.h_ret(cpu, ptr) def h_chainfix(self, cpu): if cpu.rd(31) == cpu.pc: # arrived via a table jump idx4 = cpu.rd(30) tgt = cpu.mem.r32(self.map['chain_tbl'] + idx4) cpu.wr(31, 0) cpu.pc = self.map['chain_tail'] if tgt == self.map['chain'] else tgt return True cpu.step() return True def h_receive(self, cpu): if self.qi >= len(self.queue): return 'done' act, pl = self.queue[self.qi]; self.qi += 1 buf = cpu.rd(18) cpu.mem.w32(buf, act) cpu.mem.load_blob(buf + 4, pl or b'') cpu.mem.w32(cpu.rd(16), 0) # *client cpu.mem.w32(cpu.rd(17), 4 + len(pl)) # *receive_bytes n = ANAME.get(act, act) self.done[n] = self.done.get(n, 0) + 1 if self.verbose or self.qi % 1000 == 0 or self.qi <= 8: print(f" [{self.qi:5}/{len(self.queue)}] {str(n):<22} len={len(pl):6} " f"steps={cpu.steps:>13,} igc={len(cpu.igc)} board={len(cpu.board_log)}", flush=True) return self.h_ret(cpu) def start(self): cpu = self.cpu cpu.wr(1, cpu.RET_SENTINEL); cpu.wr(2, 0x000c0000) cpu.wr(16, 0); cpu.wr(17, 0) # argc, argv cpu.pc = self.map['main'] return cpu def run(self, budget=2_000_000_000): cpu = self.start() watch = {self.map['vinit']: 'velocirender_init', self.map['doinit']: 'do_init'} while cpu.steps < budget: pc = cpu.pc if pc == cpu.RET_SENTINEL: print(f"main RETURNED after {cpu.steps:,} steps"); break h = self.hooks.get(pc) if h: r = h(cpu) if r == 'done': print(f"\ncapture exhausted after {self.qi} commands, " f"{cpu.steps:,} steps"); break continue if pc in watch: print(f" >> {watch.pop(pc)} entered (steps={cpu.steps:,})", flush=True) if not cpu.step(): print(f"\nSTOP: {cpu.stopmsg}") import dis860 for tpc, tw in cpu.tail[-12:]: m, ops = dis860.decode(tw, tpc) print(f" {tpc:#010x}: {tw:08x} {m} {ops}") break return cpu def main(): args = [a for a in sys.argv[1:] if not a.startswith('--')] cap = args[0] if args else r'C:\VWE\TeslaRel410\dpl3-revive\patha\cap7.raw.bin' fw = 'capfw7' if '--fw' in sys.argv: fw = sys.argv[sys.argv.index('--fw') + 1] verbose = '--verbose' in sys.argv mc = None if '--max-cmds' in sys.argv: mc = int(sys.argv[sys.argv.index('--max-cmds') + 1]) r = MainRunner(cap, fw=fw, verbose=verbose, max_cmds=mc) print(f"{os.path.basename(cap)}: {len(r.queue)} wire commands queued (fw={fw})") cpu = r.run() print(f"\nreplayed: {r.done}") print(f"replies: {r.replies[:20]}") print(f"IGC writes: {len(cpu.igc)} board-log entries: {len(cpu.board_log)}") if cpu.board_log: out = os.path.join(_HERE, 'igc_stream.bin') with open(out, 'wb') as f: for k, a, v in cpu.board_log: f.write(struct.pack(' {out}") if __name__ == '__main__': main()