222. Integración de cartridge en CpuBus, parte 2

Leer la PRG ROM del cartridge a través de CpuBus, parte 2.

Lección 222 de 356 · tests/chapter_02_rom_loading/test_222_cpu_bus_cartridge_integration_part2.py

Requisito previo

El campo cartridge de la lección 221, __post_init__ y el cableado de la fábrica de mappers deben estar completos antes de cambiar aquí la ruta de lectura.

Archivo a actualizar

emulator/bus/cpu_bus.py

Símbolo a actualizar

emulator.bus.cpu_bus.CpuBus.read

Qué implementa esta parte

- 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

Por qué existe este paso

La CPU ve la ROM de programa del cartridge en las direcciones de CPU

$8000-$FFFF

Pero los bytes de la PRG ROM del cartridge se almacenan desde el desplazamiento 0. El mapper es el dueño de esa traducción. CpuBus solo debe decidir que las direcciones en $8000-$FFFF pertenecen al área de PRG del cartridge, y luego delegar en el mapper.

Ruta correcta del cartridge

CpuBus.read($8000)
    -> mapper.read_prg($8000)
    -> Mapper000 translates to PRG offset $0000

Diferencia importante respecto a la antigua ruta de FakeROM

program_rom.read(addr - $8000)

usa un desplazamiento porque FakeROM es un dispositivo de pruebas sencillo, no un mapper.

mapper.read_prg(addr)

usa la dirección de CPU completa porque los mappers implementan la traducción de direcciones de CPU.

Error habitual

No llames a mapper.read_prg(addr - 0x8000). Eso pasaría un desplazamiento a un código que espera una dirección de CPU en $8000-$FFFF.

Implementación sugerida

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}")

Justificación e invariantes

CpuBus es el dueño del enrutamiento por rango de direcciones, mientras que Mapper000.read_prg es el dueño de la traducción de direcciones NROM. El mirroring de la RAM interna permanece sin cambios. Las lecturas de cartridge reciben la dirección de CPU completa; el MemoryDevice heredado recibe un desplazamiento basado en cero; y se requiere una fuente configurada en todo el rango inclusivo $8000-$FFFF. La ruta del mapper tiene prioridad únicamente porque la parte 1 prohíbe configurar ambas fuentes a la vez, de modo que nunca hay una construcción válida ambigua.

Otro concepto erróneo habitual es copiar la regla de módulo de 16KB de Mapper000 en CpuBus. La aserción del espejo de $C000 es prueba de que la delegación funciona, no una nueva responsabilidad del bus.

Fuera de alcance

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.

Ejecutar esta lección

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