240. Переключатель второй записи PPUADDR
Реализовать поведение адреса с двумя записями в PPUADDR ($2006).
Урок 240 из 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_240_ppuaddr_second_write_toggle.py
Справочник
https://www.nesdev.org/wiki/PPU_registers#PPUADDRФайл для обновления
emulator/ppu/ppu.pyСостояние для добавления
vram_addr: int = 0
temp_vram_addr: int = 0
second_write_toggle: bool = FalseЗачем нужен этот шаг
PPUADDR — видимый для CPU регистр по адресу $2006. CPU использует его, чтобы задать внутренний адрес VRAM PPU. Этот внутренний адрес позже используется PPUDATA ($2007) для чтения/записи памяти PPU.
Важная идея
$2006 — это не простой однобайтовый регистр хранения. Это порт с двумя записями.
CPU записывает адрес двумя частями
first write -> high byte
second write -> low byteПример
write $20 to $2006
write $00 to $2006Результат после обеих записей
vram_addr == $2000Важная модель внутренних регистров
Первая запись в $2006 обновляет temp_vram_addr, а не vram_addr. Вторая запись в $2006 завершает temp_vram_addr и копирует его в vram_addr.
Зачем second_write_toggle: PPU должен помнить, будет ли следующая запись в $2006 первой или второй записью. В этом руководстве second_write_toggle означает:
False -> next $2006 write is the first write / high byte
True -> next $2006 write is the second write / low byteЗачем старший байт маскируется значением 0x3F
Адресное пространство PPU 14-битное
$0000-$3FFFДля этого диапазона адресов полезны только младшие 6 бит старшего байта. Поэтому первая запись должна использовать:
(value & 0x3F) << 8Предлагаемый псевдокод реализации
@dataclass
class PPU:
...
addr: int = 0
vram_addr: int = 0
temp_vram_addr: int = 0
second_write_toggle: bool = False
def write_register(self, addr: int, value: int) -> None:
value = value & 0xFF
match addr:
...
case 0x2006:
# Keep old simple register-field behavior for test compatibility.
self.addr = value
if not self.second_write_toggle:
self.temp_vram_addr = (
(self.temp_vram_addr & 0x00FF)
| ((value & 0x3F) << 8)
)
self.second_write_toggle = True
else:
self.temp_vram_addr = (
(self.temp_vram_addr & 0x3F00)
| value
)
self.vram_addr = self.temp_vram_addr
self.second_write_toggle = False
...Вне области действия
- запись PPUDATA ($2007) через PpuBus
- сброс переключателя записи через PPUSTATUS ($2002)
- использование этого же переключателя в PPUSCROLL ($2005)
- поведение инкремента на 32 из PPUCTRL
Запустить этот урок
uv run pytest tests/chapter_03_ppu_memory_and_graphics_data/test_240_ppuaddr_second_write_toggle.py -v