# Ch285 closeout — syscall 0x40 HLE; next blocker is R5900 EI (COP0 funct 0x38) **Status:** Closed. **Verdict from re-running qbert.elf:** `elf_first_unsupported_opcode (pc=0x001000FC instr=0x42000038)` — COP0/CO funct 0x38 = R5900 `EI` (Enable Interrupts), an EE-specific extension to the MIPS COP0 CO sub-table. qbert advanced 27,091 → **27,239 retires (+148)** — the biggest single-chapter jump since Ch283. The PC dropped from 0x001113xx (deep into game code) back down to 0x001000FC (early init), which means the syscall 0x40 return successfully unstuck qbert's setup phase and it took the next hot block of work. ## What landed A narrow HLE case for syscall `$v1 == 0x40` in `ee_core_stub.sv`'s existing Ch273 dispatcher. Per Codex framing ("accept the registration, return success, continue; don't over-trust the SDK name"), the case returns `$v0 = 0` and resumes at `PC + 4`. Two lines of new RTL surrounded by a comment block: ```sv 32'h0000_0040: begin regfile[2] <= 32'd0; gpr128[2] <= 128'd0; pc <= pc + 32'd4; retire_pulse <= 1'b1; state <= S_IFETCH_REQ; end ``` The standard PS2 kernel syscall table lists names in this slot like `SetVCommonHandler` / `SetVTLBRefillHandler`. The observed call shape (`$a0=0x001DFFC0` heap-ish, `$a1=0x0011C326` code-ptr-ish) is consistent with a kernel-handler-install pattern. Real PS2 ROM implementations of these calls return the previous handler pointer; our stub returns 0 since (a) we don't store handler state, and (b) qbert clearly doesn't use the return value as a function pointer (it advanced 148 instructions past the call without re-trapping in a wild jump). If a future ELF needs the previous-handler return, this case can be widened with $a0-keyed handler-pointer storage. Not warranted yet. ## TB — `tb_ee_core_syscall_hle.sv` extended Existing TB extended with a 4th known case slot (`S_ORI_V1_40` / `S_SYS_40` / `S_BNE_40` / `S_DS_40`) plus matching latch (`v0_after_40` / `seen_40_return`) and the corresponding assert. The display summary now reports `$v0_after_40` next to the other three. Pattern identical to the existing 3C/3D/64 cases. The unknown-syscall halt still terminates the test. Result: `retired=21 halt=1 trap=0 errors=0 PASS`, with `$v0_after_3C=0x001e0000 $v0_after_3D=0x00000000 $v0_after_64=0x00000000 $v0_after_40=0x00000000 $v1_at_halt=0x00007777`. ## qbert progression | Chapter | Blocker | retire_count | |---|---|---| | Post-Ch283 (PCPYUD + gpr128) | LD at 0x00113378 | 27,067 | | Post-Ch284 (LD) | SYSCALL $v1=0x40 at 0x00111D24 | 27,091 | | **Post-Ch285 (syscall 0x40)** | **`0x42000038` (COP0 EI) at 0x001000FC** | **27,239** | The PC walking *backward* from 0x001113xx to 0x001000FC is a positive signal — qbert took the syscall return and looped or called back into earlier code, hit the next blocker there. 148 retires is the largest single-chapter jump on the qbert track since Ch283's architectural pivot. ## Ch286 framing Instr `0x42000038`: - bits 31..26: `010000` = opcode 0x10 (COP0) - bits 25..21: `10000` = rs/sub = 0x10 (COP0_CO — "coprocessor command") - bits 5..0: `111000` = funct 0x38 R5900 `EI` (Enable Interrupts). EE-specific extension to the MIPS COP0 CO sub-table (alongside `DI` at funct 0x39, plus the standard RFE/ERET/TLBP/TLBR/TLBWI/TLBWR/WAIT). Minimal implementation: NOP- class it (no model state mutated), PC += 4. We could optionally set `status[16]` (EIE bit) if a future test depends on the COP0 Status view, but qbert almost certainly doesn't poll Status after EI — it's calling EI as standard init noise. Concrete Ch286 scope: 1. `localparam FUNC_EI = 6'h38; localparam FUNC_DI = 6'h39;` 2. `is_ei = is_cop0 && (rs_idx == COP0_RS_CO) && (func == FUNC_EI)` 3. (`is_di` analogous, in case the next chapter trips DI) 4. Add `!is_ei` (and `!is_di`) to the `(is_cop0 && !is_mfc0 && !is_mtc0 && !is_rfe)` is_nop_class exclusion. 5. Default execute path retires (PC += 4 via normal `retire_advance`). 6. Focused TB: encode EI, execute, verify no trap + PC advances + retire fires. 5-ish RTL edits. Pure NOP-class extension; no register effects in the model. ## Files changed - `rtl/ee/ee_core_stub.sv` — 1 new case in the syscall HLE switch (~10 LOC with comment). - `sim/tb/integration/tb_ee_core_syscall_hle.sv` — 4 new BIOS slots, 1 new latch group, 1 new assertion, 1 expanded display line. No new TB, no new Makefile target; regression count unchanged at **172/172**. ## Pattern review (15 chapters) | Ch | Blocker | Edits | Pattern | |-----|--------------|-------|---------| | 271 | SQ | 5 | NEW 4-beat write | | 272 | DADDU | 4 | NEW ALU-low-32 | | 273 | SYSCALL HLE | 2 | NEW gated dispatcher | | 274 | BEQL | 6 | NEW branch+squash | | 275 | SD | 7 | REUSE SQ counter | | 276 | DSLL | 4 | REUSE DADDU | | 277 | BNEL | 6 | REUSE BEQL squash | | 278 | PCPYLD | 4 | NEW MMI narrow-decode | | 279 | LQ | 5 | REUSE LW path | | 280 | PSUBB | 5 | REUSE MMI narrow (byte-SIMD new) | | 281 | PNOR | 5 | REUSE MMI narrow + NOR arm | | 282 | PAND | 5 | REUSE MMI narrow + AND arm | | 283 | PCPYUD + gpr128 | architectural | NEW 128-bit shadow | | 284 | LD | 5 | REUSE Ch283 multi-beat path | | **285** | **syscall 0x40** | **~1** | **REUSE Ch273 dispatcher** | Highest-reuse chapter on record. The Ch273 dispatcher was designed to be extended — each new $v1 is one switch case. The +148 retires shows the cost-to-progress ratio remains favorable. ## Regression **172/172 PASS** (unchanged from Ch284; no new TB added in this chapter, the existing tb_ee_core_syscall_hle was extended in place).