So, it looks like when they type in the number the calculator has to keep multiplying the number it was previously storing by 10, and then adding in our new digit, to get the new number. When we press the ``+'' key the calculator performs the addition; then it does a binary-to-decimal conversion to display the result.
Hey, wait a minute -- look at the ratio of ``calculation work'' to ``IO work'' here! Especially when you consider that multiplications are 10 cycles, and divisions are 41 cycles, each.
Not to mention our calculator can only
hold a number up to 65536 (and, since the MUL
instruction
takes two eight bit operands, even getting up that high would take a
lot of finessing. There's a well-known divide-and-conquer algorithm
for multiplication like this; see http://www.cs.nmsu.edu/~pfeiffer/classes/273/notes/multiply2.html).
Something is wrong here.... maybe we can use a different
representation that will cut down on the amount of work we have to do
in the IO part (though increasing the work in the calculation part)
enough to reduce the total amount of work required.
If we do it right, we can get a useful size number to fit, too.
The representation we will use is called ``binary coded decimal.'' 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.
Case 1: the result is less than 10. The addition is correct.
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.
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.
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!
As a last aside, I don't really know whether real calculators as sold today use a BCD representation or a pure binary representation internally. The program language COBOL, which is intended for business applications, does have built-in datatype for BCD numbers, though.