71 lines
4.6 KiB
Markdown
71 lines
4.6 KiB
Markdown
# Status
|
|
|
|
## Done
|
|
|
|
- Repo set up, backed up the original game ROMs to roms/backup (gitignored, stays local)
|
|
- Got a full set of CPU test ROMs in roms/test/cpu: kevtris nestest (with reference log) plus the blargg suite (instr tests, timing, interrupts, dummy reads, reset checks)
|
|
- Cartridge module done and working (src/cartridge.rs):
|
|
- iNES header parser (magic, PRG/CHR sizes, mapper, mirroring, flags)
|
|
- Mapper 0 (NROM) support only: 16KB PRG mirroring, 32KB direct, CHR-ROM and CHR-RAM (zeroed 8KB buffer)
|
|
- read_prg/read_chr/write_chr with debug_assert guards
|
|
- Split from_file (thin I/O wrapper) / parse_data (pure parser) for testability
|
|
- 8 unit tests, all passing (header, PRG mapping, error cases, CHR-RAM roundtrip)
|
|
- Bus module done (src/bus.rs):
|
|
- Bus trait (read/write) so the CPU is decoupled from the concrete memory layout
|
|
- NesBus: 2KB mirrored CPU RAM, cartridge PRG routing, PPU/APU/SRAM/expansion regions stubbed (return 0)
|
|
- CPU skeleton done (src/cpu.rs):
|
|
- Flag bit constants, registers (A/X/Y/SP/PC/P), set/clear/flag helpers, set_zp_flags
|
|
- Memory helpers: fetch, fetch16, read, write, read16, push, pop
|
|
- All 10 addressing mode helpers, including the JMP ($xxxx) page-wrap bug and page-cross cycle penalties
|
|
- First 3 opcodes implemented (JMP abs, LDX imm, STX zp)
|
|
- step dispatch shell + trace in nestest format
|
|
- set_test_start for the nestest harness
|
|
- nestest harness working (main.rs): loads nestest.nes, runs from $C000 with CYC:7, dumps trace to ours.log
|
|
- First 5 trace lines match the nestest reference log exactly (PC, regs, P, SP, CYC), then it panics on the next unimplemented opcode (JSR 0x20), which names the next target
|
|
|
|
## Next
|
|
|
|
Finish the official CPU. Nestest analysis findings: the full 8991-line log tests illegal opcodes too (225 distinct opcodes total; first illegal is 0x04 at line 5004). So the official-only CPU is verified against lines 1-5003, then panics on 0x04 by design. Official opcodes never seen in the log: BRK 0x00, CLI 0x58.
|
|
|
|
CPU implementation batches (by family, ordered by nestest log frequency):
|
|
|
|
- Batch 1 - Subroutines + stack: JSR 0x20 (push ret-1 high then low), RTS 0x60 (pop low then high), PHA 0x48, PLA 0x68, PHP 0x08, PLP 0x28 (B-flag on push rule), RTI 0x40, BRK 0x00
|
|
- Batch 2 - Branches (all 8): BEQ F0, BNE D0, BCC 90, BCS B0, BPL 10, BMI 30, BVC 50, BVS 70. One shared method per flag. +1 cycle if taken, +1 more if target crosses page
|
|
- Batch 3 - Loads: LDA 8 modes, LDX 5 modes, LDY 5 modes (share load + set_zp_flags)
|
|
- Batch 4 - Stores: STA 7 modes, STX 3, STY 3 (share store core, no flag changes)
|
|
- Batch 5 - Compares: CMP 8 modes, CPX 3, CPY 3 (set C/Z/N, no store)
|
|
- Batch 6 - Arithmetic: ADC 8 modes, SBC 8 modes (N/V/C/Z, decimal flag inert), BIT 2 modes
|
|
- Batch 7 - Logic: AND, ORA, EOR (8 modes each)
|
|
- Batch 8 - Inc/Dec: INC/DEC 4 modes each (RMW), INX, INY, DEX, DEY
|
|
- Batch 9 - Shifts: ASL, LSR, ROL, ROR (accumulator + 4 memory modes each, RMW)
|
|
- Batch 10 - Flags + misc: CLC 18, SEC 38, CLI 58, SEI 78, CLD D8, SED F8, CLV B8, NOP EA, JMP abs 4C, JMP ind 6C, transfers TAX/TAY/TXA/TYA/TSX/TXS
|
|
|
|
Rules for each batch:
|
|
- Add the opcode's length to opcode_len in the same edit (never let them drift)
|
|
- Re-diff after each batch (cargo run > ours.log + awk column compare)
|
|
- Check base cycles per mode, feed page-cross extra into the cycle total
|
|
|
|
Verification for this phase:
|
|
- All 151 official opcodes implemented
|
|
- Trace matches lines 1-5003 field-for-field (PC/A/X/Y/P/SP/CYC)
|
|
- cargo build clean
|
|
- Commit + update STATE.md
|
|
|
|
After official CPU is done (follow-ups):
|
|
- Add MMC1 (mapper 1) to cartridge so blargg official_only.nes / all_instrs.nes can run (authoritative official check)
|
|
- Run blargg mapper-0 suite (coredump, cpu_timing_test, ram_after_reset, registers)
|
|
- Then the illegal-opcode phase for the full 8991-line nestest pass
|
|
- Then bus/PPU/timing, GUI (egui + winit) come after the CPU milestone
|
|
|
|
## Decisions
|
|
|
|
- No external dependencies yet, keeping it pure std until the GUI milestone
|
|
- GUI choice: egui + winit (decided, not yet used)
|
|
- Scope: cartridge + CPU first, verified headless before graphics
|
|
- Bus is a trait (impl Bus) so the CPU works against any memory layout (real bus, test bus, debug bus)
|
|
- Flags stored as a raw u8 with named bit-mask constants (FLAG_CARRY etc.)
|
|
- opcode length is a growing match in opcode_len() (returns u8); migrate to a full 256-entry table when it gets big
|
|
- trace diff compares PC + A/X/Y/P/SP/CYC, ignoring disassembly/PPU columns
|
|
- Mapper 0 first, other mappers (MMC1, CNROM) implemented after CPU works
|
|
- Test ROMs are homebrew/open, no ethics issue; game backups stay in gitignored roms/backup
|