This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
ALU Implementation and Flag Management
Summary
Implementation of the ALU (Arithmetic Logic Unit) of the CPU with correct flag management, especially the Half-Carry (H) which is critical for the DAA instruction and handling of decimal numbers. CPU refactoring to use a dispatch table instead of if/elif, improving code scalability. Implementation of the ADD A, d8 (0xC6) and SUB d8 (0xD6) opcodes. Complete suite of TDD tests (5 tests) validating arithmetic operations and flags.
Hardware Concept
TheALU (Arithmetic Logic Unit)is the component of the CPU responsible for performing arithmetic (addition, subtraction) and logical operations (AND, OR, XOR). On the Game Boy, the ALU operates on 8-bit values and updates a set offlagsindicating the status of the result.
The Flags of the LR35902 CPU
The CPU maintains 4 main flags in the F register (valid high bits only):
- Z (Zero, bit 7):It is activated when the result of an operation is zero.
- N (Subtract, bit 6):Indicates whether the last operation was a subtraction (1) or addition (0).
- H (Half-Carry, bit 5):Indicates if there was carry/borrow from bit 3 to 4 (low nibble).
- C (Carry, bit 4):Indicates if there was carry/borrow of bit 7 (overflow/underflow of 8 bits).
The Half-Carry: The "Black Beast" of Emulators
The flagHalf-Carry (H)is especially critical and often misunderstood. Indicates if there was a "carry" (in addition) or "borrow" (in subtraction) between the low nibble (bits 0-3) and the high nibble (bits 4-7).
Why is it important?The instructionDAA (Decimal Adjust Accumulator)uses the H flag to convert binary numbers to BCD (Binary Coded Decimal). Without correct H, the
Decimal numbers in games (scores, lives, counters) will show corrupted.
Formulas:
- Addition:H = 1 yes
(A & 0xF) + (value & 0xF) > 0xF - Subtraction:H = 1 yes
(A & 0xF)< (value & 0xF)
Example:Add 15 (0x0F) + 1 (0x01) = 16 (0x10). Low nibble goes from 0xF to 0x0
with carry to high nibble. H is activated because0xF + 0x1 = 0x10(exceeds 0xF).
Difference between Carry (C) and Half-Carry (H)
- Carrier (C):Detects full 8-bit overflow/underflow (bit 7).
- Half-Carry (H):Detects low nibble overflow/underflow (bit 3 to 4).
Both are independent: a sum can activate H without activating C (ex: 0x0F + 0x01), or both (ex: 0xFF + 0x01).
Implementation
The CPU was refactored to use adispatch tableinstead of an if/elif string. This improves scalability and performance, and is compatible with Python 3.9+.
Components created/modified
- Dispatch table:Dictionary
_opcode_tablewhich maps opcodes to handler functions. - Helper
_add():Adds a value to register A and updates flags Z, N, H, C correctly. - Helper
_sub():Subtracts a value from register A and updates flags Z, N, H, C correctly. - Opcode handlers:Individual functions for each opcode (
_op_nop(),_op_add_a_d8(), etc.). - Implemented Opcodes:0xC6 (ADD A, d8) and 0xD6 (SUB d8).
Design decisions
1. Dispatch table vs if/elif:A dictionary was chosen for better scalability. Each opcode maps to a function, making it easy to add new opcodes without modifying logic conditional. Compatible with Python 3.9+ (does not require Python 3.10+ match/case).
2. Private helpers for ALU:The methods_add()and_sub()are
private and reusable. Future add/subtract opcodes (ADD A, B; SUB A, C; etc.) can reuse
these helpers, ensuring consistency in flag management.
3. Half-Carry Formulas:They were implemented according to technical documentation (Pan Docs).
For sum:(A & 0xF) + (value & 0xF) > 0xF. For subtraction:(A & 0xF)< (value & 0xF).
4. Explicit wrap-around:All operations apply masks& 0xFFto ensure 8-bit wrap-around, simulating the behavior of real hardware.
Affected Files
src/cpu/core.py- Refactored to use dispatch table, implemented ALU helpers and 0xC6/0xD6 opcodestests/test_alu.py- New file with 5 TDD tests to validate ALU and flagsCOMPLETE_REPORT.md- Updated with log entrydocs/bitacora/entries/2025-12-16__0004__alu-flags.html- New web log entrydocs/bitacora/index.html- Updated with new entry
Tests and Verification
A complete suite of TDD tests was created intests/test_alu.pywith 5 tests:
- test_add_basic:Basic sum 10 + 5 = 15, check flags Z=0, N=0, H=0, C=0
- test_add_half_carry:Add 15 + 1 = 16, verify that H activates (CRITICAL for DAA)
- test_add_full_carry:Add 255 + 1 = 0 (wrap-around), check Z=1, H=1, C=1
- test_sub_basic:Basic subtraction 10 - 5 = 5, check flags Z=0, N=1, H=0, C=0
- test_sub_half_carry:Subtract 16 - 1 = 15, verify that H is activated (half-borrow)
Validation:All tests pass correctly. The code syntax was verified
withpy_compile. The tests especially validate the Half-Carry, which is critical for the
future implementation of DAA.
Note:Tests are executed using the full CPU cycle (fetch-decode-execute), not just the ALU helpers, ensuring that the integration works correctly.
Sources consulted
- Bread Docs:CPU Flags, Instruction Set (ADD, SUB)
- Z80/8080 Architecture Manual:Explanation of Half-Carry and its use in DAA
- Game Boy CPU Manual:Behavior of flags in arithmetic operations
Note: Half-Carry formulas are based on standard architecture technical documentation Z80/8080, of which the LR35902 is derived.
Educational Integrity
What I Understand Now
- Half-Carry:It is a flag that detects low nibble overflow/underflow (bits 0-3). It is critical for DAA and handling decimal numbers in games. Without correct H, the scores and counters will show corrupted.
- Dispatch table:A dictionary that maps opcodes to functions is more scalable than if/elif, especially when there are 256 possible opcodes. Compatible with Python 3.9+.
- Reusable Helpers:The methods
_add()and_sub()can be reused by multiple opcodes (ADD A, B; ADD A, C; SUB A, B; etc.), ensuring consistency. - Flag formulas:H in sum:
(A & 0xF) + (value & 0xF) > 0xF. H in subtraction:(A & 0xF)< (value & 0xF). C in sum:(A+value) > 0xFF. C in subtraction:TO< value.
What remains to be confirmed
- Behavior of flags in operations with previous carry:When they are implemented ADC (Add with Carry) and SBC (Subtract with Carry) instructions, it will be necessary to verify how They combine the flags with the previous carry.
- Exact flag timing:The flags are updated immediately after the operation, but it remains to be verified if there are edge cases where timing is critical (probably No, but it's something to keep in mind.
- Validation with test ROMs:Although unit tests pass, it would be ideal validate with redistributable test ROMs that test arithmetic and DAA operations.
Hypotheses and Assumptions
Main assumption:The Half-Carry formulas implemented are correct according to the technical documentation consulted. However, I have not been able to verify directly with hardware real or commercial test ROMs (which we cannot distribute). The implementation is based on:
- Standard technical documentation (Pan Docs, Z80/8080 manuals)
- Unit tests that validate known cases (15+1, 255+1, etc.)
- Mathematical logic of expected behavior
Future validation plan:When DAA is implemented, if decimal numbers are displayed correctly in games, it will confirm that H is well implemented. If there is corruption, the formulas will have to be reviewed.
Next Steps
- [ ] Implement more arithmetic opcodes (ADD A, B; ADD A, C; SUB A, B; etc.) reusing ALU helpers
- [ ] Implement ADC (Add with Carry) and SBC (Subtract with Carry) instructions
- [ ] Implement logical operations (AND, OR, XOR) with flag management
- [ ] Implement DAA (Decimal Adjust Accumulator) that uses the H flag
- [ ] Validate with redistributable test ROMs that test arithmetic operations
- [ ] Expand the dispatch table with more opcodes from the instruction set