# Demystifying the GameBoy/SM83’s DAA Instruction

Posted on Nov 19, 2023

As an off-and-on side project, I’m working on a GameBoy emulator. I definitely still have a long way to go (still working on the CPU!) but I wanted to share a detail that I felt deserved a more in-depth explanation: the DAA (“Decimal Adjust Accumulator”) instruction. This instruction is notorious for being a bit difficult to wrap your head around, but there is seemingly very little written about it.

In principle, this instruction is quite simple; if we perform an arithmetic instruction with two binary coded decimal (BCD) numbers, we should be able to get an output that is also in BCD. Of course, this raises a natural question…

## What the Heck is Binary Coded Decimal?

Binary Coded Decimal is quite literal once you learn what it is; it’s just a decimal number whose digits are encoded in binary. While that’s a bit of an obtuse explanation, it really is that literal. This is likely best illustrated through an example.

Let’s encode the number 42 in BCD. The GameBoy uses four bits to represent each digit, so we will use that here. In a four-bit representation, the digit 2 can be written as `0010`, and the digit 4 can be represented as `0100`. Put together, this means 42 in BCD is simply `0100 0010`, or `0x42` in hexadecimal. Of course, this can be extended to as many digits as you’d like, but we’re going to stick to two-digit numbers here, as this is all we can fit in an eight bit register on the GameBoy.

While this representation is certainly simple on paper, it is a bit limiting. Though a single byte can be used to represent any number from 0 through 255, a single byte of BCD can only represent 0 through 99, since we only have two four-bit “slots” to store our decimal digits (which can only be 0-9)1. Given this loss, one might wonder why anyone would use this. While inefficient, BCD is actually quite useful when displaying numbers, as you can display the digits themselves with no additional logic. You can even see it used for this if you go digging through a disassembly of Tetris.

## Adding Binary Coded Decimal Numbers

We’re on our way to understanding DAA, but before we get there, we need to learn to do some arithmetic with BCD. Going forward, I’m going to use hexadecimal for BCD, but the math will work the same in binary, as well.

Let’s add the BCD numbers `0x42` and `0x35`. This is quite simple, we just need to add the digits and don’t carry anything.

``````  0x42
+ 0x35
------
0x77
``````

Our result, in BCD, is 77, and this matches what we might expect. Things get a little more complicated if we add BCD numbers that force us stretch beyond our decimal limits, though.

``````  0x42
+ 0x29
------
0x6B
``````

Eek! Our output is not in BCD, one of the digits is more than 9! In general, the output of these operations is relatively meaningless; adding the decimal numbers 42 and 29 nets 71, and adding the hex numbers `0x42` + `0x29` is decimal 107 (0x6B), which is certainly not the same number. Instead, we must perform an adjustment to correct it. It turns out, when the unit digit is greater than nine, we can correct it by simply adding six to our result. Indeed, `0x6B` + `0x06` equals `0x71`, which is the BCD result we want! This conversion from these “hex-ish” numbers to BCD is actually precisely what the DAA instruction does to our A register (the “accumulator”).

This offset of six might seem a bit random, but it’s actually derived from the difference between the highest numbers representable in bases 10 an 16 (9 and 15/F). A hand-wavy way of looking at it is that we must “move” the excess over nine into the next digit by adding this difference.

Let’s try another example to demonstrate the point.

``````  0x54
+ 0x28
------
0x7C
``````

We have a digit greater than nine, so we add six…

``````  0x7C
+ 0x06
------
0x82
``````

and there you have it. 54 + 28 is indeed, 82, and we have a BCD representation.

### Correcting an Error in the Tens Place

While this strategy works great in the ones place, it breaks down a bit if we attempt to apply it to the tens place. Let’s add `0x54` and `0x60`.

``````  0x54
+ 0x70
------
0xC4
``````

If we add six to the tens place (i.e. add `0x60`), we will end up with `0x24` (plus a carry). This certainly seems right; decimal 54 plus 70 equals 124, and since we’re limited to two digits, 24 is a correct result. However, the rule is slightly more complicated than just “if the tens place is greater than nine, add `0x60`”. Consider the following example:

``````  0x98
+ 0x04
------
0x9C
``````

If we follow our two rules above, we would simply add `0x06`, since the ones place is greater than nine.

``````  0x98
+ 0x06
------
0xA2
``````

That’s certainly not BCD! However, if we apply our second rule to this result, we will get a correct result!

``````  0xA2
+ 0x60
------
0x02 (plus a carry)
``````

98 + 4 is 102, which wraps around past 99 to 2. It would be a bit inconvenient to have to check your result twice, though, so you’ll often see this written as “if the result is greater than `0x99`, add `0x60`”. This is equivalent to the above, though in perhaps a roundabout way. Really what we’re doing is ensuring that the two-digit number fits within our BCD representation, and adding `0x60` to if it doesn’t; anything greater than `0x99` can’t fit in BCD, but we can push the “extra data” to the next place by adding the difference between the value of place in our two bases, `0x60`. If you think about it, this actually neatly fits in line with our first rule; in that rule, we’re checking if the single-digit result (i.e. ignoring the top nibble) fits in BCD by checking if the value is greater than `0x09`. This is exactly the same principle, just applied to both numeric places2.

### Carry Bits

I have neglected one piece so far: carries. Consider the following BCD addition:

``````    1
0x_90
+ 0x_80
-------
0x110
``````

If we are simply performing eight-bit arithmetic (as we are on the GameBoy), this will wrap to `0x10`, and we will have the carry flag set. We can use our same “greater than `0x99`” rule as above, here. The presence of a carry bit indicates that the result must be greater than `0x99`, as our third place would be the hundreds place. As such, we should also add `0x60` here.

``````   0x10 # ignoring the hundreds place, since our adding wrapped
+  0x60
-------
0x70
``````

This is correct! 90 + 80 is 170, and since we wrapped around, `0x70` is the BCD result we expect.

For the exact same reason, the same applies to our weird friend, the half-carry flag.

``````     1
0x09
+  0x08
-------
0x11
``````

We performed a half-carry, so we must add `0x06`

``````   0x11
+  0x06
-------
0x17
``````

This matches our expectation; 9 plus 8 is 17, which matches our BCD result.

Let’s codify what we have so far.

``````fn get_daa_value(registers: &Registers) -> u8 {
let mut offset = 0_u8;

let a_value = registers
.get_single_8bit_register(register::SingleEightBit::A);
let half_carry = registers.get_flag_bit(register::Flag::HalfCarry);
let carry = registers.get_flag_bit(register::Flag::Carry);

if a_value & 0xF > 0x09 || half_carry == 1 {
offset |= 0x06;
}

if a_value > 0x99 || carry == 1 {
offset |= 0x60;
}

}
``````

You’ll notice that nothing in the above function depends on the original operands; the results stored in our flags are enough to perform the conversion!

## Subtracting Binary Coded Decimal Numbers

We’re well on our way to understanding the DAA instruction, but there is one more thing we must consider: subtraction. Our rules regarding carries work when subtracting as well, but two caveats apply. Firstly, we will subtract our offsets instead of adding them. This makes (hand-wavy) sense; just as we wanted to “move” our extra data up a numeric place with addition (by adding six), here we want to “move” the extra data down into the lower numeric place (by subtracting six). When running the DAA instruction, we can simply check the subtraction flag (N) to determine if a subtraction has just been performed, and negate our adjustment accordingly. Secondly, we apply these rules to borrows, rather than carries (which the GameBoy’s H and C registers already store for us). As an example, let’s subtract `0x13` from `0x20`.

``````     1
0x20
- 0x13
------
0x0D
``````

While performing this subtraction, we needed to borrow from the tens place, so we will apply our half-carry rule, yielding the expected result of 20 - 13, 7.

``````  0x0D
- 0x06
------
0x07
``````

As you might expect, we also apply this rule to our tens place if we borrow from the hundreds place.

``````    1
0x05
- 0x21
------
0xE4
``````

And applying our carry rule…

``````   0xE4
-  0x60
-------
0x84
``````

This matches what one might expect, 5 minus 21 (wrapping from 0 to 99) is 84.

### “Wait, That’s Illegal”

While our carry rules apply neatly to borrowing, the idea of a digit being greater than nine is a little messy. Simply, you cannot subtract two BCD numbers and end with a digit greater than nine unless you perform a borrow. You may have to prove this to yourself, but we can’t “grow” a digit in size unless we perform a borrow.

Unfortunately, there are no restrictions on when the DAA instruction is run; we may have not performed a BCD subtraction beforehand. Consider a state where register A is `0xF0`, and only the subtraction flag is set, indicating a subtraction has just been performed. If we blindly apply what we’ve already learned, we’d subtract `0x60` from this value, and get `0x90`. However, this is not what the GameBoy actually does. In these cases, it will actually leave the value in register A alone. We can fix this pretty easily, by only applying our “digit greater than nine” rule if the subtraction flag is zero.

Let’s codify everything we’ve learned so far. We will only follow our “greater than 9” rules if the subtraction flag is not set, and will perform a `wrapping_sub` if it is.

``````fn get_daa_value(registers: &Registers) -> u8 {
let mut offset = 0_u8;

let a_value = registers
.get_single_8bit_register(register::SingleEightBit::A);
let half_carry = registers.get_flag_bit(register::Flag::HalfCarry);
let carry = registers.get_flag_bit(register::Flag::Carry);
let subtract = registers.get_flag_bit(register::Flag::Subtract);

if (subtract == 0 && a_value & 0xF > 0x09) || half_carry == 1 {
offset |= 0x06;
}

if (subtract == 0 && a_value > 0x99) || carry == 1 {
offset |= 0x60;
}

if subtract == 0 {
} else {
a_value.wrapping_sub(offset)
}
}
``````

## DAA’s Flags

We’re so close to finishing our DAA implementation, all we need to do now is update our flags. Thankfully, most of this is pretty straight forward.

Flag Value
Zero 1 if final value is zero, 0 otherwise
Subtract Untouched
Half-Carry 0
Carry Set if final BCD value is > `0x99`

The only slightly confusing one here is the carry flag. This will be set under two conditions

1. If a subtraction has not been performed, and the unconverted value is greater than `0x99`. This indicates that our BCD value is larger than our maximum of 99, and we must indicate that.
2. If our carry bit was already set. This indicates that our input result was already greater than our maximum of 99, and thus our final BCD value will be as well.

With these in place, we can finish our implementation!

``````fn run_instruction(processor: &mut Processor, opcode: u8) {
match opcode {
// ...
0x27 => {
use register::SingleEightBit;
use register::Flag;

let registers = &mut processor.registers;
let (result, carry) = get_daa_value(registers);
registers
.set_single_8bit_register(SingleEightBit::A, result);

registers.set_flag_bit(Flag::HalfCarry, 0);
registers.set_flag_bit(Flag::Carry, carry.into());
registers.set_flag_bit(Flag::Zero, (result == 0).into());
}
}
}

fn get_daa_value(registers: &Registers) -> (u8, bool) {
let mut offset = 0_u8;
let mut should_carry = false;

let a_value = registers
.get_single_8bit_register(register::SingleEightBit::A);
let half_carry = registers.get_flag_bit(register::Flag::HalfCarry);
let carry = registers.get_flag_bit(register::Flag::Carry);
let subtract = registers.get_flag_bit(register::Flag::Subtract);

if (subtract == 0 && a_value & 0xF > 0x09) || half_carry == 1 {
offset |= 0x06;
}

if (subtract == 0 && a_value > 0x99) || carry == 1 {
offset |= 0x60;
should_carry = true;
}

let output = if subtract == 0 {
} else {
a_value.wrapping_sub(offset)
};

(output, should_carry)
}
``````

## Wrapping it up

Once you can wrap your head around it, the DAA instruction is fairly straight forward. The only tricky bit is that unlike the examples I’ve given above, DAA has no knowledge of the operands given in the original operation. However, by applying a few simple rules, we can convert the result to BCD. To summarize the rules we’ve established above:

1. If we’re not subtracting, and the unit digit is greater than nine, we will add `0x06` to register A.
2. If we’re not subtracting, and the value is greater than `0x99`, we will add `0x60` to register A.
3. If there was a half carry, we will add `0x06` to register A.
4. If there was a full carry, we will add `0x60` to register A.

Applying the these rules, and then setting the appropriate flags, you will have a working DAA implementation! This implementation passes the tests provided by the JSMoo project. I want to extend a huge thanks to the maintainers of that project for sharing these tests. Without them, I’d definitely still be scratching my head.

1. If you’re still not convinced, you may be falling into a similar trap as I did. My original implementation checked if `value & 0xF0 > 0x90`. The example I put in this post was carefully chosen. If we had `0x9C` in our A register, we would perform `0x9C & 0xF0 > 0x90`, which is false, and we would fail to perform a necessary correction! ↩︎