343. Temporización de vram vertical
Aplicar el incremento vertical de v y la copia vertical de t a v en los dots de temporización de la PPU.
Lección 343 de 356 · tests/chapter_13_scrolling/test_343_vertical_vram_timing.py
Archivo a actualizar
emulator/ppu/ppu.pyReferencias
https://www.nesdev.org/wiki/PPU_scrolling#At_dot_256_of_each_scanlinePor qué existe este paso
Los pasos 341 y 342 implementaron mecanismos de dirección vertical puros. Este paso los conecta con el tiempo de la PPU.
Mientras el renderizado está habilitado
visible and pre-render scanlines, dot 256:
increment fine/coarse Y in v
pre-render scanline, every dot 280-304:
copy fine Y, vertical nametable, and coarse Y from t into vTemporización simplificada
visible scanline:
... 255 256 257 ...
| |
| +-- horizontal reload
+------ vertical increment
pre-render scanline 261:
... 256 257 ... 280----------------304 ...
| | |
| | +-- vertical reload active at every dot
| +---------- horizontal reload
+-------------- vertical increment¿Por qué repetir la recarga vertical en cada dot 280-304? Si las escrituras de la CPU cambian t durante ese intervalo, un dot posterior puede copiar el estado vertical más reciente. Tratar el intervalo como una única operación en el dot 280 perdería ese comportamiento.
El renderizado está habilitado cuando el bit de fondo o el bit de sprites de PPUMASK está activado. Los scanlines de post-render y VBlank no realizan estas actualizaciones automáticas.
Orden importante en el dot 256
El incremento de fetch horizontal se ejecuta primero y el incremento vertical se ejecuta después. Poseen campos distintos, así que ambos cambios deben ser visibles en el v resultante.
Fuera de alcance
- el registro del viewport efectivo por scanline
- la compensación de prefetch de dos tiles
- la composición de filas del framebuffer
- la composición de filas de la máscara de opacidad
Implementación de ejemplo completa
# emulator/ppu/ppu.py
class PPU:
...
# --- NEW BLOCK: APPLY VERTICAL ADDRESS TIMING ---
def _step_vertical_rendering_address(self) -> None:
rendering_enabled = self.mask & (
MASK_SHOW_BACKGROUND | MASK_SHOW_SPRITES
)
if not rendering_enabled:
return
rendering_scanline = (
0 <= self.scanline < 240
or self.scanline == PPU_PRE_RENDER_SCANLINE
)
if not rendering_scanline:
return
if self.cycle == 256:
self.vram_addr = increment_vertical_vram_addr(
self.vram_addr
)
vertical_reload = (
self.scanline == PPU_PRE_RENDER_SCANLINE
and 280 <= self.cycle <= 304
)
if vertical_reload:
self.vram_addr = copy_vertical_scroll_bits(
self.vram_addr,
self.temp_vram_addr,
)
def step(self, cycles: int = 1) -> None:
...
for _ in range(cycles):
self.cycle += 1
self._step_horizontal_rendering_address()
# --- NEW LINE: APPLY VERTICAL TIMING AT THE CURRENT DOT ---
self._step_vertical_rendering_address()
...Ejecutar esta lección
uv run pytest tests/chapter_13_scrolling/test_343_vertical_vram_timing.py -v