341. Увеличить вертикальный адрес vram
Увеличить вертикальный компонент текущего адреса рендеринга v.
Урок 341 из 356 · tests/chapter_13_scrolling/test_341_increment_vertical_vram_addr.py
Файл для обновления
emulator/ppu/ppu.pyСсылки
https://www.nesdev.org/wiki/PPU_scrolling#Y_increment
https://www.nesdev.org/wiki/PPU_scrolling#Wrapping_aroundЗачем нужен этот шаг
На точке 256 PPU продвигает адрес рендеринга на одну строку пикселей. В отличие от горизонтального увеличения, вертикальное движение сначала продвигает fine Y внутри текущей 8-пиксельной плитки:
yyy NN YYYYY XXXXX
+++ +++++
| +---- coarse Y: bits 5-9
+------------ fine Y: bits 12-14
vertical nametable: bit 11Поведение Fine-Y
0 -> 1 -> 2 -> ... -> 7Когда fine Y уже равен 7, он переходит в 0 и coarse Y увеличивается.
Поведение Coarse-Y после переноса fine-Y
coarse Y 0-28 -> increment by one
coarse Y 29 -> 0 and toggle vertical nametable
coarse Y 30 -> 31 through the normal increment branch
coarse Y 31 -> 0 without toggling vertical nametableСтроки 0-29 — это 30 видимых строк плиток nametable. Значения 30 и 31 — это не обычные видимые строки, но они могут существовать, потому что coarse Y — это пятибитовое поле, и операции адреса CPU могут загружать эти значения.
Почему добавлять $1000 для обычного увеличения fine-Y? Fine Y начинается с бита 12, поэтому добавление 1 << 12 увеличивает это упакованное поле на один. Это не означает концептуально перемещение на 4096 байт через память PPU.
Важные инварианты
- горизонтальные coarse X и выбор nametable остаются неизменными
- несвязанные биты внутреннего адреса остаются неизменными
- вертикальный nametable переключается только при переносе строки 29
- вспомогательная функция чистая и не изменяет PPU
Распространённое заблуждение
Вертикальное увеличение не просто симметрично горизонтальному увеличению. Горизонтальное движение продвигает один столбец плиток; вертикальное движение продвигает одну строку fine пикселей и только иногда продвигает строку coarse плиток.
Вне области действия
- вызов этой вспомогательной функции на точке 256
- копирование вертикального t-в-v
- время pre-render
- запись viewport scanline
- рендеринг framebuffer
Полный пример реализации
# emulator/ppu/ppu.py
# --- NEW BLOCK: PURE VERTICAL v INCREMENT ---
def increment_vertical_vram_addr(vram_addr: int) -> int:
fine_y = (vram_addr >> 12) & 0b111
if fine_y < 7:
return vram_addr + (1 << 12)
vram_addr &= ~0b111_00_00000_00000
coarse_y = (vram_addr >> 5) & 0b1_1111
if coarse_y == 29:
coarse_y = 0
vram_addr ^= 0b000_10_00000_00000
elif coarse_y == 31:
coarse_y = 0
else:
coarse_y += 1
return (
(vram_addr & ~0b000_00_11111_00000)
| (coarse_y << 5)
)Запустить этот урок
uv run pytest tests/chapter_13_scrolling/test_341_increment_vertical_vram_addr.py -v