Files
retroDE_ps2/docs/ch290_closeout.md
T
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.3 KiB
Raw Blame History

Ch290 closeout — syscall 0x12 HLE; paired syscall 0x16 surfaces with identical args

Status: Closed. Verdict from re-running qbert.elf: elf_first_unhandled_syscall (pc=0x00112A74 $v1=0x16 (=22)) with arguments identical to the syscall 0x12 call we just HLE'd.

qbert advanced 27,930 → 27,950 retires (+20) through the handler-install syscall and into a companion call that takes the exact same args. The strongest signal of the run.

Codex's framing confirmed

Codex predicted "$v1=0x12 is a registration call, plausibly AddDmacHandler(5, fn, 0, ctx)". The runner-side observer captured the first occurrence:

syscall_0x12 = seen=1 count=1 first_pc=0x00112a54
  $a0=0x05 $a1=0x00112ab0 $a2=0x00000000 $a3=0x001328c0 → $v0=0

This is the classic 4-arg handler-install ABI: small slot index + function pointer + null ctx0 + context pointer.

The paired-syscall signal

The next blocker after 0x12 is $v1=0x16 (=22) at PC 0x00112A74, 32 bytes (8 instructions) past the 0x12 call site. Args:

Reg After syscall 0x12 At syscall 0x16 blocker
$a0 0x00000005 0x00000005
$a1 0x00112AB0 0x00112AB0
$a2 0x00000000 0x00000000
$a3 0x001328C0 0x001328C0

Identical. qbert is calling syscall 0x16 with the literally same arguments it just passed to 0x12. The PS2 syscall table cites EnableIntcHandler / EnableDmacHandler (or similar "enable-just-registered" calls) in the 0x14-0x18 range. The pattern: Add*Handler registers, Enable*Handler activates.

This is a Ch291 candidate with very high confidence:

  • Same Ch285 precedent: accept ($v0 = 0, PC += 4).
  • Parallel observer in the runner.
  • One more switch case in the dispatcher.

What landed in Ch290

Dispatcher case — rtl/ee/ee_core_stub.sv

One new case (the 6th overall) in the Ch273 HLE switch:

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

Per Codex: do NOT invoke the handler function, do NOT mutate DMAC/INTC state. Just accept the registration and observe what qbert demands next.

TB extension — tb_ee_core_syscall_hle.sv

Same mechanical pattern (slots / latch / assertion / display) used for the Ch285 0x40 and Ch289 0x78 extensions. The TB now covers six known syscall numbers (3C / 3D / 40 / 64 / 78 / 12). 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
$v1_at_halt=0x00007777

Runner-side observer — tb_ee_core_elf_runner.sv

Parallel to the Ch289 0x78 observer. Same shape: detect retire of SYSCALL with $v1 = 0x12, snapshot PC + $a0..$a3 on first occurrence, emit a SUMMARY line. Worked first try — syscall_0x12 seen=1 count=1 ....

The two observers (0x78 and 0x12) form a small library of "this HLE'd syscall is worth surfacing." The pattern is mechanical and the SUMMARY block now self-documents what qbert is calling the kernel for. As more syscalls accumulate, the SUMMARY becomes a running ledger of qbert's init sequence.

qbert progression

Chapter Blocker retire_count
Post-Ch287 (DMAC ctrl) unmapped 0x1000C000 27,912
Post-Ch288 (DMAC passive) syscall 0x78 27,920
Post-Ch289 (syscall 0x78) syscall 0x12 27,930
Post-Ch290 (syscall 0x12) syscall 0x16 at PC 0x00112A74 (identical args) 27,950 (+20)

The +20 retires include the 0x12 syscall return + 8 instructions of setup (likely loading the same args back into $a0/$a1/$a3 from some register holding pattern, or just executing the second call that already had them in place) + the 0x16 syscall trap.

Ch291 framing — syscall 0x16

Args identical to syscall 0x12 — the pattern Codex predicted at Ch290 (registration accepted; next demand will tell us if the handler needs to actually fire). The simplest hypothesis: 0x16 is Enable*Handler for the registration that just landed.

First-pass scope:

  1. Add $v1 == 0x16 case to dispatcher: $v0 = 0, PC += 4.
  2. Parallel observer in the runner (same template as 0x78/0x12).
  3. TB extension (7th case).

If qbert then goes on to poll for the handler to fire — e.g., read DMAC D_STAT looking for a channel-5 interrupt bit — then Ch292 has to model the handler-invocation path (real interrupt delivery, COP0 Cause/Status, the registered fn_ptr being called).

But based on the identical args + Ch285 precedent, $v0=0 is the right shape for the first pass. Let qbert's next demand tell us what's needed.

Pattern review (20 chapters)

20 chapters in: Ch271 + Ch290 = 12 retires → 27,950 retires (2,329× advance). The syscall HLE dispatcher now handles SIX distinct $v1 values, each added in one chapter. The runner-side observer pattern (Ch289/Ch290) makes the diagnostic free.

Files changed

  • rtl/ee/ee_core_stub.sv — one new HLE case (~10 LOC).
  • sim/tb/integration/tb_ee_core_syscall_hle.sv — extended with syscall 0x12 case.
  • sim/tb/integration/tb_ee_core_elf_runner.sv — syscall_0x12 observer + SUMMARY line.

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

Regression

175/175 PASS (unchanged from Ch289; no new TB).