221. Cpu bus cartridge integration part1
Add optional Cartridge support to CpuBus, part 1.
Lesson 221 of 356 · tests/chapter_02_rom_loading/test_221_cpu_bus_cartridge_integration_part1.py
Prerequisites
- Lesson 217 provides
Cartridge. - Lessons 218-219 provide
Mapper000. - Lesson 220 provides
create_mapper.
File to update
emulator/bus/cpu_bus.pySymbols to update
emulator.bus.cpu_bus.CpuBus.cartridge
emulator.bus.cpu_bus.CpuBus.__post_init__Required imports in that file
from emulator.cartridge.cartridge import Cartridge
from emulator.cartridge.mapper_factory import create_mapperWhat this part implements
- CpuBus accepts an optional cartridge
- CpuBus creates a mapper from that cartridge in __post_init__
- CpuBus rejects attaching both program_rom and cartridge
What this part does NOT implement yet
- CPU reads from cartridge PRG ROM
That read behavior is tested in part 2.
Why this step exists
Adding cartridge support has two separate responsibilities
1. construction-time wiring
2. read-time routingKeeping them separate makes the tutorial easier to follow. Students first learn how the bus receives a cartridge and turns it into a mapper. Only after that do they change the read path for $8000-$FFFF.
Architecture model
Cartridge
stores PRG ROM, CHR ROM, and mapper number
create_mapper(cartridge)
chooses the correct mapper implementation
Mapper000
translates CPU PRG addresses into PRG ROM offsets
CpuBus
owns routing decisionsImportant boundary
CpuBus should not implement Mapper000 mirroring rules directly. It should create or hold a mapper, then later delegate PRG reads to mapper.read_prg(addr).
Expected implementation shape
@dataclass
class CpuBus:
program_rom: Optional[MemoryDevice] = None
cartridge: Optional[Cartridge] = None
ram: RAM = field(default_factory=RAM)
def __post_init__(self):
if self.program_rom is not None and self.cartridge is not None:
raise ValueError("Cannot attach both program_rom and cartridge")
self.mapper = None
if self.cartridge is not None:
self.mapper = create_mapper(self.cartridge)Invariants
- program_rom remains the writable MemoryDevice seam used by CPU tests
- cartridge and program_rom are mutually exclusive PRG sources
- mapper is always initialized, to None without a cartridge and to the
factory result with one
- Cartridge PRG and CHR bytes reach the mapper unchanged through
create_mapper; CpuBus neither parses nor remaps those bytesCommon misconception
The cartridge is not itself a MemoryDevice replacement for program_rom. It is metadata plus ROM payloads; create_mapper(cartridge) supplies the address-aware object that the bus will use.
Out of scope for this step
1. Lesson 222 changes the $8000-$FFFF read path.
2. PPU register routing and PPU construction belong to Chapter 3.
3. Mapper writes, CHR/PPU bus routing, and later behavior are not added here.Run this lesson
uv run pytest tests/chapter_02_rom_loading/test_221_cpu_bus_cartridge_integration_part1.py -v