221. CPU 总线 cartridge 集成 第 1 部分

为 CpuBus 添加可选的 Cartridge 支持,第 1 部分。

221 / 356 · tests/chapter_02_rom_loading/test_221_cpu_bus_cartridge_integration_part1.py

前置条件

  • 第 217 课提供了 Cartridge
  • 第 218-219 课提供了 Mapper000
  • 第 220 课提供了 create_mapper

待更新文件

emulator/bus/cpu_bus.py

待更新符号

emulator.bus.cpu_bus.CpuBus.cartridge
emulator.bus.cpu_bus.CpuBus.__post_init__

该文件所需的导入

from emulator.cartridge.cartridge import Cartridge
from emulator.cartridge.mapper_factory import create_mapper

本部分实现的内容

  • CpuBus 接受一个可选的 cartridge
  • CpuBus 在 __post_init__ 中根据该 cartridge 创建一个 mapper
  • CpuBus 拒绝同时挂载 program_rom 和 cartridge

本部分尚未实现的内容

  • CPU 从 cartridge PRG ROM 中读取

该读取行为将在第 2 部分中测试。

该步骤存在的原因

添加 cartridge 支持有两个独立的职责

1. construction-time wiring
2. read-time routing

将它们分开可以让教程更容易理解。学生首先学习总线如何接收 cartridge 并将其转换为 mapper。之后才修改 $8000-$FFFF 的读取路径。

架构模型

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 decisions

重要边界

CpuBus 不应直接实现 Mapper000 的镜像规则,而应创建或持有一个 mapper,然后在之后将 PRG 读取委托给 mapper.read_prg(addr)。

预期的实现形态

@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)

不变量

- 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 bytes

常见误解

cartridge 本身并不是 program_rom 的 MemoryDevice 替代品。它是元数据加上 ROM 数据载荷;create_mapper(cartridge) 提供了总线将使用的、具有地址感知能力的对象。

本步骤的范围之外

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.

运行本课

uv run pytest tests/chapter_02_rom_loading/test_221_cpu_bus_cartridge_integration_part1.py -v