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

Análisis: Traza de CPU Post-Bucle de Inicialización

Fecha: 2025-12-20 Step ID: 0153 Estado: 🔍 DRAFT

Resumen

Después de corregir el bug del flag Cero (Z) en la instrucción `DEC B` (Step 0152), se ejecutó el emulador con la ROM de Tetris para capturar y analizar la nueva traza de la CPU. El objetivo era verificar que el bucle de inicialización terminaba correctamente y descubrir qué instrucciones ejecuta el juego después de salir del bucle. El análisis confirmó que el bucle termina correctamente cuando `B` llega a `0x00`, pero reveló que hay múltiples bucles anidados en la rutina de inicialización.

Concepto de Hardware

La rutina de inicialización de un juego de Game Boy típicamente realiza múltiples tareas secuenciales:

  1. Limpieza de memoria: Borra regiones de RAM (WRAM, VRAM, OAM) escribiendo valores específicos (generalmente `0x00` o `0xFF`).
  2. Configuración de hardware: Inicializa registros de I/O (LCDC, BGP, etc.).
  3. Carga de gráficos: Copia datos de tiles desde la ROM a la VRAM.
  4. Configuración de paletas: Establece las paletas de colores.

Cada una de estas tareas puede requerir múltiples bucles anidados. Por ejemplo, limpiar la VRAM (8KB) requiere escribir 8192 bytes, lo que típicamente se hace con un bucle que decrementa un contador hasta llegar a cero. Si hay múltiples regiones de memoria que limpiar, habrá múltiples bucles anidados.

Bucles anidados en Game Boy: Es común usar registros diferentes para cada bucle (ej: `B` para el bucle interno, `C` para el bucle externo). Cuando el bucle interno termina (cuando `B` llega a `0x00`), el código puede continuar con otra tarea o entrar en otro bucle.

Análisis de la Traza

Se ejecutó el emulador con la ROM de Tetris (`roms/tetris.gb`) y se capturó la traza de las primeras 200 instrucciones (aumentado desde 150 para capturar más información).

Resultados del Análisis

1. Confirmación de la Corrección del Bug

La traza confirmó que el fix del Step 0152 funciona correctamente:

  • El bucle `LDD (HL), A -> DEC B -> JR NZ` se ejecuta correctamente.
  • Cuando `B` llega a `0x00`, el flag `Z` se activa correctamente (`Z: 1`).
  • La instrucción `JR NZ` no salta cuando `Z=1`, y el bucle termina.
  • El PC continúa en `0x0297` después de salir del bucle.

2. Descubrimiento de Bucles Anidados

El análisis reveló que hay múltiples bucles anidados en la rutina de inicialización:

[CPU TRACE 8] PC: 0x0293 | Opcode: 0x32  (LDD (HL), A)
[CPU TRACE 9] PC: 0x0294 | Opcode: 0x05  (DEC B)
  [DEBUG DEC B] B antes: 0x00, Z antes: 1
  [DEBUG DEC B] B después: 0xFF, Z después: 0
[CPU TRACE 10] PC: 0x0295 | Opcode: 0x20  (JR NZ, e)
  [DEBUG JR NZ] B: 0xFF, Z: 0, offset: 0xFC
  [DEBUG JR NZ] SALTANDO a PC: 0x0293
...
[CPU TRACE 1224] PC: 0x0295 | Opcode: 0x20  (JR NZ, e)
  [DEBUG JR NZ] B: 0x00, Z: 1, offset: 0xFC
  [DEBUG JR NZ] NO SALTANDO, continuando en PC: 0x0297
  [DEBUG DEC B] B antes: 0x00, Z antes: 0
  [DEBUG DEC B] B después: 0xFF, Z después: 0
  [DEBUG JR NZ] B: 0xFF, Z: 0, offset: 0xFC
  [DEBUG JR NZ] SALTANDO a PC: 0x0293

Observación crítica: Inmediatamente después de que el bucle termina en `PC: 0x0297`, aparece otro `DEC B` que reinicia el bucle. Esto sugiere que:

  • Hay un bucle externo que controla múltiples iteraciones del bucle interno.
  • El código en `0x0297` probablemente contiene otra instrucción `DEC` (posiblemente `DEC C` o `DEC E`) que decrementa el contador del bucle externo.
  • Después de decrementar el contador externo, el código reinicializa `B` y vuelve a entrar en el bucle interno.

3. Límite de Traza Insuficiente

El límite de 200 instrucciones aún no es suficiente para ver qué ocurre después de que todos los bucles terminan. La traza muestra que el bucle se reinicia múltiples veces, pero no captura el momento en que todos los bucles terminan y el código continúa con la siguiente fase de inicialización.

Modificaciones Realizadas

  • src/core/cpp/CPU.cpp: Aumentado `DEBUG_INSTRUCTION_LIMIT` de 150 a 200 para capturar más instrucciones.

Archivos Afectados

  • src/core/cpp/CPU.cpp - Aumentado límite de traza de 150 a 200 instrucciones

Tests y Verificación

Este paso es de análisis, no de implementación. La verificación se realizó mediante:

  1. Ejecución del emulador: python main.py roms/tetris.gb > temp_trace2.log 2>&1
  2. Análisis del log: Búsqueda de patrones en la traza para identificar el comportamiento del bucle.
  3. Confirmación del fix: Verificación de que el bucle termina correctamente cuando `B=0x00` y `Z=1`.

Resultado: El bucle termina correctamente, pero hay múltiples bucles anidados que requieren más análisis.

Fuentes Consultadas

  • Pan Docs: Game Boy Pan Docs - Referencia general de arquitectura
  • Análisis de traza de ejecución: Logs capturados del emulador ejecutando Tetris

Integridad Educativa

Lo que Entiendo Ahora

  • Bucles anidados en inicialización: Los juegos de Game Boy usan bucles anidados para limpiar múltiples regiones de memoria. Cada bucle tiene su propio contador (registro), y cuando el bucle interno termina, el código puede reinicializar el contador y volver a entrar, o continuar con otra tarea.
  • Verificación del fix: El fix del flag Z funciona correctamente. El bucle termina cuando `B=0x00` y `Z=1`, confirmando que la corrección del Step 0152 es efectiva.
  • Límites de traza: Para analizar rutinas complejas con múltiples bucles anidados, puede ser necesario aumentar el límite de traza o usar técnicas más sofisticadas de logging (ej: logging condicional que solo registre cuando se sale de bucles).

Lo que Falta Confirmar

  • Estructura completa de los bucles: No sabemos cuántos bucles anidados hay ni qué registros se usan para cada bucle. Necesitamos ver más instrucciones o usar logging más inteligente.
  • Próxima fase de inicialización: No sabemos qué ocurre después de que todos los bucles terminan. ¿Qué instrucciones ejecuta el juego a continuación? ¿Configura registros de I/O? ¿Copia gráficos a la VRAM?
  • Opcodes no implementados: Es posible que haya opcodes no implementados que bloqueen la ejecución después de que los bucles terminen.

Hipótesis y Suposiciones

Hipótesis: El código en `0x0297` probablemente contiene una instrucción `DEC` que decrementa el contador del bucle externo (posiblemente `DEC C` o `DEC E`), seguida de una instrucción que reinicializa `B` y vuelve a entrar en el bucle interno. Esto explicaría por qué vemos otro `DEC B` inmediatamente después de salir del bucle.

Suposición: Asumimos que la estructura de bucles anidados es estándar para rutinas de limpieza de memoria en Game Boy. Esto es consistente con patrones comunes en código de inicialización.

Próximos Pasos

  • [ ] Aumentar aún más el límite de traza (ej: 500-1000 instrucciones) para capturar el momento en que todos los bucles terminan.
  • [ ] Implementar logging condicional que solo registre cuando se sale de bucles o cuando el PC cambia a una dirección fuera del rango del bucle.
  • [ ] Analizar la traza extendida para identificar qué opcodes se ejecutan después de que todos los bucles terminan.
  • [ ] Identificar cualquier opcode no implementado que pueda estar bloqueando la ejecución.
  • [ ] Documentar la estructura completa de los bucles de inicialización una vez que se capture la traza completa.