250. Ppudata 调色板读取例外
实现 PPUDATA ($2007) 的调色板读取例外。
第 250 / 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_250_ppudata_palette_read_exception.py
参考资料
https://www.nesdev.org/wiki/PPU_registers#PPUDATA要更新的文件
emulator/ppu/ppu.py要添加的常量
PALETTE_START_ADDR = 0x3F00
PALETTE_END_ADDR = 0x3FFF为什么需要这一步
正常的 PPUDATA 读取是带缓冲的
read $2007:
return old ppu_data_buffer
reload ppu_data_buffer from ppu_bus.read(vram_addr)
increment vram_addr调色板读取是一个例外。当 vram_addr 指向调色板内存时:
$3F00-$3FFFPPU 会立即返回调色板字节,而不是返回旧的缓冲区值。
重要细节
旧的缓冲区仍会被丢弃。在正常的 NES PPU 上,该缓冲区会从调色板地址背后镜像的命名表内存重新加载:
$3F00 -> reload buffer from $2F00
$3F10 -> reload buffer from $2F10这意味着调色板读取会同时影响
- 返回给 CPU 的值
- 后续正常读取所使用的内部 ppu_data_buffer
建议的实现伪代码,与显式的注释风格保持一致:
PALETTE_START_ADDR = 0x3F00
PALETTE_END_ADDR = 0x3FFF
case 0x2007: # PPU DATA read
# Palette data is returned immediately.
if PALETTE_START_ADDR <= self.vram_addr <= PALETTE_END_ADDR:
value = self.ppu_bus.read(self.vram_addr)
# Read buffer is discarded and reloaded from shadowed memory:
# vram_addr - 0x1000
# Example: 0x3F00 -> 0x2F00
self.ppu_data_buffer = self.ppu_bus.read(self.vram_addr - 0x1000)
else:
# Normal PPUDATA reads return the old buffer first.
value = self.ppu_data_buffer
# Then the buffer is reloaded from current PPU memory.
self.ppu_data_buffer = self.ppu_bus.read(self.vram_addr)
increment = 32 if self.ctrl & CTRL_VRAM_INCREMENT_BY_32 else 1
# Keep vram_addr in the 14-bit PPU address range.
self.vram_addr = (self.vram_addr + increment) & 0x3FFF
# Preserve self.data for old test compatibility/debugging.
self.data = value
return self.data超出范围
- 调色板 RAM 镜像,例如 $3F10 -> $3F00
- 精确的调色板颜色值
- 渲染
运行本课
uv run pytest tests/chapter_03_ppu_memory_and_graphics_data/test_250_ppudata_palette_read_exception.py -v