329. Декодирование позиции области просмотра фона
Декодировать упрощённую позицию области просмотра фона из состояния прокрутки PPU.
Урок 329 из 356 · tests/chapter_13_scrolling/test_329_decode_background_viewport_position.py
Файл для создания
emulator/rendering/background_viewport.pyСправочная документация
https://www.nesdev.org/wiki/PPU_scrolling
https://www.nesdev.org/wiki/PPU_scrolling#PPU_internal_registers
https://www.nesdev.org/wiki/PPU_scrolling#During_renderingЗачем нужен этот шаг
PPU хранит прокрутку в виде упакованных аппаратных полей, а не готовых к использованию пиксельных координат. Для рендеринга нужна простая позиция верхнего левого угла области просмотра, поэтому этот шаг декодирует:
temp_vram_addr (t): yyy NN YYYYY XXXXX
fine_x (x): xxxГде
XXXXX -> coarse X tile position, 5 bits
YYYYY -> coarse Y tile position, 5 bits
NN -> logical nametable X/Y selection
yyy -> fine Y pixel position, 3 bits
xxx -> fine X pixel position, 3 bits stored separatelyПреобразование в пиксели
viewport X = nametable X * 256 + coarse X * 8 + fine X
viewport Y = nametable Y * 240 + coarse Y * 8 + fine YПредлагаемая реализация
# emulator/rendering/background_viewport.py
NAMETABLE_PIXEL_WIDTH = 256
NAMETABLE_PIXEL_HEIGHT = 240
TILE_PIXEL_SIZE = 8
BackgroundViewportPosition = tuple[int, int]
def decode_background_viewport_position(
temp_vram_addr: int,
fine_x: int,
) -> BackgroundViewportPosition:
coarse_x = temp_vram_addr & 0b1_1111
coarse_y = (temp_vram_addr >> 5) & 0b1_1111
nametable_x = (temp_vram_addr >> 10) & 1
nametable_y = (temp_vram_addr >> 11) & 1
fine_y = (temp_vram_addr >> 12) & 0b111
viewport_x = (
nametable_x * NAMETABLE_PIXEL_WIDTH
+ coarse_x * TILE_PIXEL_SIZE
+ fine_x
)
viewport_y = (
nametable_y * NAMETABLE_PIXEL_HEIGHT
+ coarse_y * TILE_PIXEL_SIZE
+ fine_y
)
return viewport_x, viewport_yПочему использовать t и x вместо старого поля scroll? $2005 получает горизонтальную и вертикальную записи, тогда как поле scroll, используемое для совместимости, хранит только последний байт. Аппаратные поля t и x сохраняют полное упакованное состояние.
Граница точности
Реальное железо рендерит из текущего адреса v плюс тонкий X после синхронизированных по времени переносов из t в v. Этот туториал сейчас использует t плюс тонкий X как снимок на уровне кадра. Точные переносы на такте 257 и на пре-рендере на тактах 280-304 остаются будущей работой над таймингом.
Ещё одно упрощение
Реальные значения грубого Y 30 и 31 имеют особое поведение обёртывания. Этот шаг выполняет только прямое декодирование поля в пиксели; обёртывание области просмотра появится позже.
Вне рамок
- чтение байтов nametable
- создание фреймбуфера
- пересечение границы nametable
- точные переносы тайминга t -> v
- обёртывание грубого Y 30/31
- фикстуры коммерческих ROM
Запустить этот урок
uv run pytest tests/chapter_13_scrolling/test_329_decode_background_viewport_position.py -v