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

El Estado del GÉNESIS: Inicialización de Registros de CPU Post-BIOS

Fecha: 2025-12-20 Step ID: 0190 Estado: ✅ VERIFIED

Resumen

El emulador está completamente sincronizado, pero la pantalla sigue en blanco porque la CPU entra en un bucle de error. El diagnóstico definitivo revela que esto se debe a un estado inicial de la CPU incorrecto. Nuestro emulador no inicializa los registros de la CPU (especialmente el registro de Flags, F) a los valores específicos que la Boot ROM oficial habría dejado, causando que las primeras comprobaciones condicionales del juego fallen.

Este Step implementa el estado "Post-BIOS" directamente en el constructor de CoreRegisters en C++, asegurando que el emulador arranque con un estado de CPU idéntico al de una Game Boy real. Los valores críticos incluyen el flag Z activo (Z=1), que es esencial para que las primeras instrucciones condicionales del código de arranque tomen el camino correcto.

Concepto de Hardware: El Estado de la CPU Post-Boot ROM

La Boot ROM de 256 bytes de la Game Boy no solo inicializa los periféricos (PPU, Timer, Joypad), sino que también deja los registros de la CPU en un estado muy específico antes de transferir el control al código del cartucho en la dirección 0x0100.

En una Game Boy real, cuando se enciende la consola:

  1. La Boot ROM se ejecuta desde 0x0000 hasta 0x00FF.
  2. La Boot ROM realiza verificaciones de hardware (checksum del cartucho, timer, joypad).
  3. La Boot ROM inicializa los registros de la CPU a valores específicos.
  4. La Boot ROM transfiere el control al código del cartucho en 0x0100 mediante un salto.

El Problema Fundamental: Nuestro emulador no ejecuta una Boot ROM. En su lugar, inicializamos los registros de la CPU a cero (o a valores simples). El código del juego, al arrancar en 0x0100, ejecuta inmediatamente instrucciones condicionales como JR Z, some_error_loop que esperan que el flag Z esté en un estado concreto (por ejemplo, Z=1) que el BIOS habría dejado. Como nuestros registros empiezan en un estado "limpio" e incorrecto, la condición del salto falla, y la CPU es enviada a una sección de código que no es la de mostrar el logo. Entra en un bucle de "fallo seguro", apaga el fondo (LCDC=0x80), y se queda ahí, esperando indefinidamente.

Valores Post-BIOS para DMG (según Pan Docs - "Power Up Sequence"):

  • AF = 0x01B0 (es decir, A = 0x01 y F = 0xB0). F=0xB0 significa Z=1, N=0, H=1, C=1.
  • BC = 0x0013
  • DE = 0x00D8
  • HL = 0x014D
  • SP = 0xFFFE
  • PC = 0x0100

El estado inicial del Flag Z (Z=1) es probablemente el más crítico, ya que las primeras instrucciones suelen ser saltos condicionales basados en este flag. Si el flag Z no está en el estado correcto, el juego puede entrar en un bucle de error en lugar de ejecutar la rutina de arranque normal.

Implementación

La solución es mover la inicialización Post-BIOS directamente al constructor de CoreRegisters en C++, eliminando la necesidad de inicialización manual en Python. Esto garantiza que cada instancia de PyRegisters se cree automáticamente con el estado correcto.

Modificación del Constructor de CoreRegisters

El constructor de CoreRegisters en src/core/cpp/Registers.cpp ahora inicializa todos los registros con los valores Post-BIOS:

CoreRegisters::CoreRegisters() :
    a(0x01),
    b(0x00),
    c(0x13),
    d(0x00),
    e(0xD8),
    h(0x01),
    l(0x4D),
    f(0xB0),  // Flags: Z=1, N=0, H=1, C=1 (0xB0 = 10110000)
    pc(0x0100),
    sp(0xFFFE)
{
    // Inicialización Post-BIOS completada en la lista de inicialización
    // Estos valores simulan el estado exacto que la Boot ROM deja en la CPU
    // antes de transferir el control al código del cartucho en 0x0100
}

Simplificación de _initialize_post_boot_state en viboy.py

El método _initialize_post_boot_state en src/viboy.py ahora solo verifica que el estado Post-BIOS se estableció correctamente, eliminando todas las asignaciones redundantes. Los registros ya están inicializados correctamente en el constructor de C++.

Decisiones de Diseño

  • Inicialización en el Constructor: La inicialización Post-BIOS se realiza en el constructor de C++ para garantizar que siempre se establezca correctamente, sin depender de código Python que podría olvidarse o ejecutarse en el orden incorrecto.
  • Valores DMG: Usamos valores Post-BIOS para DMG (Game Boy Clásica) porque nuestro PPU C++ solo soporta DMG por ahora. El registro A=0x01 indica DMG, lo que hace que los juegos se comporten como en una Game Boy gris.
  • Flag Z Crítico: El flag Z se establece explícitamente a 1 porque es esencial para las primeras comprobaciones condicionales del código de arranque del juego.

Archivos Afectados

  • src/core/cpp/Registers.cpp - Modificado el constructor para inicializar registros con valores Post-BIOS DMG
  • src/viboy.py - Simplificado _initialize_post_boot_state para eliminar inicialización redundante
  • tests/test_core_registers_initial_state.py - Nuevo archivo de tests para validar el estado inicial Post-BIOS

Tests y Verificación

Se creó un nuevo archivo de tests tests/test_core_registers_initial_state.py con tres tests que validan el estado inicial Post-BIOS:

  • test_registers_post_bios_state: Verifica que todos los registros se inicializan con los valores correctos Post-BIOS.
  • test_registers_post_bios_state_consistency: Verifica que los valores de los registros individuales son consistentes con los pares de 16 bits.
  • test_registers_flag_z_critical: Verifica específicamente que el flag Z está activo, ya que es crítico para las primeras comprobaciones condicionales.

Resultado de los tests:

$ pytest tests/test_core_registers_initial_state.py -v
============================= test session starts =============================
platform win32 -- Python 3.13.5, pytest-9.0.2, pluggy-1.6.0
collecting ... collected 3 items

tests/test_core_registers_initial_state.py::test_registers_post_bios_state PASSED [ 33%]
tests/test_core_registers_initial_state.py::test_registers_post_bios_state_consistency PASSED [ 66%]
tests/test_core_registers_initial_state.py::test_registers_flag_z_critical PASSED [100%]

============================== 3 passed in 0.06s ==============================

Validación de módulo compilado C++: Los tests validan directamente el módulo C++ compilado (viboy_core), verificando que el constructor de CoreRegisters inicializa correctamente los registros con valores Post-BIOS.

Código del test clave:

def test_registers_post_bios_state():
    """Verifica que los registros de la CPU se inicializan con sus valores Post-BIOS para DMG."""
    regs = PyRegisters()
    
    # Verificar registros individuales de 8 bits
    assert regs.a == 0x01
    assert regs.f == 0xB0
    
    # Verificar pares de 16 bits
    assert regs.af == 0x01B0
    assert regs.bc == 0x0013
    assert regs.de == 0x00D8
    assert regs.hl == 0x014D
    
    # Verificar registros de 16 bits
    assert regs.sp == 0xFFFE
    assert regs.pc == 0x0100
    
    # Verificar flags individuales
    assert regs.flag_z is True
    assert regs.flag_n is False
    assert regs.flag_h is True
    assert regs.flag_c is True

Fuentes Consultadas

Integridad Educativa

Lo que Entiendo Ahora

  • Estado Post-BIOS: La Boot ROM deja los registros de la CPU en un estado muy específico antes de transferir el control al código del cartucho. Este estado no es arbitrario; los juegos dependen de él para sus primeras comprobaciones condicionales.
  • Flag Z Crítico: El flag Z es especialmente importante porque muchas de las primeras instrucciones del código de arranque son saltos condicionales basados en este flag. Si el flag Z no está en el estado correcto, el juego puede entrar en un bucle de error.
  • Inicialización en el Constructor: Mover la inicialización Post-BIOS al constructor de C++ garantiza que siempre se establezca correctamente, sin depender de código Python que podría olvidarse o ejecutarse en el orden incorrecto.

Lo que Falta Confirmar

  • Comportamiento Real del Emulador: Necesitamos ejecutar el emulador con una ROM real (ej: Tetris) para verificar que el estado Post-BIOS correcto permite que el juego ejecute la rutina de arranque normal en lugar de entrar en un bucle de error.
  • Valores Post-BIOS para CGB: Los valores Post-BIOS para Game Boy Color (CGB) son diferentes. Cuando implementemos soporte completo para CGB, necesitaremos inicializar los registros con valores diferentes (ej: A=0x11 para CGB).

Hipótesis y Suposiciones

Hipótesis Principal: Con el estado Post-BIOS correcto, el emulador debería poder ejecutar el código de arranque del juego correctamente, pasando todas las comprobaciones condicionales y llegando finalmente a la rutina que copia los gráficos del logo a la VRAM. Esta es la pieza final del rompecabezas que debería resolver el problema de la pantalla blanca persistente.

Próximos Pasos

  • [ ] Ejecutar el emulador con una ROM real (ej: Tetris) para verificar que el estado Post-BIOS correcto permite que el juego ejecute la rutina de arranque normal
  • [ ] Verificar que el logo de Nintendo aparece en la pantalla (si el estado Post-BIOS es correcto, el juego debería copiar los gráficos a la VRAM y activar el bit 0 del LCDC)
  • [ ] Si el logo aparece, celebrar el éxito y documentar el resultado en el siguiente Step
  • [ ] Si la pantalla sigue en blanco, investigar otros posibles problemas (ej: rutina de copia de gráficos, activación del LCDC, etc.)