323. Console programa el sprite zero hit por frame

Programar el sprite 0 hit automáticamente cuando Console avanza un frame.

Lección 323 de 356 · tests/chapter_11_sprite_zero_hit/test_323_console_schedules_sprite_zero_hit_per_frame.py

Archivo a actualizar

emulator/console.py

Por qué existe este paso

Los pasos anteriores proporcionan todos los mecanismos necesarios

ppu_sprite_zero_hit_position(ppu)
    -> extracts current PPU state and finds the overlap position

ppu.set_sprite_zero_hit_position(position)
    -> stores the future timing event

ppu.step(...)
    -> sets PPUSTATUS bit 6 when timing reaches that position

Este paso los conecta al bucle de frames.

Decisión de arquitectura

Ponemos la preparación del sprite zero hit dentro de Console.step_until_next_frame() de forma intencionada. Un código llamante como main.py debe pedir a Console que avance un frame emulado completo sin saber qué eventos de timing internos del PPU deben prepararse primero.

Esto mantiene main.py centrado en las responsabilidades del frontend:

input
display
FPS reporting
frame pacing

Console es el propietario de la coordinación del emulador a nivel de frame.

Cambio de implementación sugerido

# --- NEW LINE ---
from emulator.rendering.sprite_zero_hit import ppu_sprite_zero_hit_position
# --- END NEW LINE ---


def step_until_next_frame(
    self,
    max_cpu_instructions: int | None = None,
) -> int:
    # --- NEW BLOCK ---
    position = ppu_sprite_zero_hit_position(self.ppu)
    self.ppu.set_sprite_zero_hit_position(position)
    # --- END NEW BLOCK ---

    start_frame = self.ppu.frame
    executed = 0

    while self.ppu.frame == start_frame:
        ...

¿Por qué antes del bucle de pasos? La CPU puede consultar PPUSTATUS mientras se emula el frame. El hit futuro debe programarse antes de que la ejecución CPU/PPU alcance el píxel solapado.

Punto de comprobación manual de compatibilidad

Después de este paso, los estudiantes pueden cambiar temporalmente la ruta local de la ROM manual en main.py por su propia copia legal:

Cambio de implementación sugerido en main.py:

ROM_PATH = Path("Super Mario Bros.nes")

Después, ejecuta con PyPy

Linux/macOS

sh launcher.sh

Símbolo del sistema de Windows

launcher.cmd

Mejora manual esperada

Super Mario Bros. usa el sprite 0 hit como señal de timing del PPU. Con el hit ahora detectado, programado y expuesto a través de PPUSTATUS, la pantalla de título/menú debería avanzar más, Mario debería aparecer y la entrada del mando debería volverse utilizable.

Limitación conocida restante

Cuando Mario avanza horizontalmente, la escena aún puede verse incorrecta porque el renderizador de fondo actual todavía no aplica el estado de scrolling del PPU para seleccionar y desplazar la región visible del nametable. El sprite 0 hit habilita la ruta de timing del juego; no implementa el scrolling horizontal del fondo.

Regla legal y de testeo

Super Mario Bros.nes es solo un experimento manual de compatibilidad proporcionado por el usuario. No hagas commit de la ROM y no la exijas en los tests automatizados. Estos tests usan objetos CPU/PPU falsos y solo comprueban el comportamiento de coordinación.

Fuera del alcance

  • scrolling horizontal/vertical
  • scrolling X fino
  • composición de nametables adyacentes
  • comportamiento exacto de Y+1 de OAM
  • reglas del borde izquierdo de PPUMASK
  • excepción del sprite 0 hit en x=255
  • fixtures de ROM comerciales

Ejecutar esta lección

uv run pytest tests/chapter_11_sprite_zero_hit/test_323_console_schedules_sprite_zero_hit_per_frame.py -v