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>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
"""retroDE_ps2 — SPRITE census (Ch344 sprite-ingestion scoping).
|
||||
|
||||
Consumes gs_parse.parse_dump()'s event stream and characterises every SPRITE primitive in a .gs dump:
|
||||
prim-type split, per-sprite TEX0 PSM, TME/ABE/FST, the ALPHA blend equation, TEX1 mag/min filter, and an
|
||||
XYOFFSET-corrected size histogram. Output is AGGREGATE (counts/histograms) only — no copyrighted pixel
|
||||
content — so the report is committable per captures/gs/.gitignore policy. This is census/scoping ONLY;
|
||||
it renders nothing and asserts nothing.
|
||||
|
||||
Usage: gs_census_sprites.py <dump.gs[.xz|.zst]> [--report out.txt]
|
||||
"""
|
||||
import sys, os
|
||||
from collections import Counter
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from gs_parse import parse_dump
|
||||
|
||||
PSM = {0x00:"PSMCT32",0x01:"PSMCT24",0x02:"PSMCT16",0x0A:"PSMCT16S",0x13:"PSMT8",0x14:"PSMT4",
|
||||
0x1B:"PSMT8H",0x24:"PSMT4HL",0x2C:"PSMT4HH"}
|
||||
PRIMT = {0:"POINT",1:"LINE",2:"LINE_STRIP",3:"TRI",4:"TRISTRIP",5:"TRIFAN",6:"SPRITE",7:"INVALID"}
|
||||
ABCD = {0:"Cs",1:"Cd",2:"0"}; C_SEL = {0:"As",1:"Ad",2:"FIX"}
|
||||
FILT = {0:"NEAREST",1:"LINEAR",2:"N_MIPN",3:"L_MIPN",4:"N_MIPL",5:"L_MIPL"}
|
||||
|
||||
def dec_tex0(v):
|
||||
return (v>>20)&0x3F, 1<<((v>>26)&0xF), 1<<((v>>30)&0xF), v&0x3FFF, (v>>35)&3, (v>>34)&1
|
||||
def dec_alpha(v):
|
||||
a=v&3; b=(v>>2)&3; c=(v>>4)&3; d=(v>>6)&3; fix=(v>>32)&0xFF
|
||||
return f"({ABCD[a]}-{ABCD[b]})*{C_SEL[c]}>>7+{ABCD[d]}" + (f" FIX={fix}" if c==2 else "")
|
||||
|
||||
def census(path):
|
||||
h, events = parse_dump(path)
|
||||
cur = {"type":None,"tme":0,"abe":0,"fst":0}
|
||||
tex0 = {1:None, 2:None}; xyoff = {1:(0,0), 2:(0,0)}; tex1 = {1:None, 2:None}
|
||||
alpha = {1:None, 2:None}; ctxt = 0
|
||||
kicks = {}; xyz = []
|
||||
spr = {"n":0, "psm":Counter(), "abe":Counter(), "fst":Counter(), "tme":Counter(),
|
||||
"alpha":Counter(), "magfilt":Counter(), "sizes":Counter()}
|
||||
for e in events:
|
||||
if e.kind != "GSREG": continue
|
||||
a, v = e.addr, e.value
|
||||
if a == 0x00: # PRIM
|
||||
cur = {"type":v&7,"tme":(v>>4)&1,"abe":(v>>6)&1,"fst":(v>>8)&1}; ctxt=(v>>9)&1; xyz=[]
|
||||
elif a in (0x06,0x07): tex0[1 if a==0x06 else 2] = dec_tex0(v)
|
||||
elif a in (0x14,0x15): tex1[1 if a==0x14 else 2] = (v>>5)&7 # MMAG bit5 -> mag filter (1bit here: 0/1)
|
||||
elif a in (0x18,0x19): xyoff[1 if a==0x18 else 2] = (v&0xFFFF, (v>>32)&0xFFFF)
|
||||
elif a in (0x42,0x43): alpha[1 if a==0x42 else 2] = v
|
||||
elif a == 0x05: # XYZ2 vertex kick
|
||||
t = cur["type"]
|
||||
if t is None: continue
|
||||
kicks[t] = kicks.get(t,0)+1
|
||||
if t == 6:
|
||||
xyz.append(v)
|
||||
if len(xyz) == 2:
|
||||
cx = 1 if ctxt==0 else 2
|
||||
ox, oy = xyoff[cx]
|
||||
x0=(xyz[0]&0xFFFF)-ox; y0=((xyz[0]>>16)&0xFFFF)-oy
|
||||
x1=(xyz[1]&0xFFFF)-ox; y1=((xyz[1]>>16)&0xFFFF)-oy
|
||||
w=abs(x1-x0)//16; ht=abs(y1-y0)//16 # 12.4 fixed -> pixels
|
||||
spr["n"]+=1; spr["sizes"][(w,ht)]+=1
|
||||
spr["abe"][cur["abe"]]+=1; spr["fst"][cur["fst"]]+=1; spr["tme"][cur["tme"]]+=1
|
||||
tx = tex0[cx]
|
||||
spr["psm"][("(untextured)" if not cur["tme"] else
|
||||
"(no TEX0)" if tx is None else PSM.get(tx[0],f"0x{tx[0]:02x}"))] += 1
|
||||
if cur["abe"]:
|
||||
al = alpha[cx]
|
||||
spr["alpha"][dec_alpha(al) if al is not None else "(no ALPHA set)"] += 1
|
||||
if cur["tme"]:
|
||||
f1 = tex1[cx]
|
||||
spr["magfilt"][("NEAREST" if f1==0 else "LINEAR" if f1==1 else "(unset)") ] += 1
|
||||
xyz=[]
|
||||
return h, kicks, spr
|
||||
|
||||
def fmt(h, kicks, spr):
|
||||
L = []
|
||||
L.append(f"SPRITE CENSUS — serial={h.serial!r} crc=0x{h.crc:08x}")
|
||||
L.append(f"prim-kicks by type: {dict((PRIMT[k],v) for k,v in sorted(kicks.items(),key=lambda x:-x[1]))}")
|
||||
L.append(f"SPRITES: {spr['n']} rectangles")
|
||||
L.append(f" TEX0 PSM : {dict(spr['psm'].most_common())}")
|
||||
L.append(f" TME(textured) : {dict(spr['tme'])} FST(0=STQ,1=UV): {dict(spr['fst'])}")
|
||||
L.append(f" ABE(alpha) : {dict(spr['abe'])}")
|
||||
L.append(f" ALPHA eqn : {dict(spr['alpha'].most_common())}")
|
||||
L.append(f" mag filter : {dict(spr['magfilt'].most_common())}")
|
||||
L.append(f" top sizes WxH : {[(f'{w}x{ht}',n) for (w,ht),n in spr['sizes'].most_common(8)]}")
|
||||
return "\n".join(L)
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__); sys.exit(2)
|
||||
h, kicks, spr = census(sys.argv[1])
|
||||
out = fmt(h, kicks, spr)
|
||||
print(out)
|
||||
if "--report" in sys.argv:
|
||||
p = sys.argv[sys.argv.index("--report")+1]
|
||||
with open(p, "w") as f: f.write(out + "\n")
|
||||
print(f"\nwrote report -> {p}")
|
||||
Reference in New Issue
Block a user