BINARY   CODED   DECIMALS
by Raymond Filiatreault

Latest revision April 2005

INTRODUCTION

When computers were born, they didn't have highly sophisticated sytems. Although they were very slow when compared to modern computers, they could, however, crunch numbers much faster than humans. They soon became in demand by large corporations such as the banking industry where large numbers and absolute accuracy were expected. The algorithms simply simulated how computations were being done manually with the decimal system.

When the computers evolved to the desk top size with the advent of microchips, registers were initially the 8-bit type and computing with the decimal system still had to be provided. Some specific instructions were then made available to facilitate the task of the programmer for that aspect of computing. Those instructions have been maintained with every new development of microchips and will most probably continue to be provided in the future.

That set of instructions is referred to as the BCD (Binary Coded Decimal) instructions. There are only six of those instructions; four of those are for unpacked BCDs and the other two for packed BCDs.

Unpacked BCD format

This format is simply the allocation of one byte for each decimal digit. The value of each byte can only range from 0 to 9 (even though a byte can hold values from 0 to 255). A number is usually kept in memory in consecutive bytes in the order of decreasing values of the digits, i.e. in the same order they would appear as a string. This makes it easier to convert the number to its ASCII equivalent for display purposes.

However, keeping the digits in reverse order can also be used if preferred. The reverse order has the advantage that the number can be increased without the need to shift all the digits when a new most significant digit becomes necessary; it simply gets added in the next available byte.

For example, a variable could be initialized to 38519382765 in decreasing order of the relative value of the decimal digits as follows:

`   myUnpackedBCD  db  3, 8, 5, 1, 9, 3, 8, 2, 7, 6, 5`

and a hexadecimal memory dump of that data would show:

`   03 08 05 01 09 03 08 02 07 06 05`
The following code could be adapted to convert a null-terminated ASCII input string to its equivalent unpacked BCD data. Instructions are included to count the number of digits. Instructions could also be added to check for the validity of the numerical input.
```.data
inputbuf  db   "38519382765",0  ;size input buffer as required
bcdbuf    db   32 dup(?)        ;size must be adapted for expected input
digitcnt  dd   ?

.code
mov   esi,offset inputbuf
mov   edi,offset bcdbuf+?       ;start at end of buffer for reverse order
xor   ecx,ecx       ;use for counting digits
@@:
lodsb               ;get next ASCII character
or    al,al         ;check for end of null-terminated string
jz    @F
and   al,0Fh        ;keep only the binary value of the numerical digit
mov   [edi],al      ;store it
inc/dec edi         ;depending on chosen order of digits
inc   ecx           ;increment digit counter
jmp   @B

@@:
mov   digitcnt,ecx```
A binary 0 is a valid BCD digit and could not be used to indicate the end of an unpacked BCD number. However, if necessary, a -1 (0FFh) could be inserted to signal the end of such a BCD number.

Packed BCD format

Storage memory being at a premium in the early computers, the use of a full byte for each digit was considered an enormous waste of storage space. The use of the upper 4 bits of the byte was therefore given consideration and a "compressed" format was born.

This format allocates one byte for each pair of decimal digits, starting from the least significant digit. The lower value digit of the pair is kept in the lower 4 bits and the higher value in the upper 4 bits of the byte. As for the unpacked BCDs, a number is usually kept in memory in consecutive bytes in the order of decreasing values of the digits. However, keeping the digits in reverse order can also be used as exemplified by the requirement of the FPU with the FBLD instruction.

For example, the same number as above would need to be declared as follows for a packed BCD in the reverse order:

`   myPackedBCD  db  65h, 27h, 38h, 19h, 85h, 03h`
and the hexadecimal memory dump would show:
`   65 27 38 19 85 03`
The following code could be adapted to convert a null-terminated ASCII input string to its equivalent packed BCD data. It assumes that the count of characters in the returned string is already available and that all characters have been verified to be numerical. Instructions are included to obtain the size of the packed number in bytes.
```.data
inputbuf  db   "38519382765",0  ;size input buffer as required
bcdbuf    db   32 dup(?)        ;size must be adapted for expected input
charcnt   dd   11               ;count of input string characters
bytecnt   dd   ?                ;count of bytes in the packed BCD

.code
mov   esi,offset inputbuf
mov   edi,offset bcdbuf
mov   ecx,charcnt
add   esi,ecx           ;ESI now points to terminating 0
mov   bytecnt,0         ;initialize counter

@@:
dec   esi
mov   al,[esi]
and   al,0Fh            ;keep only the binary value of the numerical digit
dec   ecx
jz    last_one          ;exit now if last character processed
ror   ax,4              ;low nibble temporarily in upper 4 bits of AH
dec   esi
mov   al,[esi]
and   al,0Fh            ;keep only the binary value of the numerical digit
rol   ax,4              ;bring back low nibble to AL
dec   ecx
jz    last_one          ;exit if last character processed
stosb                   ;store packed BCD byte
inc   bytecnt           ;increment byte counter
jmp   @B

last_one:
stosb                   ;store last packed BCD byte
inc   bytecnt           ;increment byte counter

; The packed BCD number is now stored in reverse order (least significant
; byte first). Instructions could be added to change that order if needed.```
As for unpacked BCDs, a binary 0 is a valid packed BCD digit and could not be used to indicate the end of a packed BCD number. However, if necessary, a -1 (0FFh) could be inserted to signal the end of such a BCD number.

(Example code to convert a packed BCD back to a null-terminated ASCII string is included in the description of the daa and das instructions.)

Unpacked BCD instructions

The four instructions for unpacked BCDs are designed to act on the AL/AH registers following an addition, a subtraction and a multiplication, and prior to a division.

Packed BCD instructions

The two instructions for packed BCDs are designed to act on the AL register following an addition and a subtraction. (Note that packed BCDs cannot be used for multiplications and divisions.)

If you wish, you can download the entire tutorial in HTML format. In addition to the tutorial itself, the .zip file also contains two additional folders.

One of them is the AAAfun containing a short program to add any keyboard characters and convert the resulting sum as if they were unpacked BCDs. The sum is then converted to ASCII characters by adding your own number. A few examples are given in a txt file. That folder also contains the source code and the resource file for the input dialog box.

The other folder contains my WINSQR program to extract square roots with up to 9999 decimal places based on the use of BCDs for the computation. A brief help file is incorporated into the program. Again, the folder also contains the source code and the resource file for the input dialog box. (That program was written some 5-6 years ago shortly after I started with Win32 asm; my style and knowledge of Win32 has evolved somewhat since then.)

BCDtut v1.0