222. Cpu bus cartridge integration part2
Чтение PRG ROM картриджа через CpuBus, часть 2.
Урок 222 из 356 · tests/chapter_02_rom_loading/test_222_cpu_bus_cartridge_integration_part2.py
Предварительное условие
Поле cartridge, __post_init__ и подключение фабрики mapper'ов из урока 221 должны быть завершены до изменения пути чтения здесь.
Файл для обновления
emulator/bus/cpu_bus.pyСимвол для обновления
emulator.bus.cpu_bus.CpuBus.readЧто реализует эта часть
- CpuBus routes CPU reads in $8000-$FFFF to mapper.read_prg(addr) when a
cartridge-backed mapper exists
- CpuBus keeps the old program_rom=FakeROM path working
- CpuBus fails loudly if no PRG source is attachedПочему этот шаг нужен
CPU видит программную ROM картриджа по адресам CPU
$8000-$FFFFНо байты PRG ROM картриджа хранятся начиная со смещения 0. Эту трансляцию выполняет mapper. CpuBus должен только определять, что адреса в диапазоне $8000-$FFFF принадлежат области PRG картриджа, а затем делегировать вызов mapper'у.
Правильный путь для картриджа
CpuBus.read($8000)
-> mapper.read_prg($8000)
-> Mapper000 translates to PRG offset $0000Важное отличие от старого пути FakeROM
program_rom.read(addr - $8000)использует смещение, потому что FakeROM — это простое тестовое устройство, а не mapper.
mapper.read_prg(addr)использует полный адрес CPU, потому что mapper'ы реализуют трансляцию адресов CPU.
Распространённая ошибка
Не вызывайте mapper.read_prg(addr - 0x8000). Это передаст смещение коду, который ожидает адрес CPU в диапазоне $8000-$FFFF.
Предлагаемая реализация
def read(self, addr: int) -> int:
# Read from CPU Bus.
if 0x0 <= addr <= 0x1FFF:
return self.ram.read(addr & 0x07FF)
if 0x8000 <= addr <= 0xFFFF:
if self.mapper is not None:
return self.mapper.read_prg(addr)
if self.program_rom is not None:
return self.program_rom.read(addr - 0x8000)
raise ValueError("No program ROM or cartridge attached")
raise ValueError(f"Unsupported CPU bus read: {addr:04X}")Обоснование и инварианты
CpuBus отвечает за маршрутизацию по диапазонам адресов, а Mapper000.read_prg отвечает за трансляцию адресов NROM. Зеркалирование внутренней RAM остаётся неизменным. Чтения картриджа получают полный адрес CPU; устаревший MemoryDevice получает смещение от нуля; и настроенный источник обязателен на всём включительном диапазоне $8000-$FFFF. Путь mapper'а имеет приоритет только потому, что часть 1 запрещает одновременную настройку обоих источников, поэтому неоднозначной допустимой конструкции никогда не возникает.
Ещё одно распространённое заблуждение — скопировать правило деления по модулю на 16КБ из Mapper000 в CpuBus. Утверждение о зеркале $C000 — это доказательство того, что делегирование работает, а не новая обязанность шины.
Вне рамок
1. PPU register reads at $2000-$3FFF belong to Chapter 3.
2. PRG and mapper writes are not added here.
3. PPU/CHR routing, timing, and later behavior must not be anticipated.Запустить этот урок
uv run pytest tests/chapter_02_rom_loading/test_222_cpu_bus_cartridge_integration_part2.py -v