287. Console avanza hasta el siguiente fotograma

Añadir Console.step_until_next_frame() para el avance a nivel de fotograma.

Lección 287 de 356 · tests/chapter_05_rendering_pipeline/test_287_console_step_until_next_frame.py

Archivo a actualizar

emulator/console.py

Por qué existe este paso

Console ya tiene un método de avance de una instrucción

console.step()

Esa es la operación de tiempo de máquina más pequeña de este emulador. Ejecuta una instrucción de CPU, avanza la PPU en ciclos de CPU * 3 y luego consume cualquier NMI pendiente.

Los ejecutores manuales y los futuros frontends normalmente necesitan una operación más grande

run emulation until one full PPU frame completes
then ask for a framebuffer explicitly

Este paso añade

console.step_until_next_frame(max_cpu_instructions: int | None = None) -> int

Ejemplo de implementación

def step_until_next_frame(
    self,
    max_cpu_instructions: int | None = None,
) -> int:
    start_frame = self.ppu.frame
    executed = 0

    while self.ppu.frame == start_frame:
        if max_cpu_instructions is not None:
            if executed >= max_cpu_instructions:
                raise RuntimeError("Frame did not complete before instruction limit")

        self.step()
        executed += 1

    return executed

Diferencia entre step() y step_until_next_frame():

step()
    executes exactly one CPU instruction
    advances PPU by that instruction's cycles * 3
    returns CPU cycles for that instruction

step_until_next_frame()
    calls step() repeatedly until ppu.frame changes
    returns how many CPU instructions were executed

Ejemplo de uso

console.step_until_next_frame()
framebuffer = console.render_background_framebuffer()

Por qué max_cpu_instructions es opcional: este parámetro no es comportamiento de hardware del NES. Es una salvaguarda de depuración/testing del emulador.

Con None, no hay límite artificial de instrucciones. Esto es útil para la ejecución real o manual:

console.step_until_next_frame()

Con un entero, el ayudante lanza una excepción si se ejecutan esa cantidad de instrucciones de CPU sin que se complete un nuevo fotograma. Esto es útil para tests y depuración porque evita bucles infinitos si la CPU se atasca, falta un opcode o un fotograma nunca se completa:

console.step_until_next_frame(max_cpu_instructions=10)

Separación importante

step_until_next_frame()
    advances emulation time

render_background_framebuffer()
    observes current PPU memory and returns Framebuffer data

No renderices automáticamente dentro de step_until_next_frame().

Fuera de alcance

  • visualización con pygame
  • sprites
  • OAMDMA
  • latencia exacta de NMI
  • penalizaciones dinámicas de ciclos de CPU
  • entrada del mando

Ejecutar esta lección

uv run pytest tests/chapter_05_rendering_pipeline/test_287_console_step_until_next_frame.py -v