332. Преобразование логической таблицы имён Ppu во фреймбуфер

Отрисовать одну выбранную логическую таблицу имён PPU в виде фреймбуфера.

Урок 332 из 356 · tests/chapter_13_scrolling/test_332_ppu_logical_nametable_to_framebuffer.py

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

emulator/rendering/ppu_background_renderer.py

Справочные материалы

https://www.nesdev.org/wiki/PPU_nametables

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

Исходная вспомогательная функция всегда отрисовывала логическую таблицу имён $2000. Для горизонтальной прокрутки нужна также соседняя логическая таблица имён, поэтому вспомогательная функция теперь принимает одно из значений:

$2000, $2400, $2800, $2C00

Расположение в памяти относительно выбранного базового адреса

base + $000-$3BF: 960 visible tile IDs
base + $3C0-$3FF: 64 attribute bytes

Совместимость

Если параметр base_nametable_addr опущен, по-прежнему должна отрисовываться логическая таблица имён $2000. Поэтому существующим вызывающим сторонам не нужно ничего менять.

Граница зеркалирования

Отрисовка запрашивает логические адреса через PpuBus. PpuBus по-прежнему отвечает за отображение этих адресов в физическую VRAM, зеркалируемую по горизонтали или по вертикали.

Вне объёма работ

  • выбор и составление двух соседних фреймбуферов
  • выбор адресов для маски непрозрачности
  • интеграция Console
  • составление вертикальной области просмотра
  • pygame

Пример реализации

# emulator/rendering/ppu_background_renderer.py

# --- NEW LINES: ACCEPTED LOGICAL NAMETABLE BASE ADDRESSES ---
LOGICAL_NAMETABLE_BASE_ADDRS = (
    0x2000,
    0x2400,
    0x2800,
    0x2C00,
)


def ppu_background_to_framebuffer(
    ppu: PPU,
    # --- NEW LINE: OPTIONAL LOGICAL NAMETABLE SELECTION ---
    base_nametable_addr: int = BASE_NAMETABLE_ADDR,
) -> Framebuffer:
    # --- NEW BLOCK: REJECT NON-NAMETABLE BASE ADDRESSES ---
    if base_nametable_addr not in LOGICAL_NAMETABLE_BASE_ADDRS:
        raise ValueError(
            "Logical nametable base address must be $2000, $2400, $2800, $2C00"
        )

    nametable_bytes = bytes(
        # --- UPDATED LINE: READ FROM THE SELECTED NAMETABLE ---
        ppu.ppu_bus.read(base_nametable_addr + offset)
        for offset in range(NAMETABLE_SIZE)
    )

    # --- NEW LINE: DERIVE THE SELECTED ATTRIBUTE-TABLE BASE ---
    attribute_table_base = base_nametable_addr + NAMETABLE_SIZE
    attribute_table = bytes(
        # --- UPDATED LINE: READ THE SELECTED ATTRIBUTE TABLE ---
        ppu.ppu_bus.read(attribute_table_base + offset)
        for offset in range(ATTR_TABLE_SIZE)
    )

   ... 
   # Everything remains the same below this point
   

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

uv run pytest tests/chapter_13_scrolling/test_332_ppu_logical_nametable_to_framebuffer.py -v