Let x1 = y / z (division)
Let x2 = y // z (division, remainder)

divides one 16-bit value into another 16-bit value to compute a 16-bit result and remainder.

PBASIC allows you to divide any two variables--bits, bytes, words, or a mixture--to get a 16-bit result or remainder.

This routine duplicates that capability in assembly language. It uses a binary interpretation of the long-division method you were probably taught in school. It shifts the divisor left until there is a 1 in the msb. It then attempts to subtract this number from the dividend.

If the result of the subtraction is positive, the routine puts a 1 into the lsb of the quotient, shifts the quotient left, shifts the divisor right, and repeats the operation on what remains of the dividend. If the result of the subtraction is negative, the routine undoes the subtraction by adding the divisor and dividend back together, puts a 0 into the lsb of the quotient, shifts the quotient left, shifts the divisor right, and repeats the operation.

It may require a walk through the routine with paper and pencil (or the simulator) to grasp how this works. When the routine finishes, the 16-bit variable consisting of qH and qL holds the quotient or result of the division. The variable consisting of topH and topL holds the remainder.

Division by 0 is illegal, so the routine starts by checking for this. If division by 0 is attempted, the routine aborts and returns the error code 0FFh (255) in the w register. Otherwise, it returns with 0 in w. Your program should either prevent division by 0 or be prepared to act on the error code.

Demonstrating Divide.

To see Divide in operation, either run the program with the PSIM simulator, or connect the circuit below to an erasable PIC or PIC emulator, such as the Parallax downloader. Assemble and run DIVIDE.SRC. When you apply power to the PIC, the LEDs will light up in the binary pattern of the answer to the math problem 330Ah/00A3h, which is 50h with a remainder of 1Ah. So the quotient (50h) displays the following binary pattern on the LEDs:
 
 

0000 0000 0101 0000

Try dividing various values to see different results. Also, modify the program to display remainders (stored in the variables topH and topL) on the LEDs.


; DIVIDE
; Divide one 16-bit number into another, returning the 16-bit result and 
; the remainder. Upon entry, the top of the division fraction (the dividend)
; must be in topH and topL, and the bottom (divisor) in btmH and btmL. 
; If division by zero is attempted, the routine will return immediately with 
; the error code of 0FFh in w. Otherwise, it will perform the division, leaving 
; the remainder in topH and topL and the result (quotient) in qH and qL. 
; Upon return from a successful division, w contains 0. 

        org     8
topH    ds      1       ; MSB of top of fraction. 
topL    ds      1       ; LSB of top of fraction. 
btmH    ds      1       ; MSB of bottom of fraction.
btmL    ds      1       ; LSB of bottom of fraction.
qH      ds      1       ; MSB of quotient. 
qL      ds      1       ; LSB of quotient. 
count   ds      1       ; temporary counter
index   ds      1       ; temporary counter

; Device data and reset vector
        device  pic16c55,xt_osc,wdt_off,protect_off
        reset   start
        org     0
start   mov     !rb,#0  ; Set to outputs to display
        mov     !rc,#0  ; results on LEDs. 
        mov     topH,#33h       ; Problem: divide
        mov     topL,#0Ah       ; 330Ah by 00A3h. 
        mov     btmH,#0h        
        mov     btmL,#0A3h
        call    divide
        mov     rb,qL   ; Display quotient in binary
        mov     rc,qH   ; on LEDs connected to rb and rc. 
        jmp     $       ; Endless loop

Divide  mov     w,btmH  ; Check for division by 0.
        OR      w,btmL
        snz
        retw    255     ; Error code= 255: return. 
        mov     count,#1        ; Otherwise, initialize variables
        clr     index   ; for the division. 
        clr     qH
        clr     qL

:sh_loop        jb      btmH.7,:d1      ; Shift divisor left
        clc             ; until msb is in 
        rl      btmL    ; btmH.7. 
        rl      btmH    ; count = no. of shifts+1. 
        inc     count   
        jmp     :sh_loop
:d1     clc
        rl      qL      ; Shift quotient left.
        rl      qH
        sub     topL,btmL       ; top = top - btm. 
        jc      :d2     ; If top - btm < 0 then 
        sub     topH,#1 ; top = top + btm
        jc      :d2     ; The idea is to do the 
        inc     topH    ; the subtraction and comparison
        add     topL,btmL       ; (top > btm?) in one step. 
        goto    :reentr ; Then, if btm > top, undo
:d2     sub     topH,btmH       ; the subtraction by adding
        sc              ; top and btm back together
        goto    :less
        setb    qL.0
:reentr clc
        rr      btmH
        rr      btmL
        djnz    count,:d1
        ret             ; Return w/ remainder in top
                        ; and result in q. 
:less   ADD     topL,btmL       ; btm > top, so 
        snc             ; undo the subtraction by
        inc     topH    ; adding them back together. 
        ADD     topH,btmH
        goto    :reentr