328. Bits de nametable temporal de PPUCTRL
Copiar la selección de nametable base de PPUCTRL en temp_vram_addr.
Lección 328 de 356 · tests/chapter_13_scrolling/test_328_ppuctrl_temp_nametable_bits.py
Archivo a actualizar
emulator/ppu/ppu.pyDocumentación de referencia
https://www.nesdev.org/wiki/PPU_scrolling#$2000_(PPUCTRL)_write
https://www.nesdev.org/wiki/PPU_registersPor qué existe este paso
La dirección de scrolling del PPU se ensambla a partir de varias escrituras de registros visibles para la CPU. El proyecto ya gestiona la mayoría de las partes de $2005 PPUSCROLL:
coarse X -> temp_vram_addr bits 0-4
coarse Y -> temp_vram_addr bits 5-9
fine Y -> temp_vram_addr bits 12-14
fine X -> separate fine_x fieldLa pieza que falta es la selección lógica del nametable base
PPUCTRL bits 0-1 -> temp_vram_addr bits 10-11Sin esta conexión, temp_vram_addr se comporta como si el scrolling siempre comenzara desde el nametable lógico 0 ($2000), incluso cuando el software del juego seleccionó $2400, $2800 o $2C00. Por tanto, un futuro renderizador de viewport cruzaría el límite lógico equivocado.
Distribución interna del scrolling
temp_vram_addr (t): yyy NN YYYYY XXXXX
XXXXX -> coarse X tile position
YYYYY -> coarse Y tile position
NN -> logical nametable selection
yyy -> fine Y pixel position
fine_x (x) is stored separatelyMapeo de PPUCTRL
bits 0-1 = 00 -> logical nametable $2000
bits 0-1 = 01 -> logical nametable $2400
bits 0-1 = 10 -> logical nametable $2800
bits 0-1 = 11 -> logical nametable $2C00Implementación sugerida
# emulator/ppu/ppu.py
case 0x2000:
self.ctrl = value
# --- NEW LINE: COPY BASE NAMETABLE INTO temp_vram_addr: ...GH.. ........ <- value: ......GH
self.temp_vram_addr = (self.temp_vram_addr & 0b1111_0011_1111_1111) | ((value & CTRL_BASE_NAMETABLE_MASK) << 10)
# --- END NEW LINE ---¿Por qué limpiar primero los bits 10-11? Un juego puede cambiar de cualquier nametable lógico a cualquier otro nametable lógico. Usar solo OR establecería los bits nuevos pero no podría limpiar los antiguos. La operación de limpiar y luego insertar reemplaza la selección anterior conservando X grueso, Y grueso y Y fino.
¿Por qué no usar el antiguo campo de scroll? $2005 recibe dos escrituras, pero el campo de scroll de compatibilidad almacena solo el byte más reciente. temp_vram_addr junto con fine_x conservan el estado completo al estilo del hardware.
Distinción importante
Este paso registra la selección de nametable lógico. El mirroring del cartucho luego mapea esa selección lógica sobre la RAM física de nametables. Ninguna de las dos operaciones mueve por sí misma el viewport visible; la extracción/renderizado del viewport llega en pasos posteriores.
Fuera del alcance
- derivar la X/Y en píxeles del viewport
- renderizar nametables adyacentes
- transferir t a v en los dots exactos del hardware
- cambiar el mirroring de PpuBus
- fixtures de ROM comerciales
Ejecutar esta lección
uv run pytest tests/chapter_13_scrolling/test_328_ppuctrl_temp_nametable_bits.py -v