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.
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