;******************************************************
;******************************************************
;
; Main Project     : OCRG
; Project Name     : OCRG CWVM - CW Voltmeter
; File Name        : utilities.asm
; Version          : A03
; PIC Used         : PIC12HV615
; Assembler        : MPLAB MPASMWIN from Microchip Technology, Inc.
; Initial Date     : Nov 19, 2009
; Last Update date : Dec 22, 2009
; Firmware Eng.    : WA7ZVY
; HW Design Eng.   : WA7ZVY
; Copyright 2009   : WA7ZVY
;
;******************************************************
;
; Description of this code:
;
; These routines provide various utility functions.
;
;******************************************************
;
;	Function List (in top-2-bottom order):
;		long binary_to_decimal(long binary_word)
;		long divide_by_n(long dividend, long divisor)
;		void delay_100msec(char delay_time)
;
;
;******************************************************
;
;   Modification History:
;   =-=-=-=-=-=-=-=-=-=-=
;   11-19-09 pm - Initial creation.
;
;******************************************************
;******************************************************
;
;******************************************************
;   === Include files ===
;******************************************************
	LIST   P=12HV615			;List directive to define processor

#include	P12HV615.INC		;Processor specific variable definitions
#include	constants.inc		;Coding constants
#include	micro_bits.inc		;Hardware defined bits
#include	variable_def.inc	;Variable definitions


UTILITIES_CODE	CODE				;utilities code space

;*****************************************************
;*****************************************************
;
;	Name:		binary_to_decimal
;	
;	Abstract:	converts a binary word to a four nibble
;					decimal word.
;
;	Callable Routines:
		global	binary_to_decimal
;
;	Called Routines:
;		extern	modulus
;
;	Global Variables Used:
;		arg1_wrd contains binary value to convert to decimal (value is NOT preserved)
;
;	Local Variables Used:
;		b0_tmp1_wrd
;		
;	Return Values:
;		arg2_wrd contains four digits of decimal
;
;*****************************************************
;*****************************************************

binary_to_decimal
	banksel	BANK0
	clrf	b0_tmp1_hi		;clear a temporary result holding register
	clrf	b0_tmp1_lo
	clrf	arg2_hi			;setup the divisor for the modulus routine
	movlw	0x0a
	movwf	arg2_lo
	pagesel	modulus
	call	modulus			;result returned in arg3, modulus returned in arg4
	movf	arg3_hi,W		;move the result back into arg1 for doing the next nibble
	movwf	arg1_hi
	movf	arg3_lo,W
	movwf	arg1_lo
	movf	arg4_lo,W		;get the modulus, only the lowest nibble is non-zero
	movwf	b0_tmp1_lo		;and save it
	call	modulus
	movf	arg3_hi,W		;move the result back into arg1 for doing the next nibble
	movwf	arg1_hi
	movf	arg3_lo,W
	movwf	arg1_lo
	swapf	arg4_lo,W		;move the modulus to the next higher nibble and put it in W
	iorwf	b0_tmp1_lo,F	;and merge it into the holding register
	call	modulus
	movf	arg3_hi,W		;move the result back into arg1 for doing the next nibble
	movwf	arg1_hi
	movf	arg3_lo,W
	movwf	arg1_lo
	movf	arg4_lo,W		;get the modulus, only the lowest nibble is non-zero
	movwf	b0_tmp1_hi		;and save it
	call	modulus
	swapf	arg4_lo,W		;move the modulus to the next higher nibble and put it in W
	iorwf	b0_tmp1_hi,F	;and merge it into the holding register
	movf	b0_tmp1_lo,W	;now put the binary_to_decimal result into the returning register
	movwf	arg2_lo
	movf	b0_tmp1_hi,W
	movwf	arg2_hi
	return



;*****************************************************
;*****************************************************
;
;	Name:		divide_by_n
;				and
;				modulus
;	
;	Abstract:	Divides a 16 bit binary number by a 16 bit binary number
;				using a kludgy repetitive subtraction process. Also,
;				returns the modulus (remainder)
;
;	Callable Routines:
		global	divide_by_n, modulus
;
;	Called Routines:  None
;
;	Global Variables Used:
;		arg1_wrd contains the dividend (value is NOT preserved)
;		arg2_wrd contains the divisor (value is preserved)
;
;	Local Variables Used:  None.
;
;	Return Values:
;		arg3_wrd contains the result
;		arg4_wrd contains the modulus
;		
;*****************************************************
;*****************************************************

divide_by_n
modulus
	movf	arg2_hi,F	;Check to see if the divisor is zero
	btfss	STATUS,Z
	goto	dbn_1
	movf	arg2_lo,F
	btfss	STATUS,Z
	goto	dbn_1
	movlw	0xff		;The divisor was zero, so return with result = 0xffff
	movwf	arg3_hi
	movwf	arg3_lo
	clrf	arg4_hi		;And set the modulus to zero
	clrf	arg4_lo
	return

dbn_1
	clrf	arg3_lo		;Zero out the result
	clrf	arg3_hi
dbn_loop1
	movf	arg1_lo,W	;Save the current "remainder" for return as the modulus
	movwf	arg4_lo
	movf	arg1_hi,W
	movwf	arg4_hi

	movf	arg2_lo,W	;Get the LSB of the divisor
	subwf	arg1_lo,F
	btfsc	STATUS,C
	goto	dbn_no_borrow_1
	movlw	0x01		;A borrow happened, so account for that
	subwf	arg1_hi,F
	btfss	STATUS,C
	return				;A borrow happened from the high byte so we are done,
						;arg3_wrd has the result and arg4_wrd has the modulus

dbn_no_borrow_1
	movf	arg2_hi,W	;Get the MSB of the divisor
	subwf	arg1_hi,F
	btfss	STATUS,C
	return				;A borrow happened from the high byte so we are done,
						;arg3_wrd has the result and arg4_wrd has the modulus

	incfsz	arg3_lo,F	;Bump up the result, and this is a bit tricky to get the MSB right
	decf	arg3_hi,F
	nop
	incf	arg3_hi,F
	goto	dbn_loop1



;*****************************************************
;*****************************************************
;
;	Name:		multiply_by_n
;	
;	Abstract:	multiplies a 16 bit binary number by a 16 binary number
;					by using a kludgy multiple times addition method.
;					Currently it doesn't try to optimize the number of additions
;					needed to accomplish the task, but it could/should.
;
;	Callable Routines:
		global	multiply_by_n
;
;	Called Routines:  None.
;
;	Global Variables Used:
;		arg1_wrd contains multiplicand
;		arg2_wrd contains multiplier
;
;	Local Variables Used:  None.
;
;	Return Values:
;		arg3_wrd contains result
;		
;*****************************************************
;*****************************************************

multiply_by_n
	clrf	arg3_lo		;Zero the result
	clrf	arg3_hi
						;If any number is not a zero, then proceed with the multiply.
						;Otherwise, just return with a zero result
	movf	arg1_lo,F
	btfss	STATUS,Z
	goto	mbn_arg1_not_zero
	movf	arg1_hi,F
	btfss	STATUS,Z
	goto	mbn_arg1_not_zero
	return				;Multiplicand is zero, so just return with result of zero
mbn_arg1_not_zero
	movf	arg2_lo,F
	btfss	STATUS,Z
	goto	mbn_arg2_not_zero
	movf	arg2_hi,F
	btfss	STATUS,Z
	goto	mbn_arg2_not_zero
	return				;Multiplier is zero, so just return with result of zero

mbn_arg2_not_zero
mbn_loop				;Really just a bunch of adds until we get done
	movf	arg1_lo,W
	addwf	arg3_lo,F
	btfsc	STATUS,C
	incf	arg3_hi,F	;A carry happened so bump up the MSB
	movf	arg1_hi,W
	addwf	arg3_hi,F

	movlw	0x01		;Decrement the multiplier by 1 to see if we are done
	subwf	arg2_lo,F
	btfss	STATUS,C
	decf	arg2_hi,F	;A borrow happened so account for it

	nop
	movf	arg2_hi,F	;Now, check to see if we are done
	btfss	STATUS,Z
	goto	mbn_loop
	movf	arg2_lo,F
	btfss	STATUS,Z
	goto	mbn_loop

	return				;Done



;*****************************************************
;*****************************************************
;
;	Name:		delay_100msec
;	
;	Abstract:	this routine sits around and delays for
;					delay_time * 100 milliseconds.
;
;	Callable Routines:
		global	delay_100msec
;
;	Called Routines:  None.
;
;	Global Variables Used:
;		arg1_lo contains the number of 100 millisecond intervals to delay
;
;	Local Variables Used:
;		b0_tmp1_lo contains count up counter byte 0
;		b0_tmp1_hi contains count up counter byte 1
;		b0_tmp2_lo contains count up counter byte 2
;		b0_tmp2_hi contains count up counter byte 3
;		b0_tmp3_lo contains count down counter for the number
;			of 100 millisecond intervals to delay
;
;	Return Values:
;
;		
;*****************************************************
;*****************************************************

delay_100msec
	banksel	BANK0
	movf	arg1_lo,W
	movwf	b0_tmp3_lo
	movlw	0xcf
	movwf	b0_tmp1_lo
	movlw	0xcf
	movwf	b0_tmp1_hi
	movlw	0xcf
	movwf	b0_tmp2_lo
	movlw	0xff
	movwf	b0_tmp2_hi
d100ms_1						;8.00MHz clock/4  =  500ns/cpu cycle
	incfsz	b0_tmp1_lo,F		;1 cycle
	goto	d100ms_1			;2 cycles
	movlw	0xdc
	movwf	b0_tmp1_lo
	incfsz	b0_tmp1_hi,F		;1 cycle
	goto	d100ms_1			;2 cycles
	movlw	0xdc
	movwf	b0_tmp1_hi
	incfsz	b0_tmp2_lo,F		;1 cycle
	goto	d100ms_1			;2 cycles
	movlw	0xdc
	movwf	b0_tmp2_lo
	incfsz	b0_tmp2_hi,F		;1 cycle
	goto	d100ms_1			;2 cycles
	movlw	0xff
	movwf	b0_tmp2_hi
	decfsz	b0_tmp3_lo,F		;1 cycle
	goto	d100ms_1			;2 cycles

	return




;*****************************************************
;*****************************************************
;
;	Name:		adc_sample_delay
;	
;	Abstract:	this routine sits around and delays for the necessary (plus some)
;					analog to digital converter sample time delay.
;
;	Callable Routines:
		global	adc_sample_delay
;
;	Called Routines:  None.
;
;	Global Variables Used:  None.
;
;	Local Variables Used:  None.
;
;	Return Values:  None.
;
;		
;*****************************************************
;*****************************************************

adc_sample_delay
	pagesel	asd_1
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay
	call	asd_1	;A four cycle delay

asd_1:
	return



	end
;End of File (utilities.asm)
