Files
retroDE_ps2/tools/gs_make_sh3_persp_fixture.py
thejayman77 ec82764bef Initial commit: retroDE_ps2 — first-of-its-kind PS2 GS FPGA core (DE25-Nano / Agilex 5)
RTL (GS rasterizer, EE core stub, platform bridge, LPDDR4B path), sim regression
(272 TBs), docs, and tooling. Copyrighted PS2 content (BIOS, game code, GS dumps,
and all dump-derived textures/traces) is excluded via .gitignore and stays local.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 20:10:50 -04:00

105 lines
5.7 KiB
Python

#!/usr/bin/env python3
"""retroDE_ps2 — Ch348 authentic SH3 PSMT8+CLUT through the PERSPECTIVE feeder path.
Composes Ch342 (perspective ST/Q triangles via the feeder S1-path) + Ch347 (authentic SH3 PSMT8 texture +
real CLUT). The feeder staging carries a perspective QUAD (2 TME tris, word0[32]=1) AND the TEX0 (PSM=PSMT8,
CLD=1, CSM2-linear) — so the feeder's TEX0 commit fires the VRAM->CLUT load; a setup bootlet uploads the SH3
PSMT8 texture (as a PSMCT32 transfer — word writes, no byte-RMW) + the SH3 CLUT (256x1 contiguous, the Ch347
lesson). DECAL/opaque; real CLUT RGB authentic, alpha not blended.
Label (Codex): authentic SH3 PSMT8 texture + real CLUT rendered through the proven perspective-triangle path
on silicon, chosen perspective geometry (NOT authentic SH3 draw geometry).
Outputs (LOCAL, gitignored) into sim/data/top_psmct32_raster_demo/:
bios_sh3_persp.mem / payload_sh3_persp.mem setup bootlet: BITBLT CLUT + PSMT8 texture (no tris)
feeder_sh3_persp.mem feeder staging: perspective quad + TEX0(PSMT8,CLD=1)
"""
import sys, os
HERE=os.path.dirname(os.path.abspath(__file__)); ROOT=os.path.normpath(os.path.join(HERE,".."))
DATA=os.path.join(ROOT,"sim","data","top_psmct32_raster_demo")
sys.path.insert(0, DATA); import bake
W=H=64 # SH3 64x64 crop
TBP0=64 # PSMT8 texture base byte 0x4000 (past the 64x64 PSMCT32 FB = 16 KiB)
CBP=80 # CLUT base byte 0x5000 (past the 4 KiB texture)
FBW=1
RAM_QWORDS=512 # 8 KiB EE RAM (256-CLUT + 64x64 texture upload ~ 340 qw)
# perspective quad in the 64x64 FB: x 8..56, y 8..56; top FAR (w=8), bottom NEAR (w=1); UV over 0..64.
PX0,PX1,PY0,PY1 = 8,56,8,56
WF,WN = 8,1
def read_mem(name,n):
v=[int(l,16) for l in open(os.path.join(DATA,name)) if l.strip() and not l.strip().startswith("//")]
if len(v)<n: sys.exit(f"{name}: {len(v)}<{n} (run gs_extract_sh3_clut.py first)")
return v
tex_words = read_mem("sh3_tex_idx64.mem", W*H//4) # 1024 packed words (4 indices each)
clut = read_mem("sh3_clut.mem", 256)
def tex0_sh3_clut(): # PSMT8 64x64 (TW=TH=6), TFX=DECAL, + CLUT (CBP, CPSM=PSMCT32, CSM2 linear, CLD=1)
v = bake.tex0_pack(TBP0, 1, psm=0x13, tw=6, th=6, tfx=1)
v |= (CBP & 0x3FFF)<<37; v |= (0&0xF)<<51; v |= (1&1)<<55; v |= (0&0x1F)<<56; v |= (1&7)<<61
return v
# --- setup bootlet payload: BITBLT the CLUT (256x1) + the PSMT8 texture (as PSMCT32 words). No tris. ---
def build_setup_payload():
qw=[]
qw.append(bake.giftag(1,0,0,4,int('E'*4,16)))
qw.append(bake.aplusd(bake.R_BITBLTBUF, bake.bitbltbuf_pack(CBP,1,0))) # CLUT, PSMCT32
qw.append(bake.aplusd(bake.R_TRXPOS, bake.trxpos_pack(0,0)))
qw.append(bake.aplusd(bake.R_TRXREG, bake.trxreg_pack(256,1))) # 256x1 contiguous
qw.append(bake.aplusd(bake.R_TRXDIR, bake.trxdir_pack(0)))
qw.append(bake.giftag(256//4,0,2,0,0))
for q in range(256//4):
word=0
for lane in range(4): word |= (clut[q*4+lane]&0xFFFFFFFF)<<(32*lane)
qw.append(word)
qw.append(bake.giftag(1,0,0,4,int('E'*4,16)))
qw.append(bake.aplusd(bake.R_BITBLTBUF, bake.bitbltbuf_pack(TBP0,16,0x00))) # PSMT8 tex as PSMCT32 words
qw.append(bake.aplusd(bake.R_TRXPOS, bake.trxpos_pack(0,0)))
qw.append(bake.aplusd(bake.R_TRXREG, bake.trxreg_pack(W*H//4,1))) # 1024x1 PSMCT32 words
qw.append(bake.aplusd(bake.R_TRXDIR, bake.trxdir_pack(0)))
qw.append(bake.giftag(W*H//16,1,2,0,0)) # EOP
for q in range(W*H//16):
word=0
for k in range(4): word |= (tex_words[q*4+k]&0xFFFFFFFF)<<(32*k)
qw.append(word)
return qw
# --- feeder staging: perspective quad (2 tris) + TEX0(PSMT8,CLD=1). word0[32]=1. ---
def build_feeder_staging():
pv_tl=(PX0,PY0, 0, 0, WF); pv_tr=(PX1,PY0, W,0, WF)
pv_bl=(PX0,PY1, 0, H, WN); pv_br=(PX1,PY1, W,H, WN)
tris=[[pv_tl,pv_tr,pv_bl],[pv_tr,pv_bl,pv_br]]
w=[]
w.append(len(tris)|(1<<32))
w.append(bake.frame_1_psmct32(FBW))
w.append(bake.alpha_pack(0,1,0,1))
w.append(bake.test1_geq())
w.append(bake.zbuf1_pack(2))
w.append(tex0_sh3_clut()) # PSMT8 + CLUT (CLD=1) — feeder commit fires the CLUT load
w.append(3 | (1<<4)) # TRI + TME, ABE=0 (S1 perspective path)
for verts in tris:
for (sx,sy,u,v,wq) in verts:
s_fp,t_fp,q_fp = bake.persp_attrs(u,v,wq)
w.append(bake.rgbaq_with_q(0,0,0,q_fp))
w.append(bake.st_data(s_fp,t_fp))
w.append(bake.xyz2_dataz(sx,sy,0x0000_5000))
return w
payload=build_setup_payload(); qwc=len(payload)
if 16+qwc>RAM_QWORDS: sys.exit(f"payload {qwc}>{RAM_QWORDS-16}")
disp_hi=(63<<12)|63
with open(os.path.join(DATA,"payload_sh3_persp.mem"),"w") as f:
f.write(f"// Ch348 LOCAL SH3 PSMT8+CLUT perspective setup payload (CLUT@{CBP}, tex@{TBP0}). gitignored. QWC={qwc}.\n")
for _ in range(16): f.write(f"{0:032x}\n")
for x in payload: f.write(f"{x&((1<<128)-1):032x}\n")
for _ in range(RAM_QWORDS-16-qwc): f.write(f"{0:032x}\n")
bake.write_bios_mem("bios_sh3_persp.mem",
bake.build_textured_demo_bootlet_disp(qwc, disp_hi, FBW),
f"Ch348 LOCAL SH3 perspective setup bootlet (QWC={qwc}, DISPLAY1={W}x{H}). gitignored.")
stg=build_feeder_staging()
bake.write_feeder_stg_mem("feeder_sh3_persp.mem", stg,
"Ch348 LOCAL SH3 PSMT8+CLUT perspective quad through the feeder (word0[32]=1, TEX0 PSMT8 CLD=1). gitignored.")
print(f"[Ch348] payload_sh3_persp.mem: {qwc} qw (BITBLT 256-CLUT + {W}x{H} PSMT8 tex). feeder_sh3_persp.mem: {len(stg)} words (persp quad).")
print(f"[Ch348] perspective quad x[{PX0}..{PX1}] y[{PY0}..{PY1}], top FAR w={WF}, bottom NEAR w={WN}, UV 0..{W}; TEX0 PSMT8 CLD=1 CBP={CBP}.")