assemblyx86additionpacked-decimal

Unpacked Decimal Format in x86 Assembly bugs when Addition is above 15


    const char* p = rhs.digits; //temp pointer to rhs.digits.
    char* q = digits;   //temp to just digits
    _asm {
        mov EAX, 0      //clear register for addition
        mov ESI, p      //point to rhs.digits
        mov EDI, q      //point to digits
        add EDI, 4095  //last position of digits
        add ESI, 4095
        mov ECX, 4096   //set up loop
        L1:
            mov AL, [EDI]   //setup for addition
            add AL, [ESI]   //Add
            add AL, AH      //Add carry
            mov AH, 0       //Clear AH register
            AAA             //Adjust after addition
            mov [EDI], AL
            dec ESI
            dec EDI //move pointers
        loop L1
    }

This is an inline asm function written in C++. Digits is an array of characters 4096 characters long and rhs.digits is a seperate array. Digits is the destination and rhs.digits is the array being added to digits.

The problem I am having is concerning the carry when the addition results in a number larger than 15. So 8+8, 8+9, 9+9. I'm not 100% sure how AAA works, but I believe it drops the high order bits of the value and places the low order into AH. That would mean that because 16, 17 and 18 cannot be represented with one hexadecimal digit and requires two, the higher order bits are dropped and ignored.

This is reinforced by examining the outputs. if 8+8 0 is placed in that position. If 8+9, 1 and 9+9, 2. This would correspond to their hexadecimal representation. I tried using DAA instead but that didn't work either.

How can I fix this? If there an instruction that accounts for this?

Thanks for the help guys.


Solution

  • For packed BCD values you need to use the DAA instruction, for example

    clc
    L1:
    mov AL, [EDI]
    adc AL, [ESI]
    daa
    mov [EDI], AL
    ; etc
    

    For unpacked BCD numbers you need to use the AAA instruction (ASCII Adjust After Addition), for example

    clc
    L1:
    mov AL, [EDI]
    adc AL, [ESI]
    aaa                ; CF=AF=decimal carry-out from AL.  Also added to AH
    mov [EDI], AL
    ; etc
    

    Note that Carry Flag survives the dec and loop instructions.