Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 117 additions & 1 deletion AI/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,120 @@ Build a truly temporal CPU core, where observable behavior emerges from internal
- invalid boot-logo or header-check cases remain in boot instead of handing off to the cartridge
- tracing can observe fetches, accesses, and IRQ acceptance

#### Recommended sequencing inside Phase 2

Phase 2 should be executed as narrow subphases. No subphase counts as closed
unless its local acceptance criteria land together with focused automated
coverage and move the phase-level done criteria forward without reintroducing
instruction-level shortcuts or hidden timing.

1. `Phase 2.1` - CPU execution plumbing and live register state.
Acceptance criteria: the CPU stops being a startup-state-only stub, keeps a
live register file plus explicit in-flight execution state, performs opcode
fetch as a real bus read at `PC`, advances `PC` through explicit fetch flow,
and exposes traceable per-T-cycle CPU state such as fetch, execute,
service-interrupt, halted, and stopped without yet claiming broad opcode
coverage.
Validation gate: focused unit tests cover register-file initialization,
opcode fetch at `PC`, explicit `PC` progression, deterministic micro-step
traces, and scheduler-visible CPU state transitions under `SkipBoot`.
2. `Phase 2.2` - Memory-visible instruction bring-up.
Acceptance criteria: the first instruction families run through ordered bus
accesses rather than aggregate duration tables, `imm8` and `imm16` fetches
remain explicit and correctly ordered, register-only and `(HL)` forms no
longer share one flattened timing path, and memory read-modify-write cases
keep separate read and write phases.
Validation gate: unit and short integration tests cover `imm8`/`imm16`
ordering, `(HL)` timing versus register timing, direct and indirect loads,
ALU flag behavior for implemented families, and deterministic synthetic ROM
execution for those instruction groups.
3. `Phase 2.3` - Control flow, stack traffic, prefixes, and boot-prerequisite
opcode closure.
Acceptance criteria: conditional taken and untaken paths execute through
different temporal sequences, stack operations become byte-oriented bus
traffic, `CALL`/`RET`/`RST` reuse that same stack model, CB-prefixed
execution keeps the double-fetch explicit, and the project records one
concrete boot-ROM prerequisite opcode matrix before attempting real boot.
Validation gate: focused tests cover taken versus untaken timing for
`JR`/`JP`/`CALL`/`RET`, stack byte order and `SP` updates, CB-prefix fetch
sequencing, and short deterministic programs that cross branches, stack
transfers, and prefixed instructions.
4. `Phase 2.4` - Real boot execution and `FF50` cartridge handoff.
Acceptance criteria: `RealBoot` starts at `0x0000` on the same CPU core and
scheduler used after startup, boot ROM overlay stays bus-owned, boot code
reaches cartridge execution only through an executed `FF50` write, the next
fetch after that write already comes from cartridge `0x0100`, invalid logo
or checksum cases remain in boot, and `No MBC` is the first closed
real-boot cartridge baseline.
Validation gate: automated tests cover boot-ROM visibility before handoff,
next-fetch cartridge visibility after `FF50`, valid handoff versus invalid
header non-handoff, and DMG-family cartridge-entry state coming from
executed firmware rather than direct-boot literals.
Closure note: the first `2.4` landing may use a synthetic DMG boot ROM that
performs representative header reads, conditional non-handoff, and an
executed `FF50` write on `No MBC`; full production DMG boot-ROM opcode
coverage remains tracked separately.
5. `Phase 2.5` - Interrupt-controller integration and CPU accept/service flow.
Acceptance criteria: hardware producers request interrupts through the
interrupt controller, `IF` visibility remains separated from CPU acceptance,
`IME`, delayed `EI`, immediate `DI`, fixed priority, acknowledge, `RETI`,
and the real `20` T-cycle service sequence are all represented explicitly,
and scheduler step `8` versus step `9` remains visible in code and traces.
Validation gate: focused tests cover `IF`/`IE` MMIO behavior, pending IRQ
visibility with `IME = 0`, priority resolution, `EI ; NOP`, `EI ; DI`,
`RETI`, and interrupt service timing as a real multi-step CPU sequence.
Closure note: this phase closes the interrupt-controller plus CPU contract,
including phase-`8` aggregation into `IF`, phase-`9` CPU acceptance, delayed
`EI`, immediate `DI`, `RETI`, and bytewise `20` T-cycle servicing. Concrete
request-generation rules for timer, PPU, serial, and joypad still land in
their owning subsystem phases.
6. `Phase 2.6` - `HALT`, `STOP`, and the `HALT` bug.
Acceptance criteria: `HALT`, `STOP`, wake-up, and later interrupt service
remain distinct ordered events, the `HALT` bug is modeled as a next-fetch
effect instead of a generic `PC` shortcut, and DMG `STOP` wake flows
through the joypad-owned hardware path rather than a frontend-only resume.
Validation gate: focused tests cover `HALT` with `IME = 1`, `HALT` with
`IME = 0`, already-pending IRQ plus `HALT`, `HALT` bug fetch behavior,
selection-independent DMG `STOP` wake, and the ordering between wake and
later interrupt acceptance.
Closure note: this phase closes the baseline control-state model for
`HALT`, `STOP`, wake from joypad-owned input transitions, and a next-fetch
`HALT` bug implementation on the shared scheduler timeline.
7. `Phase 2.7` - Timer edge model, overflow pipeline, and delayed timer IRQ.
Acceptance criteria: timer state is driven by the shared internal divider on
the global T-cycle timeline, `DIV` stays a derived view of the internal
counter, `TAC` selection and enable feed falling-edge TIMA increments,
overflow enters an explicit delayed reload/request pipeline, timer requests
become visible in `IF` only after that delay, and `SkipBoot` synthesizes
timer hidden state coherently with the visible post-boot snapshot.
Validation gate: focused tests cover `DIV` reset behavior, `DIV` and `TAC`
glitch cases, frequency-selection edge timing, TIMA overflow and reload
windows, delayed timer request visibility, and timer-plus-interrupt
integration without flattening request and service into one instant event.
Closure note: this phase closes the timer baseline around the shared
`system_counter`, falling-edge TIMA increments, `4` T-cycle delayed reload
and request, plus CPU-visible integration with `IF` and later interrupt
service ordering.
8. `Phase 2.8` - Phase closure, regression matrix, and oracle cross-check.
Acceptance criteria: tracing can show opcode fetches, operand accesses,
`IF` visibility, interrupt acceptance, and boot handoff on one shared
timeline, Phase 2 local TODOs are either closed or explicitly documented,
and the resulting CPU/timer/IRQ/boot stack is stable enough to stop being a
moving target for later DMA and PPU work.
Validation gate: the full unit and integration suite passes, the first Phase
2 ROM automation targets land for CPU and interrupt timing, and
timing-sensitive divergences are cross-checked against SameBoy before the
phase is considered closed.
Closure note: this phase closes with one shared trace timeline exposing
phase-`5` CPU bus activity (`opcode_fetch`, `operand_read`, `data_read`,
`data_write`), phase-`8` `IF` visibility, phase-`9` post-acceptance CPU and
interrupt state, plus phase-`6` boot handoff visibility around `FF50`. The
first Phase `2` ROM automation targets now exist as typed `gb-test-runner`
suites for CPU and interrupt timing. Remaining local TODOs stay explicit
under the Phase `2` section below, and the current SameBoy cross-check is a
documented source-level comparison recorded in `AI/research/SAMEBOY.md`;
full automated first-divergence tooling still belongs to Phase `9`.

#### Risks if done late or superficially

- inability to model HALT bug correctly
Expand Down Expand Up @@ -1527,7 +1641,9 @@ Suggested entry style:

### Phase 2 — CPU and real temporal control

- None currently.
- [CPU][BOOT] Full DMG boot ROM execution beyond the Phase `2.4` synthetic handoff baseline still needs MMIO-facing `LDH` / `(C)` transfers, `[hli]` / `[hld]` address-update transfers, and the remaining subtract plus non-CB accumulator-rotate families used by the production DMG firmware.
- [CPU] Phase `2.6` still needs explicit verification and, if necessary, refinement for the `EI ; HALT` pending-IRQ edge case before the HALT-control path can be considered fully hardened.
- [TIMER] Phase `2.7` still needs exact same-cycle `TIMA` / `TMA` write semantics on the reload T-cycle itself; the current baseline closes overflow delay, pre-reload writes, and CPU-visible IRQ ordering, but not yet the finest reload-cycle write arbitration cases.

### Phase 3 — Base DMA

Expand Down
5 changes: 5 additions & 0 deletions AI/hardware/BOOT-ROM.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ Priority order:
- DMG-family observable differences should initially be assumed to come from firmware and startup state unless a proven hardware-level difference matters to the emulator.
- `FF50` should integrate with system or bus mapping control, not as a CPU-local shortcut.
- Real-boot header validation should emerge from executed boot-ROM code reading cartridge bytes, not from a parallel emulator-side validator.
- The first closed `Phase 2.4` real-boot baseline may be a synthetic DMG boot ROM that validates representative header bytes and reaches cartridge entry through an executed `FF50` write on the real CPU, bus, and scheduler path; keep the remaining production-DMG opcode gap tracked separately instead of conflating that baseline with full firmware coverage.
- Boot should consume cartridge-derived metadata such as checksum-dependent post-boot flags, `cgb_flag`, or `sgb_flag` through the cartridge subsystem's canonical parsed header view rather than by reparsing header bytes in multiple places.
- A central routine such as `initialize_post_boot_state(model, cartridge)` is the preferred shape for `SkipBoot`, with one source of truth for model-specific CPU state, visible I/O state, and hidden-state synthesis inputs.
- Keep direct-boot snapshot data centralized in typed structures rather than copying startup literals into CPU, timer, PPU, APU, or bus modules independently.
Expand All @@ -195,6 +196,10 @@ Priority order:
- Uninitialized-state policy for WRAM, HRAM, cartridge RAM whether external or mapper-local, `OBP0`, and `OBP1` should be explicit and testable.
- Do not hard-code boot ROM support around a fixed 256-byte assumption; CGB boot ROM is larger and uses a split mapped layout.
- When CGB is implemented, boot should be able to inspect cartridge header compatibility information and choose CGB mode or DMG-compatibility mode accordingly.
- In the current Phase `2.8` baseline for this repo, the boot trace should make
the `FF50` mapping state visible at phase `6` on the same timeline as the
preceding CPU write and the following cartridge fetch, so handoff ordering can
be debugged without a separate boot-only trace path.

## Known pitfalls

Expand Down
45 changes: 45 additions & 0 deletions AI/hardware/CPU.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,21 @@ The source of truth should not be "execute opcode, mutate registers, then report
- When an interrupt is accepted, the CPU should clear `IME`, clear the selected bit in `IF`, push `PC`, and jump to the matching vector as part of one explicit service sequence.
- The CPU should make that accept-or-not decision only after current-cycle MMIO side effects and interrupt aggregation are already visible; interrupt producers do not bypass that CPU-owned decision point.
- Once accepted, interrupt servicing must consume the documented DMG `20` T-cycles (`5` M-cycles) through the same ordered CPU execution model used for normal stack and control-flow work.
- In the current Phase `2.5` baseline for this repo, interrupt acceptance happens from the explicit instruction-boundary fetch state after scheduler phase `8` has already aggregated requests into `IF`, and the service sequence reuses the same bytewise stack model as `CALL`/`RET`.

## IME, HALT, and STOP baseline

- `IME` is a CPU-internal acceptance gate, distinct from the `IE` register mask.
- `DI` clears `IME` immediately.
- `EI` must not enable `IME` immediately; it should arm a delayed enable that becomes visible only after the following instruction completes.
- `RETI` should restore `PC` through the ordinary return sequence and re-enable `IME` immediately at completion rather than through the delayed-`EI` path.
- `HALT` should be represented as an explicit CPU state distinct from ordinary instruction execution.
- `STOP` should be represented distinctly from `HALT`; even before full DMG/CGB STOP behavior is implemented, the architecture must leave it as a separate CPU control state.
- The architecture must allow `STOP` to be released by an explicit hardware-originated wake path owned by the relevant subsystem, with joypad as the current DMG-family baseline owner, rather than by a frontend-only shortcut.
- The CPU must consume that documented subsystem-owned `STOP` wake policy; it must not define a second local wake rule in parallel.
- For the current repo baseline, the CPU should treat `STOP` wake as the joypad-defined selection-independent button-press wake event, while keeping that wake distinct from any later joypad interrupt service.
- The `HALT` bug must be represented explicitly as a pending effect on the next opcode fetch rather than flattened into a generic "PC did not increment" shortcut.
- In the current Phase `2.6` baseline for this repo, `HALT` entry is resolved during scheduler phase `9`, `HALT` wake and later interrupt service remain separate ordered decisions, and `STOP` resumes only through a joypad-owned wake event instead of a CPU-local or frontend-local shortcut.

## Execution-model baseline

Expand Down Expand Up @@ -148,6 +151,14 @@ Priority order:
- If helper APIs summarize instruction timing, they should still expand into per-T-cycle execution internally.
- Separate CPU state, decode tables, execution/micro-op planning, ALU helpers, interrupt control flow, and the fine-grained tick engine instead of letting one opcode table own everything.
- Keep explicit state for the instruction in flight, including the current fetch/execute/service phase and any temporary bytes or addresses needed by the next micro-step.
- Keep the configured direct-boot startup snapshot separate from the live CPU
register file so tests, debugger snapshots, and real execution can compare
the handoff state against the current in-flight machine state explicitly.
- Prefer decode-time completion for instructions that truly end on the opcode
fetch machine cycle, and reserve explicit execute steps for instructions that
need extra immediate fetches, indirect memory traffic, or distinct read and
write phases, so register-only and `(HL)` forms cannot accidentally collapse
onto one fake timing path.
- A shape like `FetchOpcode`, `ExecuteMicroOp`, `ServiceInterrupt`, `Halted`, and `Stopped` is a good conceptual fit even if final enum names differ.
- Expose either a `tick_tcycle()`-style API or a micro-step API that expands explicitly into visible T-cycle progress; the scheduler should never need to wait for a whole instruction to retire before other hardware advances.
- Decode and execution should stay distinct enough that base opcodes and CB-prefixed opcodes can reuse the same execution machinery without collapsing their separate fetches.
Expand All @@ -161,6 +172,40 @@ Priority order:
- `RETI` should be implemented as a real instruction with return plus interrupt re-enable semantics, not as `RET` plus an informal external patch.
- Prefer micro-op metadata or callbacks that let the bus/PPU observe "read", "write", and address-bearing `inc/dec` events without hard-coding an opcode blacklist for the OAM corruption bug.
- Keep implicit `HL`, `SP`, and `PC` updates explicit enough that the IDU path can be observed as part of the same T-cycle-accurate CPU model.
- In the current Phase `2.8` baseline for this repo, the scheduler-aligned CPU
trace should expose `PC`, execution state, `IME`, delayed-`IME` state, and
the last phase-`5` bus activity for the current T-cycle, distinguishing at
least opcode fetches from operand and data accesses. Phase `9` should also
emit a post-wake/post-accept CPU trace so interrupt acceptance is visible on
the same timeline as the already-visible `IF` state.

## Real-boot prerequisite matrix

Before the first `RealBoot` execution attempt in Phase `2.4`, treat the DMG
boot path as blocked until this minimum opcode matrix is satisfied or an
explicit narrower boot target is documented.

| Status | Group | Minimum expectation |
| --- | --- | --- |
| Landed by Phase `2.3` | fetch/decode foundation | opcode fetch, `imm8`, `imm16`, `(HL)`, `(a16)`, explicit register-vs-memory timing |
| Landed by Phase `2.3` | control flow | `JR`, `JP`, `CALL`, `RET`, and `RST`, including conditional taken-vs-untaken timing splits |
| Landed by Phase `2.3` | stack traffic | bytewise `PUSH` and `POP`, plus reuse of the same push/pop ordering in `CALL`, `RET`, and `RST` |
| Landed by Phase `2.3` | CB-prefixed control path | explicit second fetch for `0xCB`, plus register and `(HL)` timing distinction for representative prefixed operations such as `RL` and `BIT` |
| Pending before Phase `2.4` | boot-facing MMIO loads/stores | `LDH (a8),A`, `LDH A,(a8)`, `LD (C),A`, `LD A,(C)`, and other MMIO-visible load/store forms used by the boot ROM |
| Pending before Phase `2.4` | implicit-address transfer forms | `[hli]` / `[hld]` style transfers and other implicit address-update flows exercised by the boot ROM |
| Pending before full DMG boot ROM | subtract/accumulator rotates | the remaining boot-visible subtract and non-CB accumulator-rotate families where the production DMG boot ROM depends on them |

Keep this matrix explicit in roadmap and change reports. Real boot should not
quietly start "just to see what happens" while the remaining pending rows stay
unresolved.

Phase `2.4` in this repo currently closes against an explicit narrower boot
target: a synthetic DMG boot ROM that performs representative header reads
through `(a16)`, validates them with `CP d8`, remains in boot on failed
conditional `JR`, and reaches cartridge execution only through an executed
`LD (a16),A` write to `FF50`. Full production DMG boot-ROM execution remains
deferred until the pending MMIO, implicit-address, and accumulator-rotate
groups above land.

## Recommended implementation order

Expand Down
Loading