# Ch292 closeout — narrow SYNC accept; next blocker is syscall 0x7A (cache-sync sibling?) **Status:** Closed. **Verdict from re-running qbert.elf:** `elf_first_unhandled_syscall (pc=0x00110994 $v1=0x7A (=122))` — qbert hit another syscall, this time with `$a0 = 0x80000000` (kseg0 base / uncached-pointer base) and the **same `$a1 = 0x00112AB0` fn_ptr** that's been threaded through syscalls 0x12/0x16. PS2 syscall 122 is plausibly `SyncDCache` / `iSyncDCache` — a semantic neighbor to the MIPS SYNC barrier we just accepted. qbert advanced 27,954 → **27,968 retires (+14)** past the SYNC and through ~13 instructions into a different code region (PC 0x00110994). ## What landed ### RTL — 3 surgical edits in `ee_core_stub.sv` 1. `localparam FUNC_SYNC = 6'h0F;` next to other SPECIAL func localparams. 2. `is_sync = is_special && (func == FUNC_SYNC)` decode flag. 3. `!is_sync` added to the SPECIAL nop-class exclusion: ```sv (is_special && !is_syscall && !is_jr && !is_jalr && !is_rtype_alu && !is_hilo_op && !is_sync) // Ch292 ``` No execute-path arm needed. SYNC falls through every recognized predicate (is_lw/lq/sw/sq/sd/branch/etc.) and lands in the default `else begin` block. None of the writeback predicates match SYNC (is_rtype_alu / is_lui-family / is_jal / etc. all false), so: - No GPR write - No HI/LO write - No memory side effect - `retire_advance()` → PC += 4 - Retire pulse fires Net: side-effect-free retire, exactly what Codex specified. ## TB — `tb_ee_core_sync.sv` Mirrors `tb_ee_core_ei` from Ch286 with three correctness assertions: 1. **Retire happens** — latch keyed on `retired_pc == SYNC slot` captures `seen_sync_retire = 1`. 2. **No GPR / HI / LO mutation at retire** — $v0/$t0 sentinels + HI/LO snapshot all sampled at the SYNC retire cycle and verified unchanged. 3. **Decode is narrow** — neighbor SPECIAL funct `0x0E` (currently unallocated, encoded as `instr 0x0000000E`) MUST trap under strict mode. Asserts `trap_pc / trap_instr` at the 0x0E slot. Plus the standard "post-SYNC LUI+ORI ran" check ($t1 = SENTINEL_C end-of-sim). Result: `retired=10 halt=0 trap=1 errors=0 PASS`. Sentinels intact; HI/LO both 0; neighbor 0x0E trapped cleanly. ## Makefile + regression - `tb_ee_core_sync` target. - Added to both PHONY list and `run:` master list. - Regression: 175 → **176**. ## qbert progression | Chapter | Blocker | retire_count | |---|---|---| | Post-Ch290 (syscall 0x12) | syscall 0x16 (identical args) | 27,950 | | Post-Ch291 (syscall 0x16) | SYNC (0x0000000F) at 0x00112994 | 27,954 | | **Post-Ch292 (SYNC)** | **syscall $v1=0x7A at 0x00110994** | **27,968** | PC jumped from 0x00112994 to 0x00110994 — qbert returned to an earlier code region (likely the "main init" function that called the handler-installation helper). The +14 retires include the SYNC retire + the function-return chain + setup for the next syscall. ## Ch293 framing — syscall 0x7A Args at halt: - `$v1 = 0x7A` (= 122) - `$a0 = 0x80000000` — kseg0 base / uncached pointer. **First syscall arg that's a kseg0 address** (not heap-ish or fn-ptr). - `$a1 = 0x00112AB0` — **same fn_ptr** seen in syscalls 0x12 and 0x16 - `$a2 = 0x00000000` - `$a3 = 0x001328C0` — same global context pointer `$a0 = 0x80000000` is suggestive: in PS2 SDK code, `SyncDCache` / `iSyncDCache(start, end)` takes a kseg0 address range. The "semantic neighbor" pattern is striking — Ch292 accepted MIPS SYNC (memory barrier), and Ch293's syscall might be the cache-management companion. Per Codex's established precedent: first-pass return $v0 = 0 ("cache synced OK"), PC += 4, add a runner-side observer with args + count. If the next blocker is a poll for the cache-sync to complete, that's Ch294's problem. Alternative names for PS2 syscall 122 in various sources: - `SyncDCache(start, end)` — common name - `iSyncDCache` — interruptible variant - Could also be a thread or signal-handling call Empirically: $v0 = 0 and continue. The runner observer pattern makes "wrong return value" easy to detect on the next run. ## Pattern review (22 chapters) | Ch | Blocker | Edits | Pattern | |-----|--------------|-------|---------| | 286 | EI | 3 | NEW narrow exact-32 decode | | 287 | DMAC ctrl MMIO | ~30 | NEW MMIO stub | | 288 | DMAC passive | ~30 | REUSE Ch287 internal-stub | | 289 | syscall 0x78 | ~20 | REUSE Ch273 + NEW observer | | 290 | syscall 0x12 | ~20 | REUSE Ch289 pattern | | 291 | syscall 0x16 | ~20 | REUSE Ch289 pattern (paired-call) | | **292** | **SYNC** | **3** | **REUSE Ch286 narrow-NOP-class** | Two narrow NOP-class accepts (Ch286 EI + Ch292 SYNC) and four syscall HLE extensions (Ch285/289/290/291) since the verdict era flipped at Ch286. The dispatcher accumulates one case per chapter; the runner observer library accumulates one entry per chapter; the TB pattern is mechanical. ## Files changed - `rtl/ee/ee_core_stub.sv` — 3 edits (localparam, decode flag, nop-class exclusion). - `sim/tb/integration/tb_ee_core_sync.sv` — new focused TB. - `sim/Makefile` — target + both regression lists. ## Regression **176/176 PASS** (was 175/175 in Ch291; +1 for the new tb_ee_core_sync).