248. Memoria OAM y OAMDATA

Implementa la memoria OAM y el comportamiento de OAMADDR/OAMDATA.

Lección 248 de 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_248_oam_memory_and_oamdata.py

Referencias

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

Archivo a actualizar

emulator/ppu/ppu.py

Estado a añadir

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

Por qué existe este paso

OAM significa Object Attribute Memory. Es la memoria interna de sprites de 256 bytes de la PPU. La PPU renderiza los sprites desde la OAM, no directamente desde la RAM de la CPU.

Disposición de los sprites

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

64 sprites * 4 bytes = 256 bytes

Disposición básica de los bytes de sprite

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

Registros visibles para la CPU

$2003 OAMADDR
    selects which OAM byte is currently addressed

$2004 OAMDATA
    reads/writes OAM at the current OAMADDR

Comportamiento importante para este paso

  • escribir $2003 establece oam_addr
  • escribir $2004 guarda en oam[oam_addr]
  • escribir $2004 incrementa oam_addr con & 0xFF
  • leer $2004 devuelve oam[oam_addr]

Por qué la OAM no es VRAM de PpuBus

PpuBus gestiona el espacio de direcciones de la PPU que usan PPUADDR/PPUDATA

$0000-$3FFF

La OAM es una memoria interna de sprites aparte, a la que se accede mediante OAMADDR/OAMDATA

$2003/$2004

Pseudocódigo de implementación sugerido

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

Fuera del alcance

  • OAMDMA en $4014
  • evaluación de sprites
  • renderizado de sprites
  • comportamiento de desbordamiento de sprites
  • comportamiento de sprite 0 hit

Ejecutar esta lección

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