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.
CGB Post-Boot Clean-Room y Diagnóstico
Resumen
Implementación clean-room de separación explícita entre modos DMG (Game Boy clásico) y CGB (Game Boy Color) para inicialización correcta de registros I/O según Pan Docs Power Up Sequence. Añadido enum HardwareMode (DMG/CGB) en MMU con detección automática leyendo byte 0x0143 del header de la ROM. Implementada función initialize_io_registers() que configura registros PPU, APU, interrupciones y registros CGB-específicos (VBK, KEY1, SVBK, BCPS/BCPD, OCPS/OCPD, HDMA) según el modo actual. Wrappers Cython exponen set_hardware_mode(), get_hardware_mode() e initialize_io_registers() a Python. Detección automática funciona correctamente: Tetris DX detectado como CGB (flag=0x80), registros inicializados para modo CGB. Sistema preparado para instrumentación diagnóstica dirigida (próximos pasos) para identificar qué registros/condiciones bloquean Zelda DX/Pokémon Red en inicialización.
Concepto de Hardware (Pan Docs - Power Up Sequence, CGB Registers)
Diferencias DMG vs CGB en Power Up Sequence
Game Boy clásico (DMG) y Game Boy Color (CGB) tienen secuencias de inicialización diferentes. La Power Up Sequence documentada en Pan Docs especifica los valores iniciales de los registros I/O después de que el sistema arranca (con o sin Boot ROM).
Registros Comunes (DMG y CGB)
- LCDC (0xFF40): 0x91 (LCD ON, BG ON, Window OFF, BG Tilemap 0x9800)
- STAT (0xFF41): 0x85 (bits 3-7 escribibles, bits 0-2 controlados por PPU)
- SCY/SCX (0xFF42/0xFF43): 0x00 (scroll en posición inicial)
- BGP (0xFF47): 0xFC (paleta DMG: 11221100, blanco a negro)
- OBP0/OBP1 (0xFF48/0xFF49): 0xFF (paletas de sprites)
- WY/WX (0xFF4A/0xFF4B): 0x00 (Window en posición inicial)
- APU Registers (0xFF10-0xFF26): Valores específicos por canal
- IF (0xFF0F): 0x01 (VBlank interrupt request inicial)
- IE (0xFFFF): 0x00 (sin interrupciones habilitadas inicialmente)
Registros Específicos de CGB
CGB introduce registros adicionales que NO existen en DMG:
- VBK (0xFF4F): VRAM Bank Select. Valor inicial: 0x00 (banco 0 por defecto). Permite seleccionar entre 2 bancos de VRAM de 8KB cada uno.
- KEY1 (0xFF4D): Prepare Speed Switch. Valor inicial: 0x00 (modo normal, no double-speed). Permite cambiar entre 4.19 MHz (normal) y 8.38 MHz (double-speed).
- SVBK (0xFF70): WRAM Bank Select. Valor inicial: 0x01 (banco 1 por defecto). CGB tiene 8 bancos de WRAM (4KB cada uno), banco 0 siempre mapeado en 0xC000-0xCFFF.
- BCPS/BCPD (0xFF68/0xFF69): BG Palette Specification/Data. CGB tiene 8 paletas BG, cada una con 4 colores de 15 bits (BGR555 format). Valor inicial: 0x00 (índice 0, no auto-increment).
- OCPS/OCPD (0xFF6A/0xFF6B): OBJ Palette Specification/Data. Similar a paletas BG pero para sprites. Valor inicial: 0x00.
- HDMA1-HDMA5 (0xFF51-0xFF55): Horizontal/General DMA. Permite copiar datos de ROM/RAM a VRAM de forma eficiente. Valor inicial: 0xFF (inactivo).
Detección de Modo CGB desde el Header de la ROM
El byte 0x0143 del header del cartucho indica la compatibilidad CGB:
| Valor | Significado | Modo Emulador |
|---|---|---|
| 0x80 | CGB funcionalidad (funciona en DMG también) | CGB (preferido) |
| 0xC0 | CGB only (solo funciona en CGB) | CGB (obligatorio) |
| Otros | DMG only (Game Boy clásico) | DMG |
Fuente: Pan Docs - Cartridge Header, 0143 - CGB Flag.
Por qué esto es crítico para Zelda DX/Pokémon Red
Los juegos CGB esperan que los registros específicos de Color estén inicializados correctamente. Sin una separación clara DMG/CGB, el emulador puede:
- Inicializar registros CGB con valores incorrectos (o no inicializarlos).
- Depender excesivamente de
BGP (0xFF47)cuando juegos CGB usan paletas CGB (BCPS/BCPD). - No configurar banking de VRAM/WRAM correctamente, causando corrupción de datos.
- No soportar características como double-speed mode o HDMA.
La implementación de este paso asegura que, al cargar una ROM CGB, todos los registros CGB se inicializan a valores correctos según Pan Docs, eliminando una posible causa de bloqueo en inicialización.
Implementación
1. Enum de Modo de Hardware (MMU.hpp)
/**
* Step 0404: Hardware Mode - Modo de hardware (DMG vs CGB)
* Fuente: Pan Docs - Power Up Sequence, CGB Registers
*/
enum class HardwareMode {
DMG, // Game Boy clásico (monocromo)
CGB // Game Boy Color
};
2. Métodos de Gestión de Modo (MMU.hpp)
// Miembro privado
HardwareMode hardware_mode_; // Modo de hardware actual (DMG o CGB)
// Métodos públicos
void set_hardware_mode(HardwareMode mode);
HardwareMode get_hardware_mode() const;
void initialize_io_registers();
3. Inicialización de Registros I/O (MMU.cpp)
Función initialize_io_registers() que configura registros según el modo:
void MMU::initialize_io_registers() {
bool is_cgb = (hardware_mode_ == HardwareMode::CGB);
// ===== PPU / Video =====
memory_[0xFF40] = 0x91; // LCDC
memory_[0xFF41] = 0x85; // STAT
memory_[0xFF42] = 0x00; // SCY
memory_[0xFF43] = 0x00; // SCX
memory_[0xFF45] = 0x00; // LYC
memory_[0xFF46] = 0xFF; // DMA
memory_[0xFF47] = 0xFC; // BGP
memory_[0xFF48] = 0xFF; // OBP0
memory_[0xFF49] = 0xFF; // OBP1
memory_[0xFF4A] = 0x00; // WY
memory_[0xFF4B] = 0x00; // WX
// ===== CGB-Specific Registers =====
if (is_cgb) {
memory_[0xFF4F] = 0x00; // VBK
memory_[0xFF4D] = 0x00; // KEY1
memory_[0xFF70] = 0x01; // SVBK
memory_[0xFF68] = 0x00; // BCPS
memory_[0xFF69] = 0x00; // BCPD
memory_[0xFF6A] = 0x00; // OCPS
memory_[0xFF6B] = 0x00; // OCPD
memory_[0xFF51] = 0xFF; // HDMA1
memory_[0xFF52] = 0xFF; // HDMA2
memory_[0xFF53] = 0xFF; // HDMA3
memory_[0xFF54] = 0xFF; // HDMA4
memory_[0xFF55] = 0xFF; // HDMA5
}
// ===== Sonido (APU) =====
// ... (registros NR10-NR52) ...
// ===== Interrupciones =====
memory_[0xFF0F] = 0x01; // IF
memory_[0xFFFF] = 0x00; // IE
}
4. Detección Automática de Modo CGB (MMU.cpp - load_rom)
// Leer byte 0x0143 del header (CGB Flag)
uint8_t cgb_flag = (size > 0x0143) ? data[0x0143] : 0x00;
bool is_cgb_rom = (cgb_flag == 0x80 || cgb_flag == 0xC0);
if (is_cgb_rom) {
set_hardware_mode(HardwareMode::CGB);
printf("[MMU] ROM CGB detectada (flag=0x%02X). Modo hardware: CGB\n", cgb_flag);
} else {
set_hardware_mode(HardwareMode::DMG);
printf("[MMU] ROM DMG detectada (flag=0x%02X). Modo hardware: DMG\n", cgb_flag);
}
5. Wrappers Cython (mmu.pxd, mmu.pyx)
Exposición de funciones a Python:
# mmu.pxd
cdef enum class HardwareMode:
DMG
CGB
cdef cppclass MMU:
# ... métodos existentes ...
void set_hardware_mode(HardwareMode mode)
HardwareMode get_hardware_mode()
void initialize_io_registers()
# mmu.pyx
def set_hardware_mode(self, str mode):
"""Configura el modo de hardware (DMG o CGB)"""
if mode.upper() == "DMG":
self._mmu.set_hardware_mode(mmu.HardwareMode.DMG)
elif mode.upper() == "CGB":
self._mmu.set_hardware_mode(mmu.HardwareMode.CGB)
else:
raise ValueError(f"Modo inválido: {mode}")
def get_hardware_mode(self):
"""Obtiene el modo de hardware actual"""
cdef mmu.HardwareMode mode = self._mmu.get_hardware_mode()
return "CGB" if mode == mmu.HardwareMode.CGB else "DMG"
def initialize_io_registers(self):
"""Inicializa registros I/O según el modo actual"""
self._mmu.initialize_io_registers()
Archivos Afectados
src/core/cpp/MMU.hpp- EnumHardwareMode, declaración de métodos, miembrohardware_mode_.src/core/cpp/MMU.cpp- Constructor inicializa modo DMG, implementación deset_hardware_mode(),get_hardware_mode(),initialize_io_registers(), detección automática enload_rom().src/core/cython/mmu.pxd- Declaración del enum y métodos para Cython.src/core/cython/mmu.pyx- Wrappers Python para gestión de modo de hardware.docs/bitacora/entries/2026-01-01__0404__cgb-postboot-cleanroom-y-diagnostico.html- Documentación creada (esta página).docs/bitacora/index.html- Índice actualizado con nueva entrada.docs/informe_fase_2/parte_00_steps_0370_0402.md- Informe actualizado con entrada del Step 0404.
Tests y Verificación
Compilación
$ python3 setup.py build_ext --inplace
=== COMPILACIÓN EXITOSA ===
copying build/lib.linux-x86_64-cpython-312/viboy_core.cpython-312-x86_64-linux-gnu.so ->
Resultado: Compilación exitosa sin errores (solo warnings menores de formato printf).
Test de Detección Automática (Tetris DX - CGB)
$ timeout 5s python3 main.py roms/tetris_dx.gbc > logs/step0404_test_cgb_detection.log 2>&1
$ head -n 50 logs/step0404_test_cgb_detection.log
Salida relevante:
[MMU] Registros I/O inicializados para modo DMG # Constructor inicial
[MMU] Modo de hardware configurado: CGB # Detección automática
[MMU] Registros I/O inicializados para modo CGB # Reinicialización para CGB
[MMU] ROM CGB detectada (flag=0x80). Modo hardware: CGB
[MBC] ROM loaded: 524288 bytes (32 banks) | Type: 0x03
Resultado: ✅ Detección automática correcta. Tetris DX (flag=0x80) detectado como CGB, registros inicializados para modo CGB.
Validación de Módulo Compilado C++
Confirmado que las funciones C++ (set_hardware_mode, get_hardware_mode, initialize_io_registers) son llamadas correctamente desde Python a través de wrappers Cython. Sin errores de segmentación, sin crashes.
Fuentes Consultadas
- Pan Docs: Power Up Sequence - Valores de registros post-boot para DMG y CGB.
- Pan Docs: CGB Registers - Documentación completa de registros específicos de Game Boy Color.
- Pan Docs: Cartridge Header - 0143 CGB Flag - Detección de compatibilidad CGB.
- Pan Docs: LCD Color Palettes (CGB only) - BCPS/BCPD, OCPS/OCPD.
- Pan Docs: VRAM Bank Select (VBK) - Banking de VRAM en CGB.
Integridad Educativa
Lo que Entiendo Ahora
- Separación DMG/CGB es fundamental: No basta con "soportar CGB". El sistema debe comportarse diferente según el modo.
- Power Up Sequence varía: CGB tiene registros adicionales (VBK, KEY1, SVBK, paletas, HDMA) que deben inicializarse correctamente.
- Byte 0x0143 es confiable: El header de la ROM indica claramente si es DMG (otros valores), CGB compatible (0x80), o CGB only (0xC0).
- Paletas CGB vs BGP: Juegos CGB usan paletas de 15 bits (BGR555) vía BCPS/BCPD, no el registro DMG BGP (0xFF47).
- Detección automática funciona: Tetris DX (0x80) detectado como CGB, registros inicializados correctamente según Pan Docs.
Lo que Falta Confirmar
- Instrumentación diagnóstica: Añadir monitors de registros críticos (VBK, KEY1, SVBK, BCPS, HDMA5) para detectar qué condición bloquea Zelda DX/Pokémon (Tarea 0404-2 del plan).
- Renderizado con paletas CGB: Confirmar que el PPU usa paletas CGB (BCPS/BCPD) en vez de BGP cuando está en modo CGB (Tarea 0404-3 del plan).
- Tests extensivos: Verificar que Zelda DX/Pokémon Red mejoran objetivamente con esta separación (métricas: unique_tile_ids, tiledata_nonzero, gameplay_state) (Tarea 0404-4 del plan).
- Double-speed mode: Confirmar comportamiento de KEY1 cuando juegos CGB cambian a modo 8.38 MHz (no crítico para inicialización, pero importante para compatibilidad).
Hipótesis y Suposiciones
- Hipótesis principal: La separación DMG/CGB eliminará algunos bloqueos de inicialización en Zelda DX/Pokémon, pero aún puede haber problemas con renderizado de paletas CGB (a validar en Tareas 0404-2, 0404-3, 0404-4).
- Suposición: Los valores de Pan Docs para Power Up Sequence son suficientemente precisos. Si persisten problemas, puede ser necesario ajustar valores específicos según comportamiento de hardware real (requeriría Boot ROM real o tests en hardware físico).
Próximos Pasos
- [x] Tarea 0404-1: Separar claramente "DMG post-boot" vs "CGB post-boot" (completado en este step).
- [ ] Tarea 0404-2: Instrumentación para detectar "qué condición espera" Zelda DX/Pokémon (monitors de registros críticos VBK/KEY1/SVBK/BCPS/HDMA5/LYC/IE/IF).
- [ ] Tarea 0404-3: Ajuste de renderizado para CGB (confirmar que PPU usa paletas CGB en vez de BGP).
- [ ] Tarea 0404-4: Tests controlados con Tetris DX (baseline), Zelda DX, Pokémon Red (análisis de métricas con límites de contexto).
- [ ] Tarea 0404-5: Documentación completa del Step 0404 con hallazgos de todas las tareas.