343. Вертикальное время vram
Применить вертикальное увеличение v и копирование вертикального t-в-v на точках времени PPU.
Урок 343 из 356 · tests/chapter_13_scrolling/test_343_vertical_vram_timing.py
Файл для обновления
emulator/ppu/ppu.pyСсылки
https://www.nesdev.org/wiki/PPU_scrolling#At_dot_256_of_each_scanlineЗачем нужен этот шаг
Шаги 341 и 342 реализовали чистые вертикальные механизмы адреса. Этот шаг подключает их к времени PPU.
Во время включённого рендеринга
visible and pre-render scanlines, dot 256:
increment fine/coarse Y in v
pre-render scanline, every dot 280-304:
copy fine Y, vertical nametable, and coarse Y from t into vУпрощённое время
visible scanline:
... 255 256 257 ...
| |
| +-- horizontal reload
+------ vertical increment
pre-render scanline 261:
... 256 257 ... 280----------------304 ...
| | |
| | +-- vertical reload active at every dot
| +---------- horizontal reload
+-------------- vertical incrementПочему повторять вертикальную перезагрузку на каждой точке 280-304? Если записи CPU изменяют t в течение этого интервала, более поздняя точка может скопировать более новое вертикальное состояние. Обработка интервала как одной операции на точке 280 потеряет это поведение.
Рендеринг включён, когда установлен бит background или бит sprite в PPUMASK. Post-render и VBlank scanlines не выполняют эти автоматические обновления.
Важный порядок на точке 256
Горизонтальное увеличение fetch выполняется первым, а вертикальное увеличение выполняется вторым. Они владеют разными полями, поэтому оба изменения должны быть видны в результирующем v.
Вне области действия
- эффективная запись viewport для каждого scanline
- компенсация двухплиточного prefetch
- композиция строки framebuffer
- композиция строки opacity-mask
Полный пример реализации
# emulator/ppu/ppu.py
class PPU:
...
# --- NEW BLOCK: APPLY VERTICAL ADDRESS TIMING ---
def _step_vertical_rendering_address(self) -> None:
rendering_enabled = self.mask & (
MASK_SHOW_BACKGROUND | MASK_SHOW_SPRITES
)
if not rendering_enabled:
return
rendering_scanline = (
0 <= self.scanline < 240
or self.scanline == PPU_PRE_RENDER_SCANLINE
)
if not rendering_scanline:
return
if self.cycle == 256:
self.vram_addr = increment_vertical_vram_addr(
self.vram_addr
)
vertical_reload = (
self.scanline == PPU_PRE_RENDER_SCANLINE
and 280 <= self.cycle <= 304
)
if vertical_reload:
self.vram_addr = copy_vertical_scroll_bits(
self.vram_addr,
self.temp_vram_addr,
)
def step(self, cycles: int = 1) -> None:
...
for _ in range(cycles):
self.cycle += 1
self._step_horizontal_rendering_address()
# --- NEW LINE: APPLY VERTICAL TIMING AT THE CURRENT DOT ---
self._step_vertical_rendering_address()
...Запустить этот урок
uv run pytest tests/chapter_13_scrolling/test_343_vertical_vram_timing.py -v