327. Горизонтальное/вертикальное зеркалирование в PpuBus
Применить горизонтальное или вертикальное зеркалирование nametable в PpuBus.
Урок 327 из 356 · tests/chapter_12_mirroring/test_327_ppu_bus_horizontal_vertical_mirroring.py
Файл для обновления
emulator/bus/ppu_bus.pyЗачем нужен этот шаг
Бит зеркалирования теперь проходит через
INesHeader
-> Cartridge
-> Mapper000
-> PpuBus.mapperPpuBus наконец может использовать эти метаданные при отображении четырёх логических nametable на два физических региона RAM nametable.
Логические nametable
table 0: $2000-$23FF
table 1: $2400-$27FF
table 2: $2800-$2BFF
table 3: $2C00-$2FFFКаждая таблица занимает $400 байт. Полное логическое окно составляет $1000 байт, тогда как текущее физическое окно хранения составляет $800 байт.
Вертикальное зеркалирование
logical: 0 1 2 3
physical: 0 1 0 1
[A B]
[A B]Горизонтальное зеркалирование
logical: 0 1 2 3
physical: 0 0 1 1
[A A]
[B B]Пример предлагаемой реализации
# emulator/bus/ppu_bus.py
NAMETABLE_SIZE = 0x800
NAMETABLE_BYTES_PER_TABLE = 0x400
NAMETABLE_LOGICAL_SIZE = 0x1000
def normalize_nametable_addr(self, addr: int) -> int:
if 0x3000 <= addr <= 0x3EFF:
addr -= 0x1000
logical_offset = (
addr - NAMETABLE_START
) % NAMETABLE_LOGICAL_SIZE
logical_table = logical_offset // NAMETABLE_BYTES_PER_TABLE
offset_inside_table = logical_offset % NAMETABLE_BYTES_PER_TABLE
is_vertical_mirroring = (
True
if self.mapper is None
else self.mapper.is_vertical_mirroring
)
if is_vertical_mirroring:
physical_table = logical_table % 2
else:
physical_table = logical_table // 2
return (
NAMETABLE_START
+ physical_table * NAMETABLE_BYTES_PER_TABLE
+ offset_inside_table
)Зачем сохранять вертикальное поведение без маппера? Исторические тесты туториала создают PpuBus напрямую и вводили старое фиксированное поведение обёртывания $800 ещё до появления зеркалирования картриджа. Шины, подключённые к мапперу, используют реальные метаданные картриджа; шины без маппера сохраняют эту историческую модель тестов.
Важное различие
Зеркалирование определяет псевдонимы памяти. Оно не перемещает видимую область просмотра. Текущий рендерер по-прежнему рисует фиксированную область nametable, поэтому горизонтальная прокрутка — это отдельная будущая глава.
Вне рамок
- извлечение позиции прокрутки
- обрезка области просмотра по соседним nametable
- четырёхэкранное зеркалирование
- динамическое зеркалирование, управляемое маппером
- фикстуры коммерческих ROM
Запустить этот урок
uv run pytest tests/chapter_12_mirroring/test_327_ppu_bus_horizontal_vertical_mirroring.py -v