1

good evening everyone. I've been working on this code where the user will input two numbers and they will be multiplied and divided and show the results. I am using procedures to separate the processes. My question is: somehow if I only do the multiplication the program will end and everything will be clear. If I call both procedures the program seems to stay right after the multiplication call and won't go any further. (lets say 10 and 2. expected multiplication is 20 and quotient 5 with 0 remainer, or 12 and 5 where multiplication is 60 and division is 2 remainder 2) Maybe my logic might be wrong or my procedure calls are wrong but I would like to ask if anyone could lend me another pair of eyes so I can learn where my mistake might be. Thanks!!

this is done for NASM on 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 

UPDATE:

I was able to run the code with the fixes and suggestions given by the community, thanks for all your help and here it is the updated and running code:

;            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
mr_nyo
  • 99
  • 1
  • 9
  • 1
    You still need to correct a few comments! "returns multiplication in BX" and "returns division in BX" --> Change to `AX` – Sep Roland Apr 11 '18 at 10:58

2 Answers2

2

div DX is bound to fail.

As you can see in Intel's manual:

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

And further down:

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 *)

That is, DIV r16 will actually divide the doubleword formed by DX and AX by r16, and the quotient must fit in a word or you'll get a Divide Error.

DX:AX / DX will give you a quotient of 0x1nnnn, which does not fit in a word.

So you need to use a different register for the divisor, and you should also use CWD to sign-extend the dividend into DX:AX. For example:

mov bx,dx
cwd
div bx
Michael
  • 57,169
  • 9
  • 80
  • 125
  • first of all, thanks for the explanation. What about if I sign extend the register even before to convert it to a 32 bit number? would it make a difference? – mr_nyo Apr 10 '18 at 02:32
  • I'm not sure exactly what you mean by that. But in any case, `CWD` will set all bits in `DX` to whatever value the most significant bit in `AX` has. – Michael Apr 10 '18 at 05:49
  • ok I guess I'll follow that suggestion because its clearer than my own understanding and it actually works, thanks!! – mr_nyo Apr 11 '18 at 14:15
  • 1
    The quotes from the manual explain __signed__ division but show the mnemonic for __unsigned__ division. Worse is that the last code snippet uses `CWD` to extend the dividend and then mistakenly uses `DIV` instead of the correct `IDIV`. In its current form e.g. -5/3 would #DE. – Fifoernik Apr 14 '18 at 12:03
1

The first number is in CX. The second number is in DX.

The divis division subroutine has 3 problems.

  • You bring the first number (CX) to BX when you should have put it in the accumulator AX.
  • Since the word sized division actually divides DX:AX by the operand, you need to initialize DX beforehand.
  • Now this brings on the problem that DX can't serve both purposes (being divider and also extension to the dividend) at the same time.

You can solve these issues through:

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

In order to display the quotient, you need to write

PutInt AX

From comments

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?

The division comes in a number of ways depending on the size of the divisor. For the dividend you have no free choice. That's always going to be the accumulator or the accumulator and its extension.

  • Byte division : Dividing the word-sized dividend (always AX) by any byte-sized divisor

    div bl   ; Divides AX by BL
    
  • Word division : Dividing the dword-sized dividend (always DX:AX) by any word-sized divisor

    div cx   ; Divides DX:AX by CX
    
  • Dword division : Dividing the qword-sized dividend (always EDX:EAX) by any dword-sized divisor

    div esi  ; Divides EDX:EAX by ESI
    

The quotient is always returned in the lower half of the dividend.

  • Byte division : Lower half of AX is AL

  • Word division : Lower half of DX:AX is AX

  • Dword division : Lower half of EDX:EAX is EAX

The remainder is always returned in the upper half of the dividend.

  • Byte division : Upper half of AX is AH

  • Word division : Upper half of DX:AX is DX

  • Dword division : Upper half of EDX:EAX is EDX

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • thanks for the explanation, this has cleared several doubts I had. One more question if you don't mind. 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? Thanks for all the help!! – mr_nyo Apr 10 '18 at 02:35
  • _"Your multi multiplication subroutine delivers a signed dword result in DX:AX"_ The two operand form of `imul` does not implicitly modify (E)DX, i.e. `imul ax,dx` will modify `ax` (and `eflags`) but not `dx`. – Michael Apr 10 '18 at 05:57
  • @Michael Thanks for your comment about the 2 operand form of `imul`. I must have had a vision of the past where even Intel wrote in its manual for the 386 the 1 operand form as : `imul al, r/m8` `imul ax, r/m16` `imul eax, r/m32`. – Sep Roland Apr 11 '18 at 10:56