# 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).
