ACCEPTANCE 2: fxtest content renders -- perspective ground + textured boxes

The per-draw effect programs (0x816f000, second DMA emitter) parse and
execute through the named ISA: 9 primitives, z-buffered perspective scene
with u/v texture gradients. The earlier 'fixed per-tile program' conclusion
was a sniffer artifact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-19 09:47:21 -05:00
co-authored by Claude Opus 4.8
parent fb04de017e
commit cc7c95d738
2 changed files with 94 additions and 0 deletions
@@ -322,3 +322,16 @@ Remaining to full purity: name the texz seed op + extract the real ramp table.
the emulator (the dn* unpack programs then define the texel wire format).
- Registry census at cmd 13000: 1186 live objects (405 type-9 + 405 type-10
pairs = effect+texmap, 159 type-11, 48 type-8, ...).
## ACCEPTANCE 2: fxtest CONTENT RENDERS (2026-07-19 ~10:30)
The draws build FRESH float-coefficient programs at 0x816f000 (emitter
0xf0400ebc — a second DMA writer; the earlier "4 fixed payloads" was a sniffer
artifact) + transformed vertex buffers (0x2c7xxxxx+, 80B records). Packet
shape per primitive: {19w setup: SETENABS + op-0x2c edges + MEMltTREE z +
z-write} + {80w x4-MIP ladder: TREEintoMEM_L3(texz/texu/texv) x5 rungs, with
FOOTER.SS's Cturn_z_to_tex_by_4 constant verbatim}. render_fx.py executes
them: 9 primitives -> a PERSPECTIVE GROUND PLANE + TEXTURED 3D BOXES (the
classic VWE building scene) with correct z-occlusion; u/v gradients shown as
false colour (fx_render/fx_depth.png in the scratchpad). Remaining polish:
op-0x2c edge exact semantics (treated as winding-agnostic edge) + real texel
sampling via the TXDN stream.
+81
View File
@@ -0,0 +1,81 @@
"""Render fxtest's per-draw effect primitives from fx_program.pkl: per pair
{19w setup: edges+z, 80w ladder: texz/u/v planes}, rasterize with z-buffer and
visualize (u,v) as colour + z as depth. Structure render (no texel sampling)."""
import pickle, os, struct
import numpy as np
from PIL import Image
S = r'C:\Users\cyd\AppData\Local\Temp\claude\c--VWE-TeslaRel410\4e848c76-6e89-4034-8047-d8d491cb32d8\scratchpad'
d = pickle.load(open(os.path.join(S, 'fx_program.pkl'), 'rb'))
prog = d['prog']
def f32(w): return struct.unpack('<f', struct.pack('<I', w & 0xffffffff))[0]
def rd(a): return prog.get(a, 0)
# find setup blocks: SETENABS followed by 0x..2c00-family edge headers
setups = []
addrs = sorted(prog)
for a in addrs:
if rd(a) == 0x100 and ((rd(a + 4) >> 8) & 0xff) == 0x2c:
setups.append(a)
print("%d effect primitives" % len(setups))
W, H = 832, 512
yy, xx = np.mgrid[0:H, 0:W].astype(np.float64)
zbuf = np.full((H, W), -1e30)
rgb = np.zeros((H, W, 3))
drawn = 0
for a in setups:
# edges: 4-word {hdr(0x2c), A, B, C} groups until a non-0x2c header
en_p = np.ones((H, W), bool); en_n = np.ones((H, W), bool)
p = a + 4
nedges = 0
while ((rd(p) >> 8) & 0xff) == 0x2c and nedges < 6:
A, B, C = f32(rd(p+4)), f32(rd(p+8)), f32(rd(p+12))
v = A * xx + B * yy + C
en_p &= v >= 0; en_n &= v < 0
p += 16; nedges += 1
en = en_p | en_n
if not en.any():
continue
# scan forward for the z instr (op 0x21) then the ladder (first texu/texv L3)
zp = None; up = None; vp = None
q = p
for _ in range(40):
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 == 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 up and vp and zp:
break
q += 4
if zp is None:
continue
z = zp[0] * xx + zp[1] * yy + zp[2]
en &= z > zbuf
if not en.any():
continue
zbuf[en] = z[en]
if up and vp:
u = up[0] * xx + up[1] * yy + up[2]
v = vp[0] * xx + vp[1] * yy + vp[2]
# u/v as colour: hue-ish mapping, normalized per prim
un = np.clip((u - u[en].min()) / max(1e-9, np.ptp(u[en])), 0, 1)
vn = np.clip((v - v[en].min()) / max(1e-9, np.ptp(v[en])), 0, 1)
rgb[..., 0][en] = 60 + 180 * un[en]
rgb[..., 1][en] = 40 + 160 * vn[en]
rgb[..., 2][en] = 220 - 140 * un[en]
else:
rgb[..., 0][en] = 200; rgb[..., 1][en] = 200; rgb[..., 2][en] = 200
drawn += 1
print("drawn %d" % drawn)
img = np.clip(rgb, 0, 255).astype(np.uint8)
Image.fromarray(img, 'RGB').save(os.path.join(S, 'fx_render.png'))
zv = zbuf.copy(); m = zv > -1e29
zi = np.zeros((H, W), np.uint8)
if m.any():
zn = (zv - zv[m].min()) / max(1e-9, (zv[m].max() - zv[m].min()))
zi[m] = (40 + 215 * zn[m]).astype(np.uint8)
Image.fromarray(zi, 'L').save(os.path.join(S, 'fx_depth.png'))
print("wrote fx_render.png + fx_depth.png")