327. Mirroring horizontal y vertical del bus del PPU
Aplicar mirroring horizontal o vertical de nametables en PpuBus.
Lección 327 de 356 · tests/chapter_12_mirroring/test_327_ppu_bus_horizontal_vertical_mirroring.py
Archivo a actualizar
emulator/bus/ppu_bus.pyPor qué existe este paso
El bit de mirroring ahora viaja a través de
INesHeader
-> Cartridge
-> Mapper000
-> PpuBus.mapperPpuBus puede por fin usar esos metadatos al mapear cuatro nametables lógicos sobre dos regiones físicas de RAM de nametables.
Nametables lógicos
table 0: $2000-$23FF
table 1: $2400-$27FF
table 2: $2800-$2BFF
table 3: $2C00-$2FFFCada tabla es de $400 bytes. La ventana lógica completa es de $1000 bytes, mientras que la ventana física de respaldo actual es de $800 bytes.
Mirroring vertical
logical: 0 1 2 3
physical: 0 1 0 1
[A B]
[A B]Mirroring horizontal
logical: 0 1 2 3
physical: 0 0 1 1
[A A]
[B B]Ejemplo de implementación sugerido
# emulator/bus/ppu_bus.py
NAMETABLE_SIZE = 0x800
NAMETABLE_BYTES_PER_TABLE = 0x400
NAMETABLE_LOGICAL_SIZE = 0x1000
def normalize_nametable_addr(self, addr: int) -> int:
if 0x3000 <= addr <= 0x3EFF:
addr -= 0x1000
logical_offset = (
addr - NAMETABLE_START
) % NAMETABLE_LOGICAL_SIZE
logical_table = logical_offset // NAMETABLE_BYTES_PER_TABLE
offset_inside_table = logical_offset % NAMETABLE_BYTES_PER_TABLE
is_vertical_mirroring = (
True
if self.mapper is None
else self.mapper.is_vertical_mirroring
)
if is_vertical_mirroring:
physical_table = logical_table % 2
else:
physical_table = logical_table // 2
return (
NAMETABLE_START
+ physical_table * NAMETABLE_BYTES_PER_TABLE
+ offset_inside_table
)¿Por qué conservar el comportamiento vertical sin mapper? Los tests históricos del tutorial construyen PpuBus directamente e introdujeron el antiguo comportamiento de ajuste fijo en $800 antes de que existiera el mirroring de cartucho. Los buses con mapper usan metadatos reales del cartucho; los buses sin mapper conservan ese modelo histórico de test.
Distinción importante
El mirroring determina alias de memoria. No mueve el viewport visible. El renderizador actual sigue dibujando una región fija de nametable, por lo que el scrolling horizontal es un capítulo futuro separado.
Fuera del alcance
- extracción de la posición de scroll
- recorte del viewport a través de nametables adyacentes
- mirroring de cuatro pantallas
- mirroring dinámico controlado por el mapper
- fixtures de ROM comerciales
Ejecutar esta lección
uv run pytest tests/chapter_12_mirroring/test_327_ppu_bus_horizontal_vertical_mirroring.py -v