ec82764bef
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>
105 lines
5.7 KiB
Python
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}.")
|