# BCD math

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?

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, `1234` in BCD would be `\$1234`.

## 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 `and` with `\$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``````

68000 can do BCD operations! Albeit on a per-byte basis.

The `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 `abcd` instruction on two data registers (e.g. `d0` and `d1`) much like you'd do with normal additions:

``````    and     #0, ccr     ; Clear flags
abcd    d1, d0      ; Add numbers``````

Sadly, the `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``````

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. `a0` and `a1`) 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

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 `bcc` or `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:``````