The problem
An emulator has to reproduce the relationships between components, not just their individual operations. Executing an instruction changes registers and memory, advances time, and can affect what appears on screen.
How it is structured
The code separates the CPU, PPU, bus, ROM loader, rendering, and controller input. That separation gives hardware behavior a clear place in the implementation and makes individual edge cases easier to isolate.
Decode instructions and update execution state.
Map memory and coordinate component timing.
Turn graphics state into a rendered frame.
The memory bus
The bus contains memory mapping and tick coordination. CPU execution and graphics behavior meet through that shared boundary, rather than living in one large game loop.
CPU and PPU tests
The CPU tests include the indirect-JMP page-wrap behavior: two possible high-byte addresses hold different values, then the test asserts the resulting program counter. PPU tests cover memory mirroring and register behavior.
Current capabilities
- 6502 instruction execution, including documented support for common unofficial opcodes.
- Background and sprite rendering, nametable and palette mirroring, OAM DMA, and VBlank NMI.
- NROM cartridge mapping and one standard controller through SDL2.
Scope & limitations
Audio, cartridge mappers beyond NROM, a second controller, and save states are still missing. The regression tests cover specific CPU and PPU behavior; they don’t establish compatibility with every game.
A detail worth looking at
The indirect-JMP test is a good example of the details involved. It checks a particular address-wrap behavior of the original CPU, rather than just whether an instruction produces the usual result.