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.
Implementación de los Registros de la CPU (LR35902)
Resumen
Se implementó la clase Registers que gestiona todos los registros de la CPU LR35902 de la Game Boy.
Se incluyen registros de 8 bits (A, B, C, D, E, H, L, F), registros de 16 bits (PC, SP) y pares virtuales de 16 bits (AF, BC, DE, HL).
Se implementó la peculiaridad hardware del registro F (bits bajos siempre 0) y helpers completos para manejo de flags.
Se creó una suite completa de tests unitarios con 15 tests, todos pasando correctamente.
Concepto de Hardware
La Game Boy utiliza una CPU híbrida basada en arquitectura Z80/8080 denominada LR35902. La peculiaridad principal es que tiene registros de 8 bits que pueden combinarse en pares virtuales de 16 bits para direccionamiento y operaciones aritméticas.
Registros de 8 bits
- A (Acumulador): Registro principal para operaciones aritméticas y lógicas
- B, C, D, E, H, L: Registros de propósito general
- F (Flags): Registro de estado con peculiaridad hardware: los 4 bits bajos siempre son 0
Pares virtuales de 16 bits
Estos no son registros físicos separados, sino combinaciones lógicas de registros de 8 bits:
- AF: A (byte alto) + F (byte bajo, pero solo bits 4-7 válidos)
- BC: B (byte alto) + C (byte bajo) - usado frecuentemente para contadores y direcciones
- DE: D (byte alto) + E (byte bajo) - usado para direcciones y datos
- HL: H (byte alto) + L (byte bajo) - muy usado para direccionamiento indirecto
Registros de 16 bits
- PC (Program Counter): Contador de programa, apunta a la siguiente instrucción a ejecutar
- SP (Stack Pointer): Puntero de pila para llamadas a subrutinas y manejo de interrupciones
Flags del registro F
El registro F almacena el estado de la CPU mediante flags (banderas) en los bits altos:
- Bit 7 (Z - Zero): Se activa cuando el resultado de una operación es cero
- Bit 6 (N - Subtract): Indica si la última operación fue una resta
- Bit 5 (H - Half Carry): Indica carry del bit 3 al 4 (nibble bajo)
- Bit 4 (C - Carry): Indica carry del bit 7 (overflow en suma o borrow en resta)
Peculiaridad hardware importante: Los 4 bits bajos (0-3) del registro F siempre son 0 en el hardware real. Esto no es una convención de software, sino una característica física del chip.
Implementación
Se implementó la clase Registers en Python con tipado estricto y documentación educativa completa.
Todas las operaciones aseguran wrap-around correcto usando máscaras bitwise.
Componentes creados/modificados
- Clase Registers: Gestiona todos los registros de la CPU con métodos getters/setters individuales
- Métodos para pares virtuales: get_af, set_af, get_bc, set_bc, get_de, set_de, get_hl, set_hl
- Helpers para flags: set_flag, clear_flag, check_flag, y métodos individuales (get_flag_z, etc.)
- Constantes de flags: FLAG_Z, FLAG_N, FLAG_H, FLAG_C con valores hexadecimales
Decisiones de diseño
- Wrap-around explícito: Todas las operaciones de escritura aplican máscaras (
& 0xFFpara 8 bits,& 0xFFFFpara 16 bits) para simular el comportamiento del hardware - Máscara del registro F: Se aplica
REGISTER_F_MASK = 0xF0en todas las escrituras a F para garantizar que los bits bajos siempre sean 0 - Operaciones bitwise para pares: Los pares de 16 bits se combinan usando
(byte_alto << 8) | byte_bajoy se separan con(valor >> 8) & 0xFFyvalor & 0xFF - Métodos individuales por registro: Se proporcionan getters/setters individuales para facilitar el uso futuro en la implementación de opcodes
Estructura de paquetes
Se crearon archivos __init__.py en src/cpu/ y tests/ para convertir las carpetas en paquetes Python válidos.
Archivos Afectados
src/cpu/__init__.py- Inicialización del módulo CPU (nuevo)src/cpu/registers.py- Clase Registers con todos los registros y flags (nuevo, 361 líneas)tests/__init__.py- Inicialización del módulo de tests (nuevo)tests/test_registers.py- Suite completa de tests unitarios (nuevo, 321 líneas)INFORME_COMPLETO.md- Actualización de la bitácora (modificado)
Tests y Verificación
Se implementó una suite completa de tests unitarios con pytest:
- Test 1 - Wrap-around en registros de 8 bits: Verifica que valores > 255 hacen wrap-around correctamente (256 → 0, valores negativos se convierten correctamente)
- Test 2 - Pares de 16 bits: Verifica que leer/escribir pares virtuales (BC, DE, HL, AF) modifica correctamente los registros individuales y viceversa
- Test 3 - Máscara del registro F: Verifica que el registro F ignora los 4 bits bajos (0xFF → 0xF0, 0x0F → 0x00)
- Test 4 - Helpers de flags: Verifica set_flag, clear_flag, check_flag y métodos individuales (get_flag_z, get_flag_n, get_flag_h, get_flag_c)
- Tests adicionales: PC, SP, inicialización por defecto
Resultado: 15 tests en total, todos pasando correctamente (0.38s).
Se verificó que no hay errores de linting en los archivos creados.
Fuentes Consultadas
- Pan Docs: Game Boy CPU Manual - Descripción general de los registros y flags
- Conocimiento general de arquitectura: Conceptos de registros en CPUs de 8 bits y pares virtuales de 16 bits
Nota: La implementación se basó en conocimiento general de arquitectura de CPUs y las especificaciones generales del LR35902. La peculiaridad del registro F (bits bajos siempre 0) es una característica conocida del hardware real de la Game Boy.
Integridad Educativa
Lo que Entiendo Ahora
- Pares virtuales: Los registros de 16 bits como BC no son registros físicos separados, sino combinaciones lógicas de registros de 8 bits usando operaciones bitwise. La combinación se hace con
(byte_alto << 8) | byte_bajoy la separación con shift right y máscaras. - Máscara del registro F: El hardware real de la Game Boy fuerza físicamente los bits bajos de F a 0. No es una convención de software, sino una limitación del chip. Por eso aplicamos la máscara 0xF0 en todas las escrituras.
- Wrap-around: Es crítico para simular correctamente el comportamiento del hardware. Los valores que exceden el rango válido deben hacer wrap-around usando operaciones de módulo implementadas con máscaras bitwise.
- Flags como bits individuales: Cada flag es un bit específico en el registro F. Se pueden activar/desactivar usando operaciones OR y AND con máscaras específicas (FLAG_Z = 0x80, FLAG_C = 0x10, etc.).
Lo que Falta Confirmar
- Valores iniciales de registros: Los valores exactos de los registros al inicio del boot de la Game Boy. Pendiente de verificar con documentación o tests ROMs permitidas.
- Comportamiento de flags en operaciones complejas: El comportamiento específico de los flags (especialmente H y C) en operaciones aritméticas complejas se implementará cuando se cree la ALU (Arithmetic Logic Unit).
- Orden de inicialización: Si hay algún orden específico o estado inicial particular que deban tener los registros al iniciar el emulador.
Hipótesis y Suposiciones
Se asume que el comportamiento de wrap-around usando máscaras bitwise es correcto para todos los casos. Esta es una suposición estándar en emulación, pero se validará con tests más complejos cuando se implementen las operaciones aritméticas reales.
Se asume que la implementación de los pares virtuales usando operaciones bitwise es correcta. Esto es consistente con cómo funcionan las CPUs reales, pero se validará completamente cuando se implementen opcodes que usen estos pares.
Próximos Pasos
- [ ] Implementar la Unidad de Gestión de Memoria (MMU) para mapeo de direcciones
- [ ] Implementar sistema de carga de ROMs
- [ ] Implementar memoria RAM y VRAM
- [ ] Implementar registros de I/O básicos
- [ ] Comenzar implementación de la ALU para operaciones aritméticas básicas
- [ ] Implementar decodificación básica de opcodes (empezar con opcodes simples como NOP)