PIC Microcontoller Basic Math Method PIC18F4520

by Peter G. Harrison

(also 18Fmathstest.inc)

;.......;.......;.......;.......;.......;.......;.......;.......;.......;.......;.......;
;****************************************************************************************
;    Filename:	18Fmathstest.asm							*
;    Date:	10/07/2013								*
;    Author:	Peter G Harrison							*
;    Company:	PGH Consultants								*
;****************************************************************************************
;    Files required:	18F4520_g.lkr	Generic linker script for the PIC18F4520 	*
;			P18F4520.inc							*
;                    	18Fmathstest.inc Configuration definition			*
;****************************************************************************************
;Revision History:									*
;****************************************************************************************
;
;****************************************************************************************
 processor	18F4520			;						*
 list      	p=18F4520,  n=65     	; define processor and pagelength		*
 #include 	<P18F4520.inc>        	; processor specific variable definitions	*
 #include	<18Fmathstest.inc>	; application specific config definitions	*
 errorlevel	-302,-306          	; suppress message 302 from list file		*
;****************************************************************************************
;
;****************************************************************************************
;Define data variables									*
;****************************************************************************************
;****************************************************************************************
;ACCESS ram - addressed directly							*
;Groups separated to assist in register visibility during debug				*
;****************************************************************************************
;
;Maths - 4 byte accumulator registers
group0		udata_acs	0x000
acc1_6		res 	1		;
acc1_5		res 	1		;
acc1_4		res 	1		;
acc1_3		res 	1		;
acc1_2		res 	1		;
acc1_1		res 	1		;
;
group1		udata_acs	0x008
acc2_6		res 	1		;
acc2_5		res 	1		;
acc2_4		res 	1		; main results register
acc2_3		res 	1		;
acc2_2		res 	1		;
acc2_1		res 	1		;
;
group2		udata_acs	0x010
acc3_6		res 	1		;
acc3_5		res 	1		;
acc3_4		res 	1		;
acc3_3		res 	1		;
acc3_2		res 	1		;
acc3_1		res 	1		;
;
group3		udata_acs	0x018
acc4_6		res 	1		;
acc4_5		res 	1		;
acc4_4		res 	1		;
acc4_3		res 	1		;
acc4_2		res 	1		;
acc4_1		res 	1		;
;
group4		udata_acs	0x020
mult_tmp	res	1		; maths temporary store
shft_cnt	res	1		; multiplication and division loop count
;
;=======================================================================================*
;End of data area definition								*
;=======================================================================================*
;
;****************************************************************************************
;Baud rate constant calculations							*
;****************************************************************************************
;
;Serial I/O base frequency and system clock
xtal_f		equ	d'7372800'	; OSC freq in Hz (gives exact baud rates)
;
baud_9600	equ	(xtal_f/(4*d'9600'))-1
baud_19200	equ	(xtal_f/(4*d'19200'))-1
baud_38400	equ	(xtal_f/(4*d'38400'))-1
baud_57600	equ	(xtal_f/(4*d'57600'))-1
baud_115200	equ	(xtal_f/(4*d'115200'))-1
;
;Calculate baudrate when BRGH = 1
#define	calc(baudrate)	(xtal_f/(4*d'baudrate'))-1
;
;****************************************************************************************
;
;========================================================================================
;Start of Programme									* 
;========================================================================================
;
RESET_VECTOR	CODE	0x000		; processor reset vector
  		goto    Start		; go to initialisation
;
HPINT_VECTOR	CODE	0x008		; processor interrupt vector 
		goto	hprinth		; go to high priority interrupt handler routines
;
LPINT_VECTOR	CODE	0x018		; processor interrupt vector 
		goto	lprinth		; go to low priority interrupt handler routines
;	
;
;****************************************************************************************
;****************************************************************************************
MAIN	CODE	0x050			; allow room for message strings
;
;========================================================================================
;High priority interrupt routines							*
;========================================================================================
;
hprinth	retfie                    	; return from interrupt and re-enable
;
;========================================================================================
;Low priority interrupt routines							*
;========================================================================================
;
lprinth	retfie                    	; return from interrupt and re-enable
;
;
;========================================================================================
;Main 											*
;========================================================================================
;
;		clr1_4			; acc1_4, acc1_3, acc1_2, acc1_1 = 0
;		clr1_2			; acc1_2, acc1_1 = 0
;		clr2_4			; acc2_4, acc2_3, acc2_2, acc2_1 = 0
;		clr2_2			; acc2_2, acc2_1 = 0
;		clr3_4			; acc1_4, acc1_3, acc1_2, acc1_1 = 0
;		clr3_2			; acc1_2, acc1_1 = 0
;		clr4_4			; acc2_4, acc2_3, acc2_2, acc2_1 = 0
;		clr4_2			; acc2_2, acc2_1 = 0
;
;		sub16			; 16 bit subtract,	acc2 - acc1 -> acc2
;		add16			; 16 bit add,		acc2 + acc1 -> acc2
;
;		inc2			; 32 bit increment	acc2 + 1    -> acc2
;		dec2			; 32 bit decrement 	acc2 - 1    -> acc2
;
;		sub32			; 32 bit subtract,	acc2 - acc1 -> acc2
;		add32			; 32 bit add,		acc2 + acc1 -> acc2
;
;		comp1			; 32 bit 2's complement -acc1 -> acc1
;		comp2			; 32 bit 2's complement -acc2 -> acc2
;
;		mul16			; 16*16 multiply,	acc2 x acc1 -> acc2
;		div2by1			; 32/16 divide,	acc2/acc1 -> acc2 (R hi, Q lo)
;		div32			; 32/16 divide,	acc2/acc1 -> acc2 (R hi, Q lo)
;		div48			; 48/24 divide,	acc2/acc1 -> acc2 (R hi, Q lo)
;
;		sqrt16			; 16 bit square root  acc2_2:acc2_1 -> acc2_1
;		sqrt32			; 32 bit square root  acc2_4:acc2_1 -> acc1_2:1
;
;========================================================================================
;
begin
;========================================================================================
;Maths Routines										*
;========================================================================================
;Development basic maths routines
	call	sub32			; 32 bit subtract,	acc2 - acc1 -> acc2
	call	add32			; 32 bit add,		acc2 + acc1 -> acc2
	call	comp1			; 32 bit 2's complement -acc1 -> acc1
	call	comp2			; 32 bit 2's complement -acc2 -> acc2
	call	mul16			; 16*16 multiply,	acc2 x acc1 -> acc2
	call	div2by1			; 32/16 divide,	acc2/acc1 -> acc2 (R hi, Q lo)
	call	sqrt16			; 16 bit square root  acc2_2/1 -> acc2_1
	call	sqrt32			; 32 bit square root  acc2_4:acc2_1 -> acc1_2/1
	call	sqrt48			; 48 bit square root  acc2_6:acc2_1 -> acc1_3/2/1
;
;Test routines
	call	sqrt16test		; test routine for 16 bit square root extraction 	
	call	sqrt32test		; test routine for 32 bit square root extraction 	
	call	sqrt48test		; test routine for 48 bit square root extraction 	
	goto	begin			;  
;
;========================================================================================
;Clear accumulators acc1 to to  acc4
;========================================================================================
;
clr1_6	clrf	acc1_6			; clear acc1
	clrf	acc1_5			; 
clr1_4	clrf	acc1_4			; 
	clrf	acc1_3			; 
clr1_2	clrf	acc1_2			; 
	clrf	acc1_1			; 
	return
;
clr2_6	clrf	acc2_6			; clear acc2
	clrf	acc2_5			; 
clr2_4	clrf	acc2_4			; 
	clrf	acc2_3			; 
clr2_2	clrf	acc2_2			; 
	clrf	acc2_1			; 
	return
;
clr3_6	clrf	acc3_6			; clear acc3
	clrf	acc3_5			; 
clr3_4	clrf	acc3_4			; 
	clrf	acc3_3			; 
clr3_2	clrf	acc3_2			; 
	clrf	acc3_1			; 
	return
;
clr4_6	clrf	acc4_6			; clear acc4
	clrf	acc4_5			; 
clr4_4	clrf	acc4_4			; 
	clrf	acc4_3			; 
clr4_2	clrf	acc4_2			; 
	clrf	acc4_1			; 
		return
;
;========================================================================================
;Unsigned 16 bit subtract.	acc2_2:acc2_1 - acc1_2:acc1_1 -> acc2_2:acc2_1				*
;========================================================================================
;
sub16	movf	acc1_1,w		; unsigned 16 bit subtract
	subwf	acc2_1			; 
	movf	acc1_2,w		; 
	subwfb	acc2_2			; 
	return
;
;========================================================================================
;Unsigned 32 bit subtract.	acc2_4:acc2_1 - acc1_4:acc1_1 -> acc2_4:acc2_1				*
;========================================================================================
;
sub32	movf	acc1_1,w		; unsigned 32 bit subtract
	subwf	acc2_1			; 
	movf	acc1_2,w		; 
	subwfb	acc2_2			; 
	movf	acc1_3,w		; 
	subwfb	acc2_3			; 
	movf	acc1_4,w		; 
	subwfb	acc2_4			; 
	return
;
;========================================================================================
;Unsigned 48 bit subtract.	acc2_6:acc2_1 - acc1_6:acc1_1 -> acc2_6:acc2_1				*
;========================================================================================
;
sub48	movf	acc1_1,w		; unsigned 32 bit subtract
	subwf	acc2_1			; 
	movf	acc1_2,w		; 
	subwfb	acc2_2			; 
	movf	acc1_3,w		; 
	subwfb	acc2_3			; 
	movf	acc1_4,w		; 
	subwfb	acc2_4			; 
	movf	acc1_5,w		; 
	subwfb	acc2_5			; 
	movf	acc1_6,w		; 
	subwfb	acc2_6			; 
	return
;
;========================================================================================
;Unsigned 16 bit add.	acc2_2:acc2_1 + acc1_2:acc1_1 -> acc2_2:acc2_1				*
;========================================================================================
;
add16	movf	acc1_1,w		; unsigned 16 bit add
	addwf	acc2_1			; 
	movf	acc1_2,w		; 
	addwfc	acc2_2			; 
	return
;
;========================================================================================
;Unsigned 32 bit add.	acc2_4:acc2_1 + acc1_4:acc1_1 -> acc2_4:acc2_1				*
;========================================================================================
;
add32	movf	acc1_1,w		; unsigned 32 bit add
	addwf	acc2_1			; 
	movf	acc1_2,w		; 
	addwfc	acc2_2			; 
	movf	acc1_3,w		; 
	addwfc	acc2_3			; 
	movf	acc1_4,w		; 
	addwfc	acc2_4			; 
	return
;
;========================================================================================
;Unsigned 48 bit add.	acc2_6:acc2_1 + acc1_6:acc1_1 -> acc2_6:acc2_1				*
;========================================================================================
;
add48	movf	acc1_1,w		; unsigned 32 bit add
	addwf	acc2_1			; 
	movf	acc1_2,w		; 
	addwfc	acc2_2			; 
	movf	acc1_3,w		; 
	addwfc	acc2_3			; 
	movf	acc1_4,w		; 
	addwfc	acc2_4			; 
	movf	acc1_5,w		; 
	addwfc	acc2_5			; 
	movf	acc1_6,w		; 
	addwfc	acc2_6			; 
	return
;
;========================================================================================
;2's complement acc1_4:acc1_1								*
;========================================================================================
;
comp1	comf	acc1_1			; complement acc1
	comf	acc1_2			; 
	comf	acc1_3			; 
	comf	acc1_4			; 
	incfsz	acc1_1,f		; 
	return				; 
	incfsz	acc1_2,f		; 
	return				; 
	incfsz	acc1_3,f		; 
	return				; 
	incfsz	acc1_4,f		; 
	return
;
;========================================================================================
;2's complement acc1_4:acc1_1								*
;========================================================================================
;
comp2	comf	acc2_1			; complement acc2 
	comf	acc2_2			; 
	comf	acc2_3			; 
	comf	acc2_4			; 
	incfsz	acc2_1,f		; 
	return				; 
	incfsz	acc2_2,f		; 
	return				; 
	incfsz	acc2_3,f		; 
	return				; 
	incfsz	acc2_4,f		; 
	return
;
;========================================================================================
;Unsigned 32/16 bit division  (approx 552 to 712 cycles)
;Inputs:	Dividend - acc2_4 -> acc2_1 (32 bits)
;		Divisor	 - acc1_2 -> acc1_1 (16 bits)
;Temporary:	Counter	 - count
;Output:	Quotient - acc2_2 -> acc2_1 (32 bits)
;		Remainder- acc1_4 -> acc1_3 (16 bits)
;========================================================================================
;
div32	movlw	d'32'		; 32-bit divide by 16-bit
	movwf	shft_cnt
	clrf	acc1_4		; clear remainder
	clrf	acc1_3
;
dvloop	bcf	STATUS,C 	; set quotient bit to 0, shift left dividend & quotient
	rlcf	acc2_1		; low byte
	rlcf	acc2_2
	rlcf	acc2_3
	rlcf	acc2_4		; msb into carry
	rlcf	acc1_3		; and then into partial remainder
	rlcf	acc1_4
;
	bc	subdiv		; if overflow then remainder > divisor so can subtract
	movf	acc1_2,w	; compare partial remainder and divisor
	subwf	acc1_4,w
	bnz	testgt		; if hi bytes =, check low bytes, else check for >
;
	movf	acc1_1,w	; high bytes are = so compare low bytes
	subwf	acc1_3,w
testgt	bnc	remrlt		; if carry set remainder > divisor, so ok to subtract
;
subdiv	movf	acc1_1,w	; subtract divisor from partial remainder
	subwf	acc1_3,f
	movf	acc1_2,w
	subwfb	acc1_4,f
	bsf	acc2_1,0	; set quotient bit to 1
;				; quotient replaces dividend which is lost
remrlt	decfsz	shft_cnt
	goto	dvloop
	return
;
;========================================================================================
;Unsigned 32 / 16 bits division  ***  NOT SURE ABOUT THIS ROUTINE !!!!  ******
;Dividend in acc2_ (max val H'FFFE0001' =4,294,836,225)
;Divisor in acc1_ (max value H'FFFF' =65,535)
;Ensure being within range or if a division by zero is going to happen.
;
;Return: quotient in acc2_2:1 (16 bits) and remainder in acc2_4:3 (16 bits)
;Result of acc2_4:acc2_1 / acc1_2:acc1_1 => acc2_2:acc2_1 (quotient)
;affected register: shft_cnt
;Typically 262 cycles
;========================================================================================
;
div2by1	movlw	d'16'			;initialize the
	movwf	shft_cnt		;shift counter
;
divloop	bcf	STATUS,C		;we ensure that b0
	rlcf	acc2_1,f		;of acc2_1 is =0
	rlcf	acc2_2,f		;after the shifting
	rlcf	acc2_3,f		;to the left of the
	rlcf	acc2_4,f		;whole dividend
;
;Test carry flag, if = 1, then shift caused overflow so subtract acc1_1 from acc2_3
	bc	subtrct
	movf	acc1_2,w		; compare acc1_2
	subwf	acc2_4,w		; against acc2_4
;
;Test zero flag, if = 0,then acc1_2 != acc2_4. so check if  acc1_2 < acc2_4   
	bnz	acc1les
	movf	acc1_1,w		; acc1_2 = acc2_4; now compare
	subwf	acc2_3,w		; value of acc1_1 against acc2_3
;
;Test carry flag, if = 1, then DSOR_  < DEND_ , so subtract acc1_2:L from acc2_4:2
acc1les	bnc	dec_ctr
;
;subtract divisor from MSBs of dividend
subtrct	movf	acc1_1,w		; acc2_3 = acc2_3 - acc1_1
	subwf	acc2_3,f		; borrow not used (LSB)
	movf	acc1_2,w		; acc2_4 = acc2_4 - acc1_2
	subwfb	acc2_4,f		; borrow used
	bsf	acc2_1,0		; flag "divisor was substracted from dividend"
;
dec_ctr	decfsz	shft_cnt,f
	bra	divloop
	return				; finished
;
;========================================================================================
;Unsigned 48/24 bit division  (approx 967 to 1447 cycles)
;Inputs:	Dividend - acc2_6 -> acc2_1 (48 bits)
;		Divisor	 - acc1_3 -> acc1_1 (24 bits)
;Temporary:	Counter	 - shft_cnt
;Output:	Quotient - acc2_6 -> acc2_1 (48 bits)
;		Remainder- acc1_6 -> acc1_4 (24 bits)
;========================================================================================
;
div48	movlw	d'48'		; 48-bit divide by 24-bit
	movwf	shft_cnt
	clrf	acc1_6		; Clear remainder
	clrf	acc1_5
	clrf	acc1_4
;
divdlp	bcf	STATUS,C 	; Set quotient bit to 0, shift left dividend & quotient
	rlcf	acc2_1		; low byte
	rlcf	acc2_2
	rlcf	acc2_3
	rlcf	acc2_4
	rlcf	acc2_5
	rlcf	acc2_6		; most significant bit into carry
	rlcf	acc1_4		; and then into partial remainder
	rlcf	acc1_5
	rlcf	acc1_6
;
	bc	subtdiv		; If overflow then remainder must be > divisor
;
;After subtraction, if Z set then check less significant bytes. If Z not set and C 
;is set, partial remainder > divisor, so can subtract and set result bit in acc2
;
	movf	acc1_3,w	; Compare high bytes of partial remainder and divisor
	subwf	acc1_6,w
	bnz	test_gt		; bytes not equal, so check if remainder > divisor
;
	movf	acc1_2,w	; middle bytes of partial remainder and divisor
	subwf	acc1_5,w
	bnz	test_gt		; bytes not equal, so check if remainder > divisor
;
	movf	acc1_1,w	; 
	subwf	acc1_4,w	; 
test_gt	bnc	rmdrlt		; carry set if partial remainder > divisor
;
subtdiv	movf	acc1_1,w	; Subtract divisor from partial remainder
	subwf	acc1_4,f
	movf	acc1_2,w
	subwfb	acc1_5,f
	movf	acc1_3,w
	subwfb	acc1_6,f
	bsf	acc2_1,0	; Set quotient bit to 1
;				; Quotient replaces dividend which is lost
rmdrlt	decfsz	shft_cnt
	goto	divdlp
	return
;
;========================================================================================
;Unsigned 16 * 16 bit multiplication 
;acc1_2:acc1_1 * acc2_2:acc2_1 -> acc2_4:acc2_1  
;acc1 value remains unchanged.
;affected register: mult_tmp
;28cycles
;========================================================================================
;
mul16	movf    acc2_2,w
	mulwf   acc1_2			; acc1_2 * acc2_2
	movff   PRODH,acc2_4		; into result
	movff   PRODL,acc2_3
;
	movf    acc2_1,w
	mulwf   acc1_1			; acc1_1 * acc2_1;
	movff   PRODH,mult_tmp
	movff   PRODL,acc2_1
;
	mulwf   acc1_2			; acc1_2 * acc2_1
	movf    PRODL,w
	addwf   mult_tmp,f		; Add cross
	movf    PRODH,w			; products
	addwfc  acc2_3,f
	clrf    WREG
	addwfc  acc2_4,f
;
	movf    acc2_2,w
	mulwf   acc1_1			; acc1_1 * acc2_2
	movf    PRODL,w			;
	addwf   mult_tmp,w		; Add cross
	movwf   acc2_2
	movf    PRODH,w			; products
	addwfc  acc2_3,f
	clrf    WREG
	addwfc  acc2_4,f
	return
;
;========================================================================================
; Unsigned 24 * 24 bit multiplication 
; acc1_3:acc1_1 * acc2_3:acc2_1 -> acc2_6:acc2_1  
; 148 cycles, including initial moves and register clearances
;========================================================================================
mul24	movff	acc2_3,acc3_6	
	movff	acc2_2,acc3_5	
	movff	acc2_1,acc3_4	
	movff	acc1_3,acc3_3	
	movff	acc1_2,acc3_2	
	movff	acc1_1,acc3_1
;
	call	clr2_6
	call	clr1_6
;
	movf	acc3_6,w
	mulwf	acc3_1
	movff	PRODH,acc2_4
	movff	PRODL,acc2_3
;
	movf	acc3_3,w
	mulwf	acc3_4
	movff	PRODH,acc1_4
	movff	PRODL,acc1_3
;
	call	add48
;
	movf	acc3_2,w
	mulwf	acc3_6
	movff	PRODH,acc1_5
	movff	PRODL,acc1_4
;
	mulwf	acc3_4
	movff	PRODH,acc1_3
	movff	PRODL,acc1_2
;
	call	add48
;
	movf	acc3_5,w
	mulwf	acc3_3
	movff	PRODH,acc1_5
	movff	PRODL,acc1_4
;
	mulwf	acc3_1
	movff	PRODH,acc1_3
	movff	PRODL,acc1_2
;
	call	add48
;
	movf	acc3_6,w
	mulwf	acc3_3
	movff	PRODH,acc1_6
	movff	PRODL,acc1_5

;
	movf	acc3_5,w
	mulwf	acc3_2
	movff	PRODH,acc1_4
	movff	PRODL,acc1_3
;
	movf	acc3_4,w
	mulwf	acc3_1
	movff	PRODH,acc1_2
	movff	PRODL,acc1_1
;
	call	add48
;
	return
;
;========================================================================================
;16 bit square root - sqrt16
;Return with carry set if negative
;
;Inputs:	acc2_2 : acc2_1 
;Output:	acc2_1 returns with the 8 bit result
;
;30 Instructions 104 Cycles best case, 109 average, 122 worst case
;========================================================================================
;
sqrt16	movlw   0xc0		;Initialize value for mask
	movwf   acc2_4		;Mask
	movlw   0x40		;Initial value for the root
	clrf	STATUS
;
sq1	subwf   acc2_2,f	;Subtract the root developed so far 
	btfss	STATUS,C	;If it is larger than acc2_2
	goto	sq5		;then go restore the subtraction
;
sq2	iorwf	acc2_4,w	;Set the current bit
sq3	bcf	STATUS,C	;!!!TEMP DEBUG - see move with no extra bits!!!
	rlcf	acc2_1,f	;Shift N left one position
	rlcf	acc2_2,f
	rrcf	acc2_4,f	;Shift the mask right, and pick up msb of N
	btfsc	acc2_4,7	;If msb of N is set, then unconditionally
	goto	sq6		;set the next bit (because subtracting the 
;				;root is guranteed not to cause a borrow)
;
	xorwf	acc2_4,w	;Append "01" to the root developed so far
	btfss	STATUS,C	;If the lsb of mask was shifted into the carry
	goto	sq1		;then we're done. Otherwise, loop again.
;
;We are almost done. In the last iteration, we have 7 bits of the root. When
;"01" is appended to it, we will have a 9-bit number that must be subtracted
;from N.
;
        subwf   acc2_2,f	;
	btfss	STATUS,C	;If the upper 7 bits cause a borrow, then
	goto	exitsq		;the appended "01" will as well: We're done.
;
	btfsc	STATUS,Z	;If the result of the subtraction is zero
	btfsc	acc2_1,7	;AND the msb of acc2_1 is set then the LSB of the
;				;root is zero.
	xorlw	1		;Otherwise, it is one.
exitsq	movwf	acc2_1		;result to acc2_1
	clrf	acc2_2		;
	return			;
;
sq6	btfsc	STATUS,C	;Need to unconditionally set current bit of root.
	goto	exitsq		;However, if through iterating, then leave. Note,
                                ;C is set by shifting mask right & the LSB of root
                                ;was set by iorwf at sq2.
;
	bcf     acc2_4,7	;Clear the MSB of N that got shifted into the mask.
	xorwf   acc2_4,w	;Append "01" to the root developed so far.
	subwf   acc2_2,f	;Subtract
	goto    sq2		;Go unconditionally set the current bit.
;
sq5     addwf   acc2_2,f	;Restore acc2_2 by reversing the subwf with a ADDWF
        goto    sq3		;Don't set the current bit
;
;========================================================================================
; SQRT32 
;; Calculates the square root of a 32 bit number using the binary restoring method.
;
; Input:	acc2_4:acc2_1
; Mask:		acc3_2:acc3_1
; Result:	acc1_2:acc1_1
;
; Takes between 395 and 457 cycles (incl. call and return).
; Uses 64 words ROM, 8 bytes RAM including 4 holding the input.
;========================================================================================
;
sqrt32	movlw	0x40		; Initial value for Result is...
	movwf	acc1_2		; ... 01000000 00000000
	clrf	acc1_1		;
;
	movlw	0xC0		; Initial value for mask is...
	movwf	acc3_2		; ... 11000000 00000000
	clrf	acc3_1		; (second '1' is loop counter).
;
compare	movf	acc1_1,w	; Compare root-so-far with current
	subwf	acc2_3,f	; ... remainder.
	movf	acc1_2,w	;
	subwfb	acc2_4,f	;
	bc	$+4		;
	goto	restore		; result is -ve, so need to restore
;
setcurr	movf	acc3_1,w	; set the current bit in the result.
	iorwf	acc1_1,f	;
	movf	acc3_2,w	;
	iorwf	acc1_2,f	;
;
shftUp	rlcf	acc2_1,f	;
	rlcf	acc2_2,f	;
	rlcf	acc2_3,f	;
	rlcf	acc2_4,f	;
;
	rrcf	acc3_2,f	; Shift mask right for next bit, whilst
	rrcf	acc3_1,f	; ... shifting in MSB from remainder.
	btfsc	acc3_2,7	; If MSB is set, unconditionally set the
	goto	setnext		; ... next bit.
;
	movf	acc3_1,w	; Append '01' to root-so-far
	xorwf	acc1_1,f	;
	movf	acc3_2,w	;
	xorwf	acc1_2,f	;
;
	bc	$+4		; If second '1' in mask is shifted out,
	goto	compare		; ... then that was the last normal iteration.
;
	movf	acc1_1,w	; Last bit Generation.
	subwf	acc2_3,f	; ... The final subtract is 17-bit (15-bit root
	movf	acc1_2,w	; ... plus '01'). Subtract 16-bits: if result
	subwfb	acc2_4,f	; ... generates a carry, last bit is 0.
 	bc	$+4		;
	return
;
	movf	acc2_3,w	; Clear zero flag if remainder non zero
	xorwf	acc2_4,w	;
;
	movlw	1		;
	bnz	$+4		; If result is 0 AND msb of N is '0', result bit
	btfsc	acc2_2,7	; ... is 0, otherwise '1'.
	xorwf	acc1_1,f	;
	return
;
;
setnext	bnc	$+4		; If mask has shifted out, leave. final bit
	return			; ... has been set by iorwf at in1.
	bcf	acc3_2,7	; clear bit shifted in from input.
;
	movf	acc3_1,w	; Append '01' to root-so-far
	xorwf	acc1_1,f	;
	movf	acc3_2,w	;
	xorwf	acc1_2,f	;
;
	movf	acc1_1,w	; This subtraction is guaranteed not to
	subwf	acc2_3,f	; ... cause a borrow, so subtract and
	movf	acc1_2,w	; ... jump back to insert a '1' in the
	subwfb	acc2_4,f	; ... root.
	goto	setcurr		;
;
restore	movf	acc1_1,w	; A subtract above at Sub_Cmp was -ve, so
	addwf	acc2_3,f	; ... restore the remainder by adding.
	movf	acc1_2,w	; The current bit of the root is zero.
	addwfc	acc2_4,f	;
	goto	shftUp		;
;


;========================================================================================
; SQRT48  
; Calculates the square root of a 48 bit number using the binary restoring method.
; Input:	acc2_6:acc2_1
; Mask:		acc3_3:acc3_1
; Result:	acc1_3:acc1_1
;
; Takes between 810 and 890 cycles (incl. call and return).
; Uses 84 words ROM, 10 bytes RAM including 4 holding the input.
;========================================================================================
;
sqrt48	movlw	0x40		; Initial value for Result is...
	movwf	acc1_3		; ... 01000000 00000000 00000000 
	clrf	acc1_2		;
	clrf	acc1_1		;
;
	movlw	0xC0		; Initial value for mask is...
	movwf	acc3_3		; ... 11000000 00000000 00000000
	clrf	acc3_2		; (second '1' is loop counter).
	clrf	acc3_1		; 
;
comp	movf	acc1_1,w	; Compare root-so-far with current
	subwf	acc2_4,f	; ... remainder.
	movf	acc1_2,w	;
	subwfb	acc2_5,f	;
	movf	acc1_3,w	;  
	subwfb	acc2_6,f	;   
	bc	$+4		;
	goto	undosub		; result is -ve, so need to restore
;
setbit	movf	acc3_1,w	; set the current bit in the result.
	iorwf	acc1_1,f	;
	movf	acc3_2,w	;
	iorwf	acc1_2,f	;
	movf	acc3_3,w	; 
	iorwf	acc1_3,f	; 
;
upshft	rlcf	acc2_1,f	;
	rlcf	acc2_2,f	;
	rlcf	acc2_3,f	;
	rlcf	acc2_4,f	;
	rlcf	acc2_5,f	; 
	rlcf	acc2_6,f	; 
;
	rrcf	acc3_3,f	; Shift mask right for next bit, whilst
	rrcf	acc3_2,f	; ... shifting in MSB from remainder.
	rrcf	acc3_1,f	; 
	btfsc	acc3_3,7	; If MSB is set, unconditionally set the
	goto	nextset		; ... next bit.
;
	movf	acc3_1,w	; Append '01' to root-so-far
	xorwf	acc1_1,f	;
	movf	acc3_2,w	;
	xorwf	acc1_2,f	;
	movf	acc3_3,w	; 
	xorwf	acc1_3,f	;
;
	bc	$+4		; If second '1' in mask is shifted out,
	goto	comp		; ... then that was the last normal iteration.
;
	movf	acc1_1,w	; Last bit Generation.
	subwf	acc2_4,f	; ... The final subtract is 15-bit (23-bit root
	movf	acc1_2,w	; ... plus '01'). Subtract 24-bits: if result
	subwfb	acc2_5,f	; ... generates a carry, last bit is 0.
	movf	acc1_3,w	; 
	subwfb	acc2_6,f	; 
 	bc	$+4		;
	return
;
	movf	acc2_4,w	; Clear zero flag if remainder non zero
	xorwf	acc2_5,w	;
	xorwf	acc2_6,w	; 
;
	movlw	1		;
	bnz	$+4		; If result is 0 AND msb of N is '0', result bit
	btfsc	acc2_3,7	; ... is 0, otherwise '1'.
	xorwf	acc1_1,f	; 
	return
;
;
nextset	bnc	$+4		; If mask has shifted out, leave. final bit
	return			; ... has been set by iorwf at in1.
	bcf	acc3_3,7	; clear bit shifted in from input.
;
	movf	acc3_1,w	; Append '01' to root-so-far
	xorwf	acc1_1,f	;
	movf	acc3_2,w	;
	xorwf	acc1_2,f	;
	movf	acc3_3,w	; 
	xorwf	acc1_3,f	;
;
	movf	acc1_1,w	; This subtraction is guaranteed not to
	subwf	acc2_4,f	; ... cause a borrow, so subtract and
	movf	acc1_2,w	; ... jump back to insert a '1' in the
	subwfb	acc2_5,f	; ... root.
	movf	acc1_3,w	; 
	subwfb	acc2_6,f	;
	goto	setbit		;
;
undosub	movf	acc1_1,w	; A subtract above at Sub_Cmp was -ve, so
	addwf	acc2_4,f	; ... restore the remainder by adding.
	movf	acc1_2,w	; The current bit of the root is zero.
	addwfc	acc2_5,f	;
	movf	acc1_3,w	; 
	addwfc	acc2_6,f	; 
	goto	upshft		;
;
;========================================================================================
;End of Main Task									*
;========================================================================================
;
;========================================================================================
;16 bit square root routine test
;========================================================================================
sqrt16test
	clrf	acc4_4
	clrf	acc4_3
	clrf	acc4_2
	clrf	acc4_1
;
loop16	incf	acc4_1
	bnz	cont16
	incf	acc4_2
;
cont16	movff	acc4_1,acc2_1
	movff	acc4_2,acc2_2
	call	sqrt16
	goto	loop16
;
;========================================================================================
;32 bit square root routine test
;========================================================================================
;
sqrt32test
	clrf	acc4_4
	clrf	acc4_3
	clrf	acc4_2
	clrf	acc4_1
;
loop32	incf	acc4_1
	bnz	cont32
	incf	acc4_2
	bnz	cont32
	incf	acc4_3
	bnz	cont32
	incf	acc4_4
;
cont32	movff	acc4_1,acc2_1
	movff	acc4_2,acc2_2
	movff	acc4_3,acc2_3
	movff	acc4_4,acc2_4
	call	sqrt32
	goto	loop32
;
;========================================================================================
;48 bit square root routine test
;========================================================================================
;
sqrt48test
	clrf	acc4_6
	clrf	acc4_4
	clrf	acc4_4
	clrf	acc4_3
	clrf	acc4_2
	clrf	acc4_1
;
loop48	incf	acc4_1
	bnz	cont48
	incf	acc4_2
	bnz	cont48
	incf	acc4_3
	bnz	cont48
	incf	acc4_4
	bnz	cont48
	incf	acc4_5
	bnz	cont48
	incf	acc4_6
;
cont48	movff	acc4_1,acc2_1
	movff	acc4_2,acc2_2
	movff	acc4_3,acc2_3
	movff	acc4_4,acc2_4
	movff	acc4_5,acc2_5
	movff	acc4_6,acc2_6
	call	sqrt48
	goto	loop48
;
;========================================================================================
;Processor initialisation										*
;========================================================================================
;
Start					; Start of programme execution
;
;Set up oscllator control register
	movlw	b'00000000'             ; 
;		  |      |--------------; SCS0
;		  |     |---------------; SCS1
;		  |    |----------------; IOFS
;		  |   |-----------------; OSTS
;		  |  |------------------; IRCF0
;		  | |-------------------; IRCF1
;		  ||--------------------; IRCF2
;		  |---------------------; IDLEN
	movwf	OSCCON			; see page 32 of Data Sheet DS39631E
;
;Initialiise I/O ports
;setup PORTA
	movlw	b'00001111'		; bits 0 - 3 as input, the rest as output
	movwf	TRISA			; see page 105 of Data Sheet DS39631E
; setup PORTB
	movlw	b'11111111'             ; all inputs
	movwf	TRISB			; see page 108 of Data Sheet DS39631E
;setup PORTC 
	movlw	b'11000010'             ; 
;		  |      |--------------; T1OSO
;		  |     |---------------; T1OSI
;		  |    |----------------; RC2
;		  |   |-----------------; RC3
;		  |  |------------------; RC4
;		  | |-------------------; RC5
;		  ||--------------------; TX
;		  |---------------------; RX
	movwf	TRISC			; see page 111 of Data Sheet DS39631E
; setup PORTD
	movlw	b'00000000'		; set all as outputs
	movwf	TRISD			; see page 114 of Data Sheet DS39631E
; setup PORTE
	movlw	b'00000000'		; set all as outputs
	movwf	TRISE			; see page 118 of Data Sheet DS39631E
;
;ADC configuration
	movlw	b'00000000'		;
;		  |      |--------------; ADON
;		  |     |---------------; GO/DONE-
;		  |    |----------------; CHS0
;		  |   |-----------------; CHS1
;		  |  |------------------; CHS2
;		  | |-------------------; CHS3
;		  ||--------------------; -
;		  |---------------------; -
	movwf	ADCON0			; see page 223 of Data Sheet DS39631E
;
	movlw	b'00001011'		;
;		  |      |--------------; PCFQ0
;		  |     |---------------; PCFQ1
;		  |    |----------------; PCFQ2
;		  |   |-----------------; PCFQ3
;		  |  |------------------; VCFG0
;		  | |-------------------; VCFG1
;		  ||--------------------; -
;		  |---------------------; -
	movwf	ADCON1			; see page 224 of Data Sheet DS39631E
;
	movlw	b'10010101'		; right just, 4tad, Fosc/16
;		  |      |--------------; ADCS0
;		  |     |---------------; ADCS1
;		  |    |----------------; ADCS2
;		  |   |-----------------; ACQT0
;		  |  |------------------; ACQT1
;		  | |-------------------; ACQT2
;		  ||--------------------; -
;		  |---------------------; ADFM
	movwf	ADCON2			; see page 225 of Data Sheet DS39631E
;
;Comparator module configuration
	movlw	b'11110101'		; common CVREF, AN0/AN1 inputs, invert both o/p
;		  |      |--------------; CM0
;		  |     |---------------; CM1
;		  |    |----------------; CM2
;		  |   |-----------------; CIS
;		  |  |------------------; C1INV
;		  | |-------------------; C2INV
;		  ||--------------------; C1OUT
;		  |---------------------; C2OUT
	movwf	CMCON			; See page 233 of Data Sheet DS39631E
;
	movlw	b'11001000'		; Ref on, high range, set to 50% (current 65uA)
;		  |      |--------------; CRV0
;		  |     |---------------; CRV1
;		  |    |----------------; CRV2
;		  |   |-----------------; CRV3
;		  |  |------------------; CVRSS (0 = high range 1 = low range)
;		  | |-------------------; CVRR  (0 = Vdd - Vss 1 = Vref+ - Vref-)
;		  ||--------------------; CVROE (0 = reference disconnected from AN2 pin)
;		  |---------------------; CVREN (1 = reference enabled)
	movwf	CVRCON			; See page 239 of Data Sheet DS39631E
;
;Serial I/O configuration
	movlw	h'00'			; 0 for 9600 baud
	movwf	SPBRGH			; set high byte
;
	movlw	calc(9600)		; calculate baudrate for 9600 with brgh=1
	movwf	SPBRG			; and store it
;
	movlw	b'00001000'		;
;		  |      |--------------; ADBEN
;		  |     |---------------; WUE
;		  |    |----------------; -
;		  |   |-----------------; BRG16
;		  |  |------------------; TXCKP
;		  | |-------------------; RXDTP
;		  ||--------------------; RCIDL
;		  |---------------------; ABDOVF
	movwf	BAUDCON			; see page 204 of Data Sheet DS39631E
;
	movlw	b'00100100'		;
;		  |      |--------------; TX9D
;		  |     |---------------; TRMT
;		  |    |----------------; BRGH
;		  |   |-----------------; SENDB
;		  |  |------------------; SYNC
;		  | |-------------------; TXEN
;		  ||--------------------; TX9
;		  |---------------------; CSRC
	movwf	TXSTA			; see page 202 of Data Sheet DS39631E
;
	movlw	b'10010000'		;
;		  |      |--------------; RX9D
;		  |     |---------------; OERR
;		  |    |----------------; FERR
;		  |   |-----------------; ADDEN 
;		  |  |------------------; CREN
;		  | |-------------------; SRE
;		  ||--------------------; RX9
;		  |---------------------; SPEN
	movwf	RCSTA			; see page 203 of Data Sheet DS39631E
;
;Timer configuration
	movlw	b'11000110'		; 
;		  |      |--------------; T0PS0
;		  |     |---------------; T0PS1
;		  |    |----------------; T0PS2
;		  |   |-----------------; PSA 
;		  |  |------------------; T0SE
;		  | |-------------------; T0CS
;		  ||--------------------; T08BIT
;		  |---------------------; TMR0ON
	movwf	T0CON			; see page 123 of Data Sheet DS39631E	
;
	movlw	b'10001011'		;
;		  |      |--------------; TMR1ON
;		  |     |---------------; TMR1CS
;		  |    |----------------; T1SYNC-
;		  |   |-----------------; T1OSCEN
;		  |  |------------------; T1CKPS0
;		  | |-------------------; T1CSPS1
;		  ||--------------------; T1RUN
;		  |---------------------; RD16
	movwf	T1CON			; see page 127 of Data Sheet DS39631E	
;
	movlw	b'00000100'		;
;		  |      |--------------; T2CKPS0
;		  |     |---------------; T2CKPS1
;		  |    |----------------; TMR2ON
;		  |   |-----------------; T2OUTPS0
;		  |  |------------------; T2OUTPS1
;		  | |-------------------; T2OUTPS2
;		  ||--------------------; T2OUTPS3
;		  |---------------------; -
	movwf	T2CON			; see page 133 of Data Sheet DS39631E
;
	movlw	b'00000000'		;
;		  |      |--------------; TMR3ON
;		  |     |---------------; TNR3CS
;		  |    |----------------; T3SYNC-
;		  |   |-----------------; T3CCP1
;		  |  |------------------; T3CCPS0
;		  | |-------------------; T3CCPS1
;		  ||--------------------; T3CCP2
;		  |---------------------; RD16
	movwf	T3CON			; see page 135 of Data Sheet DS39631E	
;
;Interrupt configuration
	movlw	b'01110000'		;
;		  |      |--------------; RBIF
;		  |     |---------------; INT0IF
;		  |    |----------------; TMR0IF
;		  |   |-----------------; RBIE
;		  |  |------------------; INT0IE
;		  | |-------------------; TMR0IE
;		  ||--------------------; PEIE
;		  |---------------------; GIE
	movwf	INTCON			; see page 93 of Data Sheet DS39631E	
;
	movlw	b'11000000'		;
;		  |      |--------------; RBIP
;		  |     |---------------; -
;		  |    |----------------; TMR0IP
;		  |   |-----------------; -
;		  |  |------------------; INTEDG2
;		  | |-------------------; INTEDG1
;		  ||--------------------; INTEDG0
;		  |---------------------; RBPU-
	movwf	INTCON2			; see page 94 of Data Sheet DS39631E	
;
	movlw	b'00000000'		;
;		  |      |--------------; INT1IF
;		  |     |---------------; INT2IF
;		  |    |----------------; -
;		  |   |-----------------; INT1IE
;		  |  |------------------; INT2IE
;		  | |-------------------; -
;		  ||--------------------; INT1IP
;		  |---------------------; INT2IP
	movwf	INTCON3			; see page 95 of Data Sheet DS39631E
;	
	movlw	b'00100001'		;
;		  |      |--------------; TMR1IE
;		  |     |---------------; TMR2IE
;		  |    |----------------; CCP1IE
;		  |   |-----------------; SSPIE
;		  |  |------------------; TXIE
;		  | |-------------------; RCIE
;		  ||--------------------; ADIE
;		  |---------------------; PSPIE
	movwf	PIE1			; see page 98 of Data Sheet DS39631E	
;
	movlw	b'00000000'		;
;		  |      |--------------; CCP2IE
;		  |     |---------------; TMR3IE
;		  |    |----------------; HLVDIE
;		  |   |-----------------; BCLIE
;		  |  |------------------; EEIE
;		  | |-------------------; -
;		  ||--------------------; CMIE
;		  |---------------------; OSCFIE 
	movwf	PIE2			; see page 99 of Data Sheet DS39631E
;
	clrf	PORTC			; clear PORTC	
	clrf	PORTD			; clear PORTD	
	clrf	PORTE			; clear PORTE
;
;Clear all user ram before start of main task.
clr_ram	lfsr	FSR0,0x000		; point to beginning of RAM 
clr_nxt	clrf	POSTINC0		; clear location and inrement pointer
	movlw	h'6'			; at top of RAM ?
	xorwf	FSR0H,w			; check it
	bnz	clr_nxt			; loop till top reached
;
;
;Reset all timers and clear any ourstanding interrupt requests.
	clrf	PIR1			; clear interrupt request reg 1
	clrf	PIR2			; clear interrupt request reg 2
	clrf	TMR0			; reset all timers
	clrf	TMR1L			;
	clrf	TMR1H			;
;
;****************************************************************************************
	END     	                ; directive 'end of program'			*
;****************************************************************************************