252. Mapeo de la RAM de paleta en el bus de la PPU
Implementa el mapeo de direcciones de la RAM de paleta en PpuBus.
Lección 252 de 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_252_ppu_bus_palette_ram_mapping.py
Referencia
https://www.nesdev.org/wiki/PPU_palettes
https://www.nesdev.org/wiki/PPU_memory_mapArchivo a modificar
emulator/bus/ppu_bus.pyConstantes a añadir
PALETTE_START = 0x3F00
PALETTE_END = 0x3FFF
PALETTE_SIZE = 0x20Por qué existe este paso
El rango de direcciones de la PPU $3F00-$3FFF es la memoria de paleta. La memoria de paleta no almacena gráficos de tiles. Almacena índices de color de la NES que se usarán más adelante en el renderizado de fondo y de sprites.
Nota de hardware
En la PPU original de la NES, la RAM de paleta es una pequeña área de memoria separada de 32 bytes dentro de la PPU. No es CHR ROM, ni tampoco VRAM de nametable. Tiene su propio comportamiento de duplicado (mirroring) en el rango de direcciones de la PPU $3F00-$3FFF.
Simplificación del tutorial
En esta etapa, todavía no necesitamos separar el almacenamiento en Python en un objeto PaletteRAM independiente. Podemos seguir usando el gran array de VRAM existente como ubicación de almacenamiento físico, siempre que PpuBus normalice las direcciones de paleta antes de leer o escribir.
Modelo mental
PPU.write_register($2007, value)
-> ppu_bus.write(vram_addr, value)
-> PpuBus detects whether vram_addr is palette memory
-> PpuBus normalizes palette mirrors
-> backing storage receives the byteReglas de duplicado de paleta para este paso
$3F00-$3F1F is the 32-byte palette window
$3F20-$3FFF mirrors $3F00-$3F1FDuplicados especiales del color de fondo
$3F10 mirrors $3F00
$3F14 mirrors $3F04
$3F18 mirrors $3F08
$3F1C mirrors $3F0CEjemplo de implementación sugerida
PALETTE_START = 0x3F00
PALETTE_END = 0x3FFF
PALETTE_SIZE = 0x20
def normalize_palette_addr(self, addr: int) -> int:
index = (addr - PALETTE_START) % PALETTE_SIZE
if index in (0x10, 0x14, 0x18, 0x1C):
index -= 0x10
return PALETTE_START + index
def read(self, addr: int) -> int:
addr = addr & PPU_ADDRESS_MASK
if CHR_START <= addr <= CHR_END:
...
if PALETTE_START <= addr <= PALETTE_END:
return self.vram.read(self.normalize_palette_addr(addr))
return self.vram.read(addr)
def write(self, addr: int, value: int) -> None:
addr = addr & PPU_ADDRESS_MASK
if CHR_START <= addr <= CHR_END:
...
return
if PALETTE_START <= addr <= PALETTE_END:
self.vram.write(self.normalize_palette_addr(addr), value)
return
self.vram.write(addr, value)Decisión de diseño importante
La lógica de duplicado de paleta pertenece a PpuBus, no a la PPU. La PPU gestiona el comportamiento de los registros; PpuBus gestiona el enrutamiento de direcciones de memoria de la PPU.
Fuera de alcance
- colores RGB reales
- renderizado
- selección de paleta de fondo/sprites
- decodificación de atributos de nametable
- separar la RAM de paleta en su propia clase de almacenamiento
Ejecutar esta lección
uv run pytest tests/chapter_03_ppu_memory_and_graphics_data/test_252_ppu_bus_palette_ram_mapping.py -v