348. X del viewport de scanline

Decodificar la X del viewport horizontal a partir de un estado de scanline registrado.

Lección 348 de 356 · tests/chapter_13_scrolling/test_348_scanline_viewport_x.py

Archivo a actualizar

emulator/rendering/ppu_background_renderer.py

Referencia

https://www.nesdev.org/wiki/PPU_scrolling

Por qué existe este paso

El paso 347 seleccionó las dos nametables lógicas que forman un par de origen horizontal. Un compositor de filas posterior también necesita el píxel exacto en el que comienza este scanline dentro de ese par de 512 píxeles de ancho.

La dirección registrada usa el mismo formato empaquetado de scroll que t

yyy NN YYYYY XXXXX

La X del viewport horizontal usa

XXXXX          coarse X tile column
low N bit      horizontal nametable selection, vram_addr bit 10
state.fine_x   pixel offset inside the first tile

Conversión de píxeles

viewport_x = nametable_x * 256 + coarse_x * 8 + fine_x

Ejemplo

nametable X = 1
coarse X    = 5
fine X      = 3

viewport X = 1 * 256 + 5 * 8 + 3 = 299

¿Por qué reutilizar decode_background_viewport_position? El decodificador puro existente ya posee la conversión del campo de scroll empaquetado. Tanto t como la v registrada tienen el mismo formato de bits, así que repetir máscaras y dimensiones en este renderizador crearía dos implementaciones que podrían divergir.

Invariantes importantes

  • el bit 10 de la nametable horizontal aporta 256 píxeles
  • la X gruesa aporta ocho píxeles por tile
  • la X fina aporta el desplazamiento final de 0-7 píxeles
  • la nametable vertical, la Y gruesa y la Y fina no afectan a la X del viewport
  • el resultado permanece dentro del rango lógico horizontal 0-511
  • la dirección registrada no se rebobina de nuevo
  • el auxiliar no realiza renderizado ni acceso a memoria

Concepto erróneo común

La dirección registrada no necesita otra corrección de dos tiles. El paso 345 ya rebobinó dos veces una copia de v antes de construir BackgroundScanlineState. Repetir el rebobinado aquí desplazaría incorrectamente cada fila renderizada dieciséis píxeles a la izquierda.

Fuera de alcance

  • seleccionar direcciones lógicas de nametable
  • componer filas del framebuffer
  • aplicar el desbordamiento horizontal a los píxeles de destino
  • la composición de la máscara de opacidad
  • elegir entre los datos temporizados y el mecanismo de respaldo a nivel de frame antiguo

Implementación de ejemplo completa

# emulator/rendering/ppu_background_renderer.py

# --- NEW BLOCK: DECODE ONE SCANLINE'S HORIZONTAL VIEWPORT ---
def _scanline_viewport_x(state: BackgroundScanlineState) -> int:
    viewport_x, _ = decode_background_viewport_position(
        temp_vram_addr=state.vram_addr,
        fine_x=state.fine_x,
    )

    return viewport_x

Ejecutar esta lección

uv run pytest tests/chapter_13_scrolling/test_348_scanline_viewport_x.py -v