问题
I have a problem with my simple program in assembly. I'm using DOSBox and TASM. The problem is that the operand types don't match in lines 76, 78, and 80. This is after multiplication. I tried to make some changes by using a different variable size.
; --------------------------------------------
; Equation=(a+c*b)/d-2*c,
; --------------------------------------------
.model small
.stack 100h
.data
a db 0
b db 0
c db 0
d db 0
result1 db ?
result2 db ?
message1 db "Equation: (a+c*b)/d-2*c a=$"
message2 db "b=$"
message3 db "c=$"
message4 db "d=$"
message5 db "Result=$"
.code
start: mov ax,@data
mov ds,ax
mov ax, seg message1 ;get a and save to a variable
mov ds,ax
mov dx,offset message1
mov ah, 9h
int 21h
mov ah, 1h
int 21h
sub al,30h ;converting to real number
mov a,al
mov ax, seg message2 ;get b and save to a variable
mov ds,ax
mov dx,offset message2
mov ah, 9h
int 21h
mov ah, 1h
int 21h
sub al,30h ;converting to real number
mov b,al
mov ax, seg message3 ;get c and save to a variable
mov ds,ax
mov dx,offset message3
mov ah, 9h
int 21h
mov ah, 1h
int 21h
sub al,30h ;converting to real number
mov c,al
mov ax, seg message4 ;get d and save to a variable
mov ds,ax
mov dx,offset message4
mov ah, 9h
int 21h
mov ah, 1h
int 21h
sub al,30h ;converting to real number
mov d,al
mov al,b ; (a+c*b) ------------------------error
mul c
add ax,a ; ------------------------error
push ax ;save current ax
mov ax,c ;d-2*c------------------------error
shl ax,2
sub d,ax
pop bx ;get previous ax to bx
div bx ; div ax:bx
mov result1,al
mov result2,ah
add result1,30h ;converting to string
add result2,30h ;converting to string
mov al,result1
mov bl,result2
mov ax, seg message5
mov ds,ax
mov dx,offset message5
mov ah, 9h
int 21h
mov al,result1
mov bl,result2
mov dl, al
mov ah , 2h
int 21h
mov dl, bl
mov ah , 2h
int 21h
mov ax,4C00h
int 21h
end start
回答1:
Your program is almost good, you only have some issues with operand sizes, which is normal. So I took your code and made some little changes, those changes are commented and pointed by arrows (<========) and they are :
- Fixed the operand size problem. I still use DB because I noticed you are capturing the numbers as single chars.
- The result of (d-2*c) is stored in BX. This is because we need to divide (a+c*b) / (d-2*c), and you were popping (a+c*b) in BX, so, when you do
div bx
you were doing (d-2*c) / (a+c*b) . - Separated the display for quotient and remainder.
- Added
13,10
line breaks to messages. - Fixed
shl ax,2
byshl ax,1
. Oneshl
is x2, twoshl
are x2x2. - The remainder is obtained from
dl
because whendiv
uses a word as divisor, the remainder is left indx
.
Here is your code with the little changes (tested on EMU8086):
; --------------------------------------------
; Equation=(a+c*b)/d-2*c,
; --------------------------------------------.model small
.stack 100h
.data
a db 0
b db 0
c db 0
d db 0
result1 db ?
result2 db ?
message1 db 13,10,"Equation: (a+c*b)/d-2*c",13,10,"a=$"
message2 db 13,10,"b=$" ;<================= 13,10 IS
message3 db 13,10,"c=$" ;<================= LINEBREAK.
message4 db 13,10,"d=$" ;<=================
message5 db 13,10,"Quotient=$" ;<=================
message6 db 13,10,"Remainder=$" ;<=================
.code
start: mov ax,@data
mov ds,ax
mov ax, seg message1 ;get a and save to a variable
mov ds,ax
mov dx,offset message1
mov ah, 9h
int 21h
mov ah, 1h
int 21h
sub al,30h ;converting to real number
mov a,al
mov ax, seg message2 ;get b and save to a variable
mov ds,ax
mov dx,offset message2
mov ah, 9h
int 21h
mov ah, 1h
int 21h
sub al,30h ;converting to real number
mov b,al
mov ax, seg message3 ;get c and save to a variable
mov ds,ax
mov dx,offset message3
mov ah, 9h
int 21h
mov ah, 1h
int 21h
sub al,30h ;converting to real number
mov c,al
mov ax, seg message4 ;get d and save to a variable
mov ds,ax
mov dx,offset message4
mov ah, 9h
int 21h
mov ah, 1h
int 21h
sub al,30h ;converting to real number
mov d,al
mov al,b ; (a+c*b)
mul c
mov cl,A ;<======== MOV A TO CX TO
mov ch,0 ;<======== ADD IT TO AX.
add ax,CX ;<======== C*B + A.
;push ax ;<======== NO LONGER NECESSARY BECAUSE
;<======== IN NEXT BLOCK WE USE BX.
mov bl,C ;<======== MOV C TO BL AND CLEAR
mov bh,0 ;<======== BH. NOW C IS IN BX.
shl bx,1 ;<======== 2*c. ONE SHIFT IS x2, TWO SHIFTS ARE x2x2.
sub d,bl ;d - 2c
mov bl,d ;<======== MOV D TO BL AND CLEAR BH. NOW
mov bh,0 ;<======== D IS IN BX. BX = (D-2C).
;pop ax ;<======== NO LONGER NECESSARY. AX CONTAINS (A+C*B).
mov dx,0 ;<======== CLEAR DX, BECAUSE DIVISOR IS A WORD.
;<======== WHEN DIVISOR IS A WORD, DIV USES DX:AX.
div bx ;<======== dx:ax / bx == DX:(A+C*B) / (D-2C). THIS
;<======== DIVISION IS UNSIGNED, FOR SIGNED USE IDIV.
mov result1,al ;<===== QUOTIENT.
mov result2,dl ;<===== REMAINDER, BECAUSE DIVISOR IS WORD.
add result1,30h ;converting to string
add result2,30h ;converting to string
mov al,result1
mov bl,result2
;DISPLAY QUOTIENT <=============
mov ax, seg message5
mov ds,ax
mov dx,offset message5
mov ah, 9h
int 21h
mov al,result1
mov dl, al
mov ah , 2h
int 21h
;DISPLAY REMAINDER <=============
mov ax, seg message6
mov ds,ax
mov dx,offset message6
mov ah, 9h
int 21h
mov dl, bl
mov ah , 2h
int 21h
mov ax,4C00h
int 21h
end start
Next is your "to do" list:
- Change the size of operands from DB to DW, to allow your program to handle bigger numbers.
- Change DIV by IDIV, because DIV is unsigned while IDIV is signed. IDIV will let you handle negative results.
- Capture numbers with int=21h ah=0Ah as strings (not as single chars). Later, you convert the strings into numbers. Next two links will take you to the procedures to convert from string to number :
Assembly x86 Date to Number - Breaking a string into smaller sections
32 bit Calculator in 8086 Assembly
Finally, the test data :
(a+c*b) / (d-2*c)
a=1
b=2
c=3
d=8
a+c*b = 1+3*2 = 7
d-2*c = 8-2*3 = 2
7 / 2 = quotient 3, remainder 1
回答2:
Because this question is an big success at 9k views and because the accepted answer is essentially wrong and misguiding, I decided to post a correct version so people can finally find out how to calculate these simple expressions.
I have problem with program. Operand types do not match at line 76 78 80.
add ax,a ; line 76 push ax mov ax,c ; line 78 shl ax,2 sub d,ax ; line 80
In most assembly instructions the size of the operands on both sides of the comma must match. Since you have defined your a, b, c, and d variables as bytes, you cannot legally use them with the word-sized register AX
. That's why TASM gave your the error message.
When evaluating an expression like (a+c*b)/d-2*c
, you have to respect the algebraic rules.
- items that are parenthesised get calculated as a whole
- for items that are not parenthesised you need to follow the normal precedence rules:
*
and/
come before+
and-
Redundantly parenthesizing everything we get: (a+c*b)/d-2*c
<=> ((a+(c*b))/d)-(2*c)
- when sets of parenthesis are nested the inner set has precedence over the outer set
Considering that a, b, and c are single digit numbers from 0 to 9, and that d is a single digit number from 1 to 9, the result can range from -18 to 72. Therefore we can calculate the whole expression using byte-sized operations. It's not necessary to use the signed division idiv
since the dividend at that point will always be positive.
mov al, c ; AL = c
mul b ; AL = c * b AH is 0
add al, a ; AL = (c * b) + a AH is 0
div d ; AL = ((c * b) + a) / d AH is remainder
sub al, c ; AL = (((c * b) + a) / d) - c AH is remainder
sub al, c ; AL = ((((c * b) + a) / d) - c) - c AH is remainder
Please notice that we used just one register (AX
) to find the result. Would you have expected this?
Below is my implementation of it all. I only left out the part that displays the quotient and remainder, but I have provided a link to Displaying numbers with DOS that explains in great detail how you can output signed and unsigned numbers. It's the basic stuff that you simply must know, so it's never gonna be a waste of time if you read it whole.
; --------------------------------------------
; Expression=(a+c*b)/d-2*c,
; --------------------------------------------
ORG 256 ; Use the .COM file format
; Make sure all inputs are valid single digit numbers
cld
mov dx, offset msgA
mov ah, 09h ; DOS.PrintString
int 21h
mov di, offset a ; Storage for the a, b, c, and d variables (adjacent in memory)
mov si, offset msgB ; Offset of the incomplete message
mov bl, "a" ; Character that completes the message
Again:
mov [si+2], bl ; Completing the message
inc bl
mov dx, si
mov ah, 09h ; DOS.PrintString
int 21h
Redo:
mov ah, 01h ; DOS.GetCharacter
int 21h ; -> AL
sub al, 30h ; From character ["0","9"] to number [0,9]
cmp al, 10
jnb Redo
stosb ; Is indeed in range [0,9]
cmp bl, "e" ; Repeat for "b", "c", and "d"
jb Again
dec di
cmp al, 0
je Redo ; Can't allow d=0 since it will be used as a divider
; The calculation
mov al, c ; AL = c
mul b ; AL = c * b AH is 0
add al, a ; AL = (c * b) + a AH is 0
div d ; AL = ((c * b) + a) / d AH is remainder
sub al, c ; AL = (((c * b) + a) / d) - c AH is remainder
sub al, c ; AL = ((((c * b) + a) / d) - c) - c AH is remainder
mov Q, ax ; Storage for the Q, and R variables (adjacent in memory)
; Displaying the quotient and remainder
mov dx, offset msgB ; Offset of the incomplete message
mov ax, 0951h ; DOS.PrintString
mov [msgB + 2], al ; Completing the message with AL = "Q"
int 21h
mov al, Q
call DisplaySignedNumber8
mov dx, offset msgB ; Offset of the incomplete message
mov ax, 0952h ; DOS.PrintString
mov [msgB + 2], al ; Completing the message with AL = "R"
int 21h
mov al, R
call DisplaySignedNumber8
mov ax, 4C00h ; DOS.Terminate
int 21h
; --------------------------------
a db 0
b db 0
c db 0
d db 0
Q db 0
R db 0
msgA db "Formula: (a+c*b)/d-2*c$"
msgB db 13, 10, "? = $"
来源:https://stackoverflow.com/questions/30645621/program-solving-expression-in-assembly