"""M4b -- the offline end-to-end seam: real game wire in, FAITHFUL frame sequence out, no DOSBox. The whole chain at speed: fifodump (VPXM wire) -> production VREND.MNG on the C i860 core (351x) -> per-draw coefficient program captured LIVE from C memory -> M5 faithful render (verified perspective divide + real-texture texel decode) -> frame_NNNN.png sequence + timing report This is the dress-rehearsal for the live DOSBox seam (M4c): identical pipeline, the wire arriving from a file instead of the C012 link device. Fidelity is the already-verified M5 path (b74b0c89 / a24210ad); what M4b proves is that the FULL mission drives it, per draw, at speed. python m4b_frames.py [max_frames] """ import sys, os, time, struct HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE); sys.path.insert(0, os.path.dirname(HERE)) sys.path.insert(0, r'C:\VWE\TeslaRel410\dpl3-revive\patha') import emu860, emu_main, emu860c import numpy as np from PIL import Image from driver import boot, CpuShim from vrboard import A from texstore import build_texstore from dpl_sampler import mode_to_texflags, wrap_index, composite ANAME = {int(a): a.name for a in A} emu860.Mem.log = lambda self, *a, **k: None SRC = sys.argv[1] MAXF = int(sys.argv[2]) if len(sys.argv) > 2 else 12 PROG_LO, PROG_HI = 0x08158000, 0x08170000 # per-draw effect-program window W, H = 832, 512 EDGE = {0x42, 0x0d, 0x2c} # --- parse the wire once: build the queue AND the texture store from it ------- data = open(SRC, 'rb').read() recs = []; off = 0 while True: i = data.find(b'VPXM', off) if i < 0: break ln = struct.unpack_from('= 4: a = struct.unpack_from('> 8) & 0xff) in EDGE and len(edges) < 6: edges.append((f32(rd(p + 4)), f32(rd(p + 8)), f32(rd(p + 12)))); p += 16 if len(edges) < 3: continue ep = np.ones((H, W), bool); en = np.ones((H, W), bool) for Ae, Be, Ce in edges: v = Ae * xx + Be * yy + Ce ep &= v >= 0; en &= v < 0 inside = ep | en # inside = all-positive OR all-negative if not inside.any(): continue zp = tzp = up = vp = None; tid = None; q = p for _ in range(70): w = rd(q); op = (w >> 8) & 0xff; ad = w & 0xff if op == 0x21 and zp is None: zp = (f32(rd(q + 4)), f32(rd(q + 8)), f32(rd(q + 12))) if op == 0x43 and ad == 32 and tzp is None: tzp = (f32(rd(q + 4)), f32(rd(q + 8)), f32(rd(q + 12))) if op == 0x43 and ad == 58 and up is None: up = (f32(rd(q + 4)), f32(rd(q + 8)), f32(rd(q + 12))) if op == 0x43 and ad == 78 and vp is None: vp = (f32(rd(q + 4)), f32(rd(q + 8)), f32(rd(q + 12))) if op == 0xf7 and ad in (141, 142) and tid is None: tid = (rd(q + 4) >> 2) & 0x3f q += 4 if zp is None: continue z = zp[0] * xx + zp[1] * yy + zp[2] inside &= z > zbuf if not inside.any(): continue if up and vp and tzp and texlist: h, (tu, tv, mode, arr) = texlist[(tid or 0) % len(texlist)] wrap_u, wrap_v, cut_mode = mode_to_texflags(mode) tzv = tzp[0] * xx + tzp[1] * yy + tzp[2] tzv = np.where(np.abs(tzv) < 1.0, np.sign(tzv) + (tzv == 0), tzv) # VERIFIED divide: texel8 = (texu/texz)*2048 (3 int + 8 texel bits) ucoord = ((up[0] * xx + up[1] * yy + up[2]) / tzv * 2048).astype(np.int64) vcoord = ((vp[0] * xx + vp[1] * yy + vp[2]) / tzv * 2048).astype(np.int64) ui = wrap_index((ucoord * tu) >> 8, tu, wrap_u) vi = wrap_index((vcoord * tv) >> 8, tv, wrap_v) samp = arr[vi, ui] drawmask = composite(img, inside, samp, cut_mode) else: img[inside] = (60, 60, 70) drawmask = inside zbuf[drawmask] = z[drawmask] drawn += 1 if not drawn: return False, 0 Image.fromarray(img, 'RGB').save(os.path.join(HERE, 'frame_%04d.png' % nth)) return True, drawn # --- run the whole mission, one faithful frame per draw_scene ---------------- frames = 0 prev_draw = False t0 = time.time() render_t = 0.0 while frames < MAXF and r.qi < len(r.queue): reason, _ = emu860c.run(500_000_000) if reason == 3: continue if reason != 0: print("core reason %d" % reason, flush=True); break pc = emu860c.getstate()['pc'] h = r.hooks.get(pc) if h is None: print("sentinel", flush=True); break if pc == RECV: if prev_draw: rt0 = time.time() ok, drawn = render_faithful(frames) render_t += time.time() - rt0 if ok: print("frame %d: %d quads (cmd %d)" % (frames, drawn, r.qi), flush=True) frames += 1 prev_draw = False if r.qi < len(r.queue) and ANAME.get(r.queue[r.qi][0]) == 'draw_scene': prev_draw = True if h(shim) == 'done': break wall = time.time() - t0 print("done: %d faithful frames in %.1fs (%.1fs firmware, %.1fs render), cmd %d" % (frames, wall, wall - render_t, render_t, r.qi), flush=True)