how to do aaa instruction in assembly to number bigger than 99

396 Views Asked by At

i work on assembly for the X86 platform and i got an issue with doing bcd for numbers bigger than 99.

i got in 'AX' 123 (decimal) and i added to it 5 (decimal) then i did 'aaa' but instead of the result to be 128 its 0106. i saw the algorithm and understood why but how i do 'aaa' to number over 99
that's my code:

mov ax,123
add ax,5
aaa

i tried to divide 'AX' in 0ah to separate the number and use 'aad' but i saw that it's forbiden for numbers over 99.

1

There are 1 best solutions below

0
On

The 'AAA" instruction ("ASCII Adjust After Addition") only cares about the value in the lowest 4 bits of AL (it's literally "if(AL & 0x0F > 9) AX += (1 << 8) + (1 << 4) - 10;", or "if the previous addition caused the lowest nibble/digit to overflow; then subtract 10 from the lowest nibble/digit and add 1 to second nibble/digit to fix up the carry that should've happened but didn't, then add 1 plus the carry from second nibble/digit to the third nibble/digit while most people wish this didn't happen").

For 8-bit packed BCD addition (e.g. one digit in the lowest nibble of AL and another digit in the highest nibble of AL) this is a major pain in the neck - you have to store the first nibble somewhere then shift the value in AL to the right and do a second aaa to fix up the second nibble. This can be done with rotates - e.g. aaa then ror ax,4 then aaa then rol ax,4.

For 8-bit unpacked BCD addition (e.g. one digit in the lowest nibble of AL and another digit in the lowest nibble of AH) it's mostly the same major pain in the neck.

For "12-bit or larger" packed BCD addition it goes from bad to worse because first aaa causes the third nibble/digit to be potentially corrupted (increased by 1 too many if the first nibble overflowed), the second aaa potentially corrupts the fourth nibble/digit, etc.

For "12-bit or larger" unpacked BCD addition it goes from bad to worse because (for 16-bit code) now your digits have to be spread across multiple registers and you end up shuffling values into/out of AL just to use the aaa instruction.

The only sane solution is to never use aaa unless you're doing 4-bit addition (which is why it got removed in 64-bit code - nobody used it anyway).

The best alternative is to convert from BCD to normal integer at first sight, then do all the calculations using normal integers, then convert from normal integer back into BCD at the last possible opportunity. This approach can be especially nice if you can use the FPU - e.g. fbld to load an 18-digit (72 bit) packed BCD value into an FPU register while converting it into a normal floating point value and fbstp to convert a normal floating point value into BCD and store it.

Anyway...

    mov ax,123        ;This is not valid BCD - it's the value 0x7B or "7(11)" in decimal
    add ax,5          ;ax = 0x7B + 0x05 = 0x80
    aaa               ;This instruction does nothing because the lowest nibble is less than 9
                      ;Result = 0x80 or "80" in decimal

If the original value/s being added were valid BCD, then you might get something like:

    mov ax,121        ;ax = 0x79 or "79" in decimal
    add ax,5          ;ax = 0x79 + 0x05 = 0x7E
    aaa               ;0xE is is larger than 9, so "fix" it by adding 0x0106
                      ;ax = 0x0184
                      ;Result in AL = 0x84 or "84" in decimal

However, it this is a "lucky case" because the result fits in 2 digits. It could easily be more like:

    mov ax,152        ;ax = 0x98 or "98" in decimal
    add ax,5          ;ax = 0x99 + 0x05 = 0x9D
    aaa               ;0xD is is larger than 9, so "fix" it by adding 0x0106
                      ;ax = 0x01A3
                      ;Result in AX is a horribly broken mess (second digit not valid BCD, third digit correct)