'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):
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 |
