When building a 6502 emulator, one of the most fundamental architectural decisions you’ll face is how to organize memory access. It’s tempting to embed the memory array directly into the CPU class – especially for early prototypes – but as your emulator grows, this tight coupling becomes a liability. A better approach is to implement a dedicated memory bus and pass it into the CPU. Here’s why, and how.
Why Separate Memory from CPU Logic?
At a hardware level, the 6502 CPU doesn’t own memory – it communicates with memory-mapped peripherals and RAM through the system bus. Mirroring that in your code makes your emulator more accurate, modular, and testable.
Benefits of separating the CPU from memory:
- Cleaner abstraction: The CPU should only know how to read and write bytes, not where they come from.
- Peripheral integration: You can easily map memory addresses to I/O devices (e.g., screen, keyboard, VIA, etc.).
- Bank switching: Dynamic memory mapping becomes trivial with a proper bus.
- Testing: You can mock memory for unit testing the CPU without dragging the entire system along.
A Simple Bus Interface
Start by defining a bus interface:
public interface IMemoryBus
{
byte Read(ushort address);
void Write(ushort address, byte data);
}
CPU only interacts with this interface:
public class CPU
{
private readonly IMemoryBus _bus;
public CPU(IMemoryBus bus)
{
_bus = bus;
}
public void ExecuteNextInstruction()
{
ushort pc = /* fetch program counter */;
byte opcode = _bus.Read(pc);
// decode & execute
}
}
Modeling your emulator with a decoupled bus architecture brings your code closer to real hardware behavior. It simplifies future expansion and makes the CPU a truly generic component – unaware of the memory layout or even what devices are attached.
If you’re serious about maintainability and hardware accuracy, decoupling CPU and memory isn’t just good style – it’s essential.