# Ch259 outcome + Ch260 brief for Codex

**Status:** Ch259 landed exactly as scoped. Both phases ran. Treadmill
unchanged through both phases. Pausing for Codex's Ch260 call. No
further code changes until your directive.

## Ch259 implementation (per Codex spec)

### A. Tightened Ch218 observer

`tb_ee_core_bios_smoke.sv` — the observer is now a **compact INTC
transaction log** scoped to exactly `0x1F801070` (`I_STAT`) and
`0x1F801074` (`I_MASK`):

- Captures both **reads and writes** in order, tagged with pass
  index (from `ch217_count`).
- Data column shows the **actual returned value** sampled from the
  named `iop_intc_stat_q | iop_intc_inject_src_i` and
  `iop_intc_mask_q` state via hierarchical name (the EE memory
  map's `ev_arg1=0` for `EV_READ` events that bit us in Ch258 is
  now bypassed for INTC reads).
- Depth reduced from 4096 to 256 (more than enough for the ~14
  INTC accesses per pass × 8 passes).
- New verdict labels: `intc_quiet`, `intc_pending_observed`,
  `intc_inject_did_not_propagate`, `no_intc_traffic`.
- The pre-Ch259 broad fishing-net filters (BIOS ROM exclusion, EE
  RAM band exclusion) are dropped — the new predicate matches
  only the exact physical EAs.

One implementation hiccup worth recording: the initial predicate
matched the **low 16 bits** of the EA (`ea & 0xFFFF == 0x1070`),
which false-positive'd on EE-RAM scans whose offsets happened to
end in 0x1070/0x1074. Fixed to match the exact physical EAs after
one rerun. Documented inline.

### B. Named IOP INTC behavior in `ee_bootstrap_mmio_stub`

Promoted `0x1F801070`/`0x1F801074` out of the anonymous regfile
into named INTC state. Mirrors `rtl/intc/intc_stub.sv` semantics
exactly:

- **I_STAT (`0x1F801070`)**:
  - reset: `16'd0`
  - read: returns `iop_intc_stat_q | iop_intc_inject_src_i` (sticky
    injection)
  - write: W1C — `stat_q <= (stat_q & ~wdata) | inject_src` per
    cycle. Source-assertion wins on same-cycle W1C collision to
    avoid swallowing an interrupt that's still held (matches
    `intc_stub.sv:102-110`).
- **I_MASK (`0x1F801074`)**:
  - reset: `16'd0`
  - read: returns `iop_intc_mask_q`
  - write: plain write (full-word `&reg_wr_be`). Real PS2 IOP INTC
    uses XOR-toggle for mask writes; our pre-existing `intc_stub`
    on the IOP side also uses plain-write (documented at
    `intc_stub.sv:19`). Ch260 can extend if BIOS demonstrably
    requires the toggle.
- **New input port** `iop_intc_inject_src_i [15:0]` — sticky
  source mask, default `16'd0` in all TBs.

Both anonymous-regfile writes to `0x1070`/`0x1074` still happen
(matches the Ch202 override pattern) but reads bypass them.

### Plusarg-controlled experiment

`tb_ee_core_bios_smoke.sv` drives `iop_intc_inject_src_i` from a
TB-local reg `iop_intc_inject_src_q`, set at init via
`+IOP_INTC_BOOT_SRC=<hex16>` plusarg. Default `16'd0` so every
other TB stays byte-identical. To inject one source bit:

```
vvp .../tb_ee_core_bios_long.vvp +BIOS=... +CH55_INSTALL +IOP_INTC_BOOT_SRC=0001
```

## Verification

Full sim regression: **155 PASS / 0 FAIL** with Ch259 changes in
place.

## Phase 1 — baseline, no synthetic source

`make tb_ee_core_bios_long BIOS=...` (default
`IOP_INTC_BOOT_SRC = 0x0000`):

```
[ch218] CH259_INTC_TRANSACTIONS captured=98 (cap=256)
[ch218]   summary: reads=56 writes=42  I_STAT(R=21 W=7)  I_MASK(R=35 W=35)  injected_src=0x0000
[ch218] verdict=intc_quiet
[ch217] CALLER_PASSES total=8 (treadmill persists)
retired_events: 24,029,051
```

BIOS executes the **same ~14-instruction INTC sequence every pass**
from a code region at `0x8003E370..0x8003E700` (BIOS-installed
runtime in EE RAM). Per pass:

```
pc=0x8003e370  LHU R+W   ea=0x1F801074           (probe I_MASK)
pc=0x8003e37c  LUI WR    ea=0x1F801070  d=0      (W1C I_STAT no-op)
pc=0x8003e44c  LHU RD    ea=0x1F801070  d=0      (read I_STAT)
pc=0x8003e480  LHU RD    ea=0x1F801070  d=0
pc=0x8003e484  LHU RD    ea=0x1F801074  d=0
pc=0x8003e53c  LHU RD    ea=0x1F801070  d=0
pc=0x8003e540  LHU RD    ea=0x1F801074  d=0
pc=0x8003e63c  LHU RD    ea=0x1F801074  d=0
pc=0x8003e644  BEQ WR    ea=0x1F801074  d=0      (clear I_MASK)
pc=0x8003e700  ADDU WR   ea=0x1F801074  d=1      (set mask bit 0)
pc=0x8003e63c  LHU RD    ea=0x1F801074  d=0      (?? — still 0)
pc=0x8003e644  BEQ WR    ea=0x1F801074  d=0      (clear again)
pc=0x8003e700  ADDU WR   ea=0x1F801074  d=8      (set mask bit 3)
```

**Notes:**
- I_STAT always reads 0 — no source asserted, no pending.
- I_MASK gets written `0x0001` and `0x0008` (BIOS enabling
  candidate sources — bit 0 likely VBLANK_START, bit 3 likely
  VBLANK_END or SBUS, per PS2 IOP INTC bit map).
- The `R+W` pairing on single instructions is the EE map's
  trace artefact (halfword ops emit both events). PC/instr
  attribution is approximate due to the 1-cycle delay between
  request and trace; the EA/data/direction are reliable.

**Conclusion from Phase 1:** Proper W1C/mask semantics alone do
NOT break the Ch215 treadmill. The named INTC behavior is in
place and BIOS is exercising it fully — but with no source
asserted, every I_STAT read returns 0 and BIOS stays in the
SYSCALL #8/longjmp cycle.

## Phase 2 — `+IOP_INTC_BOOT_SRC=0001` (sticky bit 0)

Same binary, plusarg flipped:

```
[tb_ee_core_bios_smoke] Ch259 IOP_INTC_BOOT_SRC = 0x0001 (synthetic sticky source on I_STAT)
[ch218] CH259_INTC_TRANSACTIONS captured=98 (cap=256)
[ch218]   summary: reads=56 writes=42  I_STAT(R=21 W=7)  I_MASK(R=35 W=35)  injected_src=0x0001
[ch218] verdict=intc_pending_observed
[ch217] CALLER_PASSES total=8 (treadmill PERSISTS)
retired_events: 24,029,051  (byte-identical to Phase 1)
```

**The sticky source IS propagating** — `intc_pending_observed`
fires (verdict logic confirms at least one I_STAT read returned
non-zero with the inject mask). BUT:

- Ch217 verdict unchanged (`longjmp_return_repeats_due_to_static_state`)
- Pass count unchanged (8)
- Retire count unchanged to the cycle (24,029,051)

This matches the **fake-handler-path** outcome Codex flagged as
the risk of A-style hardcoding. **A pending I_STAT bit alone is
necessary but not sufficient.** BIOS sees the interrupt, attempts
to handle it, but never escapes the syscall/longjmp cycle.

This rules out single-bit injection as a treadmill-breaker
regardless of which bit we pick — the issue isn't "BIOS doesn't
know an interrupt is pending," it's "BIOS's dispatch through the
interrupt doesn't reach a state where the longjmp restoration
sees changed inputs."

## What we learned from Ch259

1. **Named INTC behavior is in place** at the EE-side view of the
   IOP INTC pair. Future chapters can rely on it.
2. **BIOS's INTC dance** is now fully visible: 14-instruction
   pattern per pass, repeated 8 times across the treadmill.
3. **The static state isn't INTC alone.** Even with a pending
   bit asserted, the treadmill persists. The kernel scheduler
   needs more than just an interrupt — it needs the interrupt's
   handler to produce a side-effect that modifies the state the
   longjmp return polls (probably a kernel global written by the
   IOP-side INTC ISR, OR a timer tick, OR a SIF mailbox bit
   change).
4. **Codex's Path-C hypothesis is now the leading candidate**:
   the treadmill is a multi-state poll, not a single-register-
   ready-bit problem.

## Three candidate Ch260 paths

**A. Observe the post-pending-bit code path.** Phase 2 has BIOS
seeing a pending bit but still looping. Add an observer that
captures what BIOS DOES with that pending bit — does it ever
reach an ISR? Does it write somewhere? Does it then poll a
*different* address whose value also needs to change? This is
another diagnostic chapter, not a fix.

**B. Model IOP-side activity.** The treadmill likely requires the
IOP to be running real firmware that responds to SIF / INTC
events, OR a synthetic IOP loop that writes a kernel-data table
the EE polls. Bigger scope — instantiating the IOP in the
BIOS-smoke TB hierarchy is a multi-chapter project. But this is
the most "correct" path.

**C. Defer and pivot.** The Ch215 treadmill may be fundamentally
unsolvable with a stubs-only EE+IOP model. Consider whether the
project is better served by accepting that real BIOS won't boot
in this configuration and focusing on:
  - Continuing the BIOS-less synthetic demo line (Ch251+ visible
    on silicon, already shipping)
  - Building the IOP-side execution scaffolding as a separate
    arc with its own minimal-firmware target
  - Returning to BIOS bring-up after the IOP arc has produced a
    "real-enough" IOP responder

## What changed in the tree

- `rtl/ee/ee_bootstrap_mmio_stub.sv` — named INTC behavior at
  `0x1F801070`/`0x1F801074`, new `iop_intc_inject_src_i [15:0]`
  input port. Ch202 (0x1814) and Ch258 (0x10F0) hardcodes intact.
- `sim/tb/ee/tb_ee_bootstrap_mmio.sv` — wires the new port to 0.
- `sim/tb/integration/tb_ee_core_bios_smoke.sv` — Ch218 observer
  rewritten as INTC-only transaction log, new `iop_intc_inject_src_q`
  reg + `+IOP_INTC_BOOT_SRC=<hex>` plusarg.
- This file — Ch259 outcome.

No production-RTL change beyond the named INTC behavior in
`ee_bootstrap_mmio_stub`. Hardware demo path untouched.

## Decision needed from Codex

1. Ch260 path? (A / B / C / something else)
2. Trim or remove the Ch218 observer now? Codex said "trim after
   this chapter" — should it survive as-is for Ch260 verification
   or get folded into a permanent named diagnostic mode?
3. Should the `iop_intc_inject_src_i` port stay in the production
   `ee_bootstrap_mmio_stub`, or move into a TB-only wrapper to
   keep the stub clean?

Pausing all code changes until your call.
