Files
retroDE_ps2/docs/ch302_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

4.7 KiB

Ch302 closeout — syscall 0x13 HLE; channel-5 syscall sequence emerging

Status: Closed. Verdict from re-running qbert.elf: elf_first_unhandled_syscall (pc=0x00111D64 $v1=0x6B (=107)) — qbert advanced 28,726 → 28,813 retires (+87) through the paired 0x13 and into a THIRD syscall sharing the same channel-5 args.

What landed

10th narrow $v0=0 case in the Ch273 dispatcher + 7th runner observer (distinct-tuple tracking, paralleling 0x17). All mechanical. Regression 177/177 (no new TB).

The channel-5 syscall sequence (NEW structural finding)

The runner observers now show qbert running a repeating per-channel sequence, not just isolated paired calls:

syscall_0x17 = count=2  args=(5, 0, -1, 0x00137568) distinct_tuples=1
syscall_0x13 = count=2  args=(5, 0, -1, 0x00137568) distinct_tuples=1
(next blocker) $v1=0x6B  args=(5, 0, -1, 0x00137568)

Three observations:

  1. 0x17 and 0x13 are each now called TWICE (count=2, up from count=1 in Ch301). When Ch301 HLE'd 0x17, qbert was blocked before its second 0x17 call. With 0x13 now HLE'd too, qbert loops back and makes both calls a second time — then hits 0x6B.

  2. All three syscalls (0x17, 0x13, 0x6B) share identical args: $a0=5 (channel id), $a1=0, $a2=0xFFFFFFFF (-1 sentinel), $a3=0x00137568 (the per-channel ctx).

  3. This is a per-channel-resource sequence, not a one-shot pair. qbert appears to be iterating: for each channel resource, it calls a sequence of kernel functions (0x17, 0x13, 0x6B, …) with the same channel id and context.

Codex's pause-for-autopsy condition — assessment

Codex said: "if this clears and the next thing is a wait loop or channel-5 event, pause for autopsy rather than adding more blind success cases."

The next blocker IS a channel-5 event (0x6B with $a0=5). But it is not a wait loop — it's a concrete unhandled syscall (elf_first_unhandled_syscall, not elf_timeout_with_hot_pc). qbert is making forward progress (+87 retires), not spinning.

My read: this is the boundary Codex flagged. The pattern has shifted from "isolated syscall blockers" to "a repeating channel-init sequence." Two paths for Ch303:

Path A — continue mechanical (one more $v0=0 for 0x6B)

If 0x6B is just the third call in a finite per-channel init sequence (e.g., the SDK does SetX(ch); RegisterY(ch); EnableZ(ch) for each channel), then a few more mechanical $v0=0 cases will clear the whole sequence and qbert moves on. Cheap to try; the runner observers will show whether the sequence is finite.

Path B — autopsy the sequence now

Disassemble the code region around PC 0x00111D64..0x00111DA0 (the caller of these syscall wrappers) to understand the loop structure. If it's for (ch = 0..N) { syscall_0x17(ch); ... }, we learn N and the full syscall set up front, instead of discovering them one trap at a time.

Recommendation: Path B (brief autopsy) before Ch303. The triplet + count=2 pattern is strong evidence of a bounded loop. A 20-minute disassembly of the caller would reveal:

  • the loop bound (how many channels),
  • the full syscall sequence per channel,
  • whether any of these syscalls' return values are checked (which would make a blind $v0=0 wrong).

This matches Codex's instinct: stop adding blind success cases once a structured sequence (not isolated calls) emerges. The autopsy is cheap and prevents a string of one-trap-at-a-time chapters.

qbert progression

Chapter Blocker retire_count
Post-Ch301 (0x17) syscall $v1=0x13 28,726
Post-Ch302 (0x13) syscall $v1=0x6B at 0x00111D64 (channel-5 args) 28,813 (+87)

Files changed

  • rtl/ee/ee_core_stub.sv — 1 new HLE case.
  • sim/tb/integration/tb_ee_core_syscall_hle.sv — 0x13 subcase.
  • sim/tb/integration/tb_ee_core_elf_runner.sv — 0x13 observer (distinct-tuple) + SUMMARY.

No new TB; regression unchanged at 177/177.

Ch303 framing — autopsy the channel-init sequence

Concrete first step for Codex/next chapter:

  1. Disassemble 0x00111D40..0x00111DC0 (the wrappers + their caller). The syscall wrappers are likely 4-instruction stubs like 0x00110990 (the 0x7A wrapper from Ch294); the caller is what loops.
  2. Identify the loop: is it for each channel or for each resource? What's the bound?
  3. Enumerate the full syscall sequence per iteration (0x17, 0x13, 0x6B, and whatever follows).
  4. Decide: mechanical batch (add all the sequence's syscalls as $v0=0 at once) vs. modeling actual per-channel state.

The runner observer infrastructure (distinct-tuple tracking) is already in place to validate whatever Ch303 decides.

Regression

177/177 PASS (unchanged from Ch301; no new TB).