'Assembly two digit number subtraction

I have a project for varsity where I need to add or subtract two 2-digit numbers (the user inputs the numbers). I got the addition to work. The subtraction gives the tens digit correct, but gives odd characters in the ones digit place. Here is what I mean (a picture of the result):

Link To Flickr Screen Shot

I use:

  • TASM and TLINK
  • x86
  • Windows XP (Virtual Box)

Code:

.MODEL SMALL
.STACK 100h
.DATA 

    startMsg DB 13,10,'1.) Add ',10,'2.) Subtract',10,'3.) Exit',10,10,'Select a function: $'
    integer1Msg DB 13,10,10,'Enter the first integer: $'
    integer2Msg DB 13,10,'Enter the second integer: $'
    errorOccuredMsg DB 13,10,13,10,'An error occured, please try again! $'

    sumMsg DB 13,10,10,'The sum is: $'
    subMsg DB 13,10,10,'The differance is: $'

    gotNum  DB  0
    func    DB  0

.CODE
start:
    mov ax,@data
    mov ds,ax

    mov gotNum, 0   ; initialize var

    ;display & get the selection
    mov ah,09
    mov dx,OFFSET startMsg
    int 21h
    mov ah,01
    int 21h

    mov func,al

    ;check what the selection was
    cmp func,'1'
    je additionIntermediate
    cmp func,'2'
    je subtractionIntermediate
    cmp func,'3'
    je exit

exit:
    mov ah,4ch
    int 21h

getNum1:
    ;get the first integer (1st digit)
    mov ah,09
    mov dx,OFFSET integer1Msg
    int 21h
    mov ah,01
    int 21h

    ;check that input is a number
    cmp al, 30h  ;0 (ASCII 48)
    jl errorIntermediate  
    cmp al, 39h  ;9 (ASCII 57)
    jg errorIntermediate

    mov bh,al
    sub bh,30h

    ;get the first integer (2nd digit)
    mov ah,01
    int 21h

    ;check that input is a number
    cmp al, 30h  ;0 (ASCII 48)
    jl errorIntermediate
    cmp al, 39h  ;9 (ASCII 57)
    jg errorIntermediate

    mov bl,al
    sub bl,30h

    jmp getNum2

additionIntermediate:
    jmp addition

subtractionIntermediate:
    jmp subtraction

errorIntermediate:
    jmp errorOccured

getNum2:
    ;get the second integer
    mov ah,09
    mov dx,OFFSET integer2Msg
    int 21h
    mov ah,01
    int 21h

    ;check that input is a number
    cmp al, 30h  ;0 (ASCII 48)
    jl errorOccured  
    cmp al, 39h  ;9 (ASCII 57)
    jg errorOccured

    mov ch,al
    sub ch,30h

    ;get the second integer
    mov ah,01
    int 21h

    ;check that input is a number
    cmp al, 30h  ;0 (ASCII 48)
    jl errorOccured  
    cmp al, 39h  ;9 (ASCII 57)
    jg errorOccured

    mov cl,al
    sub cl,30h

    mov gotNum,1

    cmp func,'1'
    je addition
    cmp func,'2'
    je subtraction
    cmp func,'3'
    je errorOccured

getNumIntermediate:
    jmp getNum1

addition:
    cmp gotNum,0
    je getNumIntermediate

    ;add the two numbers and adjust for addition
    mov ah,0
    add bx,cx
    aaa
    or bx,3030h

    ;display result
    mov ah,09
    mov dx,OFFSET sumMsg
    int 21h
    mov dl,bh
    mov ah,02
    int 21h
    mov dl,bl
    mov ah,02
    int 21h

    ;return to beginning
    jmp start

errorOccured:
    lea dx, errorOccuredMsg
    mov ah,09
    int 21h
    jmp start

subtraction:
    cmp gotNum,0
    je getNumIntermediate

    ;determine which subtraction to use
    cmp bx,cx
    jg subtractionPos
    cmp bx,cx
    jl subtractionNeg

subtractionPos: ;integer1 is larger than integer2
    ;subtract
    sub bx,cx
    aas
    or bx,3030h

    ;display result
    mov ah,09
    mov dx,OFFSET subMsg
    int 21h
    mov dl,bh
    mov ah,02
    int 21h
    mov dl,bl
    mov ah,02
    int 21h

    ;return to beginning
    jmp start

subtractionNeg: ;integer2 is larger than integer1
    ;subtract
    sub cx,bx
    aas
    or cx,3030h

    ;display result
    mov ah,09
    mov dx, OFFSET subMsg
    int 21h
    mov ah,06
    mov dl,2dh ;displays the negative sign
    int 21h
    mov dl,ch
    mov ah,02
    int 21h
    mov dl,cl
    mov ah,02
    int 21h

    ;return to beginning
    jmp start

end start

I am very new to assembly so any advice would be great.

EDIT

;subtract
    sub bx,cx       
    mov ch,bh    ;store the value of bh
    xchg bx, ax
    mov bl,0Ah
    div bl
    xchg ah, al
    xchg ax, bx
    mov bh,ch    ;restore the value of bh
    or bx,3030h


Solution 1:[1]

add bx,cx
aaa

Following this addition it is pointless to use the aaa instruction because that one operates on the AX register exclusively.

sub bx,cx
aas

Following this subtraction it is pointless to use the aas instruction because that one operates on the AX register exclusively.

Using aaa

The program will add a couple of 2-digit numbers inputted from the keyboard. Each digit is stored in its own byte-sized register. For the first number that's BH (tens) and BL (ones). And for the second number it's CH (tens) and CL (ones).

The first step in the cascaded addition will be to add the 'ones'. If the result of this addition exceeds the range [0,9], we will have to adjust the value and propagate a carry of 1 to the next step. The aaa instruction does this adjustment.

    mov     al, bl
    add     al, cl          ; Ones
    aaa                     ; -> AL CF
    mov     bl, al

The second step in the cascaded addition will be to add the 'tens' and the carry from the previous step. Therefore we'll use adc instead of add. If the result of this addition exceeds the range [0,9], we will have to adjust the value and propagate a carry of 1 to a further step.

    mov     al, bh
    adc     al, ch          ; Tens
    aaa                     ; -> AL CF
    mov     bh, al

The third step will display a "1", but only if there was a carry from the previous step. This shows that the result of the addition was greater than 99. The maximum sum is 198.

Using aas

The program will subtract a couple of 2-digit numbers inputted from the keyboard. Each digit is stored in its own byte-sized register. For the first number that's BH (tens) and BL (ones). And for the second number it's CH (tens) and CL (ones).

The first step in the cascaded subtraction will be to subtract the 'ones'. If the result of this subtraction exceeds the range [0,9], we will have to adjust the value and propagate a borrow of 1 to the next step. The aas instruction does this adjustment.

    mov     al, bl
    sub     al, cl          ; Ones
    aas                     ; -> AL CF
    mov     bl, al

The second step in the cascaded subtraction will be to subtract the 'tens' and the borrow from the previous step. Therefore we'll use sbb instead of sub. The result of this subtraction will be in the range [0,9] because the maximum difference is 99.

    sbb     bh, ch          ; Tens

The .COM version of the program (FASM):

        ORG     256

start:
        mov     dx, startMsg
        mov     ah, 09h         ; DOS.PrintString
        int     21h
        mov     ah, 01h         ; DOS.GetCharacter
        int     21h             ; -> AL
        cmp     al, '1'
        je      addition
        cmp     al, '2'
        je      subtraction
        cmp     al, '3'
        je      exit
        jmp     errorOccured

exit:
        mov     ax, 4C00h       ; DOS.Terminate
        int     21h

addition:
        call    GetNumbers      ; -> BX is 1st number, CX is 2nd number (AX DX)
        mov     dx, sumMsg
        mov     ah, 09h         ; DOS.PrintString
        int     21h

        mov     al, bl
        add     al, cl          ; Ones
        aaa
        mov     bl, al
        mov     al, bh
        adc     al, ch          ; Tens
        aaa
        mov     bh, al

        jnc     additionBelow100
        mov     dl, "1"         ; Hundreds
        mov     ah, 02h         ; DOS.PrintCharacter
        int     21h
additionBelow100:
ShowTwoDigits:
        mov     ah, 02h         ; DOS.PrintCharacter
        or      bx, 3030h       ; Convert to characters
        mov     dl, bh          ; Tens
        int     21h
        mov     dl, bl          ; Ones
        int     21h
        jmp     start

subtraction:
        call    GetNumbers      ; -> BX is 1st number, CX is 2nd number (AX DX)
        mov     dx, subMsg
        mov     ah, 09h         ; DOS.PrintString
        int     21h

        cmp     bx, cx
        jae     subtractionPos

        xchg    bx, cx          ; Swap if 1st number < 2nd number
        mov     dl, "-"         ; ... and display negative sign
        mov     ah, 02h         ; DOS.PrintCharacter
        int     21h

subtractionPos:
        mov     al, bl
        sub     al, cl          ; Ones
        aas
        mov     bl, al
        sbb     bh, ch          ; Tens
        jmp     ShowTwoDigits

; ------------------------------

; IN () OUT (bx,cx) MOD (ax,dx)
GetNumbers:
        mov     dx, integer1Msg
        call    GetNumber       ; -> DX (AX)
        mov     bx, dx
        mov     dx, integer2Msg
        call    GetNumber       ; -> DX (AX)
        mov     cx, dx
        ret

; IN (dx) OUT (dx) MOD (ax)
GetNumber:
        mov     ah, 09h         ; DOS.PrintString
        int     21h
        call    GetDigit        ; -> AL (AH)
        mov     dh, al          ; Tens
        call    GetDigit        ; -> AL (AH)
        mov     dl, al          ; Ones
        ret

; IN () OUT (al) MOD (ah)
GetDigit:
        mov     ah, 01h         ; DOS.GetCharacter
        int     21h             ; -> AL
        sub     al, "0"
        cmp     al, 9
        ja      errorOccured
        ret

errorOccured:
        mov     dx, errorOccuredMsg
        mov     ah, 09h         ; DOS.PrintString
        int     21h
        jmp     start


startMsg:        DB 13,10,'1.) Add ',10,'2.) Subtract',10,'3.) Exit',10,10
                 DB 'Select a function: $'
integer1Msg:     DB 13,10,10,'Enter the first integer: $'
integer2Msg:     DB 13,10,'Enter the second integer: $'
errorOccuredMsg: DB 13,10,13,10,'An error occured, please try again! $'

sumMsg:          DB 13,10,10,'The sum is: $'
subMsg:          DB 13,10,10,'The difference is: $'

Using daa

The program will add a couple of 2-digit numbers inputted from the keyboard. The tens-digit is stored in the high nibble and the ones-digit is stored in the low nibble of the same byte-sized register. For the first number that's BL, and for the second number it's CL.

After the addition of these packed digits, we use the daa instruction to adjust the result so it keeps representing packed digits.

mov     al, bl
add     al, cl
daa                     ; -> AL CF

Next we display a "1", but only if there was a carry from using daa. This shows that the result of the addition was greater than 99. The maximum sum is 198.

Using das

The program will subtract a couple of 2-digit numbers inputted from the keyboard. The tens-digit is stored in the high nibble and the ones-digit is stored in the low nibble of the same byte-sized register. For the first number that's BL, and for the second number it's CL.

After the subtraction of these packed digits, we use the das instruction to adjust the result so it keeps representing packed digits.

mov     al, bl
sub     al, cl
das                     ; -> AL CF=0

The result of this subtraction will be in range [0,99] because the maximum difference is 99.

The .COM version of the program (FASM):

        ORG     256

start:
        mov     dx, startMsg
        mov     ah, 09h         ; DOS.PrintString
        int     21h
        mov     ah, 01h         ; DOS.GetCharacter
        int     21h             ; -> AL
        cmp     al, '1'
        je      addition
        cmp     al, '2'
        je      subtraction
        cmp     al, '3'
        je      exit
        jmp     errorOccured

exit:
        mov     ax, 4C00h       ; DOS.Terminate
        int     21h

addition:
        call    GetNumbers      ; -> BL is 1st number, CL is 2nd number (AX DX)
        mov     dx, sumMsg
        mov     ah, 09h         ; DOS.PrintString
        int     21h

        mov     al, bl
        add     al, cl
        daa
        jnc     additionBelow100
        push    ax              ; (1)
        mov     dl, "1"         ; Hundreds
        mov     ah, 02h         ; DOS.PrintCharacter
        int     21h
        pop     ax              ; (1)
additionBelow100:
ShowPackedDigits:
        aam     16              ; -> AH is Tens, AL is Ones
        add     ax, 3030h       ; Convert to characters
        xchg    bx, ax
        mov     dl, bh          ; Tens
        mov     ah, 02h         ; DOS.PrintCharacter
        int     21h
        mov     dl, bl          ; Ones
        int     21h
        jmp     start

subtraction:
        call    GetNumbers      ; -> BL is 1st number, CL is 2nd number (AX DX)
        mov     dx, subMsg
        mov     ah, 09h         ; DOS.PrintString
        int     21h

        cmp     bl, cl
        jae     subtractionPos

        xchg    bl, cl          ; Swap if 1st number < 2nd number
        mov     dl, "-"         ; ... and display negative sign
        mov     ah, 02h         ; DOS.PrintCharacter
        int     21h

subtractionPos:
        mov     al, bl
        sub     al, cl
        das
        jmp     ShowPackedDigits

; ------------------------------

; IN () OUT (bl,cl) MOD (ax,dx)
GetNumbers:
        mov     dx, integer1Msg
        call    GetNumber       ; -> AX (DL)
        mov     bl, al
        mov     dx, integer2Msg
        call    GetNumber       ; -> AX (DL)
        mov     cl, al
        ret

; IN (dx) OUT (ax) MOD (dl)
GetNumber:
        mov     ah, 09h         ; DOS.PrintString
        int     21h
        call    GetDigit        ; -> AL (AH)
        mov     dl, al          ; Tens
        call    GetDigit        ; -> AL (AH)
        mov     ah, dl
        aad     16              ; AX = (TensFromAH * 16) + OnesFromAL
        ret

; IN () OUT (al) MOD (ah)
GetDigit:
        mov     ah, 01h         ; DOS.GetCharacter
        int     21h             ; -> AL
        sub     al, "0"
        cmp     al, 9
        ja      errorOccured
        ret

errorOccured:
        mov     dx, errorOccuredMsg
        mov     ah, 09h         ; DOS.PrintString
        int     21h
        jmp     start


startMsg:        DB 13,10,'1.) Add ',10,'2.) Subtract',10,'3.) Exit',10,10
                 DB 'Select a function: $'
integer1Msg:     DB 13,10,10,'Enter the first integer: $'
integer2Msg:     DB 13,10,'Enter the second integer: $'
errorOccuredMsg: DB 13,10,13,10,'An error occured, please try again! $'

sumMsg:          DB 13,10,10,'The sum is: $'
subMsg:          DB 13,10,10,'The difference is: $'

This program uses the aad 16 instruction in order to combine the tens-digit and the ones-digit in the AL register (for storing):

aad                     ; AH = 0, AL = (AH * 10) + AL
aad     16              ; AH = 0, AL = (AH * 16) + AL

This program uses the aam 16 instruction in order to separate both nibbles from the AL register (for printing):

aam                     ; AH = AL / 10, AL = AL mod 10
aam     16              ; AH = AL / 16, AL = AL mod 16

Both these programs were tested in DOSBox and worked fine.

Solution 2:[2]

;subtract
sub bx,cx
aas
or bx,3030h

AAS instruction means Adjust Al after Subtraction. It adjusts AL, not BX. To work with BX, you can use this code:

sub bx, cx
xchg bx, ax
    mov bl, 0x0A
    div bl
    or ax, 0x3030
xchg ah, al
xchg ax, bx

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 user35443