248. Oam 内存与 oamdata

实现 OAM 内存以及 OAMADDR/OAMDATA 的行为。

248 / 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_248_oam_memory_and_oamdata.py

参考资料

https://www.nesdev.org/wiki/PPU_registers#OAMADDR
https://www.nesdev.org/wiki/PPU_registers#OAMDATA
https://www.nesdev.org/wiki/PPU_OAM

要更新的文件

emulator/ppu/ppu.py

要添加的状态

OAM_SIZE = 256
oam: bytearray = field(default_factory=lambda: bytearray(OAM_SIZE))

为什么需要这一步

OAM 的全称是 Object Attribute Memory(对象属性内存)。它是 PPU 内部的 256 字节精灵内存。PPU 渲染的精灵来自 OAM,而不是直接来自 CPU RAM。

精灵布局

NES OAM stores 64 sprites.
Each sprite uses 4 bytes.

64 sprites * 4 bytes = 256 bytes

基本的精灵字节布局

byte 0: Y position
byte 1: tile index
byte 2: attributes
byte 3: X position

CPU 可见寄存器

$2003 OAMADDR
    selects which OAM byte is currently addressed

$2004 OAMDATA
    reads/writes OAM at the current OAMADDR

本步骤的重要行为

  • 写入 $2003 会设置 oam_addr
  • 写入 $2004 会存入 oam[oam_addr]
  • 写入 $2004 会递增 oam_addr,并应用 & 0xFF
  • 读取 $2004 会返回 oam[oam_addr]

为什么 OAM 不是 PpuBus 的 VRAM

PpuBus 处理的是 PPUADDR/PPUDATA 所使用的 PPU 地址空间

$0000-$3FFF

OAM 是独立的内部精灵内存,通过 OAMADDR/OAMDATA 访问

$2003/$2004

建议的实现伪代码

OAM_SIZE = 256

@dataclass
class PPU:
    ...
    oam_addr: int = 0
    oam_data: int = 0
    oam: bytearray = field(default_factory=lambda: bytearray(OAM_SIZE))

    def write_register(self, addr: int, value: int) -> None:
        value = value & 0xFF

        match addr:
            ...
            case 0x2003:
                self.oam_addr = value

            case 0x2004:
                # Preserve old compatibility/debug field.
                self.oam_data = value

                # Write to current OAM byte.
                self.oam[self.oam_addr] = value

                # OAMADDR increments after OAMDATA write.
                self.oam_addr = (self.oam_addr + 1) & 0xFF

            ...

    def read_register(self, addr: int) -> int:
        match addr:
            ...
            case 0x2004:
                self.oam_data = self.oam[self.oam_addr]
                return self.oam_data

超出范围

  • 位于 $4014 的 OAMDMA
  • 精灵评估
  • 精灵渲染
  • 精灵溢出行为
  • 精灵 0 命中行为

运行本课

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