ec82764bef
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>
189 lines
7.1 KiB
Markdown
189 lines
7.1 KiB
Markdown
# Ch293 closeout — syscall 0x7A HLE; **the opcode-trap era ENDS**
|
||
|
||
**Status:** Closed. **Verdict from re-running qbert.elf:**
|
||
`elf_timeout_with_hot_pc (watchdog after 50000000 ns, 1661413
|
||
retires, hot_pc=0x0011242C count=29/256)` — for the **first time**
|
||
qbert is not hitting an opcode trap, an unmapped MMIO, or an
|
||
unhandled syscall. It's running real code in a steady-state loop.
|
||
|
||
## The 60× retire jump
|
||
|
||
| Chapter | retire_count | delta | verdict |
|
||
|---------|--------------|-------|---------|
|
||
| Post-Ch292 (SYNC) | 27,968 | — | unhandled_syscall (0x7A) |
|
||
| **Post-Ch293 (syscall 0x7A)** | **1,661,413** | **+1,633,445** | **timeout_with_hot_pc** |
|
||
|
||
That's a **60× advance** in a single chapter. The 27k retires it
|
||
took us 23 chapters (Ch271..Ch292) to accumulate is now barely
|
||
1.6% of where we are.
|
||
|
||
## What changed
|
||
|
||
The mechanical Ch289-pattern extension landed exactly:
|
||
|
||
### Dispatcher case — `rtl/ee/ee_core_stub.sv`
|
||
|
||
```sv
|
||
32'h0000_007A: begin
|
||
regfile[2] <= 32'd0;
|
||
gpr128[2] <= 128'd0;
|
||
pc <= pc + 32'd4;
|
||
retire_pulse <= 1'b1;
|
||
state <= S_IFETCH_REQ;
|
||
end
|
||
```
|
||
|
||
The 8th case in the Ch273 HLE switch. ~10 LOC.
|
||
|
||
### TB extension — `tb_ee_core_syscall_hle.sv`
|
||
|
||
Same mechanical pattern (slots / latch / assertion / display). The
|
||
TB now covers eight known syscalls (3C / 3D / 40 / 64 / 78 / 12 /
|
||
16 / 7A) plus the unknown-halt path.
|
||
|
||
### Runner observer — `tb_ee_core_elf_runner.sv`
|
||
|
||
Fourth observer in the library (after 0x78, 0x12, 0x16). The
|
||
SUMMARY block now self-documents all four tracked syscalls.
|
||
|
||
## What the runner showed
|
||
|
||
```
|
||
syscall_0x78 = seen=1 count=1 first_pc=0x00112aa4
|
||
syscall_0x12 = seen=1 count=1 first_pc=0x00112a54
|
||
syscall_0x16 = seen=1 count=1 first_pc=0x00112a74
|
||
syscall_0x7A = seen=1 count=181494 first_pc=0x00110994 ← !!!
|
||
```
|
||
|
||
**count=181,494** for syscall 0x7A. qbert is in a loop calling
|
||
SyncDCache **on the order of every 9 retires**. At halt-time the
|
||
observer's first-occurrence `$a0=0x80000000` but the live halt-
|
||
time `$a0=0x00000004` — qbert is iterating sync ranges (likely
|
||
"sync this address" with the address changing each iteration).
|
||
|
||
`hot_pc = 0x0011242C` (count 29/256) is the loop center. The
|
||
181k SyncDCache calls suggest the loop body is something like:
|
||
```
|
||
loop:
|
||
<modify data at addr>
|
||
syscall SyncDCache(addr)
|
||
advance addr
|
||
branch back to loop
|
||
```
|
||
|
||
Or:
|
||
```
|
||
loop:
|
||
<poll some completion bit>
|
||
syscall SyncDCache(stale_cache_line)
|
||
branch back to loop if not done
|
||
```
|
||
|
||
Without examining the disassembly at 0x0011242c we can't tell
|
||
which. But the SUMMARY's "qbert reached entry and ran real code"
|
||
language is now literally correct — this isn't the runner's
|
||
boilerplate "expected verdict for synthetic" case; this is real
|
||
qbert.elf execution.
|
||
|
||
## What this means for the project
|
||
|
||
**The opcode-trap whack-a-mole phase is over.** Ch271..Ch292
|
||
exhaustively added every R5900 opcode and MMIO surface qbert
|
||
needs to reach init quiescence. Ch293's tiny addition (syscall
|
||
0x7A HLE) was the last brick.
|
||
|
||
The next blocker is not "implement opcode X" or "stub MMIO Y" —
|
||
it's "qbert is waiting on something we haven't modelled." The
|
||
possibilities, ranked by likelihood:
|
||
|
||
1. **DMAC interrupt delivery.** Ch290/291 registered + enabled a
|
||
handler on DMAC channel 5 (SIF0). The handler at 0x00112AB0
|
||
was never called because the model has no interrupt-delivery
|
||
path from DMAC completion → COP0 Cause/Status → handler
|
||
invocation. qbert may be polling for handler-side state that
|
||
never updates.
|
||
2. **VBLANK / VSYNC.** PS2 game loops typically wait for VBLANK
|
||
to advance frame state. The model has no VBLANK generator yet
|
||
(GS PCRTC stub doesn't emit the VSYNC interrupt).
|
||
3. **A specific kernel-state poll.** qbert might be reading a
|
||
global flag (e.g., a thread-control-block field) that some
|
||
missing kernel service should update.
|
||
4. **A combination** — most PS2 game main loops wait on multiple
|
||
signals (VBLANK + DMAC-complete + thread-message).
|
||
|
||
## Ch294 framing — investigation, not mechanical extension
|
||
|
||
The opcode-by-opcode + syscall-by-syscall mechanical recipe that
|
||
served Ch271..Ch293 is **no longer the right approach**. The next
|
||
chapter needs to:
|
||
|
||
1. **Disassemble** the loop body at `hot_pc = 0x0011242C` (and
|
||
immediately adjacent PCs in the ring) to understand what
|
||
qbert is checking each iteration.
|
||
2. **Trace** what memory addresses / MMIO addresses qbert reads
|
||
in the loop. The runner already emits a per-retire trace; the
|
||
trace file at `sim/traces/ee_core_elf_runner_core.trace`
|
||
should show every read with EA + region.
|
||
3. **Identify** the specific service that's missing — most
|
||
likely an interrupt-delivery path or a VBLANK generator.
|
||
|
||
Concrete first investigation step: read the qbert.elf
|
||
disassembly around 0x00112400..0x00112460 (~16 instructions
|
||
covering the hot_pc and its likely branch targets). This will
|
||
identify the exact wait condition.
|
||
|
||
Codex should frame Ch294 as an **investigation chapter** — like
|
||
the Ch263..Ch269 BIOS-treadmill autopsies — not as another
|
||
mechanical extension. The right output is a "what is qbert
|
||
waiting on" answer + a concrete proposal for the minimal model
|
||
change that breaks the wait.
|
||
|
||
## Cumulative HLE coverage at the inflection point
|
||
|
||
| $v1 | Probable name | Return | Chapter | qbert call count |
|
||
|-----|---------------|--------|---------|------------------|
|
||
| 0x3C | EndOfHeap | SYSCALL_HEAP_END | Ch273 | not observed |
|
||
| 0x3D | InitMainThread | 0 | Ch273 | not observed |
|
||
| 0x40 | SetV*Handler? | 0 | Ch285 | not observed |
|
||
| 0x64 | FlushCache | 0 | Ch273 | not observed |
|
||
| 0x78 | (kernel setup) | 0 | Ch289 | 1 |
|
||
| 0x12 | AddDmacHandler? | 0 | Ch290 | 1 |
|
||
| 0x16 | EnableDmacHandler? | 0 | Ch291 | 1 |
|
||
| 0x7A | SyncDCache? | 0 | Ch293 | **181,494** |
|
||
|
||
The count=1 entries are init-time calls. count=181,494 is the
|
||
"main loop is grinding" signal — and it's only that one syscall.
|
||
Whatever the loop is, SyncDCache is its central operation.
|
||
|
||
## 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 0x7A.
|
||
- `sim/tb/integration/tb_ee_core_elf_runner.sv` — syscall_0x7A
|
||
observer + SUMMARY line.
|
||
|
||
No new TB, no new Makefile target; regression count unchanged at
|
||
**176/176**.
|
||
|
||
## Pattern review (23 chapters)
|
||
|
||
| Phase | Chapters | Description |
|
||
|-------|----------|-------------|
|
||
| Opcode-blocker era | Ch271..Ch286 | New R5900 opcodes, one per chapter |
|
||
| MMIO era | Ch287..Ch288 | DMAC ctrl + per-channel surfaces |
|
||
| Syscall HLE era | Ch273, 285, 289, 290, 291, 293 | Six narrow $v0=0 extensions |
|
||
| Narrow-NOP era | Ch286 (EI), Ch292 (SYNC) | Side-effect-free accepts |
|
||
| **Investigation era** | **Ch294+** | **Find what qbert is waiting on** |
|
||
|
||
The "opcode + MMIO + syscall HLE" toolkit accumulated over the
|
||
previous 23 chapters has now exhaustively covered everything
|
||
qbert *demands* during its init phase. The remaining work is
|
||
*model fidelity*: making the system actually deliver the
|
||
asynchronous events (interrupts, VBLANK, scheduled threads) that
|
||
real PS2 hardware provides.
|
||
|
||
## Regression
|
||
|
||
**176/176 PASS** (unchanged from Ch292; no new TB).
|