326. El mapper conserva el mirroring

Propagar los metadatos de mirroring de nametables de Cartridge a Mapper000.

Lección 326 de 356 · tests/chapter_12_mirroring/test_326_mapper_preserves_mirroring.py

Archivos a actualizar

emulator/cartridge/mapper000.py
emulator/cartridge/mapper_factory.py
emulator/cartridge/mapper_interface.py

Por qué existe este paso

PpuBus ya recibe el mapper del cartucho. Exponer el mirroring a través del mapper mantiene una única ruta de propiedad del hardware del cartucho:

INesHeader
    -> Cartridge
    -> Mapper000
    -> PpuBus (next step)

Esto es preferible a conectar Cartridge directamente a PpuBus por separado. Mappers futuros pueden controlar el mirroring mediante registros del mapper, por lo que el mapper es la frontera útil a largo plazo.

Cambios de implementación sugeridos

# emulator/cartridge/mapper000.py

@dataclass
class Mapper000:
    prg_rom: bytes
    chr_rom: bytes

    # --- NEW LINE ---
    is_vertical_mirroring: bool = False
    # --- END NEW LINE ---

    ...


# emulator/cartridge/mapper_factory.py

def create_mapper(cartridge: Cartridge):
    if cartridge.mapper_number == 0:
        return Mapper000(
            prg_rom=cartridge.prg_rom,
            chr_rom=cartridge.chr_rom,

            # --- NEW LINE ---
            is_vertical_mirroring=cartridge.is_vertical_mirroring,
            # --- END NEW LINE ---
        )

    ...


# emulator/cartridge/mapper_interface.py

class MapperInterface(Protocol):
    # --- NEW LINE ---
    is_vertical_mirroring: bool
    # --- END NEW LINE ---

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

Compatibilidad de anotaciones

El protocolo puede usar anotaciones inmediatas o from __future__ import annotations. El test ejecutable resuelve los type hints antes de comprobar que el mirroring es un booleano, por lo que el contrato no depende de si Python almacena la anotación como bool o como la cadena "bool".

¿Por qué añadir un campo de Mapper000 con valor por defecto? Los tests históricos y el código del tutorial construyen Mapper000 con solo PRG y CHR ROM. Añadir un valor por defecto False conserva ese constructor y significa mirroring horizontal cuando no se proporcionan metadatos.

Significado

False -> horizontal mirroring
True  -> vertical mirroring

Fuera del alcance

  • cambiar el mapeo de direcciones de PpuBus
  • cambiar la propiedad de CpuBus
  • mirroring de cuatro pantallas
  • scrolling
  • fixtures de ROM comerciales

Ejecutar esta lección

uv run pytest tests/chapter_12_mirroring/test_326_mapper_preserves_mirroring.py -v