287. Console step until next frame
Add Console.step_until_next_frame() for frame-level stepping.
Lesson 287 of 356 · tests/chapter_05_rendering_pipeline/test_287_console_step_until_next_frame.py
File to update
emulator/console.pyWhy this step exists
Console already has a one-instruction stepping method
console.step()That is the smallest machine-time operation in this emulator. It executes one CPU instruction, advances the PPU by CPU cycles * 3, then consumes any pending NMI.
Manual runners and future frontends usually need a larger operation
run emulation until one full PPU frame completes
then ask for a framebuffer explicitlyThis step adds
console.step_until_next_frame(max_cpu_instructions: int | None = None) -> intExample implementation
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 executedDifference between step() and 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 executedExample usage
console.step_until_next_frame()
framebuffer = console.render_background_framebuffer()Why max_cpu_instructions is optional: This parameter is not NES hardware behavior. It is an emulator debugging/testing guard.
With None, there is no artificial instruction limit. This is useful for real or manual execution:
console.step_until_next_frame()With an integer, the helper raises if that many CPU instructions execute without a new frame. This is useful for tests and debugging because it prevents infinite loops if the CPU gets stuck, an opcode is missing, or a frame never completes:
console.step_until_next_frame(max_cpu_instructions=10)Important separation
step_until_next_frame()
advances emulation time
render_background_framebuffer()
observes current PPU memory and returns Framebuffer dataDo not render automatically inside step_until_next_frame().
Out of scope
- pygame display
- sprites
- OAMDMA
- exact NMI latency
- dynamic CPU cycle penalties
- controller input
Run this lesson
uv run pytest tests/chapter_05_rendering_pipeline/test_287_console_step_until_next_frame.py -v