Ever need to display a number on screen but you don't want to deal with slow divisions? (especially with large numbers) Then you should look into BCD, which stores numbers in a way that makes it easy to extract the digits.
BCD is ideal for anything that'd be on screen like score, lives, ammo, seconds, etc.
- What's BCD?
- Extracting the digits
- Adding byte-sized numbers
- Adding larger numbers
- Checking if result fits
BCD (Binary Coded Decimal) is a way to store decimal numbers that uses four bits per digit (10 out of the 16 possible combinations are used). The advantage is that you can split away the digits with only bit shifting (instead of slow divisions), making it ideal for values that need to be displayed on screen (like score).
To figure out the BCD equivalent of a number, just take the number in
decimal and write it as hexadecimal. For example,
BCD would be
Extracting the digits
The idea behind BCD is that each digit is exactly four bits. You can
extract each digit by shifting (or rotating) four bits, then doing an
$0F (which will mask out the bottom
four bits). This example goes through all digits of a 32-bit BCD number
(which can hold 8 digits):
; d0.l = BCD number moveq #8-1, d7 Loop: ; Push the next digit into the ; bottom bits and mask them out rol.l #4, d0 move.w d0, d1 and.w #$0F, d1 ; Now d1.w contains the digit ; Do whatever you need to do dbf d7, Loop
Adding byte-sized numbers
68000 can do BCD operations! Albeit on a per-byte basis.
abcd instruction lets you add two BCD bytes. This is
good enough if you only need two digits. First you need to clear the
flags (later you'll understand why). Then use the
instruction on two data registers (e.g.
d1) much like you'd do with normal additions:
and #0, ccr ; Clear flags abcd d1, d0 ; Add numbers
abcd instruction is rather limited, so you can't
add fixed values as-is... you'll need to store the value into a register
then do the above:
moveq #1, d1 ; Number to add and #0, ccr ; Clear flags abcd d1, d0 ; Add numbers
Adding larger numbers
The above won't help with larger numbers (like score). For that, there's
another variant of
abcd that uses memory instead. Of course
you need the numbers in RAM for this.
Take two address registers (e.g.
and make them point to the first byte after the numbers. Then
clear the flags, and use
abcd for as many bytes as needed,
like in the example below (which is for 32-bit numbers):
lea (Number1+4), a0 lea (Number2+4), a1 and #0, ccr ; Clear flags abcd -(a1), -(a0) ; Add first two digits abcd -(a1), -(a0) ; Add next two digits abcd -(a1), -(a0) ; Add next two digits abcd -(a1), -(a0) ; Add last two digits
(now you can see why you need to clear the flags: the carry is also added)
Substraction works the same way but you use
sbcd instead of
abcd. That's all.
Checking if result fits
If you need to check if the result is too large to fit (or too small,
if substracting), all you need to do is check the carry flag after you're
done with it (with
bcs). If carry is set,
it means the result doesn't fit and you should do something.
Simple example on how to prevent score from going back to zero:
lea (Score+4), a0 lea (Points+4), a1 ; Add points to the score and #0, ccr abcd -(a1), -(a0) abcd -(a1), -(a0) abcd -(a1), -(a0) abcd -(a1), -(a0) ; Check if score went beyond the limit ; If so, force it back to the maximum bcc ScoreOk move.l #$99999999, (Score) ScoreOk: