汇编中求解表达式的程序

Program solving expression in assembly

我的简单程序集有问题。我正在使用 DOSBox 和 TASM。问题是第 76、78 和 80 行的操作数类型不匹配。这是在乘法之后。我尝试使用不同的变量大小进行一些更改。

; --------------------------------------------
; 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

你的程序差不多不错,只是操作数大小有一些问题,这是正常的。所以我拿了你的代码并做了一些小改动,这些改动被注释并用箭头指出 (<========) 它们是:

  • 修复了操作数大小问题。我仍然使用 DB,因为我注意到您将数字捕获为单个字符。
  • (d-2*c)的结果存入BX。这是因为我们需要划分 (a+c*b) / (d-2*c),而你在 BX 中弹出 (a+c*b),所以,当你做 div bx 时,你在做(d-2*c) / (a+c*b) .
  • 分开显示商和余数。
  • 在消息中添加了 13,10 换行符。
  • shl ax,1 修复了 shl ax,2。一个shl是x2,两个shl是x2x2.
  • 余数是从dl得到的,因为当div用一个词作为除数时,余数留在dx

这是你的代码,稍作改动(在 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

下一个是您的 "to do" 列表:

  • 将操作数的大小从 DB 更改为 DW,以允许您的程序处理更大的数字。
  • 将DIV改成IDIV,因为DIV是无符号的,而IDIV是有符号的。我DIV会让你处理负面结果。
  • 将 int=21h ah=0Ah 的数字捕获为字符串(而不是单个字符)。稍后,您将字符串转换为数字。接下来的两个链接将带您到从字符串转换为数字的过程:

最后,测试数据:

(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

因为这个问题在 9000 次浏览中大获成功并且因为接受的答案本质上是错误的和误导性的,我决定 post 一个正确的版本以便人们可以终于知道如何计算这些简单的表达式了。


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

在大多数汇编指令中,逗号两边的操作数大小必须匹配。由于您已经定义了 abcd 变量作为 字节 ,您不能合法地将它们用于 大小的寄存器 AX。这就是 TASM 给你错误信息的原因。

在计算像 (a+c*b)/d-2*c 这样的表达式时,您必须遵守代数规则。

  • 括号内的项目作为一个整体计算
  • 对于没有括号的项目,您需要遵循正常的优先级规则:*/+-
  • 之前

将我们得到的所有内容都用括号括起来:(a+c*b)/d-2*c <=> ((a+(c*b))/d)-(2*c)

  • 当括号组嵌套时,内部组优先于外部组

考虑到abc是0到9,而d是一个从1到9的单数,结果可以是-18到72。因此我们可以使用字节大小的操作来计算整个表达式。没有必要使用带符号的除法 idiv,因为此时的股息总是正数。

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

请注意,我们只使用了一个寄存器 (AX) 来查找结果。你会预料到吗?

下面是我的全部实现。我只省略了显示商和余数的部分,但我提供了一个 link 到 Displaying numbers with DOS 详细解释了如何输出有符号和无符号数。这是您必须了解的基本知识,所以如果您完整阅读它绝不会浪费时间。

; --------------------------------------------
; 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, "? = $"