252. Отображение памяти палитры шины PPU
Реализуйте отображение адресов памяти палитры PpuBus.
Урок 252 из 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_252_ppu_bus_palette_ram_mapping.py
Справка
https://www.nesdev.org/wiki/PPU_palettes
https://www.nesdev.org/wiki/PPU_memory_mapФайл для обновления
emulator/bus/ppu_bus.pyКонстанты для добавления
PALETTE_START = 0x3F00
PALETTE_END = 0x3FFF
PALETTE_SIZE = 0x20Почему этот шаг существует
Диапазон адресов PPU $3F00-$3FFF — это память палитры. Память палитры не хранит графику плиток. Она хранит индексы цветов NES, используемые позже при рендеринге фона и спрайтов.
Примечание об оборудовании
На оригинальном PPU NES память палитры — это небольшая отдельная область памяти размером 32 байта внутри PPU. Это не CHR ROM и не VRAM таблицы имён. Она имеет собственное поведение зеркалирования в диапазоне адресов PPU $3F00-$3FFF.
Упрощение в учебнике
На этом этапе нам не нужно разделять хранилище Python на отдельный объект PaletteRAM. Мы можем продолжать использовать существующий большой массив резервного VRAM в качестве физического места хранения, если PpuBus нормализует адреса палитры перед чтением или записью.
Ментальная модель
PPU.write_register($2007, value)
-> ppu_bus.write(vram_addr, value)
-> PpuBus detects whether vram_addr is palette memory
-> PpuBus normalizes palette mirrors
-> backing storage receives the byteПравила зеркалирования палитры для этого шага
$3F00-$3F1F is the 32-byte palette window
$3F20-$3FFF mirrors $3F00-$3F1FСпециальные зеркала фона
$3F10 mirrors $3F00
$3F14 mirrors $3F04
$3F18 mirrors $3F08
$3F1C mirrors $3F0CПредлагаемый пример реализации
PALETTE_START = 0x3F00
PALETTE_END = 0x3FFF
PALETTE_SIZE = 0x20
def normalize_palette_addr(self, addr: int) -> int:
index = (addr - PALETTE_START) % PALETTE_SIZE
if index in (0x10, 0x14, 0x18, 0x1C):
index -= 0x10
return PALETTE_START + index
def read(self, addr: int) -> int:
addr = addr & PPU_ADDRESS_MASK
if CHR_START <= addr <= CHR_END:
...
if PALETTE_START <= addr <= PALETTE_END:
return self.vram.read(self.normalize_palette_addr(addr))
return self.vram.read(addr)
def write(self, addr: int, value: int) -> None:
addr = addr & PPU_ADDRESS_MASK
if CHR_START <= addr <= CHR_END:
...
return
if PALETTE_START <= addr <= PALETTE_END:
self.vram.write(self.normalize_palette_addr(addr), value)
return
self.vram.write(addr, value)Важный выбор дизайна
Логика зеркалирования палитры принадлежит PpuBus, а не PPU. PPU обрабатывает поведение регистров; PpuBus обрабатывает маршрутизацию адресов памяти PPU.
Вне области действия
- фактические цвета RGB
- рендеринг
- выбор палитры фона/спрайта
- декодирование атрибутов таблицы имён
- разделение памяти палитры в собственный класс хранилища
Запустить этот урок
uv run pytest tests/chapter_03_ppu_memory_and_graphics_data/test_252_ppu_bus_palette_ram_mapping.py -v