253. PPU 总线的名称表 VRAM 地址映射

实现 PpuBus 的名称表 VRAM 地址映射。

253 / 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_253_ppu_bus_nametable_vram_mapping.py

参考资料

https://www.nesdev.org/wiki/PPU_memory_map
https://www.nesdev.org/wiki/PPU_nametables

需要更新的文件

emulator/bus/ppu_bus.py

需要添加的常量

NAMETABLE_START = 0x2000
NAMETABLE_END = 0x3EFF
NAMETABLE_SIZE = 0x0800

什么是名称表?名称表是背景布局内存。它不存储像素,而是存储图块编号,告诉 PPU 在背景的每个位置要绘制哪个 CHR 图块。

简单示例

PPU memory $2000 contains $24

含义

the top-left background cell uses CHR tile index $24

PPU 暴露出四个逻辑上的 1KB 名称表区域

$2000-$23FF
$2400-$27FF
$2800-$2BFF
$2C00-$2FFF

$3000-$3EFF 范围镜像了 $2000-$2EFF。

教程简化

目前先使用现有的大型 VRAM 后备存储,将名称表地址归一化到一个简单的 2KB 窗口内。暂时不要创建独立的名称表 RAM 对象。

建议的实现示例

NAMETABLE_START = 0x2000
NAMETABLE_END = 0x3EFF
NAMETABLE_SIZE = 0x0800

def normalize_nametable_addr(self, addr: int) -> int:
    if 0x3000 <= addr <= 0x3EFF:
        addr -= 0x1000

    index = (addr - NAMETABLE_START) % NAMETABLE_SIZE
    return NAMETABLE_START + index

def read(self, addr: int) -> int:
    addr = addr & PPU_ADDRESS_MASK

    if CHR_START <= addr <= CHR_END:
        ...

    if NAMETABLE_START <= addr <= NAMETABLE_END:
        return self.vram.read(self.normalize_nametable_addr(addr))

    if PALETTE_START <= addr <= PALETTE_END:
        ...

    return self.vram.read(addr)

不在本步骤范围内

  • 卡带的水平/垂直镜像
  • 四屏镜像
  • 属性表的解读
  • 渲染
  • 把名称表存储从大型 VRAM 后备中分离出去

运行本课

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