#!/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}.")
