 TITLE  "ameter.asm"            ; digital microampermeter
 LIST P=16F684, R=DEC
 INCLUDE "p16f684.inc"
 ERRORLEVEL -311         		; no "operand of HIGH" warnings

 __CONFIG _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

SCL				EQU	5			; I2C clock line setup
SDA				EQU 4			; I2C data line setup
I2C_port		EQU PORTC		; I2C port setup	
I2C_conf 		EQU TRISC		; I2C PIC's direction register
rangeOverlap 	EQU 97			; set range overlap for 5 units 5=100-97*250/256
noiseLevel		EQU 5			; values below noiseLevel*10^(-2)mka are treated as 0

; data segment
 CBLOCK 0x20                   
 	del:2						; delay duration 
 	dataIO						; I2C I/O register
	cnt							; variable for organizing various counters
 	tmp:3						; temporary variable for local processing 
 	curr:2						; storage for current samples
 	sum:4						; storage for the averaging sum value
 	digits:3					; storage for LCD digits
 	idleCnt:2					; counter of idle time 
	sampleCnt					; counter for samples to determine the current range
 	range						; encoding: 1 for 0-9.9mkA, 2 for 10-99.9mkA,  
 ENDC							; 4 for 100-999mkA, 8 for 1-10mA

; code segment
 ORG 0                   
	movlw	5					; preset 1 at RA2 abd RA0 to turn on the device
	movwf	PORTA				; when the PORTA will be set up
	movlw	0x0E
	movwf	PORTC				; open all MOSFET keys
	movlw	0x07
	movwf	CMCON0				; comparators OFF

  	bsf    	STATUS, RP0      	; switch to BANK 1
	movlw	b'00010010'
	movwf	ANSEL ^ 0x80		; configure RA1 and RC0 as analog inputs	

	movlw	b'001010'
	movwf	TRISA ^ 0x80		; enable RA3 and RA1 for input
	movlw	b'010001'	
  	movwf   TRISC ^ 0x80      	; enable RC4 and RC0 for input
 
	bcf		OSCCON^0x80, 4		; set oscillator freq 1MHz
	bcf		OSCCON^0x80, 5
	bsf		OSCCON^0x80, 6
	clrf 	ADCON1 ^ 0x80 		; ADC clock = FOSC/2

  	bcf    	STATUS, RP0        	; back to BANK 0
	movlw   b'11010001' 		; ADC setup: right justify,
	movwf 	ADCON0 				;   external Vref, AN4, On

	call 	initialize			; configure PCF8566
	clrf	idleCnt				; clear the idle time counter
	clrf	idleCnt+1

loop							; main program loop
	call	getCurrent			; get the current value (10 bits)
	call 	bin2BCD				; convert it into BCD
	call	displayData			; send it to display controller

	movlw	250					; delay before the next current sampling
	movwf	del
	call	delay

	btfss	idleCnt+1, 1		; turn off the device after approx. 3 min of inactivity	
	 goto	loop				; otherwise, return to the current sampling/display loop
	bcf		PORTA, 2			; turning OFF signal to MAX619
	goto	$					; wait for powering off


;;;;;;;;;;;;;;;;;;;PROCEDURES
encode7segR						; setup for 7-seg digit codes
	andlw	0x0F				; for the rightmost digit
	addwf	PCL, f				; no decimal point for that digit is on the LCD
  	retlw	b'11111100' 		; digit 0 
	retlw	b'00110000' 		; digit 1
	retlw	b'11010110'			; digit 2
	retlw	b'11110010'			; digit 3	
	retlw	b'00111010'			; digit 4
	retlw	b'11101010'			; digit 5
	retlw	b'11101110'			; digit 6
	retlw	b'01110000'			; digit 7
	retlw	b'11111110' 		; digit 8
	retlw	b'11111010'			; digit 9
	retlw	b'00000010'			; minus sign
	retlw	b'00000000'			; blank
 	retlw	b'00000000'			; blank
	retlw	b'00000000'			; blank
	retlw	b'00000000'			; blank
	retlw	b'00000000'			; blank 

encode7segL						; setup for 7-seg digit codes
	andlw	0x0F				; for the leftmost and middle digits
	addwf	PCL, f				; bit 7 is the decimal point for those digits
  	retlw	b'01111110' 		; digit 0 
	retlw	b'01100000' 		; digit 1
	retlw	b'01011011'			; digit 2
	retlw	b'01111001'			; digit 3
	retlw	b'01100101'			; digit 4	
	retlw	b'00111101'			; digit 5
	retlw	b'00111111'			; digit 6
	retlw	b'01110000'			; digit 7
	retlw	b'01111111' 		; digit 8
	retlw	b'01111101'			; digit 9
	retlw	b'00000001'			; minus sign
	retlw	b'00000000'			; blank
 	retlw	b'00000000'			; blank
	retlw	b'00000000'			; blank
	retlw	b'00000000'			; blank
	retlw	b'00000000'			; blank 

getCurrent
	movlw	64					; take that many current samples
	movwf	cnt					; for averaging
	clrf	sum
	clrf	sum+1				; clear the average current sum
	clrf	sum+2
	clrf	sum+3
	clrf	range
 
samplingLoop					; start curent sampling with 1-10ma range
	clrf	sampleCnt
	movf 	range, w
	addwf	PCL, f
	goto	check10ma
	goto	check1ma
	goto	check100mka
	goto	check10mka	

check10ma						; try this range first
	bsf		PORTA, 0			; open current switch
	movlw	0xF1
	andwf	PORTC, f			; close all other MOSFET switches 
	call	getSample
	addwf	PCL, f
	goto	check1ma			; try a lower range if underflow
	nop							; ignore the overflow and proceed
	
	movf	curr+1, w			; compute tmp = curr*1000
	movwf	tmp+2				; we represent 1000 as 8*(128 - 3)
	movf	curr, w
	movwf	tmp+1
	clrf	tmp					; so far tmp = curr*256

	bcf		STATUS, C
	rlf		curr, f
	rlf		curr+1, f			; curr *= 2
	
	addwf	curr, f
	movf	tmp+2, w
	btfsc	STATUS, C
	 addlw	1
	addwf	curr+1, f			; curr *= 3

	bcf		STATUS, C
	rrf		tmp+2, f
	rrf		tmp+1, f
	rrf		tmp, f				; tmp = curr*128

	movf	curr, w				; 24-bit subtract
	subwf	tmp, f		
	movf	curr+1, w	
	btfss	STATUS, C
	 incfsz	curr+1, w		
	  subwf	tmp+1, f
	btfss	STATUS, C
	 decf	tmp+2, f			; tmp = curr*125

	bcf		STATUS, C			; multiplication on tmp by 8 
	rlf		tmp, f				; by using three left shifts
	rlf		tmp+1, f			
	rlf		tmp+2, f
	rlf		tmp, f
	rlf		tmp+1, f			
	rlf		tmp+2, f
	rlf		tmp, f
	rlf		tmp+1, f			
	rlf		tmp+2, f			; tmp = curr*1000
	clrf	range
	goto	process

check1ma						; now try 0.1 - 1ma range
	bsf		PORTC, 3			; open current MOSFET switch
	bcf		PORTA, 0			; close other MOSFET switches
	bcf		PORTC, 2
	bcf		PORTC, 1
	call	getSample			
	addwf	PCL, f
	goto	check100mka			; try a lower range if underflow
	goto	check10ma			; try a higher range if overflow

	movf	curr, w				; compute temp = curr*100
	movwf	tmp					; we use the schema 100 = 4(16+8+1)
	movf	curr+1, w
	movwf	tmp+1				; tmp = curr
	clrf	tmp+2

	bcf		STATUS, C
	rlf		curr, f
	rlf		curr+1, f
	rlf		curr, f	
	rlf		curr+1, f
	rlf		curr, f
	rlf		curr+1, f			; curr *= 8

	movf	curr, w
	addwf	tmp, f
	movf	curr+1, w
	btfsc	STATUS, C
	 incf	curr+1, w
	addwf	tmp+1, f			; tmp = curr*9 - note that C=0

	rlf		curr, f
	rlf		curr+1, f			; curr *= 16
	movf	curr, w
	addwf	tmp, f
	movf	curr+1, w
	btfsc	STATUS, C
	 incf	curr+1, w
	addwf	tmp+1, f			; tmp = curr*25 - note that C=0

	rlf		tmp, f				; multiplication by 4
	rlf		tmp+1, f
	rlf		tmp, f				; at this point tmp+2 is not affected
	rlf		tmp+1, f
	rlf		tmp+2, f			; tmp = curr*100 (at most 17 bit)
	movlw	1
	movwf	range
	goto	$+1					; a short delay to equate the sampling time
	goto	process

check100mka						; now try 0.01 - 0.1ma range
	bsf		PORTC, 2			; open current switch
	bcf		PORTA, 0
	bcf		PORTC, 3			; close other MOSFET switches
	bcf		PORTC, 1
	call	getSample			
	addwf	PCL, f
	goto	check10mka			; try a lower range if underflow
	goto	check1ma			; try a higher range if overflow	

	movf	curr, w				; compute tmp = curr*10
	movwf	tmp					; we use the schema 10 = 2*(4+1)
	movf	curr+1, w
	movwf	tmp+1
	clrf	tmp+2				; tmp = curr

	bcf		STATUS, C
	rlf		curr, f
	rlf		curr+1, f
	rlf		curr, f
	rlf		curr+1, f			; curr *= 4

	movf	curr, w
	addwf	tmp, f
	movf	curr+1, w
	btfsc	STATUS, C
	 incf	curr+1, w
	addwf	tmp+1, f 			; tmp = curr*5 - note that C=0
		
	rlf		tmp, f				; multiplication by 2
	rlf		tmp+1, f
	movlw	2
	movwf	range

	movlw 	4					; short delay to equate the sampling time
	addlw	-1			
	btfss	STATUS, Z
	 goto 	$-2	
	goto	process

check10mka						; now try 0.001 - 0.01ma range
	bsf		PORTC, 1			; open current switch
	bcf		PORTA, 0
	bcf		PORTC, 3			; close other MOSFET switches
	bcf		PORTC, 2
	call	getSample
	addwf	PCL, f
	goto	$+2					; ignore underflow and proceed
	goto	check100mka			; try a higher range if overflow
	
	movf	curr, w				; tmp = curr
	movwf	tmp
	movf	curr+1, w
	movwf	tmp+1
	clrf	tmp+2
	movlw	3
	movwf	range

	movlw 	6					; short delay to equate the sampling time
	addlw	-1
	nop			
	btfss	STATUS, Z
	 goto 	$-3	

process
	movf	tmp, w				; 32-bit addition
	addwf	sum, f				; compute sum += tmp	
	movf	tmp+1, w
	btfsc	STATUS, C
	 incfsz	tmp+1, w
	  addwf	sum+1, f
	movf	tmp+2, w
	btfsc	STATUS, C
	 incfsz	tmp+2, w
	  addwf	sum+2, f
	btfsc	STATUS, C
	 incf	sum+3, f

	decfsz	sampleCnt, w
	 goto	$+6
	movlw 	8					; a short delay between samples
	sublw	1					; this loop takes 20us*25 = 0.5ms
	sublw	0					; for PIC @ 1 Mhz
	btfss	STATUS, Z
	 goto 	$-3

	decfsz	cnt, f				; get that many current samples
	 goto 	samplingLoop

	bsf		PORTA, 0
	movlw	0x0E
	iorwf	PORTC, f			; open all MOSFET switches

rounding1
	movlw	32					; rounding off the 32-bit value in sum
	addwf	sum, f				; by adding to it 32, which is equivalent
	btfsc	STATUS, C			; to ading 1/2 after the followig division by 64
	 incf	sum+1, f
	btfsc	STATUS, Z	
	 incf	sum+2, f
	btfsc	STATUS, Z
	 incf	sum+3, f

	bcf		STATUS, C			; shift sum 2 bits to the left 
	rlf		sum, f				; and clear its LSB
	rlf		sum+1, f			; this is equivalent to diving sum by 64 
	rlf		sum+2, f			; and then multiplying it by 256
	rlf		sum+3, f
	rlf		sum, f				; we treate a 3-byte value of sum located in
	rlf		sum+1, f			; memory at sum+3:sum+2:sum+1 as the original
	rlf		sum+2, f			; value of sum (before multiplying it by 256)
	rlf		sum+3, f
	clrf	sum					
 
normalize						; compute sum = sum*1000/1024 = sum*250/256
	movf	sum+1, w			; we use the identity 250 = 256 - 6
	movwf	tmp					; since LSB(sum) = 0, we can consider the value in
	movf	sum+2, w			; sum (4 bytes) as a product (sum+3:sum+2:sum+1)*256
	movwf	tmp+1				; so we only need to subtract sum*6 from it
	movf	sum+3, w
	movwf	tmp+2				; copy the original value of sum to tmp (24 bits)

	bcf		STATUS, C
	rlf		tmp, f
	rlf		tmp+1, f
	rlf		tmp+2, f			; tmp = sum*2

	movf	sum+1, w			; 24-bit addition
	addwf	tmp, f				; compute tmp += sum which is sum*3	
	movf	sum+2, w
	btfsc	STATUS, C
	 incfsz	sum+2, w
	  addwf	tmp+1, f
	movf	sum+3, w
	btfsc	STATUS, C
	 incfsz	sum+3, w
	  addwf	tmp+2, f
	bcf		STATUS, C
	rlf		tmp, f
	rlf		tmp+1, f
	rlf		tmp+2, f			; tmp = sum*6

	movf	tmp, w				; 32-bit subtract
	subwf	sum, f				; computing sum*256 - sum*6 = sum*250
	movf	tmp+1, w	
	btfss	STATUS, C
	 incfsz	tmp+1, w		
	  subwf	sum+1, f
	movf	tmp+2, w	
	btfss	STATUS, C
	 incfsz	tmp+2, w		
	  subwf	sum+2, f
	btfss	STATUS, C
	 decf	sum+3, f			; subtraction is completed

rounding2						; computing the appropriate rounding off constant
	movlw	UPPER 100000		; is sum >= 100,000?
	movwf	tmp+2				; in this case the value of sum is 6-digit
	movlw	HIGH 100000			; and the rounding off constant is 500*256
	movwf	tmp+1
	movlw	UPPER 100000
	subwf	sum+1, w			; 24-bit subtract
	movf	tmp+1, w	
	btfss	STATUS, C
	 incfsz	tmp+1, w		
	  subwf	sum+2, w
	movf	tmp+2, w
	btfss	STATUS, C
	 incfsz	tmp+2, w
	  subwf	sum+3, w			; subtraction is completed		
	btfss	STATUS, C
 	 goto	$+7					; check the next range
	clrf	tmp
	movlw	LOW 500
	movwf	tmp+1
	movlw	HIGH 500
	movwf	tmp+2
	goto	rounding3

	movlw	HIGH 10000			; is sum >= 10,000? then sum is 5-digit
	movwf	tmp+1				; and the rounding off const is 50*256
	clrf	tmp+2				; 10,000 is a 2-byte value
	movlw	LOW 10000
	subwf	sum+1, w			; 24-bit subtract
	movf	tmp+1, w	
	btfss	STATUS, C
	 incfsz	tmp+1, w		
	  subwf	sum+2, w
	movf	tmp+2, w
	btfss	STATUS, C
	 incfsz	tmp+2, w
	  subwf	sum+3, w			; subtraction is completed		
	btfss	STATUS, C
 	 goto	$+6					; chek the next range
	clrf	tmp
	movlw	50
	movwf	tmp+1
	clrf	tmp+2
	goto	rounding3

	movlw	HIGH 1000			; is sum >= 1000? then sum is 4-digit
	movwf	tmp+1				; and the rounding off const is 5*256
	movlw 	LOW 1000
	subwf	sum+1, w			; 16-bit subtract
	movf	tmp+1, w	
	btfss	STATUS, C
	 incfsz	tmp+1, w		
	  subwf	sum+2, w			; subtraction is completed		
	btfss	STATUS, C
 	 goto	$+6					; check the next range
	clrf	tmp
	movlw	5
	movwf	tmp+1
	clrf	tmp+2
	goto	rounding3

	movlw	128					; this is the rounding off const for the calue
	movwf	tmp					; of sum < 1000, in which case sum is 3-digit
	clrf	tmp+1				; this const is an equivalent of 0.5 after
	clrf	tmp+2				; dropping the LSB of sum

rounding3
	movf	tmp, w				; 32-bit addition
	addwf	sum, f				; add the rounding constant to sum
	movf	tmp+1, w
	btfsc	STATUS, C
	 incfsz	tmp+1, w		
	  addwf	sum+1, f
	movf	tmp+2, w
	btfsc	STATUS, C
	 incfsz	tmp+2, w		
	  addwf	sum+2, f
	btfsc	STATUS, C
	 incf	sum+3, f			; rounding off is completed

checkMode
	movlw	8					; compute range: 8 for 1-10ma, 4 for 0.1-1ma
	movwf	range				; 2 for 10-100mka, and 1 for 0-10mka
	movlw	LOW 999999			; make sure that the normalized value does not exceed	
	movwf	tmp					; 999999 after all processing above
	movlw	HIGH 999999
	movwf	tmp+1
	movlw	UPPER 999999
	movwf	tmp+2
	movf	tmp, w				; 24-bit subtract
	subwf	sum+1, w		
	movf	tmp+1, w	
	btfss	STATUS, C
	 incfsz	tmp+1, w		
	  subwf	sum+2, w
	movf	tmp+2, w
	btfss	STATUS, C
	 incfsz	tmp+2, w
	  subwf	sum+3, w			; subtraction is completed		
	btfss	STATUS, C
 	 goto	checkRange			; sum < the max value, check its range
	movlw	LOW 999999			; setup current vale to be maximum possible 999999	
	movwf	sum+1				; if it exceeds it
	movlw	HIGH 999999
	movwf	sum+2
	movlw	UPPER 999999
	movwf	sum+3
	bsf		range, 4			; report overflow
	goto	clearIdleCnt		; we are done - exit

checkRange
	movlw	UPPER 100000		; is current range 1ma - 10ma?	
	movwf	tmp+2				; in this case sum >= 100000
	movlw	HIGH 100000
	movwf	tmp+1
	movlw	UPPER 100000
	subwf	sum+1, w			; 24-bit subtract
	movf	tmp+1, w	
	btfss	STATUS, C
	 incfsz	tmp+1, w		
	  subwf	sum+2, w
	movf	tmp+2, w
	btfss	STATUS, C
	 incfsz	tmp+2, w
	  subwf	sum+3, w			; subtraction is completed		
	btfsc	STATUS, C
 	 goto	clearIdleCnt		; the range is determined (=8)

	rrf		range, f			; at this point sum < 100,000
	movlw	HIGH 10000			; is current range 100mka - 1ma?
	movwf	tmp+1
	clrf	tmp+2				; 10,000 is a 2-byte value
	movlw	LOW 10000
	subwf	sum+1, w			; 24-bit subtract
	movf	tmp+1, w	
	btfss	STATUS, C
	 incfsz	tmp+1, w		
	  subwf	sum+2, w
	movf	tmp+2, w
	btfss	STATUS, C
	 incfsz	tmp+2, w
	  subwf	sum+3, w			; subtraction is completed		
	btfsc	STATUS, C
 	 goto	clearIdleCnt		; the range is determined (=4)

	rrf		range, f			; at this point sum < 10,000 (at most 15 bit)
	movlw	HIGH 1000			; is current range 10mka - 100mka?
	movwf	tmp+1
	movlw 	LOW 1000
	subwf	sum+1, w			; 16-bit subtract
	movf	tmp+1, w	
	btfss	STATUS, C
	 incfsz	tmp+1, w		
	  subwf	sum+2, w			; subtraction is completed		
	btfsc	STATUS, C
 	 goto	clearIdleCnt		; the range is determined (=2)	

	rrf 	range, f			; at this point sum < 1000 (at most 10 bit)
	movf	sum+2, f			; the current range is then below 10mka
	btfss	STATUS, Z			; is MSB of sum = 0?
	 goto	clearIdleCnt		; NO - exit
	movlw	noiseLevel			; zero the value sum if it is below noiseLevel to get
	subwf	sum+1, w			; rid of digitation noise
	btfsc	STATUS, C			; current value in range_1 < noiseLevel ?
	 goto 	clearIdleCnt		; NO - exit
	clrf	sum+1				; YES - clear the current value
	clrf	sum+2				; to get rid of digitation noise when inactive
	incf	idleCnt, f			; update the idle time counter (2 bytes)
	btfsc	STATUS, Z
	 incf	idleCnt+1, f	
	return						; the range is determined (=1)

clearIdleCnt					; clear the idle time counter if the value of
	clrf	idleCnt				; current is nonzero
	clrf	idleCnt+1
	return

getSample						; puts the ADC value in curr+1:curr
	movlw 	50
	sublw	1					; this loop takes 20us*50 = 1ms
	sublw	0					; for PIC @ 1 Mhz
	btfss	STATUS, Z
	 goto 	$-3

	bsf 	ADCON0, GO 			; start ADC conversion
	btfsc 	ADCON0, GO 			; is conversion done?
	 goto	$-1 				; no - wait

	movf	ADRESH, w
	andlw	3					; make sure that the higher order 6 bits are 0s
	movwf	curr+1				; 8 higher order bits of ADC
	bsf		STATUS, RP0
	movf 	ADRESL^0x80, w 		; 8 lower order bits of ADC
	bcf		STATUS, RP0
	movwf	curr				; 8 lower order bits of ADC
	incf	sampleCnt, f	
 
	movlw	5			
	subwf	sampleCnt, w		; is it a 5th sample?
	btfsc	STATUS, C
	 retlw 	2					; YES - accept the value

	movlw	0xFF				; check the value for overflow
	xorwf	curr, w				
	btfss	STATUS, Z
	 goto	$+5					; no overflow - procced with checking of underflow
	movlw	0x03
	xorwf	curr+1, w
	btfsc	STATUS, Z
	 retlw	1					; report overflow

	movf	curr+1, f			; check the value for underflow
	btfss	STATUS, Z
	 retlw	2					; the value is good, accept it
	movlw	rangeOverlap
	subwf	curr, w				; otherwise, compare the LSB with overlap
	btfss	STATUS, C			; C=1 if curr > overlap
	 retlw	0					; report underflow
	retlw	2					; accept the value for further processing

bin2BCD							; convert the value in sum+3:sum+2:sum+1 into BCD
	btfss	range, 4			; process overflow (display "---")
	 goto	$+6
	movlw	10					; code for "-"
	movwf	digits	 
	movwf	digits+1	
	movwf	digits+2
	return

	movlw	0xFF				; main bin to BCD conversion starts here
	movwf	digits				; initialize counters with -1
	movwf	digits+1			; we only need 3 higher order decimal digits
	movwf	digits+2			; no matter what range is it

	btfsc	range, 0
	 goto	case4_digit1		; sum < 1,000 (0 - 10mka range)
	btfsc	range, 1
	 goto	case3_digit1 		; 1,000 <= sum < 10,000 (10 - 100 mka range)
	btfsc	range, 2
	 goto	case2_digit1		; 10,000 <= sum < 100,000 (100 - 1000mka range)

case1_digit1					; here 100,000 <= sum <= 999,998 (a 3-byte value)
	movlw 	LOW 100000			; processing the current range 1ma - 10ma
	subwf	sum+1, f			; 24-bit subtract
	movlw	HIGH 100000
	movwf	tmp	
	btfss	STATUS, C
	 incfsz	tmp, w		
	  subwf	sum+2, f
	movlw	UPPER 100000
	movwf	tmp
	btfss	STATUS, C
	 incfsz	tmp, w
	  subwf	sum+3, f			; subtraction is completed
	incf	digits+2, f			; counter of hundreds
	btfsc	STATUS, C
	 goto	case1_digit1
	movlw	LOW 100000			; add 100,000 back
	addwf	sum+1, f		
	movlw	HIGH 100000
	movwf	tmp	
	btfsc	STATUS, C
	 incfsz	tmp, w		
	  addwf	sum+2, f
	movlw	UPPER 100000
	btfsc	STATUS, C
	 addlw	1
	addwf	sum+3, f			; addition is completed

case1_digit2
	movlw 	LOW 10000			; at this point sum < 100,000 (2 or 3-byte value)
	subwf	sum+1, f			; 24-bit subtract
	movlw	HIGH 10000
	movwf	tmp	
	btfss	STATUS, C
	 incfsz	tmp, w		
	  subwf	sum+2, f
	clrw	
	clrf	tmp
	btfss	STATUS, C
	 incfsz	tmp, w
	  subwf	sum+3, f			; subtraction is completed
	incf	digits+1, f			; counter of tens
	btfsc	STATUS, C
	 goto	case1_digit2
	movlw	LOW 10000			; add 10,000 back
	addwf	sum+1, f		
	movlw	HIGH 10000
	movwf	tmp	
	btfsc	STATUS, C
	 incfsz	tmp, w		
	  addwf	sum+2, f
	btfsc	STATUS, C
	 incf	sum+3, f			; addition is completed

case1_digit3
	movlw 	LOW 1000			; at this point sum < 10,000 (a 2-byte value)
	subwf	sum+1, f			; 16-bit subtract
	movlw	HIGH 1000
	movwf	tmp	
	btfss	STATUS, C
	 incfsz	tmp, w		
	  subwf	sum+2, f			; subtraction is completed
	incf	digits, f			; counter of units
	btfsc	STATUS, C
	 goto	case1_digit3
	return

case2_digit1					; here 10,000 <= sum < 100,000 (2 or 3-byte value)
	movlw 	LOW 10000			; processing current range 100mka - 1ma
	subwf	sum+1, f			; 24-bit subtract
	movlw	HIGH 10000
	movwf	tmp	
	btfss	STATUS, C
	 incfsz	tmp, w		
	  subwf	sum+2, f
	clrw	
	clrf	tmp
	btfss	STATUS, C
	 incfsz	tmp, w
	  subwf	sum+3, f			; subtraction is completed
	incf	digits+2, f			; counter of hundreds
	btfsc	STATUS, C
	 goto	case2_digit1
	movlw	LOW 10000			; add 10,000 back
	addwf	sum+1, f		
	movlw	HIGH 10000
	movwf	tmp	
	btfsc	STATUS, C
	 incfsz	tmp, w		
	  addwf	sum+2, f
	btfsc	STATUS, C
	 incf	sum+3, f			; addition is completed

case2_digit2
	movlw 	LOW 1000			; at this point sum < 10,000 (a 2-byte value)
	subwf	sum+1, f			; 16-bit subtract
	movlw	HIGH 1000
	movwf	tmp	
	btfss	STATUS, C
	 incfsz	tmp, w		
	  subwf	sum+2, f			; subtraction is completed
	incf	digits+1, f			; counter of tens
	btfsc	STATUS, C
	 goto	case2_digit2
	movlw	LOW 1000			; add 1,000 back
	addwf	sum+1, f		
	movlw	HIGH 1000	
	btfsc	STATUS, C
	 addlw	1	
	addwf	sum+2, f			; addition is completed

case2_digit3
	movlw 	100					; at this point sum < 1000 (a 2-byte value)
	subwf	sum+1, f			; 16-bit subtract
	clrw	
	clrf	tmp	
	btfss	STATUS, C
	 incfsz	tmp, w		
	  subwf	sum+2, f			; subtraction is completed
	incf	digits, f			; counter of units
	btfsc	STATUS, C
	 goto	case2_digit3
	return

case3_digit1					; here 1,000 <= sum < 10,000 (a 2-byte value)
	movlw 	LOW 1000			; processing current range 10mka - 100mka
	subwf	sum+1, f			; 16-bit subtract
	movlw	HIGH 1000
	movwf	tmp	
	btfss	STATUS, C
	 incfsz	tmp, w		
	  subwf	sum+2, f			; subtraction is completed
	incf	digits+2, f			; counter of hundreds
	btfsc	STATUS, C
	 goto	case3_digit1
	movlw	LOW 1000			; add 1000 back
	addwf	sum+1, f		
	movlw	HIGH 1000
	movwf	tmp	
	btfsc	STATUS, C
	 incf	tmp, w		
	addwf	sum+2, f			; addition is completed

case3_digit2
	movlw 	100					; at this point sum < 1,000 (a 2-byte value)
	subwf	sum+1, f			; 16-bit subtract
	clrw
	clrf	tmp	
	btfss	STATUS, C
	 incfsz	tmp, w		
	  subwf	sum+2, f			; subtraction is completed
	incf	digits+1, f			; counter of tens
	btfsc	STATUS, C
	 goto	case3_digit2
	movlw	100					; add 100 back
	addwf	sum+1, f			
	btfsc	STATUS, C
	 incf	sum+2, f			; addition is completed

case3_digit3
	movlw 	10					; at this point sum < 100 (a 1-byte value)
	subwf	sum+1, f			; 8-bit subtract
	incf	digits, f			; counter of units
	btfsc	STATUS, C
	 goto	case3_digit3
	return

case4_digit1					; here 100 <= sum < 1,000 (a 2-byte value)
	movlw	100					; 16-bit subtract
	subwf	sum+1, f		
	clrw	
	btfss	STATUS, C
	 addlw	1		
	subwf	sum+2, f
	incf	digits+2, f			; counter of hundreds
	btfsc	STATUS, C
	 goto	case4_digit1
	movlw	100					; add 100 back
	addwf	sum+1, f
	btfsc	STATUS, C
	 incf	sum+2, f

case4_digit2	
	movlw	10
	subwf	sum+1, f	
	incf	digits+1, f			; counter of tens
	btfsc	STATUS, C
 	 goto	case4_digit2
	movlw	10
	addwf	sum+1, w			; add 10 back
	movwf	digits				; get ones
	return

displayData						; this method sends digits, digits+1, and digits+2
	call	start_I2C			; to PCF8566 LCD controller for displaying
	movlw	b'01111100'			; slave address
	movwf	dataIO
	call	write_I2C
	movlw	b'11100000'			; select device at I2C bus address 000
	movwf	dataIO
	call	write_I2C
	clrf	dataIO				; clear the address pointer
	call	write_I2C

	movf	digits, w			; send right digit
	call	encode7segR			; the used LCD has no decimal point for this digit
	movwf	dataIO				; hence, nothing to worry
	call	write_I2C

	movf	digits+1, w			; send middle digit
	call	encode7segL
	movwf	dataIO
	movlw	10
	andwf	range, w			; display decimal point if range=8 or 2
	btfss	STATUS, Z
	 bsf	dataIO, 7			; request displaying decimal point
	call	write_I2C

	movf	digits+2, w			; send left digit
	call	encode7segL
	movwf	dataIO	
	movlw	9
	andwf	range, w			; display decimal point if range=8 or 1
	btfss	STATUS, Z
	 bsf	dataIO, 7 
	call	write_I2C
	call	stop_I2C
	return

initialize
	movlw	100
	movwf	del
	call	delay				; required for PCF8566 to settle
	call 	start_I2C			; PCF8566 initialization
	movlw	b'01111100'			; slave address
	movwf	dataIO
	call	write_I2C	
	movlw	b'11100000'			; select device at address 000
	movwf	dataIO
	call	write_I2C
	movlw	b'11001001'			; static drive mode 
	movwf	dataIO
	call	write_I2C
	clrf	dataIO				; reset the address pointer
	call	write_I2C
	clrw
	call	encode7segR			; display 0	
	movwf	dataIO
	call	write_I2C
	call	stop_I2C			; end of initialization
	return

delay							; a delay for del milliseconds
	movlw 	50
	sublw	1					; this loop takes 20us*50 = 1ms
	sublw	0					; for PIC @ 1 Mhz
	btfss	STATUS, Z
	 goto 	$-3

	decfsz	del, f
	 goto 	delay
	return

;;;;;;;;;;;;;;;;;;;;;;;implementation of the I2C interface
;       ___        
; DATA:    |_______
;       ______    
; SCK :       |___
start_I2C						; start setup for I2C interface
	movlw	I2C_conf			; setup FSR to point to TRISC
	movwf	FSR
	bsf		INDF, SDA			; pull high SDA and SCL
	bsf		I2C_port, SCL
	nop							; delay slot
	bcf		I2C_port, SDA
	bcf		INDF, SDA			; set data low
	goto	$+1					; wait for slave to detect it
	bcf		I2C_port, SCL		; set clock low
	return
;              ___        
; DATA: ______|   
;           ______    
; SCK : ___|
stop_I2C						; stop setup for I2C interface
	bcf		I2C_port, SCL		; make sure that clock and data
	bcf		I2C_port, SDA
	bcf		INDF, SDA			; lines are low
	nop
	bsf		I2C_port, SCL		; release clock line
	goto	$+1
	bsf		INDF, SDA			; release data line
	return

write_I2C						; write a byte to the I2C bus
	bcf		INDF, SCL			; make sure that clock is low
	movlw	8
	movwf	cnt					; bits counter

w_loop
	bcf		I2C_port, SDA		; preset 0 on data line
	bcf		INDF, SDA			; start w. data bit low
	rlf		dataIO, f			; get the rightmost data bit
	btfsc	STATUS, C
	 bsf	INDF, SDA			; now the data bit is on the data bus
	bsf		I2C_port, SCL		; raise clock up
	nop
	bcf		I2C_port, SCL		; ... and low
	decfsz	cnt, f
	 goto	w_loop				; repeat this 8 times

	bsf		INDF, SDA			; get ACK from slave		
	goto	$+1					; let slave respond
	bsf		I2C_port, SCL		; clock up
	nop							; slave responds OK (hopefully)
	bcf		I2C_port, SCL		; clock down
	return
 END