347. Горизонтальная пара scanline
Выбрать горизонтальную логическую пару nametable для одного записанного scanline.
Урок 347 из 356 · tests/chapter_13_scrolling/test_347_scanline_horizontal_pair.py
Файл для обновления
emulator/rendering/ppu_background_renderer.pyСсылки
https://www.nesdev.org/wiki/PPU_scrolling
https://www.nesdev.org/wiki/PPU_nametablesЗачем нужен этот шаг
Завершённый синхронизированный кадр хранит один эффективный vram_addr и значение fine X для каждого видимого scanline. Перед тем как позже рендерер сможет скопировать пиксели этого scanline, он должен знать, какие две горизонтально соседние логические nametable образуют исходную пару.
Поля выбора nametable внутри vram_addr:
bit 10: horizontal nametable position inside a 512-pixel-wide pair
bit 11: vertical nametable row that selects the pairВыбор пары поэтому использует только бит 11
bit 11 clear: ($2000, $2400)
bit 11 set: ($2800, $2C00)Интуитивная модель
upper logical row: $2000 | $2400
lower logical row: $2800 | $2C00Вспомогательная функция выбирает одну полную строку. Позже шаг будет использовать бит 10, coarse X и fine X для выбора горизонтальной позиции пикселя внутри этой строки.
Важные инварианты
- только бит 11 vram_addr влияет на выбранную пару
- бит 10 не изменяет пару
- coarse X, coarse Y, fine Y и fine X не изменяют пару
- возвращаемые значения — это логические адреса PPU nametable
- зеркалирование картриджа остаётся поведением PpuBus
- вспомогательная функция чистая и не выполняет рендеринг или доступ к памяти
Распространённое заблуждение
Бит 10 не выбирает между верхней и нижней горизонтальными парами. Он идентифицирует левый или правый горизонтальный nametable внутри пары и станет частью расчёта viewport-X в следующем уроке.
Вне области действия
- декодирование горизонтального viewport X
- чтение байтов nametable или pattern-table
- применение зеркалирования картриджа
- композиция строк framebuffer
- выбор завершённых синхронизированных данных в сравнении со старым fallback
Полный пример реализации
# emulator/rendering/ppu_background_renderer.py
# --- UPDATED LINES: IMPORT THE RECORDED SCANLINE VALUE ---
from emulator.ppu.ppu import (
BackgroundScanlineState,
CTRL_BACKGROUND_PATTERN_TABLE,
PPU,
)
...
# --- NEW BLOCK: SELECT ONE SCANLINE'S LOGICAL HORIZONTAL PAIR ---
def _scanline_horizontal_pair(
state: BackgroundScanlineState,
) -> tuple[int, int]:
nametable_y = (state.vram_addr >> 11) & 1
left_base = BASE_NAMETABLE_ADDR + nametable_y * 0x0800
return left_base, left_base + 0x0400Запустить этот урок
uv run pytest tests/chapter_13_scrolling/test_347_scanline_horizontal_pair.py -v