221. Cpu bus cartridge integration part1

Добавление опциональной поддержки Cartridge в CpuBus, часть 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 принимает опциональный картридж
  • CpuBus создаёт mapper из этого картриджа в __post_init__
  • CpuBus запрещает одновременное подключение program_rom и cartridge

Что эта часть пока НЕ реализует

  • чтение CPU из PRG ROM картриджа

Это поведение чтения тестируется во второй части.

Почему этот шаг нужен

Добавление поддержки картриджа имеет две отдельные зоны ответственности

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

Разделение этих зон облегчает изучение материала. Сначала студенты узнают, как шина получает картридж и превращает его в 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

Распространённое заблуждение

Картридж сам по себе не является заменой MemoryDevice для program_rom. Это метаданные плюс полезные данные 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