260. Генерация VBlank PPU из синхронизации

Реализуйте генерацию VBlank из синхронизации PPU.

Урок 260 из 356 · tests/chapter_04_ppu_timing_and_vblank/test_260_ppu_vblank_generation_from_timing.py

Справка

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

Файл для обновления

emulator/ppu/ppu.py

Константы для добавления

PPU_VBLANK_START_SCANLINE = 241
PPU_PRE_RENDER_SCANLINE = 261

Почему этот шаг существует

Многие программы NES ждут VBlank перед записью в память PPU. Они обычно опрашивают PPUSTATUS ($2002) до тех пор, пока бит 7 не установится.

Концептуальное поведение для этого шага учебника

when the PPU enters scanline 241:
    set PPUSTATUS bit 7, VBLANK_STARTED

when the PPU enters scanline 261, the pre-render scanline:
    clear PPUSTATUS bit 7

Это даёт ROM базовый сигнал кадра без реализации полного рендеринга.

Предлагаемый пример реализации

PPU_VBLANK_START_SCANLINE = 241
PPU_PRE_RENDER_SCANLINE = 261

def step(self, cycles: int = 1) -> None:
    for _ in range(cycles):
        self.cycle += 1

        if self.cycle >= PPU_CYCLES_PER_SCANLINE:
            self.cycle = 0
            self.scanline += 1

            if self.scanline >= PPU_SCANLINES_PER_FRAME:
                self.scanline = 0
                self.frame += 1

            # Simplified scanline-level timing event.
            if self.scanline == PPU_VBLANK_START_SCANLINE:
                self.status |= VBLANK_STARTED

            if self.scanline == PPU_PRE_RENDER_SCANLINE:
                self.status &= ~VBLANK_STARTED

Важное упрощение

Это не точная по точкам синхронизация PPU. На реальном оборудовании изменения состояния происходят в определённых точках/циклах PPU, обычно моделируемых около точки/цикла 1 соответствующей строки развёртки. Пока этот учебник использует поведение входа в строку развёртки, потому что цель — научить концепции кадра/VBlank перед полной точностью цикла.

Вне целей учебника на данный момент

  • точная по точкам/циклам синхронизация VBlank
  • пропуск цикла нечётного кадра
  • рендеринг пикселей во время видимых строк развёртки
  • попадание спрайта 0
  • переполнение спрайтов
  • генерация NMI/обработка прерывания CPU

Запустить этот урок

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