⚠️ Clean-Room / Educativo

Este proyecto es educativo y Open Source. No se copia código de otros emuladores. Implementación basada únicamente en documentación técnica y tests permitidas.

Monitor Global de Accesos VRAM y Búsqueda de Rutinas de Carga

Fecha: 2025-12-25 Step ID: 0295 Estado: VERIFIED

Resumen

Implementación de cinco monitores globales para rastrear TODOS los accesos a VRAM sin importar dónde ocurran en el flujo de ejecución. El análisis del Step 0294 rechazó parcialmente la hipótesis: las ISRs se ejecutan pero no acceden a VRAM, y el código post-BG tampoco accede. Necesitamos determinar si el código de carga existe y cuándo debería ejecutarse, o si simplemente no existe en esta fase del juego.

Los nuevos monitores detectan accesos a VRAM en cualquier momento del flujo, correlacionan PCs con accesos VRAM, identifican secuencias consecutivas de carga, detectan copias desde ROM usando LDIR, y rastrean el timing de accesos VRAM.

Concepto de Hardware

Video RAM (VRAM) y Carga de Tiles

La VRAM (0x8000-0x9FFF) contiene dos tipos de datos:

  • Tile Data (0x8000-0x97FF): 384 tiles de 8x8 píxeles, cada tile ocupa 16 bytes (2 bytes por fila de 8 píxeles).
  • Tile Maps (0x9800-0x9FFF): Dos mapas de tiles que indican qué tile se muestra en cada posición de la pantalla.

Los tiles se cargan típicamente desde la ROM del cartucho a VRAM usando instrucciones de bloque como LDIR (Load, Increment, Repeat), que copia una secuencia de bytes desde una dirección fuente (ROM) a una dirección destino (VRAM).

Patrones de Carga de Tiles

Los juegos cargan tiles usando varios métodos:

  1. LDIR (0xED 0xB0): Patrón más común. Copia (HL++) a (DE++), decrementa BC, repite hasta BC=0.
  2. Loops manuales: LD A,(HL) ; LD (DE),A ; INC HL ; INC DE ; DEC BC ; JR NZ
  3. Escritura directa: LD (HL+), A o LD (HL), n en secuencias consecutivas

Una secuencia típica carga 16 bytes consecutivos (un tile completo) desde una dirección en ROM a una dirección en VRAM.

Timing de Carga

Los tiles pueden cargarse en varios momentos:

  • Durante la inicialización: Antes del primer frame, cuando el LCD está apagado
  • Durante V-Blank: Cuando VRAM es accesible (aunque esto es más común para actualizaciones)
  • Durante H-Blank: Limitado, solo en ciertos modos de LCD-STAT
  • En el flujo principal: En cualquier momento cuando VRAM es accesible

Fuente: Pan Docs - "Video RAM (VRAM)", "CPU Instruction Set - LDIR", "Tile Data", "LCD Modes"

Implementación

Se implementaron cinco monitores globales en CPU::step() que detectan accesos a VRAM sin importar dónde ocurran en el flujo de ejecución. Todos los monitores se ejecutan antes de cualquier early return para asegurar que capturamos todas las ejecuciones.

Componentes creados/modificados

  • CPU.cpp: Añadidos cinco monitores globales después de capturar original_pc.
  • CPU.cpp: Añadido #include <map> para el monitor de correlación PC-VRAM.

Monitores Implementados

[VRAM-ACCESS-GLOBAL] - Monitor Global de Accesos VRAM

Detecta TODOS los accesos de escritura a VRAM (0x8000-0x9FFF) independientemente de dónde ocurran. Verifica si HL apunta a VRAM cuando se ejecutan opcodes de escritura:

  • LD (HL+), A (0x22)
  • LD (HL-), A (0x32)
  • LD (HL), A (0x77)
  • LD (HL), n (0x36)
  • LD (HL), r (0x70-0x75)

Para cada acceso, reporta PC, opcode, dirección VRAM, valor escrito, si es Tile Data o Tile Map, Tile ID aproximado, si es dato real o limpieza, y banco ROM actual. Límite: 1000 accesos.

[PC-VRAM-CORRELATION] - Correlación PC-VRAM

Usa un std::map<uint16_t, int> para rastrear qué PCs acceden a VRAM y cuántas veces. Imprime inmediatamente cuando detecta un PC nuevo o cuando es dato (no limpieza). Esto permite identificar rutinas específicas que cargan tiles.

[LOAD-SEQUENCE] - Secuencias de Carga

Detecta secuencias consecutivas de escrituras a VRAM que podrían ser carga de tiles. Rastrea direcciones consecutivas (incremento o decremento) y reporta cuando se completa una secuencia de 16 bytes (un tile completo). Alerta especial cuando se detecta una secuencia completa.

[ROM-TO-VRAM] - Copias desde ROM

Detecta cuando se ejecuta LDIR (0xED 0xB0) con DE apuntando a VRAM. Esto indica una copia bloque desde ROM (HL) a VRAM (DE) de longitud BC. Reporta PC, direcciones origen y destino, longitud y banco ROM.

[TIMING-VRAM] - Timing de Accesos VRAM

Rastrea el timing de accesos a VRAM usando un contador de instrucciones aproximado. Calcula el frame aproximado basado en instrucciones (asumiendo ~4 ciclos por instrucción promedio). Reporta PC, frame aproximado, LY (scanline actual), estado del LCD (ON/OFF), estado de BG Display (ON/OFF), dirección VRAM y valor escrito.

Decisiones de diseño

Los monitores usan variables static para mantener estado entre llamadas. Esto es apropiado porque los monitores deben rastrear el estado global del sistema durante toda la ejecución. El monitor de correlación usa std::map para almacenar eficientemente el conteo de accesos por PC.

El límite de 1000 accesos para [VRAM-ACCESS-GLOBAL] previene la saturación de logs mientras permite capturar suficiente información para análisis. El frame counter aproximado en [TIMING-VRAM] es suficiente para identificar el timing relativo de los accesos sin necesidad de acceso directo al frame counter real de PPU.

Archivos Afectados

  • src/core/cpp/CPU.cpp - Añadidos cinco monitores globales de accesos VRAM
  • src/core/cpp/CPU.cpp - Añadido #include <map> para monitor de correlación

Tests y Verificación

El código compiló exitosamente sin errores. Los monitores están listos para ejecución.

  • Compilación: python setup.py build_ext --inplace - Exitoso
  • Validación de sintaxis: Sin errores de linter
  • Validación de módulo compilado C++: Módulo viboy_core compilado correctamente

Nota: Los monitores generarán logs durante la ejecución del emulador. El análisis de estos logs permitirá determinar si hay accesos a VRAM en algún momento del flujo y cuándo ocurren.

Fuentes Consultadas

Nota: Implementación basada en documentación técnica de Pan Docs y conocimiento general de arquitectura LR35902.

Integridad Educativa

Lo que Entiendo Ahora

  • Monitoreo Global: Para encontrar código de carga que puede ejecutarse en cualquier momento, necesitamos monitores que rastreen TODOS los accesos a VRAM, no solo en contextos específicos como ISRs o flujo principal después de eventos.
  • Correlación PC-VRAM: Identificar qué rutinas específicas (PCs) acceden a VRAM permite encontrar el código de carga incluso si se ejecuta en momentos inesperados.
  • Secuencias de Carga: Los tiles se cargan típicamente en secuencias consecutivas de 16 bytes, lo cual es un patrón detectable.
  • LDIR: La instrucción LDIR es el método más común para copiar bloques de datos desde ROM a VRAM.

Lo que Falta Confirmar

  • Existencia del código de carga: ¿Existe código que carga tiles en esta fase del juego, o el juego carga tiles más tarde?
  • Timing de carga: Si el código existe, ¿cuándo se ejecuta? ¿Durante inicialización, V-Blank, o en otro momento?
  • Condiciones de ejecución: Si el código existe pero no se ejecuta, ¿qué condiciones faltan?

Hipótesis y Suposiciones

El análisis del Step 0294 rechazó parcialmente la hipótesis de que el código de carga está en una ISR. Los nuevos monitores permitirán probar nuevas hipótesis:

  • Hipótesis A: El código de carga existe pero se ejecuta ANTES de habilitar BG Display
  • Hipótesis B: El código de carga existe pero se ejecuta MUCHO DESPUÉS de habilitar BG Display
  • Hipótesis C: El código de carga existe pero usa métodos no detectados (ej: DMA no estándar, acceso indirecto)
  • Hipótesis D: El código de carga NO existe en esta fase del juego - el juego carga tiles más tarde o en otra pantalla

Próximos Pasos

  • [ ] Ejecutar el emulador con los nuevos monitores activos
  • [ ] Analizar logs para determinar si hay accesos a VRAM en algún momento
  • [ ] Identificar rutinas específicas que acceden a VRAM (si existen)
  • [ ] Detectar secuencias de carga (si existen)
  • [ ] Determinar el timing de accesos a VRAM
  • [ ] Concluir si el código de carga existe o no en esta fase del juego