280. Paletas de fondo a partir de la RAM de paletas

Construir paletas de fondo RGB a partir de bytes de la RAM de paletas de la PPU.

Lección 280 de 356 · tests/chapter_05_rendering_pipeline/test_280_background_palettes_from_palette_ram.py

Referencia

https://www.nesdev.org/wiki/PPU_palettes#Palette_RAM

Archivo a crear

emulator/rendering/palette_ram.py

Por qué existe este paso

El renderizador de nametable consciente de atributos espera cuatro paletas de fondo RGB ya resueltas:

background_palettes[palette_id][color_index]

Pero los juegos reales de la NES no almacenan colores RGB en el nametable ni en la tabla de atributos. Escriben índices de color de la NES en la RAM de paletas de la PPU.

Este ayudante convierte

PPU palette RAM bytes -> RGB background palettes

¿Qué es la RAM de paletas de la PPU?

La RAM de paletas de la PPU es una pequeña área de 32 bytes mapeada en

$3F00-$3F1F

Para este paso solo usamos los primeros 16 bytes

$3F00-$3F0F = background palette area

Esos bytes almacenan índices de color de la NES $00-$3F, no valores RGB.

Flujo

palette RAM byte
    -> NES color index $00-$3F
    -> get_nes_rgb_color(index)
    -> RGB tuple

Color de respaldo / de fondo universal

Para el renderizado de fondo, el índice de color 0 usa el color de respaldo compartido en $3F00.

Así que cada paleta de fondo devuelta usa el mismo primer color RGB

background_palettes[0][0] == backdrop
background_palettes[1][0] == backdrop
background_palettes[2][0] == backdrop
background_palettes[3][0] == backdrop

Diseño de paleta de fondo para $3F00-$3F0F:

$3F00 -> backdrop / universal background color

palette 0:
    entry 0 -> $3F00
    entry 1 -> $3F01
    entry 2 -> $3F02
    entry 3 -> $3F03

palette 1:
    entry 0 -> $3F00
    entry 1 -> $3F05
    entry 2 -> $3F06
    entry 3 -> $3F07

palette 2:
    entry 0 -> $3F00
    entry 1 -> $3F09
    entry 2 -> $3F0A
    entry 3 -> $3F0B

palette 3:
    entry 0 -> $3F00
    entry 1 -> $3F0D
    entry 2 -> $3F0E
    entry 3 -> $3F0F

Observa

$3F04, $3F08, and $3F0C are not used as independent background color-0
entries in this simplified helper. Color index 0 uses the shared backdrop.

Ejemplo de implementación sugerida

from emulator.rendering.framebuffer import RGBColor
from emulator.rendering.nes_palette import get_nes_rgb_color

PALETTE_RAM_SIZE = 16
TOTAL_PALETTES = 4
COLORS_PER_PALETTE = 4

BackgroundPalettes = list[list[RGBColor]]


def build_background_palettes_from_palette_ram(
    palette_ram: bytes,
) -> BackgroundPalettes:
    if len(palette_ram) != PALETTE_RAM_SIZE:
        raise ValueError(f"Background palette RAM must be {PALETTE_RAM_SIZE} bytes")

    backdrop_color = get_nes_rgb_color(palette_ram[0])
    background_palettes = []

    for palette_id in range(TOTAL_PALETTES):
        base = palette_id * COLORS_PER_PALETTE

        palette = [
            backdrop_color,
            get_nes_rgb_color(palette_ram[base + 1]),
            get_nes_rgb_color(palette_ram[base + 2]),
            get_nes_rgb_color(palette_ram[base + 3]),
        ]

        background_palettes.append(palette)

    return background_palettes

Fuera de alcance

  • paletas de sprites
  • comportamiento de transparencia de sprites
  • espejado de la RAM de paletas
  • lectura directa desde el bus de la PPU
  • colores de énfasis de PPUMASK
  • pantalla de pygame

Ejecutar esta lección

uv run pytest tests/chapter_05_rendering_pipeline/test_280_background_palettes_from_palette_ram.py -v