344. Уменьшить горизонтальный адрес vram

Отменить одно горизонтальное увеличение background-fetch на скопированном адресе.

Урок 344 из 356 · tests/chapter_13_scrolling/test_344_decrement_horizontal_vram_addr.py

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

emulator/ppu/ppu.py

Ссылки

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

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

Точки 321-336 выполняют prefetch первых двух background плиток для следующего scanline. Каждый fetch плитки продвигает горизонтальный v, поэтому на точке 1 адрес уже на два столбца плиток впереди пикселей, хранящихся в background shifters:

intended visible coarse X = 5
prefetch tile 5 -> v advances to 6
prefetch tile 6 -> v advances to 7

dot 1:
    shifters begin displaying tile 5
    v already contains coarse X 7

Позже запись scanline скопирует v и отмотает эту копию дважды

7 -> 6 -> 5

Эта вспомогательная функция отменяет одно увеличение. Это не синхронизированная операция PPU, и она никогда не должна перемещать реальный PPU.vram_addr назад.

Нормальное поведение

coarse X 31 -> 30
coarse X 7  -> 6
coarse X 1  -> 0

Поведение на границе

coarse X 0 -> 31 and toggle horizontal nametable

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

  • только coarse X и, при переносе, горизонтальный nametable могут изменяться
  • каждое вертикальное и несвязанное поле остаётся неизменным
  • уменьшение является обратным протестированному горизонтальному увеличению
  • функция чистая и не изменяет PPU

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

NES PPU не выполняет это уменьшение во время рендеринга. Оно существует только для преобразования адреса fetch, который впереди дисплея, в адрес видимого viewport для существующего высокоуровневого рендерера.

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

  • вызов вспомогательной функции из PPU.step()
  • запись 240 позиций scanline
  • изменения framebuffer или opacity-mask

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

# emulator/ppu/ppu.py

# --- NEW BLOCK: REVERSE ONE HORIZONTAL FETCH INCREMENT ---
def decrement_horizontal_vram_addr(vram_addr: int) -> int:
    coarse_x = vram_addr & 0b1_1111

    if coarse_x == 0:
        vram_addr = (
            (vram_addr & ~0b1_1111)
            | 0b1_1111
        )
        vram_addr ^= 0b000_01_00000_00000
        return vram_addr

    return vram_addr - 1

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

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