输入两个数字并在 NASM 上进行乘除

entering two numbers and multiplying and dividing them on NASM

大家晚上好。我一直在研究这段代码,用户将输入两个数字,然后将它们相乘和相除并显示结果。我正在使用程序来分离进程。我的问题是:不知何故,如果我只做乘法,程序就会结束,一切都会清楚。如果我同时调用这两个过程,程序似乎会在乘法调用之后停留,不会再继续下去。 (比方说 10 和 2。预期的乘法是 20 和商 5,余数为 0,或者 12 和 5,其中乘法是 60,除法是 2 余数 2)也许我的逻辑可能是错误的,或者我的过程调用是错误的,但我想问是否有人可以借给我另一双眼睛,以便我可以了解我的错误可能在哪里。谢谢!!

这是为 NASM 在 DosBox 0.74

上完成的
;            Input: Requests two integers from the user.
;           Output: Outputs the multiplication and division of the input integers.

%include "io.mac"
.STACK 100H
.DATA
     prompt_msg1  db   "Please input the first number: ",0
     prompt_msg2  db   "Please input second number: ",0
     mul_msg      db   "multiplication N1 * N2 is: ",0
     div_msg      db   "Division N1/N2 is: ", 0
     rem_msg      db   "Remainder N1/N2 is: ", 0

.CODE
      .STARTUP
      PutStr  prompt_msg1    ; request first number
      GetInt  CX             ; CX = first number
      nwln

      PutStr  prompt_msg2    ; request second number
      GetInt  DX             ; DX = second number
      nwln


      ;multiplication call
      call multi             ; returns multiplication in BX
      PutStr mul_msg         ; display multiplication message
      PutInt AX              ; display multiplication result


      ;division call
      call divis             ; returns division in BX
      PutStr div_msg         ; display Division message
      PutInt BX              ; display quotient result
      nwln
      PutStr rem_msg         ; display remainder message
      PutInt DX              ; display remainder result

done:
      .EXIT

multi:
      mov      AX, CX       ; imul= first number
      imul     AX, DX       ; imul = imul * second number
      ret                   ; return 

divis:      
      mov      BX, CX
      div      DX           ; idiv = first number / second number
      ret                   ; return 

更新:

我能够 运行 代码以及社区提供的修复和建议,感谢您的所有帮助,这里是更新的 运行ning 代码:

;            Input: Requests two integers from the user.
;           Output: Outputs the multiplication and division of the input integers.

%include "io.mac"
.STACK 100H
.DATA
     prompt_msg1  db   "Please input the first number: ",0
     prompt_msg2  db   "Please input second number: ",0
     mul_msg      db   "multiplication N1 * N2 is: ",0
     div_msg      db   "Division N1/N2 is: ", 0
     rem_msg      db   "Remainder N1/N2 is: ", 0

.CODE
      .STARTUP
      PutStr  prompt_msg1    ; request first number
      GetInt  CX             ; CX = first number
      nwln

      PutStr  prompt_msg2    ; request second number
      GetInt  DX             ; DX = second number
      nwln


      ;multiplication call
      call multi             ; returns multiplication in BX
      PutStr mul_msg         ; display multiplication message
      PutInt AX              ; display multiplication result


      ;division call
      call divis             ; returns division in BX
      PutStr div_msg         ; display Division message
      PutInt AX              ; display quotient result
      nwln
      PutStr rem_msg         ; display remainder message
      PutInt DX              ; display remainder result

done:
      .EXIT

multi:
      mov      AX, CX       ; imul= first number
      imul     AX, DX       ; imul = imul * second number
      ret                   ; return and clear parameters

divis:      
      mov      BX, DX
      mov      AX, CX
      cwd
      idiv     BX           ; idiv = first number / second number
      ret                   ; return and clear parameters

div DX注定要失败。

在Intel的手册中可以看到:

DIV
Signed divide DX:AX by r/m16, with result stored in AX ← Quotient, DX ← Remainder.

再往下:

temp ← DX:AX / SRC; (* Signed division *)
IF (temp > 7FFFH) or (temp < 8000H)
(* If a positive result is greater than 7FFFH
or a negative result is less than 8000H *)
THEN
#DE; (* Divide error *)

也就是说,DIV r16实际上会将DXAX形成的双字除以r16,商必须适合一个字,否则你会出现除法错误。

DX:AX / DX 会给你一个 0x1nnnn 的商,这不适合一个词。

所以你需要使用不同的除数寄存器,你也应该使用CWD将被除数sign-extend变成DX:AX。例如:

mov bx,dx
cwd
div bx

第一个数字在CX中。第二个数字在 DX.

divis 除法子程序有 3 个问题。

  • 当您应该将第一个数字 (CX) 放入累加器 AX.
  • 时,您将第一个数字 (CX) 带到了 BX
  • 由于word大小的除法实际上是将DX:AX除以操作数,所以需要预先初始化DX
  • 现在这带来的问题是 DX 不能同时满足两个目的(作为分频器和红利的扩展)。

您可以通过以下方式解决这些问题:

mov  bx, dx   ;Divider (2nd number) to BX
mov  ax, cx   ;Dividend (1st number) to AX
cwd           ;Extend dividend in DX:AX
idiv bx       ;Signed division -> Quotient AX, Remainder DX

PutInt BX              ; display quotient result

为了显示商,需要这样写

PutInt AX

来自评论

According to my notes, I understood that once the division has taken place, the remainder will be stored on a smaller size register (lets say if AX is being divided by DX, the quoutient will stay in AX but the remainder will be stored for instance on AL or AH) is my reasoning ok or maybe I am catching something wrong?

根据除数的大小,除法有多种形式。对于股息,您没有自由选择。那总是累加器或累加器及其扩展。

  • 字节除法:将word-sized被除数(总是AX)除以任何byte-sized除数

    div bl   ; Divides AX by BL
    
  • 字除法:将dword-sized被除数(总是DX:AX)除以任何word-sized除数

    div cx   ; Divides DX:AX by CX
    
  • 双字除法:将 qword-sized 被除数(总是 EDX:EAX)除以任何 dword-sized 除数

    div esi  ; Divides EDX:EAX by ESI
    

商总是在被除数的下半部分返回。

  • 字节划分 : AX的下半部分是AL

  • 分词 : DX:AX的下半部分是AX

  • 双字除法 : EDX:EAX的下半部分是EAX

余数总是在股息的上半部分返还。

  • 字节划分 : AX的上半部分是AH

  • 分词 : DX:AX的上半部分是DX

  • 双字除法 : EDX:EAX的上半部分是EDX