CS 273: Binary Coded Decimal Arithmetic

Frequently, we want to enter and display numbers with very little arithmetic in between. Typical applications involve things like timers and clocks.

One way to do this, of course, would be to do it all in binary. We'd need to take our input and convert it to binary (this involves multiplications), and then take the results of our calculations and convert them to decimal to display them (which involves divisions).

For quite a few applications like this, it's easier to just use a number representation that's more like decimal. A couple of representations like this are "binary coded decimal" and "packed binary coded decimal". Let's talk about them.

Let's talk about BCD first. The idea is, instead of representing a number in binary, we'll represent it as an array of decimal digits. Now, to add, we can just do the following for every digit of x and y.

  1. Load x digit into accumulator
  2. Add y digit to it
  3. If result is greater than 10,
    1. Subtract 10 from result
    2. Make sure to add a carry into the next place over
  4. Store result
We've just made addition much more painful, but displaying a result is much, much easier and we can make the array as wide as we want. But maybe we can do better yet: notice that we never use half the bits in each place. Maybe we can pack two digits into every array element? Yes, we can.

Packed BCD

OK, that's what we'll do. If we're clever, maybe we can even make it easier to do arithmetic at the same time. Let's ask what happens if we add two packed BCD numbers together... we have three cases involving the least significant nybble:
  1. Case 1: the result is less than 10. The addition is correct.

  2. Case 2: the least significant nybble gets a result greater than 9 but less than 16. We need to subtract 10 from the least significant nybble, and add 1 to the most significant nybble.

  3. Case 3: the least significant nybble gets a result greater than 15. We need to subtract 10 from the least significant nybble (we don't need to add 1 to the most significant nybble, because we already had a carry into those bits). Note that we can't use the four bits themselves to see if we've got this case; the values in the bits aren't distinguishable from getting a 1 (for 17), 2 (for 18), or 3 (for 19) which would be the same as the first case. So, we need an extra condition code bit to keep track of whether there is a carry across the nybbles - precisely the purpose of the H bit.

There are three corresponding cases for the most significant nybble. if we consider all the possibilities, we end up with a table like this:

Before DAANumber Added
by DAA
Carry
After DAA
C BitUpper Nybble
of ACCA
H BitLower Nybble
of ACCA
00-900-9000
00-80a-f060
00-910-3060
0a-f00-9601
09-f0a-f661
0a-f10-3661
10-200-9601
10-20a-f661
10-310-3661

This, of course, looks like a mess to try to implement. Unless we have an instruction in the instruction set to do it for us - like the DAA (Decimal Adjust A) instruction! This instruction applies exactly the adjustments we described above.

Now we can add two packed BCD strings of arbitrary length (limited by memory size, of course). The central loop looks like


loop    ldaa    op1,x
        adca    op2,x
        daa
        staa    res,x
        dex
        bne     loop

(notice, by the way, that this same basic loop but missing the daa instruction would let us do arbitrary precision arithmetic.

We subtract by using 10's complement. Mom's Comptometer strikes again!


Last modified: Fri Nov 20 09:02:41 MST 2009

Valid HTML 4.01 Transitional