268. Console.step avanza la PPU

Hacer que Console.step() avance el tiempo de la PPU a partir de los ciclos de la CPU.

Lección 268 de 356 · tests/chapter_04_ppu_timing_and_vblank/test_268_console_step_advances_ppu.py

Referencias

https://www.nesdev.org/wiki/CPU
https://www.nesdev.org/wiki/PPU_rendering#Line-by-line_timing

Archivo a actualizar

emulator/console.py

Por qué existe este paso

El emulador ahora tiene las piezas necesarias para conectar el tiempo de la CPU con el tiempo de la PPU

CPU.step() returns base CPU cycles
PPU.step(cycles) advances PPU timing counters
Console.consume_nmi_if_requested() connects PPU NMI requests to CPU NMI

Console.step() es donde esas piezas se convierten en un paso a nivel de máquina.

¿Qué es el stepping a nivel de máquina? El stepping a nivel de máquina significa avanzar múltiples chips emulados juntos según su relación de reloj.

Regla de temporización mínima de la NES

1 CPU cycle = 3 PPU cycles

Ejemplo mínimo

CPU executes NOP
NOP takes 2 CPU cycles
PPU advances 2 * 3 = 6 PPU cycles

Concepto erróneo común

No hagas que CPU.step() llame a PPU.step(). La CPU no debería poseer la temporización de vídeo. Console es quien coordina los chips.

Ejemplo de implementación sugerido

@dataclass
class Console:
    cpu: CPU
    ppu: PPU

    def consume_nmi_if_requested(self) -> None:
        if not self.ppu.nmi_requested:
            return

        self.ppu.nmi_requested = False
        self.cpu.interrupt_nmi()

    def step(self) -> int:
        # This is base timing only. Later steps can add branch penalties,
        # NMI latency, DMA stalls, and other timing details.
        cpu_cycles = self.cpu.step()
        self.ppu.step(cpu_cycles * 3)
        self.consume_nmi_if_requested()
        return cpu_cycles

Limitación importante

Esta es todavía una temporización simplificada. CPU.step() actualmente devuelve solo ciclos base.

Las mejoras posteriores pueden tener en cuenta

branch taken extra cycles
branch page-cross extra cycles
indexed addressing page-cross extra cycles
NMI latency
OAM DMA stalls

Fuera de alcance

  • penalizaciones dinámicas de ciclo de la CPU
  • latencia NMI precisa a nivel de punto
  • OAM DMA
  • renderizado de píxeles
  • bucle de pygame/frontend
  • entrada de controlador

Ejecutar esta lección

uv run pytest tests/chapter_04_ppu_timing_and_vblank/test_268_console_step_advances_ppu.py -v