337. Planificar el desplazamiento temporizado de vram
Comprende el plan de desplazamiento temporizado de v/t/x antes de modificar el stepping de la PPU.
Lección 337 de 356 · tests/chapter_13_scrolling/test_337_plan_timed_vram_scrolling.py
Archivo a confirmar tras la lectura
emulator/ppu/ppu.pyReferencias
https://www.nesdev.org/wiki/PPU_scrolling
https://www.nesdev.org/wiki/PPU_scrolling#During_rendering
https://www.nesdev.org/wiki/PPU_rendering#Line-by-line_timingPor qué existe este paso de lectura
PPU.step() es un subsistema crítico en cuanto a temporización. Un pequeño error puede producir una imagen que parece casi correcta mientras usa un scanline, nametable o posición de desplazamiento incorrectos. Antes de modificarlo, necesitamos un modelo mental estable y un plan de migración claro.
El problema actual
El viewport existente a nivel de fotograma decodifica temp_vram_addr (t) más fine X después del fotograma. Eso funciona para desplazamiento sintético simple pero falla con juegos que cambian el desplazamiento durante un fotograma.
Super Mario Bros. usa aproximadamente esta forma:
visible rows 0-30:
fixed status-bar scroll
sprite-zero split:
CPU prepares another horizontal position
visible rows 31-239:
moving gameplay scrollUn único valor muestreado después del fotograma no puede describir ambas regiones.
Hay un segundo problema: t no es exclusivamente un valor de desplazamiento. $2006 (PPUADDR) también escribe t mientras los juegos cargan nametables y paletas. Por ejemplo:
CPU writes PPUADDR $3F00
-> t becomes $3F00Decodificar ese valor final como desplazamiento puede seleccionar brevemente el viewport equivocado. Por eso el inicio o las transiciones de nivel pueden parecer que se desplazan rápidamente mientras la pantalla está en negro.
Los cuatro valores internos de desplazamiento
v = current VRAM address used by rendering
t = temporary VRAM address prepared by CPU register writes
x = fine horizontal pixel offset
w = first/second-write toggle for $2005 and $2006Modelo intuitivo
t is the next address configuration being prepared.
v is the address currently moving through rendering.
x is the 0-7 pixel offset inside the first tile.
w remembers which half of a two-write register comes next.Error común
t is the current scroll position for the entire frame.Modelo correcto
CPU writes assemble t and x.
PPU timing copies selected fields from t into v.
Rendering advances v while tiles and scanlines are processed.Operaciones temporizadas importantes
background-fetch dots, every 8 dots:
increment horizontal v
dot 256:
increment vertical v
dot 257:
copy horizontal fields from t into v
pre-render dots 280-304:
copy vertical fields from t into vCampos horizontales
coarse X
horizontal nametable bitCampos verticales
coarse Y
fine Y
vertical nametable bitPor qué las copias son selectivas
En el dot 257, solo debe refrescarse la posición horizontal del siguiente scanline. Copiar todo t también reemplazaría el estado vertical en el momento equivocado.
Por qué se registra por scanline
El renderizador de nametables existente ya produce framebuffers de origen RGB correctos. Todavía no necesitamos reemplazarlo por un pipeline completo de obtención de píxeles por dot. En su lugar, la temporización de la PPU registrará la posición efectiva v + x una vez por cada scanline visible:
scanline 0 -> viewport X 0
scanline 1 -> viewport X 0
...
scanline 30 -> viewport X 0
scanline 31 -> viewport X 40
...Después del fotograma, el renderizador de alto nivel usa esas posiciones registradas mientras copia cada fila de salida exactamente una vez.
Detalle del prefetch
En el dot 1, v ya está dos tiles por delante porque los dots 321-336 obtuvieron los dos primeros tiles de fondo en los registros de desplazamiento de hardware. La instantánea del scanline debe compensar esos dos incrementos de coarse X al derivar el viewport visible. De lo contrario, todo el fondo aparece desplazado 16 píxeles.
Plan de compatibilidad
- mantener el comportamiento existente de los registros v, t, x, w y las funciones auxiliares de renderizado públicas
- añadir operaciones de dirección puras antes de aplicarlas dentro de PPU.step()
- conservar la antigua ruta del viewport a nivel de fotograma como mecanismo de reserva durante la migración
- añadir el renderizado por scanline temporizado solo cuando exista un fotograma completo de estados
- mantener idéntica la selección de filas del framebuffer y de la máscara de opacidad
- ejecutar
uv run pytestdespués de cada paso numerado
Próximos pasos centrados
- incremento horizontal puro de v y wrap del nametable
- copia horizontal pura de campos de t a v
- incrementos en los dots de obtención horizontales y copia en el dot 257
- incremento vertical puro con fine Y y filas 29-31
- copia vertical pura de campos de t a v
- incremento vertical en el dot 256 y copia vertical en el pre-renderizado
- estado efectivo de v + fine X para cada scanline visible
- composición del framebuffer limitada por filas
- composición idéntica de la máscara de opacidad limitada por filas
- máscara consciente del scanline para la planificación del sprite-zero-hit
- validación manual de la barra de estado, el movimiento, las transiciones y los FPS de Super Mario Bros.
Límite de precisión
Este enfoque modela la temporización de las direcciones de desplazamiento y la selección del viewport por scanline. Todavía no es un renderizador completo de obtención y registros de desplazamiento del fondo por dot. Las escrituras en registros de la CPU también se temporizan con la granularidad actual de avance instrucción a instrucción.
Acción requerida tras la lectura
Añade exactamente este comentario cerca de los campos vram_addr, temp_vram_addr, fine_x y second_write_toggle en emulator/ppu/ppu.py:
# pass_test_337No implementes cambios de desplazamiento en este paso. El comentario es el único cambio en archivos de producción necesario para pasar la prueba 337.
Ejecutar esta lección
uv run pytest tests/chapter_13_scrolling/test_337_plan_timed_vram_scrolling.py -v