⚠️ 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.

Step 0250: La Precuela (Volcado ROM Expandido)

Fecha: 2025-12-23 Step ID: 0250 Estado: Draft

Resumen

El Step 0249 reveló que el bucle infinito en 0x2B20 busca el valor 0xFD en la memoria apuntada por HL. Como nuestra memoria está vacía (todo 0x00), el bucle nunca termina. Este Step expande el volcado de ROM al rango anterior (0x2AE0 - 0x2B20) para encontrar cómo se inicializa HL antes de entrar en el bucle. El análisis revela que HL se inicializa desde una tabla de punteros en 0x2BAC, y que el valor final depende de datos leídos desde la memoria RAM que no están inicializados correctamente.

Concepto de Hardware

Tablas de Punteros en ROM: Los juegos de Game Boy frecuentemente almacenan tablas de punteros en la ROM que apuntan a datos en RAM. Estas tablas permiten que el código acceda dinámicamente a diferentes regiones de memoria basándose en un índice. El formato típico es little-endian: el byte bajo va primero, seguido del byte alto.

Indirección de Memoria: El código puede usar múltiples niveles de indirección: primero lee un puntero desde la ROM, luego usa ese puntero para leer datos desde la RAM, y finalmente usa esos datos como otra dirección o valor. Si cualquiera de estos niveles no está inicializado correctamente, el programa puede fallar o entrar en un bucle infinito.

Inicialización de Memoria: El Game Boy no inicializa automáticamente la RAM al encender. Los juegos deben inicializar explícitamente las regiones de memoria que van a usar. Si un juego asume que ciertos datos ya están en memoria (por ejemplo, copiados por DMA o por una rutina de inicialización), pero esos datos nunca se copian, el programa puede fallar.

Implementación

Se modificó el script de volcado para analizar el rango anterior al bucle infinito y se creó un script de análisis de flujo para entender cómo se inicializa HL.

Componentes creados/modificados

  • tools/dump_rom_zone.py: Modificado para volcar el rango 0x2AE0 - 0x2B20.
  • tools/analyze_code_flow.py: Script nuevo que desensambla y analiza el flujo de código entre 0x2B05 y 0x2B20.

Análisis del Flujo de Código

El código entre 0x2B05 y 0x2B20 realiza las siguientes operaciones:

  1. 0x2B05: LD HL, 0x2BAC - Inicializa HL apuntando a una tabla de punteros en ROM.
  2. 0x2B08: RLCA - Rota el registro A (probablemente un índice).
  3. 0x2B09-0x2B0A: LD E,A / LD D,0x00 - Convierte A en un offset de 16 bits.
  4. 0x2B0C: ADD HL,DE - Calcula la dirección de la entrada en la tabla: HL = 0x2BAC + A.
  5. 0x2B0D-0x2B0F: Lee un puntero desde [HL] y lo almacena en DE.
  6. 0x2B10-0x2B14: Lee datos desde [DE] y los usa para configurar HL.
  7. 0x2B16-0x2B1B: Escribe datos a registros de hardware (0xFF90, 0xFF91).
  8. 0x2B1D-0x2B1F: Lee otro puntero desde [HL] y lo almacena en DE.
  9. 0x2B20: INC HL - ¡AQUÍ EMPIEZA EL BUCLE!

Hallazgo Clave

El volcado de 0x2BAC revela una tabla de direcciones:

2BAC: 68 2C 6C 2C 70 2C 74 2C 78 2C 7C 2C 80 2C 84 2C
2BBC: 88 2C 8C 2C 90 2C 94 2C 98 2C 9C 2C A0 2C A4 2C
2BCC: A8 2C AC 2C

Estos son punteros little-endian que apuntan a direcciones en el rango 0x2C68 - 0x2CAC. El código usa el valor de A como índice para seleccionar uno de estos punteros, luego lee datos desde la dirección apuntada, y finalmente usa esos datos para configurar HL.

El Problema: Si los datos en la dirección apuntada por la tabla no están inicializados (son 0x00), HL se configurará incorrectamente, y el bucle en 0x2B20 buscará 0xFD en una dirección incorrecta o en memoria vacía.

Archivos Afectados

  • tools/dump_rom_zone.py - Modificado: Cambiado rango por defecto a 0x2AE0 - 0x2B20.
  • tools/analyze_code_flow.py - Nuevo: Script de análisis de flujo de código con desensamblado detallado.

Tests y Verificación

Se ejecutó el script de volcado y el script de análisis para verificar el flujo de código:

$ python tools/dump_rom_zone.py
📦 VOLCADO DE ROM: tetris.gb
📍 Zona: 0x2ae0 - 0x2b20 (64 bytes)
...
✅ Volcado completado: 64 bytes

$ python tools/analyze_code_flow.py
🔍 ANÁLISIS DEL FLUJO DE CÓDIGO (0x2B05 - 0x2B20)
...
📊 RESUMEN DEL FLUJO:
1. 2B05: LD HL, 0x2BAC  → HL apunta a tabla de punteros en ROM
2. 2B08: RLCA           → Rota A (índice?)
3. 2B09: LD E,A / 2B0A: LD D,0x00  → DE = A (como offset)
4. 2B0C: ADD HL,DE      → HL = 0x2BAC + A (apunta a entrada de tabla)
5. 2B0D-2B0F: Lee puntero desde [HL] → DE = [HL] (dirección en memoria)
6. 2B10-2B14: Lee datos desde [DE] → HL = [DE] (nueva dirección)
7. 2B16-2B1B: Escribe datos a registros de hardware (0xFF90, 0xFF91)
8. 2B1D-2B1F: Lee OTRO puntero desde [HL] → DE = [HL+1]
9. 2B20: INC HL  → ¡AQUÍ EMPIEZA EL BUCLE!

Validación: El análisis confirma que HL se inicializa desde una tabla de punteros y que el valor final depende de datos leídos desde la RAM que no están inicializados.

Fuentes Consultadas

Nota: El análisis de flujo de código se basa en la especificación del conjunto de instrucciones del LR35902.

Integridad Educativa

Lo que Entiendo Ahora

  • Inicialización de HL: El registro HL se inicializa desde una tabla de punteros en ROM usando un índice calculado a partir del registro A.
  • Indirección Múltiple: El código usa múltiples niveles de indirección: ROM → RAM → RAM, lo que significa que si cualquier nivel no está inicializado, el programa falla.
  • Dependencia de Inicialización: El juego asume que ciertos datos ya están en memoria RAM antes de ejecutar este código, pero esos datos nunca se copian porque DMA o las rutinas de inicialización no funcionan correctamente.

Lo que Falta Confirmar

  • Valor de A: ¿Qué valor tiene el registro A cuando se ejecuta RLCA en 0x2B08? Esto determina qué entrada de la tabla se selecciona.
  • Datos Esperados: ¿Qué datos específicos espera el juego encontrar en la dirección apuntada por la tabla? ¿Son datos de configuración, sprites, o algo más?
  • Rutina de Inicialización: ¿Qué rutina debería copiar estos datos a RAM? ¿Es DMA, una interrupción V-Blank, o una rutina de inicialización del juego?

Hipótesis y Suposiciones

Hipótesis Principal: El juego espera que una rutina de inicialización (probablemente ejecutada durante el boot o en una interrupción V-Blank) copie datos desde la ROM a la RAM antes de ejecutar el código en 0x2B05. Como esta rutina nunca se ejecuta o falla, los datos no están en RAM, HL se configura incorrectamente, y el bucle en 0x2B20 nunca encuentra el terminador 0xFD.

Próximos Pasos

  • [ ] Verificar el valor de A cuando se ejecuta RLCA en 0x2B08 (tracking de registros).
  • [ ] Volcar la región de memoria apuntada por la tabla (por ejemplo, 0x2C68) para ver qué datos espera el juego.
  • [ ] Buscar en la ROM rutinas de inicialización que copien datos a RAM (buscando patrones como LD HL, ... seguido de LD (HL+),A o LDI).
  • [ ] Verificar si el juego espera que DMA copie estos datos (revisar si hay escrituras a 0xFF46 antes de 0x2B05).
  • [ ] Implementar tracking de registros para ver el valor exacto de HL cuando entra al bucle en 0x2B20.