341. Incrementar la dirección vertical de vram

Incrementar el componente vertical de la dirección de renderizado actual v.

Lección 341 de 356 · tests/chapter_13_scrolling/test_341_increment_vertical_vram_addr.py

Archivo a actualizar

emulator/ppu/ppu.py

Referencias

https://www.nesdev.org/wiki/PPU_scrolling#Y_increment
https://www.nesdev.org/wiki/PPU_scrolling#Wrapping_around

Por qué existe este paso

En el dot 256, la PPU avanza la dirección de renderizado en una fila de píxeles. A diferencia del incremento horizontal, el movimiento vertical primero avanza la Y fina dentro del tile actual de 8 píxeles:

yyy NN YYYYY XXXXX
+++    +++++
 |       +---- coarse Y: bits 5-9
 +------------ fine Y: bits 12-14

vertical nametable: bit 11

Comportamiento de Y fina

0 -> 1 -> 2 -> ... -> 7

Cuando la Y fina ya vale 7, vuelve a 0 y la Y gruesa avanza.

Comportamiento de la Y gruesa tras el desbordamiento de la Y fina

coarse Y 0-28 -> increment by one
coarse Y 29   -> 0 and toggle vertical nametable
coarse Y 30   -> 31 through the normal increment branch
coarse Y 31   -> 0 without toggling vertical nametable

Las filas 0-29 son las 30 filas de tiles visibles de una nametable. Los valores 30 y 31 no son filas visibles normales, pero pueden existir porque la Y gruesa es un campo de cinco bits y las operaciones de dirección de la CPU pueden cargar esos valores.

¿Por qué sumar $1000 para un incremento normal de Y fina? La Y fina comienza en el bit 12, así que sumar 1 << 12 incrementa ese campo empaquetado en uno. Esto no significa conceptualmente desplazarse 4096 bytes por la memoria de la PPU.

Invariantes importantes

  • la X gruesa horizontal y la selección de nametable permanecen sin cambios
  • los bits internos de dirección no relacionados permanecen sin cambios
  • la nametable vertical solo se alterna cuando la fila 29 se desborda
  • el auxiliar es puro y no modifica la PPU

Concepto erróneo común

El incremento vertical no es simplemente simétrico al incremento horizontal. El movimiento horizontal avanza una columna de tiles; el movimiento vertical avanza una fila de píxeles fina y solo a veces avanza la fila de tiles gruesa.

Fuera de alcance

  • invocar este auxiliar en el dot 256
  • la copia vertical de t a v
  • la temporización del pre-render
  • el registro del viewport por scanline
  • el renderizado del framebuffer

Implementación de ejemplo completa

# emulator/ppu/ppu.py

# --- NEW BLOCK: PURE VERTICAL v INCREMENT ---
def increment_vertical_vram_addr(vram_addr: int) -> int:
    fine_y = (vram_addr >> 12) & 0b111

    if fine_y < 7:
        return vram_addr + (1 << 12)

    vram_addr &= ~0b111_00_00000_00000
    coarse_y = (vram_addr >> 5) & 0b1_1111

    if coarse_y == 29:
        coarse_y = 0
        vram_addr ^= 0b000_10_00000_00000
    elif coarse_y == 31:
        coarse_y = 0
    else:
        coarse_y += 1

    return (
        (vram_addr & ~0b000_00_11111_00000)
        | (coarse_y << 5)
    )

Ejecutar esta lección

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