263. Маршрутизация записей в PRG на шине CPU
Проложить маршрут записей в PRG-пространство CPU через маппер перед тестами NMI в CPU.
Урок 263 из 356 · tests/chapter_04_ppu_timing_and_vblank/test_263_cpu_bus_prg_write_routing.py
Файлы для обновления
emulator/cartridge/mapper_interface.py
emulator/cartridge/mapper000.py
emulator/bus/cpu_bus.pyЗачем нужен этот шаг
Следующий тест прерываний CPU должен будет разместить байты вектора NMI по адресам
$FFFA = NMI vector low byte
$FFFB = NMI vector high byteЭти адреса находятся внутри области PRG CPU
$8000-$FFFFПеред тестированием CPU.interrupt_nmi() нужно сделать поведение записи на шине CPU в этом диапазоне явным и безопасным.
Что такое маршрутизация записей в PRG? Маршрутизация записей в PRG означает, что CpuBus не изменяет байты ROM картриджа напрямую. Вместо этого записи в $8000-$FFFF пересылаются мапперу картриджа:
CpuBus.write($8000-$FFFF, value)
-> mapper.write_prg(addr, value)Минимальный пример
CPU writes $8000 = $12
CpuBus calls mapper.write_prg($8000, $12)
Mapper000/NROM ignores it because it has no PRG write registers
Later mappers may treat the same write as a bank-switch commandРаспространённое заблуждение
Запись в $8000 не означает, что CPU изменяет байты ROM. На настоящих картриджах записи в этом диапазоне обычно игнорируются простыми платами или интерпретируются более продвинутыми платами как управляющие сигналы маппера.
Важное разделение
Real cartridge path:
CpuBus.write($8000-$FFFF) -> mapper.write_prg(...)
Tutorial FakeROM path:
CpuBus.write($8000-$FFFF) -> FakeROM.write(addr - $8000, value)Путь через FakeROM — это лишь удобство для подготовки тестов. Он позволяет предстоящим тестам прерываний записать байты вектора по адресам $FFFA/$FFFB, не делая реальный PRG ROM Mapper000 доступным для записи.
Примеры предлагаемой реализации
class MapperInterface(Protocol):
def read_prg(self, addr: int) -> int:
...
def write_prg(self, addr: int, value: int) -> None:
...
def read_chr(self, addr: int) -> int:
...
def write_chr(self, addr: int, value: int) -> None:
...
class Mapper000:
def write_prg(self, addr: int, value: int) -> None:
if not (PRG_ROM_START <= addr <= PRG_ROM_END):
raise ValueError(f"Address out of PRG ROM range: {addr:04X}")
# Mapper000/NROM has no writable PRG registers.
# Real hardware ignores writes to PRG ROM space, so we ignore them too.
return
class CpuBus:
def write(self, addr: int, value: int) -> None:
...
if 0x8000 <= addr <= 0xFFFF:
if self.mapper is not None:
self.mapper.write_prg(addr, value)
return
if self.program_rom is not None:
self.program_rom.write(addr - 0x8000, value)
return
raise ValueError(f"Unsupported CPU bus write: {addr:04X}")Выходит за рамки
- поведение CPU.interrupt_nmi()
- обработка CPU запроса NMI от PPU
- переключение банков Mapper001/MMC
- делать реальный PRG ROM доступным для записи
Запустить этот урок
uv run pytest tests/chapter_04_ppu_timing_and_vblank/test_263_cpu_bus_prg_write_routing.py -v