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)
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 rango0x2AE0-0x2B20.tools/analyze_code_flow.py: Script nuevo que desensambla y analiza el flujo de código entre0x2B05y0x2B20.
Análisis del Flujo de Código
El código entre 0x2B05 y 0x2B20 realiza las siguientes operaciones:
- 0x2B05:
LD HL, 0x2BAC- Inicializa HL apuntando a una tabla de punteros en ROM. - 0x2B08:
RLCA- Rota el registro A (probablemente un índice). - 0x2B09-0x2B0A:
LD E,A/LD D,0x00- Convierte A en un offset de 16 bits. - 0x2B0C:
ADD HL,DE- Calcula la dirección de la entrada en la tabla:HL = 0x2BAC + A. - 0x2B0D-0x2B0F: Lee un puntero desde
[HL]y lo almacena enDE. - 0x2B10-0x2B14: Lee datos desde
[DE]y los usa para configurarHL. - 0x2B16-0x2B1B: Escribe datos a registros de hardware (
0xFF90,0xFF91). - 0x2B1D-0x2B1F: Lee otro puntero desde
[HL]y lo almacena enDE. - 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 a0x2AE0-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
- Pan Docs: CPU Instruction Set - Referencia de opcodes LR35902
- GBEDG: Game Boy Opcodes Reference - Tabla completa de opcodes
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
HLse inicializa desde una tabla de punteros en ROM usando un índice calculado a partir del registroA. - 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
Acuando se ejecutaRLCAen0x2B08? 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
Acuando se ejecutaRLCAen0x2B08(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 deLD (HL+),AoLDI). - [ ] Verificar si el juego espera que DMA copie estos datos (revisar si hay escrituras a
0xFF46antes de0x2B05). - [ ] Implementar tracking de registros para ver el valor exacto de
HLcuando entra al bucle en0x2B20.