Initial commit: retroDE_ps2 — first-of-its-kind PS2 GS FPGA core (DE25-Nano / Agilex 5)
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>
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
# Ch289 closeout — syscall 0x78 HLE + runner-side observer; next is syscall 0x12 (handler install)
|
||||
|
||||
**Status:** Closed. **Verdict from re-running qbert.elf:**
|
||||
`elf_first_unhandled_syscall (pc=0x00112A54 $v1=0x12 (=18))`.
|
||||
qbert advanced 27,920 → **27,930 retires (+10)** through the
|
||||
Ch289 syscall and into the next one. The new runner-side observer
|
||||
worked first try:
|
||||
|
||||
```
|
||||
syscall_0x78 = seen=1 count=1 first_pc=0x00112aa4
|
||||
$a0=0x00000000 $a1=0x00130000 $a2=0x20000000 $a3=0x001328c0 → $v0=0
|
||||
```
|
||||
|
||||
count=1 means qbert called syscall 0x78 exactly once, took our
|
||||
$v0=0 return, and continued. No tight loop or error branch — the
|
||||
return shape is good for at least the first occurrence.
|
||||
|
||||
## What landed
|
||||
|
||||
### Dispatcher case — `rtl/ee/ee_core_stub.sv`
|
||||
|
||||
One new case in the existing Ch273 HLE switch, identical shape to
|
||||
Ch285's 0x40 case:
|
||||
|
||||
```sv
|
||||
32'h0000_0078: begin
|
||||
regfile[2] <= 32'd0;
|
||||
gpr128[2] <= 128'd0;
|
||||
pc <= pc + 32'd4;
|
||||
retire_pulse <= 1'b1;
|
||||
state <= S_IFETCH_REQ;
|
||||
end
|
||||
```
|
||||
|
||||
### Focused TB extension — `tb_ee_core_syscall_hle.sv`
|
||||
|
||||
The same mechanical pattern used for the Ch285 0x40 extension:
|
||||
4 new BIOS slots (`S_ORI_V1_78` / `S_SYS_78` / `S_BNE_78` /
|
||||
`S_DS_78`), a new latch group (`v0_after_78` / `seen_78_return`),
|
||||
a new init in the initial block, a new arm in the trace
|
||||
always_ff, a new post-halt assertion, and a new field in the final
|
||||
display. The UN/FAIL slot indices bumped by 4. The TB now covers
|
||||
five known syscall numbers (3C / 3D / 40 / 64 / 78) plus the
|
||||
unknown-halt path.
|
||||
|
||||
Result: `retired=25 halt=1 trap=0 errors=0 PASS`. Final display:
|
||||
```
|
||||
$v0_after_3C=0x001e0000 $v0_after_3D=0x00000000 $v0_after_64=0x00000000 $v0_after_40=0x00000000 $v0_after_78=0x00000000 $v1_at_halt=0x00007777
|
||||
```
|
||||
|
||||
### Runner-side observer — `tb_ee_core_elf_runner.sv`
|
||||
|
||||
Per Codex's "named trace/log line for syscall 0x78" ask, a small
|
||||
observer block captures the first occurrence of the syscall during
|
||||
the qbert run:
|
||||
|
||||
```sv
|
||||
if (core_ev_valid && u_core.retired_instr == 32'h0000_000C
|
||||
&& u_core.regfile[3] == 32'h0000_0078) begin
|
||||
syscall_0x78_count <= syscall_0x78_count + 1;
|
||||
if (!seen_syscall_0x78) begin
|
||||
seen_syscall_0x78 <= 1'b1;
|
||||
syscall_0x78_first_pc <= u_core.retired_pc;
|
||||
syscall_0x78_first_a0 <= u_core.regfile[4];
|
||||
...
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
And a SUMMARY line:
|
||||
```
|
||||
[tb_ee_core_elf_runner] syscall_0x78 = seen=1 count=1 first_pc=0x00112aa4
|
||||
$a0=0x00000000 $a1=0x00130000 $a2=0x20000000 $a3=0x001328c0 → $v0=0
|
||||
```
|
||||
|
||||
Pattern is extensible: any future HLE'd syscall whose arg shape
|
||||
matters can drop a parallel observer block in. Each new tracked
|
||||
syscall costs ~10 LOC: declarations, init, observer, summary line.
|
||||
|
||||
## qbert progression
|
||||
|
||||
| Chapter | Blocker | retire_count |
|
||||
|---|---|---|
|
||||
| Post-Ch286 (EI) | unmapped 0x1000E010 D_STAT | 27,907 |
|
||||
| Post-Ch287 (DMAC ctrl) | unmapped 0x1000C000 ch4 | 27,912 |
|
||||
| Post-Ch288 (DMAC passive) | syscall $v1=0x78 at 0x00112AA4 | 27,920 |
|
||||
| **Post-Ch289 (syscall 0x78)** | **syscall $v1=0x12 at 0x00112A54** | **27,930** |
|
||||
|
||||
Three chapters in a row each in the +5 to +10 range — qbert is
|
||||
sweeping through its kernel-init sequence one HLE call at a time.
|
||||
|
||||
## Ch290 framing — syscall 0x12
|
||||
|
||||
Args at halt (the new blocker):
|
||||
- `$v1 = 0x12` (= 18 decimal)
|
||||
- `$a0 = 0x00000005` — small int. Likely an IRQ number, priority,
|
||||
or handler slot index.
|
||||
- `$a1 = 0x00112AB0` — falls in code segment (qbert main range
|
||||
was around 0x00112xxx). Almost certainly a **function pointer**.
|
||||
- `$a2 = 0x00000000` — null/context.
|
||||
- `$a3 = 0x001328C0` — data pointer (consistent with the
|
||||
$a3 seen in 0x78 and earlier syscalls — looks like a global
|
||||
context block).
|
||||
|
||||
Shape: `(int small_id, fn_ptr handler, void* ctx0, void* ctx1)` —
|
||||
this is the classic **handler-install** pattern. PS2 standard
|
||||
syscall table cites names like `AddIntcHandler` (syscall 16/0x10),
|
||||
`RemoveIntcHandler` (syscall 17/0x11), and **`AddDmacHandler`**
|
||||
(syscall 18/0x12) in this range — so $a0=5 is plausibly a DMAC
|
||||
channel number (we landed in the DMAC region last chapter; channel
|
||||
5 = SIF0).
|
||||
|
||||
Per the Ch285 precedent: first pass returns `$v0 = 0` ("handler
|
||||
installed OK") and PC += 4. If qbert misbranches downstream, the
|
||||
fallback shapes to try are: $v0 = $a0 (returns the slot index for
|
||||
later RemoveIntcHandler), or $v0 = some non-zero handle. The
|
||||
runner-side observer pattern from Ch289 makes the diagnostic cheap.
|
||||
|
||||
## 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 0x78 case (slots / latch / assertion / display).
|
||||
- `sim/tb/integration/tb_ee_core_elf_runner.sv` — syscall_0x78
|
||||
observer + SUMMARY line.
|
||||
|
||||
No new TB, no new Makefile target; regression count unchanged at
|
||||
**175/175**.
|
||||
|
||||
## Pattern review (19 chapters)
|
||||
|
||||
| Ch | Blocker | Pattern |
|
||||
|-----|--------------|---------|
|
||||
| 271..286 | opcodes | opcode-era |
|
||||
| 287 | DMAC ctrl MMIO | NEW MMIO stub |
|
||||
| 288 | DMAC passive | REUSE Ch287 pattern |
|
||||
| **289** | **syscall 0x78** | **REUSE Ch273/285 dispatcher** |
|
||||
|
||||
Two narrow HLE extensions in five chapters (Ch285 + Ch289). The
|
||||
Ch273 dispatcher's switch-by-$v1 architecture continues to absorb
|
||||
new cases with minimal cost. The new runner-side observer pattern
|
||||
is a small upgrade that pays for itself the first time a syscall
|
||||
return value is wrong — instead of re-reading the trace file, the
|
||||
SUMMARY block tells you immediately what qbert handed the kernel.
|
||||
|
||||
## Regression
|
||||
|
||||
**175/175 PASS** (unchanged from Ch288; no new TB added in this
|
||||
chapter, existing tb_ee_core_syscall_hle extended in place and
|
||||
runner observer added).
|
||||
Reference in New Issue
Block a user