3

I'm currently working on an assignment, where I write a subroutine where 2 unsigned numbers get multiplied and yield a result in the DX:AX pair. But i cannot use the instructions mul, imul, div, and idiv. When i run my code, the bottom half (the AX register) is always correct, but the DX register is not. Can anyone point me in the right direction as to what I am doing wrong?

;-----------------------------------------------------------
;
; Program:  MULTIPLY
;
; Function: Multiplies two 16 bit unsigned values ...
;           .... duplicating the MUL instruction
;
; Input:    The two values to be multiplied are passed on the stack
;           The code conforms to the C/C++ calling sequence
;
; Output:   The 32 bit result is returned in the dx:ax pair
;           Registers required by C/C++ need to be saved and restored
;
; Owner:    Andrew F.
;
; Changes:  Date        Reason
;           ------------------
;           07/20/2013  Original version
;
;
;---------------------------------------
         .model    small
         .8086
         public    _multiply

         .data
;---------------------------------------
; Multiply data
;---------------------------------------


         .code
;---------------------------------------
; Multiply code
;---------------------------------------
_multiply:                             
         push      bp                  ; save bp
         mov       bp,sp               ; anchor bp into the stack
         mov       ax,[bp+4]           ; load multiplicand from the stack
         mov       dx,[bp+6]           ; load multiplier   from the stack

    push    bx
    push    cx
    push    di
;---------------------------------------
; copy ax to cx, and dx to bx
;---------------------------------------  
    mov cx,ax       ;using bx and cx as my inputs
    mov bx,dx
;---------------------------------------
; Check for zeros, zero out ax and dx
;---------------------------------------  
start:
    xor   ax,ax         ; check for multiplication by zero
    mov   dx,ax         ; and zero out ax and dx
    mov   di,cx     ; 
    or    di,bx         ; 
    jz    done      ;
    mov   di,ax         ; DI used for reg,reg adc
;---------------------------------------
; loop / multiply algorithm
;---------------------------------------  
loopp:
    shr   cx,1          ; divide by two, bottom bit moved to carry flag
    jnc   skipAddToResult   ;no carry -> just add to result
    add   ax,bx     ;add bx to ax 
    adc   dx,di         ;add the carry to dx

skipAddToResult:
    add   bx,bx         ;double bx current value
    or    cx,cx         ; zero check
    jnz   loopp     ; if cx isnt zero, loop again


;---------------------------------------
; Restore register values, return
;---------------------------------------  
done:
     pop       di           ;restore di
     pop       cx           ;restore cx
     pop       bx           ;restore bx

         pop       bp                  ; restore bp
         ret                           ; return with result in dx:ax
                                       ;
         end                           ; end source code
;---------------------------------------
CadetFayed
  • 67
  • 2
  • 10
  • When doubling the `bx` you need to consider carry into `di` and you need to double `di` too. I suppose `adc di, di` would work. PS: learn to use a debugger so you can step through your code and see where it goes wrong. – Jester Jul 21 '15 at 14:27

1 Answers1

3

You are strangely using di when adding another shifted value of bx. Your algorithm seems to be like this:

  1. Gather values, put them into BX and CX.
  2. While CX>0:
    1. Shift CX rightwards.
    2. If shifted bit had a one, add BX to AX and add DI (there's zero in DI?) to DX with carry.
    3. Add BX to BX.
  3. Return DX:AX.

You are missing a left-shift of DI:BX after each right shift of CX. You are shifting only BX (and I'd use shl bx,1 instead of add bx,bx) while DI remains at zero, therefore when BX exceeds 16 bits, you lose bits that should go to DX. To remedy, use rotate through carry against DI.

loopp:
    shr   cx,1          ; divide by two, bottom bit moved to carry flag
    jnc   skipAddToResult   ;no carry -> just add to result
    add   ax,bx     ;add bx to ax 
    adc   dx,di         ;add the carry to dx

skipAddToResult:
    shl   bx,1         ;double bx current value
    rcl   di,1         ; and put overflow bits to DI
                       ; this together doubles the number in DI:BX
    or    cx,cx         ; zero check
    jnz   loopp     ; if cx isnt zero, loop again
Vesper
  • 18,037
  • 4
  • 33
  • 55
  • Thanks a lot for your help Vesper! That was the exact problem, and a quick and easy fix helped my code pass all of the test cases. – CadetFayed Jul 21 '15 at 14:42
  • It's always better to use `test cx,cx` instead of `or cx,cx` ([for performance as well as human readability](https://stackoverflow.com/questions/33721204/test-whether-a-register-is-zero-with-cmp-reg-0-vs-or-reg-reg/33724806#33724806)). Also, `shl bx, 1` would be more efficient as `add bx,bx`, but `shl` is more clear for humans. – Peter Cordes Oct 29 '17 at 05:41
  • Also, you could use the ZF result of the shift as the loop-exit check if you duplicate the loop body for the final iteration. (i.e. `shr cx,1` / `jnz loopp` to set up for the next iteration, or fall out of the loop with a potentially non-zero CF from the last bit). – Peter Cordes Oct 29 '17 at 05:43