In my earlier blog BCD: Remembering Retro Arithmetic, I barely touched on some of the interesting aspects of this form of numerical representation. Now, I have a challenge for you. But first, some review.
As you will recall, a 4-bit nybble (or nibble if you prefer) can be used to represent 16 different values: 0...9 and A...F in hexadecimal. By comparison, the same nybble can only be used to represent the values 0...9 in BCD, which means 6 of the 16 possible combinations remain unused. In turn, this means that, in its “raw” state, BCD requires approximately 20 percent more storage than pure binary representations.
It gets worse when you consider a full byte. In the case of an 8-bit microprocessor, assuming for the moment that we are dealing only with unsigned (positive) values, an 8-bit field can be used to represent the values %00000000 to %11111111 in binary, which equates to 0 to 255 in decimal, or $00 to $FF in hexadecimal, but only #00 to #99 in Packed BCD, which uses both nybbles to represent BCD digits. There is also unpacked BCD, where only the least significant nybble in an 8-bit byte is used to represent a BCD digit. Using this form means that a byte can represent only #00 to #09.
As an aside, there are a variety of different ways in which we can indicate binary and hexadecimal values. My use of "%" and "$" goes back to some of the early assemblers upon which I cut my teeth. The use of "#" to indicate a BCD value is something I came up with myself; but if you know of a more official syntax I would be delighted to hear of it.
Anyway, along with the added storage needs, a problem with BCD occurs when we add two BCD nybbles together and their sum is greater than nine. If we add #03 and #05 together, for example, then the result is a legitimate #08. But if we add #03 and #07 together, the result is #0A, which is not a legitimate BCD value. In this case what we really want to do is to subtract #A (decimal 10) from the least significant nybble and to increment the most significant nybble, thereby resulting in #10.
The designers of early microprocessors came up with a variety of ways with which to handle this situation. (For the purposes of this portion of our discussions let’s focus only on addition, and we won’t worry about what happens with the carry flag.) One option was to have separate BADD (binary add) and DADD (decimal add) instructions. Another approach was to have a special instruction that was used to toggle back and forth between the binary and decimal modes; you would then use an ADD instruction that would treat the values as binary or BCD depending on the current mode.
Yet another alternative was to perform your addition as a pure binary operation using an ADD instruction, but to then follow this with a DAA (Decimal Add Adjust) instruction. The DAA instructed the microprocessor to treat the result as a BCD value and to check for illegal (overflow) values and correct for them. A variation on this theme had the status register contain a special AC (Auxiliary Carry) or HC (Half Carry) flag, which would be set to logic 1 if the least significant nybble contained a value greater than #9. The user could then test this flag and perform any necessary BCD correction "by hand."
So here’s your challenge question. Assume that we don’t have any special instructions or flags -- just an 8-bit microcontroller that supports only a binary ADD instruction. Also assume that we wish to add two Packed BCD bytes together. How could we go about detecting and correcting any result nybbles containing illegitimate BCD values?