NES TDD

282. Фон PPU во framebuffer

Рендеринг текущей фоновой памяти PPU во framebuffer.

Урок 282 из 356 · tests/chapter_05_rendering_pipeline/test_282_ppu_background_to_framebuffer.py

Файл для создания

emulator/rendering/ppu_background_renderer.py

Зачем нужен этот шаг

Рендерер уже умеет создавать фоновый framebuffer из явных массивов байтов:

nametable bytes
attribute table bytes
pattern table bytes
palette RAM bytes

Но текущее состояние эмулятора хранит эти байты за интерфейсом памяти PPU/PpuBus. На этом шаге создаётся тонкий вспомогательный экстрактор:

ppu_background_to_framebuffer(ppu)

Он читает нужные области памяти PPU, а затем передаёт данные чистому рендереру.

Области памяти, используемые в этом упрощённом рендерере

$2000-$23BF -> visible nametable tile IDs, 960 bytes
$23C0-$23FF -> attribute table, 64 bytes
$0000-$0FFF -> pattern table 0, if PPUCTRL bit 4 is clear
$1000-$1FFF -> pattern table 1, if PPUCTRL bit 4 is set
$3F00-$3F0F -> background palette RAM, 16 bytes

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

def ppu_background_to_framebuffer(ppu: PPU) -> Framebuffer:
    nametable_bytes = bytes(
        ppu.ppu_bus.read(BASE_NAMETABLE_ADDR + offset)
        for offset in range(NAMETABLE_SIZE)
    )

    attribute_table = bytes(
        ppu.ppu_bus.read(BASE_ATTR_TABLE_ADDR + offset)
        for offset in range(ATTR_TABLE_SIZE)
    )

    pattern_table_base = (
        PATTERN_TABLE_1_ADDR
        if ppu.ctrl & CTRL_BACKGROUND_PATTERN_TABLE
        else PATTERN_TABLE_0_ADDR
    )

    pattern_table_bytes = bytes(
        ppu.ppu_bus.read(pattern_table_base + offset)
        for offset in range(PATTERN_TABLE_SIZE)
    )

    palette_ram = bytes(
        ppu.ppu_bus.read(PALETTE_RAM_ADDR + offset)
        for offset in range(PALETTE_RAM_SIZE)
    )

    return nametable_with_palette_ram_to_framebuffer(
        nametable_bytes,
        attribute_table,
        pattern_table_bytes,
        palette_ram,
    )

Архитектурное правило

Этот помощник извлекает данные из памяти PPU. Он не должен дублировать логику рендеринга nametable, атрибутов, CHR или палитры.

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

Здесь рендерится только базовый nametable по адресу $2000. Скроллинг и биты выбора nametable из PPUCTRL пока не применяются.

Вне рамок этого шага

  • скроллинг
  • выбор базового nametable через PPUCTRL
  • спрайты
  • OAMDMA
  • отображение pygame
  • полный цикл кадров

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

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