diff --git a/emulator/firmware-decomp/IGC-ENCODING-DERIVATION.md b/emulator/firmware-decomp/IGC-ENCODING-DERIVATION.md index 96ef9bd6..7f6f71bf 100644 --- a/emulator/firmware-decomp/IGC-ENCODING-DERIVATION.md +++ b/emulator/firmware-decomp/IGC-ENCODING-DERIVATION.md @@ -211,6 +211,24 @@ the eof-block ops 0xfa/0x7c/0x1f/0xf2/0x97/0x05/0x49/0x30/0x75/0xdb (builder = one linear fn 0xf041ee30-0xf041f7b0, per-word pcs logged in builder_trace2.log — disasm each site to name them). +## FRAME PIPELINE STATUS (2026-07-18 early) + +frame_render.py (scratchpad) executes a send-captured frame end-to-end: +cap7 draw#1 = 52 tiles (13x4 ✓ for 832x512; tile-id origin = (id&0x1f)*64, +(id>>5&0x1f)*128 ✓), 5 distinct payloads (4/16/33/41/69 words) broadcast to all +tiles. Output with 11 ops stubbed = uniform navy (constant from SCAintoMEM in +the eof planes) — structurally sound, pattern generators missing. + +**ARCHITECTURE FINDING:** fxtest's animation frame ALSO sends only 4 distinct +payloads — the per-tile IGC program is FIXED; scene content flows through the +TEXTURE path (TXDN per tile + texu/texv computed per-pixel by the 69-block +transform + ramp/mode bits). The wire 'set_texmap_texels' payloads for cap7 are +FLOAT blocks (vertex/normal-like: the action-25 geometry sub-protocol), not +texel arrays — the bar colours are NOT a simple texel table; they come through +the ramp/eof machinery (the stubbed ops). NEXT: name the 11 stubbed ops from the +builder disasm (writer pcs in builder_trace2.log, builder = 0xf041ee30-f7b0 +straight-line) + implement the texture/ramp lookup; then bars. + ## NEXT (the miner) Write `eofs_mine.py`: parse EOF.S per function; track `st.l`/`fst.l` stores through the coeff pointer; recover, in order, each emitted word (int literals diff --git a/emulator/firmware-decomp/render_wide30.py b/emulator/firmware-decomp/render_wide30.py new file mode 100644 index 00000000..dab4559e --- /dev/null +++ b/emulator/firmware-decomp/render_wide30.py @@ -0,0 +1,100 @@ +"""Render the cap7 test-scene from cap7_wide30.pkl's REAL captured IGC packets. +Packets = {SETENABS, 3x TREEgeZERO_L3 edges, MEMltTREE_L3 z-test, TREEintoMEM_L0 +z-write, 3x color-plane instrs, scalar}. Executed vectorized (numpy) on a full +832x512 surface with a persistent 1/w z-buffer (MEMltTREE: keep where old < new +=> greater 1/w wins = closer wins).""" +import sys, pickle, struct +import numpy as np + +S = r'C:\Users\cyd\AppData\Local\Temp\claude\c--VWE-TeslaRel410\4e848c76-6e89-4034-8047-d8d491cb32d8\scratchpad' +W, H = 832, 512 + +d = pickle.load(open(S + r'\cap7_wide30.pkl', 'rb')) +mem = d['mem'] + +def f32(w): + return struct.unpack('= 0 + en_neg &= v < 0 + en = en_pos | en_neg # winding-agnostic + if not en.any(): + continue + # z instr: scan forward from a+48 for the MEMltTREE header (preambles vary) + za = None + for off in range(48, 48 + 44, 4): + if (rd(a + off) & 0xffffff00) == 0x44ea2100: + za = a + off + break + if za is None: + continue + A, B, C = rdf(za + 4), rdf(za + 8), rdf(za + 12) + z = A * xx + B * yy + C + en &= z > zbuf + if not en.any(): + continue + zbuf[en] = z[en] + # z-write at a+64 (1 word), colors from a+68: three 5-word groups + # {clmp-hdr, pword, w2, w3, w4} -- empirical: value plane = {B?, A?, C?} + cols = [] + ca = za + 20 + for c in range(3): + g = ca + 20 * c + hdr = rd(g) + w1, w2, w3, w4 = rd(g + 4), rd(g + 8), rd(g + 12), rd(g + 16) + # try: the plane floats are the last three words {A?, B?, C?} + fA, fB, fC = f32(w2), f32(w3), f32(w4) + cols.append((fA, fB, fC)) + for c, (fA, fB, fC) in enumerate(cols): + plane = fA * xx + fB * yy + fC + rgb[..., c][en] = np.clip(plane[en], 0, 255) + drawn += 1 + +print("drawn: %d packets" % drawn) +print("rgb range:", rgb.min(), rgb.max()) +# normalize for viewing if the scale is off +mx = rgb.max() or 1.0 +img = (np.clip(rgb / (mx if mx > 255 else 255), 0, 1) * 255).astype(np.uint8) +from PIL import Image +Image.fromarray(img, 'RGB').save(S + r'\wide30_render.png') +# also a z-buffer visualization (structure check independent of colors) +zv = zbuf.copy() +m = zv > -1e29 +if m.any(): + zn = (zv - zv[m].min()) / max(1e-9, (zv[m].max() - zv[m].min())) + zi = np.zeros((H, W), np.uint8) + zi[m] = (40 + 215 * zn[m]).astype(np.uint8) + Image.fromarray(zi, 'L').save(S + r'\wide30_depth.png') +print("wrote wide30_render.png + wide30_depth.png")