#!/usr/bin/env python3 """retroDE_ps2 — Ch351 step 1: isolate reciprocal quantization from S1 under-interpolation banding. Compares the RTL FB dump (sh3_real_fb_out.mem, written by tb_top_psmct32_sh3_real_draw_demo) against TWO host references over the identical crop geometry: FLOAT — ideal perspective (the Codex pixel-diff oracle). RECIP-8b — RTL-FAITHFUL: fixed-point vertex attrs + the 8-bit gs_reciprocal_stub (gs_make_sh3_real_draw_fixture). Verdict: - RTL ≈ RECIP-8b (tight) AND RECIP-8b ≠ FLOAT (loose) -> the residual is RECIPROCAL QUANTIZATION. Widening gs_reciprocal_stub IDX_BITS should fix it (Ch351 step 2). - RTL ≠ RECIP-8b (still loose) -> S1 attribute UNDER-INTERPOLATION beyond the reciprocal. Usage: gs_ch351_oracle.py (run gs_make_sh3_real_draw_fixture.py + the TB first to produce the .mem files) """ import sys, os ROOT=os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)),"..")) DATA=os.path.join(ROOT,"sim","data","top_psmct32_raster_demo") FBPXW, CH, TW, TH = 256, 120, 512, 512 def loadmem(name): return [int(l,16) for l in open(os.path.join(DATA,name)) if l.strip() and not l.strip().startswith("//")] def main(): fb = loadmem("sh3_real_fb_out.mem") rmF = loadmem("sh3_real_refmap.mem") rmR = loadmem("sh3_real_refmap_recip.mem") idxw = loadmem("sh3_real_idx.mem") pal = loadmem("sh3_real_pal.mem") def sh3_idx(u,v): lin=v*TW+u; return (idxw[lin>>2] >> (8*(lin&3))) & 0xFF def exp_cell(u,v): return pal[sh3_idx(u,v)] & 0xFFFFFF palset = set(p & 0xFFFFFF for p in pal) def histo(refmap, label): hist=[0]*5; ihist=[0]*5; tot=0; itot=0; exact_color=0; clut_bad=0 sdu=sdv=0.0; nd=0 for o in range(FBPXW*CH): rm=refmap[o] if not (rm>>31): continue interior=(rm>>30)&1; tu=(rm>>9)&0x1FF; tv=rm&0x1FF col = fb[o] & 0xFFFFFF tot+=1 if col not in palset: clut_bad+=1 if col == exp_cell(tu,tv): exact_color+=1 D=99; mdu=mdv=0 for rad in range(0,4): for du in range(-rad,rad+1): for dv in range(-rad,rad+1): if max(abs(du),abs(dv))!=rad or D!=99: continue ttu=tu+du; ttv=tv+dv if 0<=ttu>31) and (rmF[o]&0x3FFFF)!=(rmR[o]&0x3FFFF)) covered=sum(1 for o in range(FBPXW*CH) if rmF[o]>>31) print(f"FLOAT vs RECIP8b reference texel disagreement: {diff}/{covered} ({100.0*diff/max(1,covered):.1f}%) " f"— the reciprocal LUT's intrinsic error on this geometry") print() rtl_tight = (ih[2]+ih[3]+ih[4]) < 0.05*max(1,sum(ih)) if rtl_tight: print("VERDICT: RTL matches the RTL-faithful 8-bit-reciprocal reference -> residual is RECIPROCAL") print(" QUANTIZATION. Ch351 step 2: widen gs_reciprocal_stub IDX_BITS (10/11/12).") else: print("VERDICT: RTL does NOT match even the 8-bit-reciprocal reference -> S1 attribute UNDER-") print(" INTERPOLATION banding beyond the reciprocal. Ch351: fix S1 interpolation.") return 0 if __name__ == "__main__": sys.exit(main())