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

Volcado de Zona Cero (Desensamblador de ROM)

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

Resumen

El Step 0248 reveló que el juego ejecuta `EI` (Enable Interrupts) en `0x033A`, pero el GPS muestra `IME:0` permanentemente. El análisis forense identificó un bucle infinito en `0x2B24` y escrituras en HRAM en `0x2BA3`. Este Step crea una herramienta de volcado de ROM para desensamblar la zona crítica (`0x2B20` - `0x2BC0`) y entender exactamente qué está haciendo el código del juego en esa región.

Concepto de Hardware

El Game Boy almacena el código del juego en la ROM del cartucho. La CPU (LR35902) lee instrucciones desde la ROM y las ejecuta secuencialmente. Cuando un emulador se atasca en un bucle infinito, es crucial poder examinar el código máquina (opcodes) en esa zona para entender la lógica del programa.

Desensamblado: El proceso de convertir código máquina (bytes) en instrucciones legibles (mnemónicos) se llama desensamblado. Cada opcode tiene un significado específico según la especificación del procesador LR35902.

Análisis de Flujo: Al examinar una secuencia de opcodes, podemos reconstruir el flujo de control del programa: saltos condicionales, bucles, llamadas a subrutinas, etc. Esto es esencial para entender por qué un programa se queda atascado.

Implementación

Se creó el script tools/dump_rom_zone.py que:

  • Lee una zona específica de la ROM (por defecto: `0x2B20` - `0x2BC0`)
  • Muestra los bytes en formato hexadecimal con direcciones
  • Intenta desensamblar las instrucciones usando un diccionario de opcodes Game Boy
  • Muestra operandos y calcula direcciones de destino para saltos relativos

Componentes creados

  • tools/dump_rom_zone.py: Script principal de volcado con desensamblado básico
  • tools/analizar_zona_critica.py: Script de análisis que interpreta los resultados del volcado

Características del desensamblador

  • Diccionario de opcodes: Mapeo completo de los 256 opcodes posibles del LR35902
  • Detección de longitud: Identifica automáticamente si una instrucción tiene 1, 2 o 3 bytes
  • Cálculo de saltos: Para saltos relativos (`JR r8`), calcula la dirección de destino
  • Formato legible: Muestra dirección, bytes hexadecimales, representación ASCII y mnemónico

Archivos Afectados

  • tools/dump_rom_zone.py - Script de volcado de ROM con desensamblado básico (nuevo)
  • tools/analizar_zona_critica.py - Script de análisis de la zona crítica (nuevo)

Tests y Verificación

El script se ejecutó exitosamente sobre la ROM de Tetris:

$ python tools/dump_rom_zone.py roms/tetris.gb 0x2B20 0x2BC0

================================================================================
📦 VOLCADO DE ROM: tetris.gb
📍 Zona: 0x2b20 - 0x2bc0 (160 bytes)
================================================================================

2B20 | 23 F0 8C E0 94 7E FE FF 28 C7 FE FD 20 0E F0 8C | #....~..(... ... | INC HL
2B30 | EE 20 E0 94 23 7E 18 08 13 13 18 E4 FE FE 28 F8 | . ..#~........(. | XOR d8
2B40 | E0 89 F0 87 47 1A 4F F0 8B CB 77 20 06 F0 90 80 | ....G.O...w .... | LDH (a8),A
2B50 | 89 18 0A 78 F5 F0 90 47 F1 90 99 DE 08 E0 93 F0 | ...x...G........ | ADC A,C
2B60 | 88 47 13 1A 13 4F F0 8B CB 6F 20 06 F0 91 80 89 | .G...O...o ..... | ADC A,B
2B70 | 18 0A 78 F5 F0 91 47 F1 90 99 DE 08 E0 92 E5 F0 | ..x...G......... | JR r8
2B80 | 8D 67 F0 8E 6F F0 95 A7 28 04 3E FF 18 02 F0 93 | .g..o...(.>..... | ADC A,L
2B90 | 22 F0 92 22 F0 89 22 F0 94 47 F0 8B B0 47 F0 8A | "..".."..G...G.. | LD (HL+),A
2BA0 | B0 22 7C E0 8D 7D E0 8E E1 C3 20 2B 68 2C 6C 2C | ."|..}.... +h,l, | OR B
2BB0 | 70 2C 74 2C 78 2C 7C 2C 80 2C 84 2C 88 2C 8C 2C | p,t,x,|,.,.,.,., | LD (HL),B

================================================================================
✅ Volcado completado: 160 bytes
================================================================================

Hallazgos Clave del Volcado

  • 0x2B20: INC HL - Inicio del bucle, incrementa el puntero HL
  • 0x2B24: LD A,(HL) seguido de CP 0xFF - Compara el byte en (HL) con 0xFF
  • 0x2B96: LD (HL+),A - Escribe A en (HL) e incrementa HL (parte de rutina de copia)
  • 0x2BA3: LDH (FF8D),A - Escribe en HRAM[0xFF8D] (configuración)
  • 0x2BA9: JP 2B20 - ⚠️ SALTO INCONDICIONAL AL INICIO (BUCLE INFINITO)

Fuentes Consultadas

Integridad Educativa

Lo que Entiendo Ahora

  • Bucle Infinito Confirmado: El código en `0x2BA9` ejecuta `JP 2B20`, saltando incondicionalmente al inicio del bucle en `0x2B20`. Esto confirma que el juego está atascado en un bucle.
  • Condición de Salida: El código en `0x2B24` compara el byte en (HL) con `0xFF`. Si encuentra `0xFF`, probablemente sale del bucle (hay un `JR Z` hacia atrás). Si nunca encuentra `0xFF`, el bucle continúa indefinidamente.
  • Rutina de Copia: El código en `0x2B96` usa `LD (HL+),A` para copiar datos. Esto sugiere que el juego está intentando copiar datos desde una fuente a un destino.
  • Configuración en HRAM: El código escribe en `HRAM[0xFF8D]` y otros registros HRAM, sugiriendo que está configurando parámetros para alguna operación.

Lo que Falta Confirmar

  • ¿Qué datos lee el bucle? Necesitamos verificar qué dirección apunta HL cuando el bucle comienza y qué datos espera encontrar.
  • ¿Por qué nunca encuentra 0xFF? Si el bucle espera encontrar `0xFF` como terminador, ¿por qué nunca lo encuentra? ¿Los datos están mal inicializados?
  • ¿Depende de DMA o interrupciones? El juego podría estar esperando que DMA o una interrupción modifique los datos que el bucle está leyendo.
  • ¿Hay un flag que nunca se activa? El código podría estar esperando que algún flag en memoria cambie, pero si ese flag nunca cambia, el bucle nunca termina.

Hipótesis y Suposiciones

Hipótesis Principal: El juego está en un bucle que lee datos desde una dirección (apuntada por HL) y espera encontrar `0xFF` como terminador. Si nunca encuentra `0xFF`, el bucle continúa indefinidamente. El juego probablemente espera que DMA o una interrupción modifique esos datos o active un flag, pero como esas operaciones no funcionan correctamente en el emulador, el bucle nunca termina.

Suposición: El código en `0x2B96` (LD (HL+),A) es parte de una rutina de copia que debería ejecutarse desde HRAM (como es común en las rutinas de DMA de OAM). Si esa rutina nunca se ejecuta, los datos nunca se copian y el bucle nunca encuentra el terminador `0xFF`.

Próximos Pasos

  • [ ] Verificar qué dirección apunta HL cuando el bucle comienza (tracking de registros)
  • [ ] Verificar qué datos están en esa dirección y si contienen el terminador `0xFF`
  • [ ] Verificar si el juego espera que DMA modifique esos datos
  • [ ] Verificar si el juego espera una interrupción que modifique un flag
  • [ ] Comparar el comportamiento con un emulador de referencia para confirmar la hipótesis
  • [ ] Implementar tracking de HL durante el bucle para ver qué datos está leyendo