The third system for printing numbers is very similar to the second – where we stored each digit of the number separately. This gave us complete control over how big the number could be, and removed some of the drawbacks of storing the number as an 8 or 16-bit value.
However… doesn’t it seem a little wasteful to store a single digit – 0 to 9 – using a whole byte? And doesn’t it also seem a little strange there there appears to be no support for doing this kind of thing in Z80 itself? Surely it’s not such a ‘custom’ concept?
Well, welcome to Binary Coded Decimal!
Binary Coded Decimal – or BCD for short – is a system where two digits are held in a single byte. Consider the number 49, which we can store as two bytes:
'4' - binary 0000 0100 '9' - binary 0000 1001
Notice how only the lower four bits of each byte are actually used? We could therefore combine them both in a single value:
'49' - binary 0100 1001
And that’s halved our memory usage! Okay, so you might be thinking “So what? That’s only a byte you’ve saved” but if you’re working with a limited amount of memory then every byte counts. Imagine you have a game with an 8-digit score – so that’s 8 bytes, or 4 with BCD. Now you’ll probably want a highscore table to got with that game, and 10 names is good number – so that’s 80 bytes usually, or only 40 with BCD. Believe me, as you try to squeeze your game into a 1K machine that 40 bytes could be very useful!
Now, as BCD is a ‘packed’ value – 2 digits in one byte – we need to use new methods to print and perform calculations on it.
Printing is straightforward – we print the two halves by masking the value with binary 1111 0000, shifting it down by four bits, and then adding it to ASCII ‘0’ to form the printable first digit, and for the second we mask with 0000 1111 and add to ASCII ‘0’. If we have a number spread across multiple BCD bytes we just loop through them all, from left to right.
Calculation is also straightforward thanks to a Z80 instruction call DAA – Decimal Adjust Accumulator. With this, you can place your BCD value into the accumulator, perform the required addition or subtraction operation, and then call DAA afterwards to ensure the accumulator is fixed to be a valid BCD value again.
Take the example of adding 49 to 23. 49 in BCD is 0100 1001 (decimal 73) and 23 in BCD is 0010 0011 (decimal 35), so:
LD A, %01001001 ; BCD for '49' LD B, %00100011 ; BCD for '23' ADD A, B
After this, the value in the accumulator will be 108, which is BCD for… erm.. well, it’s a bit broken actually, it’s not even a valid BCD number! That’s were DAA comes in and fixes everything:
This fixes the value in the accumulator to be BCD ’72’ – (0111 0010 – decimal 114)
You need to be a little careful though – think of DAA as being bound to ADD, SUB, ADC, SBC, & NEG instructions – once you do one of them, call DAA immediately afterwards, or otherwise it won’t know how to ‘fix’ the value to ensure it is valid BCD. Again, if we have a number spread across multiple BCD bytes we need to ensure that any overflow from one pair is carried over to the next, by doing a suitable ADC or SBC call on the next BCD pair, working from right to left.