【问题标题】:Program solving expression in assembly汇编中的程序求解表达式
【发布时间】:2015-08-19 04:04:35
【问题描述】:

我在汇编中的简单程序有问题。我正在使用 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

【问题讨论】:

  • 直接在这里发布您的代码。
  • .. 并标记线条。例如,在粘贴线 76 和 80 中是空的。我以某种方式怀疑错误是否存在......
  • 你的程序应该处理的最大数字是多少?
  • 您已将 ac 声明为字节。所以add ax,amov ax,c 可能不会做你想做的事,即使他们组装了。使您的变量成为单词,或使用扩展 movs。
  • 因为这个问题在 9k 浏览量中取得了巨大的成功并且因为接受的答案本质上是错误和误导,所以我决定发布一个正确的版本,以便人们最终了解如何计算这些简单的表达式。

标签: assembly expression x86-16 tasm dosbox


【解决方案1】:

您的程序几乎很好,只是操作数大小有一些问题,这很正常。所以我拿了你的代码做了一些小改动,这些改动用箭头(

  • 修复了操作数大小问题。我仍然使用 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

接下来是你的“待办事项”清单:

  • 将操作数的大小从 DB 更改为 DW,以允许您的程序处理更大的数字。
  • 通过 IDIV 更改 DIV,因为在 IDIV 已签名时 DIV 未签名。 IDIV 将让您处理负面结果。
  • 将 int=21h ah=0Ah 的数字捕获为字符串(而不是单个字符)。稍后,您将字符串转换为数字。接下来的两个链接将带您了解从字符串转换为数字的过程:

Assembly x86 Date to Number - Breaking a string into smaller sections

32 bit Calculator in 8086 Assembly

最后是测试数据:

(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

【讨论】:

  • 谢谢。现在我会像你说的那样让它更有用:>
  • 这正确(我认为)评估(a+c*b) / (d-2*c),而不是遵循问题所要求的(a+c*b)/d -2*c 公式中的操作顺序/运算符优先级。从表达式的中间部分给出余数很奇怪,但这就是公式的工作方式。 (除非它是从数学符号中错误地转录出来的,例如 $\frac{ (a+c*b }{ d-2*c } 的打印方式。
【解决方案2】:

因为这个问题在 9k 浏览量中取得了巨大的成功并且因为接受的答案本质上是错误的和误导性的,所以我决定发布一个正确的版本,以便人们最终可以找出如何计算这些简单的表达式.


我的程序有问题。操作数类型在第 76 78 80 行不匹配。

add  ax,a   ; line 76
push ax
mov  ax,c   ; line 78    
shl  ax,2
sub  d,ax   ; line 80

在大多数汇编指令中,逗号两边的操作数的大小必须匹配。由于您已将 abcd 变量定义为 bytes,您不能合法地将它们与 word 大小的寄存器 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) 来查找结果。你会预料到吗?

以下是我对这一切的实现。我只省略了显示商和余数的部分,但我提供了一个链接 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, "? = $"

【讨论】:

  • @PeterCordes 我确实将问题从equation 重新标记为expression,但在我的回答中错过了同样的事情。感谢您的编辑。
  • 干杯,很高兴您同意人们应该正确使用数学术语,而且我不是唯一一个被人们“解决”简单公式/表达式并称它们为方程式所困扰的人。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多