NES TDD

249. Буфер чтения PPUDATA

Реализовать буферизованное чтение PPUDATA ($2007).

Урок 249 из 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_249_ppudata_read_buffer.py

Ссылка

https://www.nesdev.org/wiki/PPU_registers#PPUDATA

Файл для обновления

emulator/ppu/ppu.py

Состояние для добавления

ppu_data_buffer: int = 0

Почему этот шаг важен

PPUDATA — это не обычный простой регистр. Это видимый для CPU порт в память PPU. Для большинства операций чтения памяти PPU NES возвращает старое значение внутреннего буфера чтения, а затем перезагружает этот буфер из текущего адреса памяти PPU.

Обычное поведение буферизованного чтения

read $2007:
    value = ppu_data_buffer
    ppu_data_buffer = ppu_bus.read(vram_addr)
    vram_addr += increment
    return value

Это означает, что первое чтение обычно возвращает устаревшие/старые данные буфера, а второе чтение возвращает байт, загруженный первым чтением.

Пример

ppu_bus[$2000] = $AA
ppu_bus[$2001] = $BB
ppu_data_buffer = $00
vram_addr = $2000

read $2007 -> returns $00, buffer becomes $AA, vram_addr becomes $2001
read $2007 -> returns $AA, buffer becomes $BB, vram_addr becomes $2002

Поле совместимости/отладки

В более старой модели курса поле data было простым значением PPUDATA. Сохраните self.data как поле совместимости/отладки, содержащее значение, возвращённое последним

чтением PPUDATA, но реальное поведение должно использовать

ppu_data_buffer
ppu_bus.read(vram_addr)

Предлагаемый псевдокод реализации

@dataclass
class PPU:
    ...
    data: int = 0  # Compatibility/debug: last PPUDATA value
    ppu_data_buffer: int = 0

    def read_register(self, addr: int) -> int:
        match addr:
            ...
            case 0x2007:
                value = self.ppu_data_buffer
                self.ppu_data_buffer = self.ppu_bus.read(self.vram_addr)

                increment = 32 if self.ctrl & CTRL_VRAM_INCREMENT_BY_32 else 1
                self.vram_addr = (self.vram_addr + increment) & 0x3FFF

                # Preserve old compatibility/debug field.
                self.data = value
                return self.data

Важное замечание на будущее

Чтение палитры из диапазона $3F00-$3FFF является исключением на реальном оборудовании. Оно возвращает байт палитры немедленно вместо возврата отложенного значения буфера. Это исключение намеренно не реализовано в этом тесте.

Вне рамок

  • исключение при чтении палитры
  • точность палитровой RAM
  • запись в PPUDATA, уже протестированная ранее

Запустить этот урок

uv run pytest tests/chapter_03_ppu_memory_and_graphics_data/test_249_ppudata_read_buffer.py -v