340. Temporización horizontal de vram
Aplica los incrementos horizontales de v y la copia horizontal de t a v en los dots de temporización de la PPU.
Lección 340 de 356 · tests/chapter_13_scrolling/test_340_horizontal_vram_timing.py
Archivo a actualizar
emulator/ppu/ppu.pyReferencias
https://www.nesdev.org/wiki/PPU_scrolling#During_rendering
https://www.nesdev.org/wiki/PPU_rendering#Line-by-line_timingPor qué existe este paso
Los pasos 338 y 339 implementaron mecanismos de dirección horizontales puros. Este paso los conecta con el tiempo de la PPU.
En scanlines visibles y de pre-renderizado mientras el renderizado está activo
dots 8, 16, 24, ... 256:
increment horizontal v after each fetched tile
dot 257:
copy coarse X and horizontal nametable from t into v
dots 328 and 336:
increment horizontal v while prefetching the first tiles for the next scanlineEl renderizado está activo cuando en PPUMASK se activa el fondo o el renderizado de sprites. La temporización de direcciones está inactiva durante los scanlines de post-renderizado y de VBlank.
Línea temporal simplificada
1---------256 257 --------320 321------336 ----340
tile fetches reload X prefetch tiles
^ every 8 ^ 328 and 336Orden importante
PPU.step() primero avanza self.cycle. El valor resultante se trata como el dot actual y después se aplica la temporización de direcciones horizontales. Por lo tanto, una PPU que empieza en el ciclo 256 realiza la recarga horizontal después de un paso, en el dot 257.
Error común
Los incrementos horizontales no se limitan a los píxeles visibles. Los dots 328 y 336 preparan los dos primeros tiles de fondo para el siguiente scanline y también incrementan v.
Fuera del alcance
- incremento vertical en el dot 256
- copia vertical de t a v durante el pre-renderizado
- registro del viewport por scanline
- cambios del framebuffer y de la máscara de opacidad
Implementación de ejemplo completa
# emulator/ppu/ppu.py
class PPU:
...
# --- NEW BLOCK: APPLY HORIZONTAL ADDRESS TIMING ---
def _step_horizontal_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
visible_fetch_increment = (
1 <= self.cycle <= 256
and self.cycle % 8 == 0
)
prefetch_increment = (
321 <= self.cycle <= 336
and self.cycle % 8 == 0
)
if visible_fetch_increment or prefetch_increment:
self.vram_addr = increment_horizontal_vram_addr(
self.vram_addr
)
if self.cycle == 257:
self.vram_addr = copy_horizontal_scroll_bits(
self.vram_addr,
self.temp_vram_addr,
)
def step(self, cycles: int = 1) -> None:
...
for _ in range(cycles):
self.cycle += 1
# --- NEW LINE: APPLY TIMING AT THE CURRENT DOT ---
self._step_horizontal_rendering_address()
...Ejecutar esta lección
uv run pytest tests/chapter_13_scrolling/test_340_horizontal_vram_timing.py -v