NES TDD

250. Исключение чтения палитры в PPUDATA

Реализовать исключение чтения палитры в PPUDATA ($2007).

Урок 250 из 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_250_ppudata_palette_read_exception.py

Ссылка

https://www.nesdev.org/wiki/PPU_registers#PPUDATA

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

emulator/ppu/ppu.py

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

PALETTE_START_ADDR = 0x3F00
PALETTE_END_ADDR = 0x3FFF

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

Обычное чтение PPUDATA буферизовано

read $2007:
    return old ppu_data_buffer
    reload ppu_data_buffer from ppu_bus.read(vram_addr)
    increment vram_addr

Чтение палитры является исключением. Когда vram_addr указывает на память палитры:

$3F00-$3FFF

PPU возвращает байт палитры немедленно, вместо того чтобы возвращать старый буфер.

Важная деталь

Старый буфер всё равно отбрасывается. На обычных PPU NES буфер перезагружается из теневой памяти таблицы имён за адресом палитры:

$3F00 -> reload buffer from $2F00
$3F10 -> reload buffer from $2F10

Это означает, что чтение палитры влияет как на

  • значение, возвращаемое CPU
  • внутренний ppu_data_buffer, используемый будущими обычными чтениями

Предлагаемый псевдокод реализации, соответствующий стилю явных комментариев:

PALETTE_START_ADDR = 0x3F00
PALETTE_END_ADDR = 0x3FFF

case 0x2007:  # PPU DATA read
    # Palette data is returned immediately.
    if PALETTE_START_ADDR <= self.vram_addr <= PALETTE_END_ADDR:
        value = self.ppu_bus.read(self.vram_addr)

        # Read buffer is discarded and reloaded from shadowed memory:
        # vram_addr - 0x1000
        # Example: 0x3F00 -> 0x2F00
        self.ppu_data_buffer = self.ppu_bus.read(self.vram_addr - 0x1000)
    else:
        # Normal PPUDATA reads return the old buffer first.
        value = self.ppu_data_buffer

        # Then the buffer is reloaded from current PPU memory.
        self.ppu_data_buffer = self.ppu_bus.read(self.vram_addr)

    increment = 32 if self.ctrl & CTRL_VRAM_INCREMENT_BY_32 else 1

    # Keep vram_addr in the 14-bit PPU address range.
    self.vram_addr = (self.vram_addr + increment) & 0x3FFF

    # Preserve self.data for old test compatibility/debugging.
    self.data = value
    return self.data

Вне рамок

  • зеркалирование палитровой RAM, например $3F10 -> $3F00
  • точные значения цветов палитры
  • рендеринг

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

uv run pytest tests/chapter_03_ppu_memory_and_graphics_data/test_250_ppudata_palette_read_exception.py -v