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>
5.6 KiB
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:
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:
localparam FUNC_EI = 6'h38; localparam FUNC_DI = 6'h39;is_ei = is_cop0 && (rs_idx == COP0_RS_CO) && (func == FUNC_EI)- (
is_dianalogous, in case the next chapter trips DI) - Add
!is_ei(and!is_di) to the(is_cop0 && !is_mfc0 && !is_mtc0 && !is_rfe)is_nop_class exclusion. - Default execute path retires (PC += 4 via normal
retire_advance). - 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).