289. Oamdma 4014

Implementar OAMDMA en la dirección de CPU $4014.

Lección 289 de 356 · tests/chapter_06_rom_startup_preparation/test_289_oamdma_4014.py

Archivo a actualizar

emulator/bus/cpu_bus.py

Por qué existe este paso

Los juegos reales de NES normalmente preparan los bytes de sprites en la memoria de la CPU y luego activan OAMDMA para copiar esos bytes en la OAM de la PPU. Incluso antes de que exista el renderizado de sprites, este mecanismo de copia importa porque permite que el código de arranque de una ROM real siga ejecutándose y proporciona al futuro renderizador de sprites datos reales de OAM que consumir.

¿Qué es la OAM? OAM significa Memoria de Atributos de Objeto (Object Attribute Memory). Es la memoria de sprites de 256 bytes de la PPU:

64 sprites * 4 bytes each = 256 bytes

Cada entrada de sprite tiene la forma

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

¿Qué es OAMDMA? OAMDMA es un mecanismo de escritura en el bus de la CPU. Escribir un byte en $4014 selecciona una página de memoria de la CPU y copia los 256 bytes de esa página en la OAM de la PPU.

Ejemplo mínimo

CPU writes $02 to $4014

Esto significa

copy CPU $0200-$02FF -> PPU.oam[0x00-0xFF]

Ejemplo de implementación sugerida

def write(self, addr: int, value: int) -> None:
    ...

    # OAMDMA: copy one CPU page into PPU OAM.
    if addr == 0x4014:
        page_start = (value & 0xFF) << 8

        for offset in range(256):
            self.ppu.oam[offset] = self.read(page_start + offset)

        return

    ...

Detalle importante

OAMDMA debería leer a través de CpuBus.read(), no directamente desde la RAM en bruto. El origen es el espacio de direcciones de la CPU. La mayoría de los juegos usan páginas como $0200-$02FF, pero usar lecturas del bus mantiene el mecanismo correcto y evita futuras refactorizaciones.

Concepto erróneo común

"OAMDMA means sprites are rendered."

No. Este paso solo copia bytes en PPU.oam. La decodificación/renderizado de sprites llega más adelante.

Comportamiento de lectura

$4014 es un registro activado por escritura para este tutorial. CpuBus.read($4014) debería seguir sin estar soportado por ahora.

Fuera de alcance

  • renderizado de sprites
  • prioridad de sprites
  • transparencia de sprites
  • sprite 0 hit
  • desbordamiento de sprites (sprite overflow)
  • temporización de bloqueo de DMA de 513/514 ciclos de CPU
  • mando $4016

Ejecutar esta lección

uv run pytest tests/chapter_06_rom_startup_preparation/test_289_oamdma_4014.py -v