322. Posición del sprite zero hit del PPU

Extraer la posición de solapamiento del sprite 0 del estado actual del PPU.

Lección 322 de 356 · tests/chapter_11_sprite_zero_hit/test_322_ppu_sprite_zero_hit_position.py

Archivo a actualizar

emulator/rendering/sprite_zero_hit.py

Por qué existe este paso

El helper puro existente requiere datos de renderizado explícitos

find_sprite_zero_hit_position(
    sprite_zero,
    pattern_table,
    background_opaque_mask,
)

Esa API es útil para tests centrados, pero un futuro paso de Console parte de un PPU. Este paso añade un pequeño adaptador que extrae los datos requeridos del estado actual del PPU y delega en el helper puro existente.

Mantener ambas funciones en sprite_zero_hit.py queda claro con el tamaño actual del proyecto:

find_sprite_zero_hit_position(...)
    explicit data -> overlap position

ppu_sprite_zero_hit_position(ppu)
    PPU state -> explicit data -> overlap position

La segunda función es un adaptador, no otro algoritmo de solapamiento.

Extracción requerida

  • decodificar solo la entrada 0 de OAM
  • construir la máscara de opacidad del fondo mediante ppu_background_to_opaque_mask(ppu)
  • seleccionar la tabla de patrones de sprite usando el bit 3 de PPUCTRL
  • leer PATTERN_TABLE_SIZE bytes a través de PpuBus
  • delegar en find_sprite_zero_hit_position(...)

Ejemplo de implementación sugerido

from emulator.ppu.chr_decoder import PATTERN_TABLE_SIZE, decode_chr_tile
from emulator.ppu.ppu import CTRL_SPRITE_PATTERN_TABLE, PPU
from emulator.rendering.ppu_background_renderer import (
    PATTERN_TABLE_0_ADDR,
    PATTERN_TABLE_1_ADDR,
    ppu_background_to_opaque_mask,
)
from emulator.rendering.sprite_renderer import (
    SpriteEntry,
    decode_sprite_attributes,
    decode_sprite_entry,
)

...

def ppu_sprite_zero_hit_position(
    ppu: PPU,
) -> SpriteZeroHitPosition | None:
    sprite_zero = decode_sprite_entry(
        oam=ppu.oam,
        sprite_index=0,
    )

    background_opaque_mask = ppu_background_to_opaque_mask(ppu)

    sprite_pattern_table_base = (
        PATTERN_TABLE_1_ADDR
        if ppu.ctrl & CTRL_SPRITE_PATTERN_TABLE
        else PATTERN_TABLE_0_ADDR
    )

    sprite_pattern_table = bytes(
        ppu.ppu_bus.read(sprite_pattern_table_base + offset)
        for offset in range(PATTERN_TABLE_SIZE)
    )

    return find_sprite_zero_hit_position(
        sprite_zero=sprite_zero,
        pattern_table=sprite_pattern_table,
        background_opaque_mask=background_opaque_mask,
    )

Distinción importante

La opacidad del fondo usa la ruta de selección de la tabla de patrones del fondo, que se basa en el bit 4 de PPUCTRL. Los datos CHR del sprite 0 usan el bit 3 de PPUCTRL. Las dos selecciones pueden apuntar a tablas de patrones distintas.

Límite importante

Esta función solo devuelve una posición. No establece PPUSTATUS ni llama a PPU.set_sprite_zero_hit_position(). La conexión de Console queda para un paso posterior.

Fuera del alcance

  • Conexión de Console
  • programación automática
  • reglas de activación de renderizado de PPUMASK
  • reglas de recorte del borde izquierdo
  • excepción de hardware en x=255
  • corrección de Y+1 de OAM
  • validación con Super Mario Bros.

Ejecutar esta lección

uv run pytest tests/chapter_11_sprite_zero_hit/test_322_ppu_sprite_zero_hit_position.py -v