348. Viewport x scanline

Декодировать горизонтальный viewport X из одного записанного состояния scanline.

Урок 348 из 356 · tests/chapter_13_scrolling/test_348_scanline_viewport_x.py

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

emulator/rendering/ppu_background_renderer.py

Ссылка

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

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

Шаг 347 выбрал две логические nametable, которые образуют горизонтальную исходную пару. Позже композитор строк также нуждается в точном пикселе, где этот scanline начинается внутри этой 512-пиксельной пары.

Записанный адрес использует тот же упакованный макет полей прокрутки, что и t

yyy NN YYYYY XXXXX

Горизонтальный viewport X использует

XXXXX          coarse X tile column
low N bit      horizontal nametable selection, vram_addr bit 10
state.fine_x   pixel offset inside the first tile

Преобразование пикселей

viewport_x = nametable_x * 256 + coarse_x * 8 + fine_x

Пример

nametable X = 1
coarse X    = 5
fine X      = 3

viewport X = 1 * 256 + 5 * 8 + 3 = 299

Почему переиспользовать decode_background_viewport_position? Существующий чистый декодер уже владеет преобразованием упакованного поля прокрутки. И t, и записанный v имеют одинаковый макет битов, поэтому повторение масок и размеров в этом рендерере создаст две реализации, которые могут расходиться.

Важные инварианты

  • горизонтальный бит nametable 10 вносит 256 пикселей
  • coarse X вносит восемь пикселей на плитку
  • fine X вносит финальное смещение пикселя 0-7
  • вертикальный nametable, coarse Y и fine Y не влияют на viewport X
  • результат остаётся внутри логического горизонтального диапазона 0-511
  • записанный адрес не отмотан снова
  • вспомогательная функция не выполняет рендеринг или доступ к памяти

Распространённое заблуждение

Записанный адрес не нуждается в другой двухплиточной коррекции. Шаг 345 уже отмотал копию v дважды перед построением BackgroundScanlineState. Повторение отмотки здесь неправильно сдвинет каждую отрендеренную строку на шестнадцать пикселей влево.

Вне области действия

  • выбор логических адресов nametable
  • композиция строк framebuffer
  • применение горизонтального переноса к пикселям назначения
  • композиция opacity-mask
  • выбор синхронизированных данных в сравнении со старым fallback уровня кадра

Полный пример реализации

# emulator/rendering/ppu_background_renderer.py

# --- NEW BLOCK: DECODE ONE SCANLINE'S HORIZONTAL VIEWPORT ---
def _scanline_viewport_x(state: BackgroundScanlineState) -> int:
    viewport_x, _ = decode_background_viewport_position(
        temp_vram_addr=state.vram_addr,
        fine_x=state.fine_x,
    )

    return viewport_x

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

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