MORE PIC TRICKS (Peter Hemsley - July '01) - updated 12July01

The original version of this very useful library routine has been used on many occasions for scaling data. Many amateur programmers who do not fully understand binary numbers often find binary arithmetic a daunting prospect and may resort to cheating, i.e. using multiple additions and subtractions to perform multiplication and division.

The routine divides two 16-bit numbers, the dividend by the divisor, which have been pre-loaded into dividl,h and divisl,h respectively, and returns the result (quotient) in dividl,h with the remainder in remdrl,h. The original dividend is lost, being overwritten by the quotient.

Readers who are familiar with arithmetic routines will not find anything unusual in the listing, in fact it is based on a standard algorithm, optimised for the PIC instruction set. Note the lines that show how to compare two 16-bit numbers using the limited instructions of the PIC.

However, it is worth noting that a bug exists in Microchip's standard division routine. It's the carry (or borrow) out from double precision addition or subtraction that I found is not guaranteed to be correct for all possible input values. The double precision addition and  subtraction codes are standard ones published in various Microchip documents and must have been used unimaginable number of times by pic programmers around the world. The good news is that my division routine is ok because a borrow out from the double precision subtraction is guaranteed not to occur.

Here is the Microchip double precision addition code.

ACCb + ACCa -> ACCb
movf     ACCaLO,w
addwf   ACCbLO
btfsc     STATUS,CARRY
incf       ACCbHI
movf     ACCaHI
addwf   ACCbHI

Assume for the moment that the addition of the low bytes produces a carry, this means that the instruction incf ACCbHI will be executed. Now if the input value of ACCbHI is $FF, it will be incremented to $00. The subsequent addition of the high bytes will not produce a carry out, which is not correct since the incf instruction produced an overflow condition and the carry should be set. I have checked Microchip's published multiply routine and it also suffers the same problem. 

*****************

;Divide 16bit dividend (dividL,H) by 16bit divisor (divisL,H)
;Result (quotient) in dividL,H and remdrL,H

	processor 16f84
	include p16f84.inc
	radix dec

;Ram equates
dividL	equ	0xC		;Dividend and quotient
quotL	equ	dividL
dividH	equ	0xD
quotH	equ	dividH
remdrL	equ	0xE		;Remainder
remdrH	equ	0xF
divisL	equ	0x10		;Divisor
divisH	equ	0x11
bitcnt	equ	0x12		;Bit count


divd	equ	5432		;Test code for MPLAB simulator
divs	equ	22
	org	0
test	movlw	low divd
	movwf	dividL
	movlw	high divd
	movwf	dividH
	movlw	low divs
	movwf	divisL
	movlw	high divs
	movwf	divisH
	call	divide
	return


divide	movfw	divisL
	iorwf	divisH,w
	skpnz
	goto	div0		;Division by zero !

	movlw	16		;16 bit	division
	movwf	bitcnt
	clrf	remdrH		;Clear remainder
	clrf	remdrL

dvloop	clrc			;Set quotient bit to 0
	rlf	dividL		;Shift left dividend and quotient
	rlf	dividH		;Msb into carry
	rlf	remdrL		;and then into partial remainder
	rlf	remdrH

	movfw	divisH		;Compare partial remainder and divisor
	subwf	remdrH,w
	skpz
	goto	testgt		;Not equal so test if remdrH is greater
	movfw	divisL		;High bytes are equal,compare low bytes
	subwf	remdrL,w
testgt	skpc			;Carry set if remdr >= divis
	goto	remrlt

	movfw	divisL		;Subtract divisor from partial remainder
	subwf	remdrL
	skpc			;Test for borrow
	decf	remdrH		;Subtract borrow
	movfw	divisH
	subwf	remdrH
	bsf	dividL,0	;Set quotient bit to 1
				;Quotient replaces dividend which is lost
remrlt	decfsz	bitcnt
	goto	dvloop
	clrz			;Clear error flag (z)
div0	return			;Return with z set if error

	end

