Copyright 2005
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 05The 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,ecxA 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, 03hand the hexadecimal memory dump would show:
65 27 38 19 85 03The 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 ;packing must start with least significant digit 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.)
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.
aaa (ASCII Adjust after Addition)
aas (ASCII Adjust after Subtraction)
aam (ASCII Adjust after Multiplication)
aad (ASCII Adjust before Division)
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.)
daa (Decimal Adjust after Addition)
das (Decimal Adjust after Subtraction)
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.)