254. 映射器的 CHR 写入路由

实现通过映射器进行的 CHR 写入路由。

254 / 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_254_mapper_chr_write_routing.py

参考资料

https://www.nesdev.org/wiki/PPU_memory_map
https://www.nesdev.org/wiki/NROM

需要更新的文件

emulator/cartridge/mapper_interface.py
emulator/cartridge/mapper000.py
emulator/bus/ppu_bus.py

什么是 CHR 内存?CHR 内存是 PPU 读取图块图案字节的地方。PPU 地址范围 $0000-$1FFF 就是 CHR 区域。

简单示例

PPU reads $0000
mapper returns the first byte of CHR ROM

对于本教程中官方的 Mapper000/NROM,CHR 是 ROM,因此写入会被拒绝。

为什么需要这一步

PpuBus 应该只负责路由地址,不应该决定 CHR 写入是否合法,这个策略应由映射器来负责。

正确的职责划分

PpuBus.write($0000-$1FFF, value)
    -> mapper.write_chr(addr, value)

Mapper000.write_chr(addr, value)
    -> rejects writes because official Mapper000 CHR ROM is read-only

建议的实现示例

class MapperInterface(Protocol):
    def read_prg(self, addr: int) -> int:
        ...

    def read_chr(self, addr: int) -> int:
        ...

    def write_chr(self, addr: int, value: int) -> None:
        ...

class Mapper000:
    def write_chr(self, addr: int, value: int) -> None:
        if not (CHR_ROM_START <= addr <= CHR_ROM_END):
            raise ValueError(f"Address out of CHR ROM range: {addr:04X}")

        raise ValueError("CHR ROM is read-only for official Mapper000")

class PpuBus:
    def write(self, addr: int, value: int) -> None:
        addr = addr & PPU_ADDRESS_MASK

        if CHR_START <= addr <= CHR_END:
            if self.mapper is not None:
                self.mapper.write_chr(addr, value)
                return

            self.vram.write(addr, value)
            return

不在本步骤范围内

  • CHR RAM 支持
  • 带有可写 CHR RAM 的非官方/自制 Mapper000 变体
  • 映射器的存储体切换
  • 渲染 CHR 图块

运行本课

uv run pytest tests/chapter_03_ppu_memory_and_graphics_data/test_254_mapper_chr_write_routing.py -v