Files
retroDE_ps2/docs/ch296_closeout.md
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

5.7 KiB

Ch296 closeout — syscall 0x79 HLE; new arg-shape surfaces at syscall 0x77

Status: Closed. Verdict from re-running qbert.elf: elf_first_unhandled_syscall (pc=0x00111D84 $v1=0x77 (=119)) — qbert advanced 27,996 → 28,101 retires (+105) through the Ch296 0x79 acceptance and into a new function entry with a markedly different arg shape.

What landed

The 7th narrow $v0=0 case in the Ch273 dispatcher, plus the 5th runner-side observer. Mechanical recipe — identical structure to Ch289/290/291/293's extensions.

Dispatcher case — rtl/ee/ee_core_stub.sv

32'h0000_0079: begin
    regfile[2]   <= 32'd0;
    gpr128[2]    <= 128'd0;
    pc           <= pc + 32'd4;
    retire_pulse <= 1'b1;
    state        <= S_IFETCH_REQ;
end

TB extension — tb_ee_core_syscall_hle.sv

Standard 4-slot subcase + latch + assertion + display. The TB now covers eight known syscall numbers (3C / 3D / 40 / 64 / 78 / 12 / 16 / 7A with $a0=0 and $a0=4 / 79) plus the unknown-halt path. Result:

$v0_after_3C=0x001e0000 $v0_after_3D=0x00000000 $v0_after_64=0x00000000
$v0_after_40=0x00000000 $v0_after_78=0x00000000 $v0_after_12=0x00000000
$v0_after_16=0x00000000 $v0_after_7A=0x00000000 $v0_after_7A_a0_4=0x00020000
$v0_after_79=0x00000000 $v1_at_halt=0x00007777

Runner observer — tb_ee_core_elf_runner.sv

The 5th observer. Captures first-PC + args + count. From qbert's run:

syscall_0x79 = seen=1 count=2 first_pc=0x00111d94
  $a0=0x80000000 $a1=0 $a2=0 $a3=0x001328c0 → $v0=0

count=2 — qbert called syscall 0x79 twice during the run. The first call was at PC 0x00111d94 with the kseg0-base + global-ctx arg shape; the second is in nearby code (not separately observed).

The new arg-shape signal at syscall 0x77

The next blocker has a completely different arg shape from every prior syscall we've HLE'd:

Field This blocker (0x77) Prior pattern
PC 0x00111D84 0x00111D24..D94 (similar region)
$a0 0x001DFD50 (heap addr) 0x80000000 (kseg0 base) OR 5 (slot id)
$a1 1 0 or fn_ptr (0x00112AB0)
$a2 0 0 or 0x20000000
$a3 20 (= 0x14) 0x001328C0 (global ctx pointer)

$a3 has flipped from "global ctx pointer" to "small int 20." This is a strong signal that qbert is now in a different subsystem of its init/runtime, calling kernel services with different argument conventions. The kernel-handler-install / sema / sync chain we've been tracking through 0x78/0x12/0x16/0x7A/ 0x79 seems to be done (it threaded $a3=0x001328C0 throughout).

PS2 syscall 119 (0x77) in standard references is commonly cited as SetVTLBRefillHandler or similar — distinct from the DMAC/interrupt-handler family. The args ($a0=address, $a1=1, $a3=20) could plausibly be:

  • SetVTLBRefillHandler(addr, ???, ???, 20) — 20 might be a TLB entry count or buffer size
  • RegisterLibraryEntries(addr, 1, 0, 20) — a registration call with a count
  • A memory-allocation / heap-management call with a size

Codex framing: any of these can take $v0=0 for the first pass. If qbert misbranches downstream, the arg shape gives more clues.

qbert progression

Chapter Blocker retire_count
Post-Ch295 ($a0-aware 0x7A) syscall $v1=0x79 at 0x00111D94 27,996
Post-Ch296 (syscall 0x79) syscall $v1=0x77 at 0x00111D84 28,101 (+105)

Small advance (+105 retires) but the verdict-shape transition is clean: another mechanical syscall HLE chapter advanced exactly one step. The arg-shape change at the new blocker indicates a subsystem boundary.

Ch297 framing — syscall 0x77

Per Codex's established precedent: narrow $v0=0 dispatcher case

  • runner observer for syscall 0x77 (= 119). Mechanical.

Notable for Ch297: since the arg shape changed (no global ctx in $a3), worth instrumenting the observer to track $a0/$a1/$a3 values — the args may CHANGE between calls (count > 1 might show different shapes per call).

Watch points for the Ch297 qbert run:

  • If count_0x77 == 1 and qbert proceeds: good, continue mechanical recipe.
  • If count_0x77 >> 1 with constant args: might be another wait loop (like Ch293's 0x7A spin) — autopsy needed.
  • If count_0x77 > 1 with varying args: qbert is iterating over something — likely processing a list/table.

Pattern review (26 chapters)

Ch Syscall Args (qbert) Pattern
273 0x3C/0x3D/0x64 init crt0 initial dispatcher
285 0x40 (no observer) narrow $v0=0
289 0x78 (0, 0x130000, 0x20000000, ctx) narrow $v0=0 + 1st observer
290 0x12 (5, fn, 0, ctx) handler-install
291 0x16 (5, fn, 0, ctx) — identical to 0x12 paired enable
293 0x7A varying $a0 wait-loop trigger
295 0x7A ($a0=4) poll case $a0-aware HLE (experimental)
296 0x79 (kseg0_base, 0, 0, ctx) finalize/adjacent
(Ch297) 0x77 (heap_addr, 1, 0, 20) NEW subsystem — non-ctx args

The cumulative HLE coverage is now 9 distinct $v1 values. The runner observer library tracks 5 of them with full arg shape + counts. The Ch295 $a0-aware pattern is available for any future syscall where a single $v0 isn't sufficient.

Files changed

  • rtl/ee/ee_core_stub.sv — 1 new HLE case (~15 LOC with comment).
  • sim/tb/integration/tb_ee_core_syscall_hle.sv — 4 new slots + 1 latch + 1 assertion + 1 display field.
  • sim/tb/integration/tb_ee_core_elf_runner.sv — 1 new observer block + SUMMARY line.

No new TB, no new Makefile target; regression count unchanged at 176/176.

Regression

176/176 PASS (unchanged from Ch295; no new TB).