250. Excepción de lectura de paleta de PPUDATA

Implementa la excepción de lectura de paleta de PPUDATA ($2007).

Lección 250 de 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_250_ppudata_palette_read_exception.py

Referencia

https://www.nesdev.org/wiki/PPU_registers#PPUDATA

Archivo a actualizar

emulator/ppu/ppu.py

Constantes a añadir

PALETTE_START_ADDR = 0x3F00
PALETTE_END_ADDR = 0x3FFF

Por qué existe este paso

Las lecturas normales de PPUDATA se realizan con búfer

read $2007:
    return old ppu_data_buffer
    reload ppu_data_buffer from ppu_bus.read(vram_addr)
    increment vram_addr

Las lecturas de paleta son la excepción. Cuando vram_addr apunta a la memoria de paleta:

$3F00-$3FFF

la PPU devuelve el byte de paleta inmediatamente en lugar de devolver el búfer antiguo.

Detalle importante

El búfer antiguo se descarta igualmente. En las PPU normales de la NES, el búfer se recarga desde la memoria de nametable espejada tras la dirección de paleta:

$3F00 -> reload buffer from $2F00
$3F10 -> reload buffer from $2F10

Eso significa que las lecturas de paleta afectan a ambos

  • el valor devuelto a la CPU
  • el búfer interno ppu_data_buffer usado por futuras lecturas normales

Pseudocódigo de implementación sugerido, siguiendo el estilo explícito de comentarios:

PALETTE_START_ADDR = 0x3F00
PALETTE_END_ADDR = 0x3FFF

case 0x2007:  # PPU DATA read
    # Palette data is returned immediately.
    if PALETTE_START_ADDR <= self.vram_addr <= PALETTE_END_ADDR:
        value = self.ppu_bus.read(self.vram_addr)

        # Read buffer is discarded and reloaded from shadowed memory:
        # vram_addr - 0x1000
        # Example: 0x3F00 -> 0x2F00
        self.ppu_data_buffer = self.ppu_bus.read(self.vram_addr - 0x1000)
    else:
        # Normal PPUDATA reads return the old buffer first.
        value = self.ppu_data_buffer

        # Then the buffer is reloaded from current PPU memory.
        self.ppu_data_buffer = self.ppu_bus.read(self.vram_addr)

    increment = 32 if self.ctrl & CTRL_VRAM_INCREMENT_BY_32 else 1

    # Keep vram_addr in the 14-bit PPU address range.
    self.vram_addr = (self.vram_addr + increment) & 0x3FFF

    # Preserve self.data for old test compatibility/debugging.
    self.data = value
    return self.data

Fuera del alcance

  • espejado de la RAM de paleta, como $3F10 -> $3F00
  • valores de color de paleta precisos
  • renderizado

Ejecutar esta lección

uv run pytest tests/chapter_03_ppu_memory_and_graphics_data/test_250_ppudata_palette_read_exception.py -v