265. Console обрабатывает запрос NMI от PPU
Смоделировать Console как координатор, обрабатывающий запросы NMI от PPU.
Урок 265 из 356 · tests/chapter_04_ppu_timing_and_vblank/test_265_console_consumes_ppu_nmi_request.py
Справочные материалы
https://www.nesdev.org/wiki/CPU_interrupts
https://www.nesdev.org/wiki/PPU_registers#Vblank_NMIФайл для создания
emulator/console.pyЗачем нужен этот шаг
В эмуляторе теперь есть два отдельных механизма, необходимых для NMI при VBlank
PPU mechanism:
PPU enters VBlank and sets ppu.nmi_requested = True
CPU mechanism:
CPU.interrupt_nmi() pushes PC/status and jumps through $FFFA/$FFFBТеперь нужен небольшой координатор, который соединит эти механизмы, не заставляя CPU и PPU напрямую зависеть друг от друга.
Что такое Console? Console — это будущая эмулируемая машина NES верхнего уровня. Со временем она сможет владеть и координировать такие подсистемы, как:
CPU
PPU
cartridge/mapper
controllers
APU/audio
frame steppingНа этом этапе Console начинается очень маленькой. Её первая задача — только обработать запрос NMI от PPU и вызвать CPU.interrupt_nmi().
Корректное разделение ответственности
PPU:
produces nmi_requested
CPU:
implements interrupt_nmi()
Console:
connects the PPU signal to the CPU mechanismВажное архитектурное правило
Не помещайте владение PPU внутрь CPU. CPU не должен спрашивать у PPU, есть ли ожидающий NMI. Это создало бы скрытую связанность между выполнением CPU и видеоаппаратурой.
Минимальный пример реализации
from dataclasses import dataclass
from emulator.cpu.cpu import CPU
from emulator.ppu.ppu import PPU
@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()Будущий вид, вводится в одном из последующих тестов синхронизации
class Console:
...
def step(self) -> None:
cpu_cycles = self.cpu.step()
self.ppu.step(cpu_cycles * 3)
self.consume_nmi_if_requested()Этот файл не тестирует Console.step(). Интеграция синхронизации CPU/PPU тестируется отдельным шагом после того, как CPU.step() возвращает такты инструкции.
Распространённое заблуждение
Поскольку NMI исходит от PPU, может показаться естественным сделать PPU собственностью CPU. Избегайте этого. CPU получает сигналы прерываний; он не должен владеть видеоустройством.
Выходит за рамки
- соотношение тактов CPU/PPU
- Console.step()
- рендеринг
- контроллеры
- APU/звук
- точная задержка NMI
Запустить этот урок
uv run pytest tests/chapter_04_ppu_timing_and_vblank/test_265_console_consumes_ppu_nmi_request.py -v