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

Implementación de la ALU y Gestión de Flags

Fecha: 2025-12-16 Step ID: 0004 Estado: Verified

Resumen

Implementación de la ALU (Unidad Aritmética Lógica) de la CPU con gestión correcta de flags, especialmente el Half-Carry (H) que es crítico para la instrucción DAA y el manejo de números decimales. Refactorización de la CPU para usar una tabla de despacho (dispatch table) en lugar de if/elif, mejorando la escalabilidad del código. Implementación de los opcodes ADD A, d8 (0xC6) y SUB d8 (0xD6). Suite completa de tests TDD (5 tests) validando operaciones aritméticas y flags.

Concepto de Hardware

La ALU (Unidad Aritmética Lógica) es el componente de la CPU responsable de realizar operaciones aritméticas (suma, resta) y lógicas (AND, OR, XOR). En la Game Boy, la ALU opera sobre valores de 8 bits y actualiza un conjunto de flags que indican el estado del resultado.

Los Flags de la CPU LR35902

La CPU mantiene 4 flags principales en el registro F (solo bits altos válidos):

  • Z (Zero, bit 7): Se activa cuando el resultado de una operación es cero.
  • N (Subtract, bit 6): Indica si la última operación fue una resta (1) o suma (0).
  • H (Half-Carry, bit 5): Indica si hubo carry/borrow del bit 3 al 4 (nibble bajo).
  • C (Carry, bit 4): Indica si hubo carry/borrow del bit 7 (overflow/underflow de 8 bits).

El Half-Carry: La "Bestia Negra" de los Emuladores

El flag Half-Carry (H) es especialmente crítico y a menudo malentendido. Indica si hubo un "carry" (en suma) o "borrow" (en resta) entre el nibble bajo (bits 0-3) y el nibble alto (bits 4-7).

¿Por qué es importante? La instrucción DAA (Decimal Adjust Accumulator) utiliza el flag H para convertir números binarios a BCD (Binary Coded Decimal). Sin H correcto, los números decimales en juegos (puntuaciones, vidas, contadores) se mostrarán corruptos.

Fórmulas:

  • Suma: H = 1 si (A & 0xF) + (value & 0xF) > 0xF
  • Resta: H = 1 si (A & 0xF) < (value & 0xF)

Ejemplo: Sumar 15 (0x0F) + 1 (0x01) = 16 (0x10). El nibble bajo pasa de 0xF a 0x0 con carry al nibble alto. H se activa porque 0xF + 0x1 = 0x10 (excede 0xF).

Diferencia entre Carry (C) y Half-Carry (H)

- Carry (C): Detecta overflow/underflow de 8 bits completos (bit 7).
- Half-Carry (H): Detecta overflow/underflow del nibble bajo (bit 3 a 4).

Ambos son independientes: una suma puede activar H sin activar C (ej: 0x0F + 0x01), o ambos (ej: 0xFF + 0x01).

Implementación

Se refactorizó la CPU para usar una tabla de despacho (dispatch table) en lugar de una cadena de if/elif. Esto mejora la escalabilidad y el rendimiento, y es compatible con Python 3.9+.

Componentes creados/modificados

  • Tabla de despacho: Diccionario _opcode_table que mapea opcodes a funciones manejadoras.
  • Helper _add(): Suma un valor al registro A y actualiza flags Z, N, H, C correctamente.
  • Helper _sub(): Resta un valor del registro A y actualiza flags Z, N, H, C correctamente.
  • Handlers de opcodes: Funciones individuales para cada opcode (_op_nop(), _op_add_a_d8(), etc.).
  • Opcodes implementados: 0xC6 (ADD A, d8) y 0xD6 (SUB d8).

Decisiones de diseño

1. Tabla de despacho vs if/elif: Se eligió un diccionario para mejor escalabilidad. Cada opcode se mapea a una función, facilitando la adición de nuevos opcodes sin modificar lógica condicional. Compatible con Python 3.9+ (no requiere match/case de Python 3.10+).

2. Helpers privados para ALU: Los métodos _add() y _sub() son privados y reutilizables. Futuros opcodes que sumen/resten (ADD A, B; SUB A, C; etc.) pueden reutilizar estos helpers, asegurando consistencia en la gestión de flags.

3. Fórmulas de Half-Carry: Se implementaron según documentación técnica (Pan Docs). Para suma: (A & 0xF) + (value & 0xF) > 0xF. Para resta: (A & 0xF) < (value & 0xF).

4. Wrap-around explícito: Todas las operaciones aplican máscaras & 0xFF para asegurar wrap-around de 8 bits, simulando el comportamiento del hardware real.

Archivos Afectados

  • src/cpu/core.py - Refactorizado para usar tabla de despacho, implementados helpers ALU y opcodes 0xC6/0xD6
  • tests/test_alu.py - Nuevo archivo con 5 tests TDD para validar ALU y flags
  • INFORME_COMPLETO.md - Actualizado con entrada de bitácora
  • docs/bitacora/entries/2025-12-16__0004__alu-flags.html - Nueva entrada de bitácora web
  • docs/bitacora/index.html - Actualizado con nueva entrada

Tests y Verificación

Se creó una suite completa de tests TDD en tests/test_alu.py con 5 tests:

  • test_add_basic: Suma básica 10 + 5 = 15, verifica flags Z=0, N=0, H=0, C=0
  • test_add_half_carry: Suma 15 + 1 = 16, verifica que H se activa (CRÍTICO para DAA)
  • test_add_full_carry: Suma 255 + 1 = 0 (wrap-around), verifica Z=1, H=1, C=1
  • test_sub_basic: Resta básica 10 - 5 = 5, verifica flags Z=0, N=1, H=0, C=0
  • test_sub_half_carry: Resta 16 - 1 = 15, verifica que H se activa (half-borrow)

Validación: Todos los tests pasan correctamente. La sintaxis del código fue verificada con py_compile. Los tests validan especialmente el Half-Carry, que es crítico para la futura implementación de DAA.

Nota: Los tests se ejecutan usando el ciclo completo de la CPU (fetch-decode-execute), no solo los helpers ALU, asegurando que la integración funciona correctamente.

Fuentes Consultadas

  • Pan Docs: CPU Flags, Instruction Set (ADD, SUB)
  • Z80/8080 Architecture Manual: Explicación de Half-Carry y su uso en DAA
  • Game Boy CPU Manual: Comportamiento de flags en operaciones aritméticas

Nota: Las fórmulas de Half-Carry están basadas en documentación técnica estándar de arquitecturas Z80/8080, de las cuales la LR35902 es derivada.

Integridad Educativa

Lo que Entiendo Ahora

  • Half-Carry: Es un flag que detecta overflow/underflow del nibble bajo (bits 0-3). Es crítico para DAA y el manejo de números decimales en juegos. Sin H correcto, las puntuaciones y contadores se mostrarán corruptos.
  • Tabla de despacho: Un diccionario que mapea opcodes a funciones es más escalable que if/elif, especialmente cuando hay 256 opcodes posibles. Compatible con Python 3.9+.
  • Helpers reutilizables: Los métodos _add() y _sub() pueden ser reutilizados por múltiples opcodes (ADD A, B; ADD A, C; SUB A, B; etc.), asegurando consistencia.
  • Fórmulas de flags: H en suma: (A & 0xF) + (value & 0xF) > 0xF. H en resta: (A & 0xF) < (value & 0xF). C en suma: (A + value) > 0xFF. C en resta: A < value.

Lo que Falta Confirmar

  • Comportamiento de flags en operaciones con carry previo: Cuando se implementen instrucciones ADC (Add with Carry) y SBC (Subtract with Carry), habrá que verificar cómo se combinan los flags con el carry previo.
  • Timing exacto de flags: Los flags se actualizan inmediatamente después de la operación, pero falta verificar si hay casos edge donde el timing sea crítico (probablemente no, pero es algo a tener en cuenta).
  • Validación con ROMs de test: Aunque los tests unitarios pasan, sería ideal validar con ROMs de test redistribuibles que prueben operaciones aritméticas y DAA.

Hipótesis y Suposiciones

Suposición principal: Las fórmulas de Half-Carry implementadas son correctas según la documentación técnica consultada. Sin embargo, no he podido verificar directamente con hardware real o ROMs de test comerciales (que no podemos distribuir). La implementación se basa en:

  • Documentación técnica estándar (Pan Docs, manuales Z80/8080)
  • Tests unitarios que validan casos conocidos (15+1, 255+1, etc.)
  • Lógica matemática del comportamiento esperado

Plan de validación futura: Cuando se implemente DAA, si los números decimales se muestran correctamente en juegos, confirmará que H está bien implementado. Si hay corrupción, habrá que revisar las fórmulas.

Próximos Pasos

  • [ ] Implementar más opcodes aritméticos (ADD A, B; ADD A, C; SUB A, B; etc.) reutilizando helpers ALU
  • [ ] Implementar instrucciones ADC (Add with Carry) y SBC (Subtract with Carry)
  • [ ] Implementar operaciones lógicas (AND, OR, XOR) con gestión de flags
  • [ ] Implementar DAA (Decimal Adjust Accumulator) que utiliza el flag H
  • [ ] Validar con ROMs de test redistribuibles que prueben operaciones aritméticas
  • [ ] Expandir la tabla de despacho con más opcodes del set de instrucciones