Files
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

71 lines
3.2 KiB
Python

#!/usr/bin/env python3
"""Ch340 byte-exact parser gate. Regenerates the synthetic fixture and asserts gs_parse decodes it
into the EXACT expected normalized event stream (container header + every GIF mode). No board, no
real dump needed. Exit non-zero on any mismatch."""
import os, sys, subprocess
HERE = os.path.dirname(__file__)
sys.path.insert(0, HERE)
import gs_parse
def main():
subprocess.run([sys.executable, os.path.join(HERE, "gs_make_synthetic.py")], check=True)
fix = os.path.join(HERE, "..", "captures", "gs", "synthetic", "mini.gs")
h, ev = gs_parse.parse_dump(fix)
fail = 0
def check(cond, msg):
nonlocal fail
print((" ok " if cond else " FAIL ") + msg)
if not cond: fail = 1
check(h.serial == "SYNTH001", f"serial == SYNTH001 (got {h.serial!r})")
check(h.crc == 0x12345678, f"crc == 0x12345678 (got 0x{h.crc:08x})")
check(h.state_size == 16, f"state_size == 16 (got {h.state_size})")
# The expected ordered (kind, reg, value) skeleton of the event stream.
got = [(e.kind, e.reg, e.value) for e in ev]
exp = [
("TRANSFER","",0),
("GIFTAG","",0),
("GSREG","FRAME_1",0x0000_0000_0C00_1807),
("TRANSFER","",0),
("GIFTAG","",0),
("GSREG","PRIM", 3 | (1<<4) | (1<<6)), # via PRE
("GSREG","RGBAQ", 0xFF | (0<<8) | (0<<16) | (0x80<<24)), # vtx0 red
("GSREG","XYZ2", (100<<4) | ((50<<4)<<16) | (0x5000<<32)),
("GSREG","RGBAQ", 0x00 | (0xFF<<8) | (0<<16) | (0x80<<24)),# vtx1 green
("GSREG","XYZ2", (200<<4) | ((50<<4)<<16) | (0x5000<<32)),
("GSREG","RGBAQ", 0x00 | (0<<8) | (0xFF<<16) | (0x80<<24)),# vtx2 blue
("GSREG","XYZ2", (100<<4) | ((150<<4)<<16) | (0x5000<<32)),
("TRANSFER","",0),
("GIFTAG","",0),
("IMAGE","",0),
("FRAME_BOUNDARY","",0),
]
check(len(got)==len(exp), f"event count == {len(exp)} (got {len(got)})")
for i,(g,e) in enumerate(zip(got,exp)):
# compare kind always; reg+value only where the expected has them
if e[0] in ("GSREG",):
check(g==e, f"event[{i}] {e} (got {g})")
else:
check(g[0]==e[0], f"event[{i}] kind {e[0]} (got {g[0]})")
# IMAGE qwc + no malformed
img=[e for e in ev if e.kind=="IMAGE"]
check(len(img)==1 and img[0].info.get("qwc")==2, f"one IMAGE qwc==2 (got {[e.info for e in img]})")
check(all(e.kind!="MALFORMED" for e in ev), "no MALFORMED events")
# Ch342 — PACKED ST must expose the STQ Q lane as info["q_stq"] (don't drop RGBAQ.Q).
h2, ev2 = gs_parse.parse_dump(os.path.join(HERE, "..", "captures", "gs", "synthetic", "mini_st.gs"))
sts = [e for e in ev2 if e.kind=="GSREG" and e.reg=="ST"]
check(len(sts)==1, f"mini_st: one ST event (got {len(sts)})")
if sts:
e=sts[0]
check((e.value & 0xFFFFFFFF)==0x11111111, "mini_st: ST.S == 0x11111111")
check(((e.value>>32)&0xFFFFFFFF)==0x22222222, "mini_st: ST.T == 0x22222222")
check(e.info.get("q_stq")==0x33333333, f"mini_st: q_stq == 0x33333333 (got {e.info.get('q_stq')})")
print("RESULT:", "PASS" if not fail else "FAIL")
return fail
if __name__ == "__main__":
sys.exit(main())