# Ch297 closeout — syscall 0x77 HLE; richer observer pays off; another wait loop surfaces

**Status:** Closed. **Verdict from re-running qbert.elf:**
`elf_timeout_with_hot_pc (watchdog after 50000000 ns, 1469235
retires, hot_pc=0x00112554 count=26/256)` — qbert advanced
**28,101 → 1,469,235 retires (+1,441,134)**, then hit another
steady-state wait loop at a NEW hot_pc.

This is the second time the runner has surfaced `elf_timeout_with_hot_pc`
on qbert (after Ch293). Pattern is repeating from Ch293→Ch294:
mechanical syscall HLE chapter unlocks a big advance, then a new
wait loop surfaces requiring investigation.

## What landed

### Dispatcher case — `rtl/ee/ee_core_stub.sv`

8th narrow $v0=0 case in the Ch273 dispatcher:

```sv
32'h0000_0077: begin
    regfile[2]   <= 32'd0;
    gpr128[2]    <= 128'd0;
    pc           <= pc + 32'd4;
    retire_pulse <= 1'b1;
    state        <= S_IFETCH_REQ;
end
```

### TB extension — `tb_ee_core_syscall_hle.sv`

Standard 4-slot subcase. The TB now covers nine known syscall
numbers plus the unknown-halt path. All assertions pass.

### Runner observer — RICHER than prior observers

Per Codex's framing, the 0x77 observer captures more than just
"first call" — it tracks up to **4 distinct ($a0,$a1,$a2,$a3)
tuples** with per-tuple count. Implementation:

```sv
logic         syscall_0x77_tuple_valid [0:3];
logic [31:0]  syscall_0x77_tuple_a0    [0:3];
... (a1, a2, a3)
int           syscall_0x77_tuple_count [0:3];
int           syscall_0x77_distinct_tuples;
```

On every syscall 0x77 retire, the observer:
1. Bumps total count.
2. Snapshots first/last args.
3. Looks up the current ($a0,$a1,$a2,$a3) tuple in the table.
   If found, increments its count. If not found and a slot is
   free, records the new tuple.

This means: at end-of-sim, the SUMMARY block shows whether qbert
made the same call repeatedly (count > 1 with `distinct_tuples =
1`) or iterated over a table (count > 1 with `distinct_tuples > 1`,
with per-tuple counts visible).

**Cost:** ~50 LOC. **Value:** decisive answer to "is qbert calling
this syscall in a loop?"

## The qbert run's smoking gun

```
syscall_0x77 = count=2 distinct_tuples=2
  tuple[0] = ($a0=0x001dfd50, $a1=1, $a2=0, $a3=20) count=1
  tuple[1] = ($a0=0x001dfdb0, $a1=1, $a2=0, $a3=16) count=1
```

Two distinct calls. The arg-pattern is striking:
- `$a0` increments by **0x60 = 96 bytes** (0x001dfd50 → 0x001dfdb0).
- `$a3` is a count: 20 then 16.
- `$a1 = 1` and `$a2 = 0` constant across calls.

This shape strongly fits a **registration-iteration** call:
- `$a0` = base address of registration record (heap-resident
  buffer at 0x001dfd50, then a second record 96 bytes later).
- `$a1 = 1` = mode flag (constant).
- `$a3` = number of entries in the record (20 first, 16 second).

PS2 standard references for syscall 0x77 (= 119) cite plausible
names like `RegisterLibraryEntries` or similar — both consistent
with this 4-tuple shape.

## qbert progression

| Chapter | retire_count | Verdict | Note |
|---------|--------------|---------|------|
| Post-Ch296 (0x79)   | 28,101       | elf_first_unhandled_syscall | $v1=0x77 |
| **Post-Ch297 (0x77)** | **1,469,235** | **elf_timeout_with_hot_pc** | **new wait loop at hot_pc=0x00112554** |

**+1.44M retire jump.** Comparable to Ch293's inflection 60× jump.
qbert is back in steady-state-loop territory at a different
hot_pc. This is **Ch298 investigation territory.**

## Cross-observation: syscall 0x7A traffic changed too

```
syscall_0x7A = count=4 (was 3 in Ch295/Ch296)
syscall_0x7A_split = count_a0_4=1 count_a0_0x80000000=1 count_a0_other=2 (was 1)
                     last_a0=0x80000002
```

qbert called 0x7A four times this run vs three times in
Ch295/296. The extra call is in the "other" bucket
($a0=0x80000002 — close to but not equal to 0x80000000 or 4).

So syscall 0x7A is being used with more arg shapes as qbert
progresses further. The Ch295 $a0-aware fix is *not* generalizing
correctly: $a0=0x80000002 takes the `else` path and returns 0,
which may or may not be what qbert expects. Worth keeping in mind
for downstream debugging.

## Ch298 framing — investigation of the new wait loop

Hot_pc = 0x00112554 with count = 26/256. **This is NOT 0x0011242C**
(Ch293's hot_pc), so it's a different wait loop. Ch298 should
mirror Ch294's autopsy approach:

1. Disassemble 0x00112540..0x001125A0 (~24 instructions around
   the new hot_pc).
2. Classify reads/writes in that PC window from the trace file.
3. Identify the branch condition.
4. Pick one of Codex's verdicts:
   - `qbert_waiting_on_dmac_handler`
   - `qbert_waiting_on_vblank`
   - `qbert_waiting_on_thread_scheduler`
   - `qbert_waiting_on_memory_flag` (likely, by analogy with Ch294)
   - `qbert_wait_loop_unknown`

The richer-observer pattern's `tuple` machinery is reusable for
ANY future investigation chapter — it can be retargeted at
whatever syscall or function the new loop polls.

## Pattern review (27 chapters)

| Phase | Effect |
|-------|--------|
| Opcode-blocker | Ch271..Ch286 |
| MMIO stubs | Ch287..Ch288 |
| Syscall HLE narrow | Ch273/285/289/290/291/293/296/297 |
| Narrow NOP-class | Ch286/292 |
| **Inflection #1** | **Ch293 — first wait loop surfaces** |
| **Investigation #1** | **Ch294 — bit-17 polled flag identified** |
| **Experimental unblock** | **Ch295 — $a0-aware HLE** |
| **Inflection #2** | **Ch297 — second wait loop surfaces** |
| **(Investigation #2)** | **Ch298 — autopsy required** |

The Ch293→Ch294→Ch295 cycle (inflection → autopsy → unblock) took
3 chapters and resulted in a 60× retire-count jump. Ch297 has
surfaced an inflection of comparable magnitude (+1.44M retires).
Ch298 should be the analogous autopsy.

## Files changed

- `rtl/ee/ee_core_stub.sv` — 1 new HLE case (~25 LOC with comment).
- `sim/tb/integration/tb_ee_core_syscall_hle.sv` — 4 new slots +
  1 latch + 1 assertion + 1 display field.
- `sim/tb/integration/tb_ee_core_elf_runner.sv` — 6 new state
  signals + observer block with distinct-tuple table + SUMMARY
  display lines.

No new TB, no new Makefile target; regression count unchanged at
**176/176**.

## Regression

**176/176 PASS** (unchanged from Ch296; no new TB).
