Program solving expression in assembly

丶灬走出姿态 提交于 2020-12-31 04:43:51

问题


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 by shl ax,1. One shl is x2, two shl are x2x2.
  • The remainder is obtained from dl because when div uses a word as divisor, the remainder is left in dx.

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!