 TITLE "Frequency meter/counter"
 List P=16F913, R=DEC
 INCLUDE "p16F913.inc"
 errorlevel -302         		; no bank warnings

 __CONFIG _DEBUG_OFF & _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF &  _CP_OFF & _MCLRE_OFF & _PWRTE_ON  & _WDT_ON & _HS_OSC

switch0	EQU	LOW	7000			; switching frequency from couting mode 
switch1	EQU HIGH 7000			; to the measuring one
ledPin	EQU	3					; pin for lighting up the LEDs (PORTC,3)
#define	LEDport	TRISC,ledPin	; status of the LED lighting pin
#define	LEDpin	PORTC,ledPin	; port for lighting up the LEDs
#define	LT1719	PORTC,4			; port for shutting down LT1719 
#define	POWER	PORTA,5			; port for shutting down the device
#define MODBUT	PORTE,3			; button for switching the mode

; DATA SEGMENTS
 CBLOCK 0x20
 period1:3
 period:3						; counter for the period
 devidend:3						; storage for 5*10^6
 del:2							; used for delays
 freq:3							; counter for the frequency
 w_save, stat_save, pc_save		; ISR variables
 err							; error byte its bits are as follows
                                ; 0 - too low freq (much below 1Hz)
								; 1 - too high freq (above 15MHz)
								; 2 - not used in this version
								; 3 - freq counting error
								; 4 - need 0.1sec delay after measurement
 unit							; Hz, KHz, or MHz
 idle:2							; idle time counter
 mode							; enables 1sec measurement intervals
 debounce:2						; used for debouncing
 ENDC

 CBLOCK 0x70             	 	
 bcd:4							; storage for 8 decimal digits
 lcd:4							; storage for 4 LCD digits
 length							; number of decimal digits
 temp, cnt, dp					; local temporary variables
 ENDC

; CODE SEGMENT
 ORG 0
	goto 	main
	
 ORG 4							; interrupt service routine
	movwf	w_save				; save Wreg
	swapf	STATUS, w			; save stat reg without
	movwf	stat_save			; changing flags

	btfss	PIR1, CCP1IF		; CCP1 interrupt?
	 goto	tmr1Int				; no

startCapture
	bcf		PIR1, CCP1IF		; clear interrupt flag
	btfsc	cnt, 0				; was it the second interrupt?
	 goto	endCapture
	
	bsf		cnt, 0				; mark the interrupt as the first one
	movf	CCPR1L, w			; get the first pulse timing
	movwf	period1+0
	movf	CCPR1H, w
	movwf	period1+1		
	clrf	period1+2
	clrf	freq+2
	goto	ISR_end

endCapture
	movlw	2					; mark interrupt as the second one
	movwf	cnt

	movf	CCPR1L, w			; get the second pulse timing
	movwf	period+0
	movf	CCPR1H, w
	movwf	period+1
	movf	freq+2, w
	movwf	period+2
	bcf		PIR1, TMR1IF 
	goto	ISR_end

tmr1Int
	btfss	PIR1, TMR1IF		; TMR1 interrupt?
	 goto	ISR_end				; no

	bcf		PIR1, TMR1IF		; clear interrupt flag
	incfsz	freq+2, f			; increment interrupt counter
 	 goto	ISR_end
	
	clrf	period				; report an error (too low freq)
	clrf	period+1
	clrf	period+2
	bsf		err, 0
	bsf		cnt, 1

ISR_end
	swapf	stat_save, w		; get original flags in STATUS
	movwf	STATUS
	swapf	w_save, f			; restore Wreg		
	swapf	w_save, w	
	retfie

encode7seg
	andlw	0x0F				; make sure that bits WREG<7:4> are zeros
	addwf	PCL, f
	retlw	b'01101111'			; code for '0'
	retlw	b'00001001'			; code for '1'
	retlw	b'01110011'			; code for '2'
	retlw	b'01011011'			; code for '3'
	retlw	b'00011101'			; code for '4'
	retlw	b'01011110'			; code for '5'
	retlw	b'01111110'			; code for '6'
	retlw	b'00001011'			; code for '7'
	retlw	b'01111111'			; code for '8'
	retlw	b'01011111'			; code for '9'
	retlw	b'00111101'			; code for 'H'
	retlw	b'01100100'			; code for 'L'
	retlw	b'00110110'			; code for 'F'
	retlw	b'01110110'			; code for 'E'
	retlw	b'00110000'			; code for 'r'
	retlw	b'00010000'			; code for '-'

setDecimalPoint
	andlw	0x0F				; make sure that bits WREG<7:4> are zeros	
	addwf 	PCL, f
	retlw	0					; for length = 0
	retlw	0					; for length = 1
	retlw	0					; for length = 2
	retlw	0					; for length = 3
	retlw	b'0010'				; for length = 4 (dp.1)
	retlw	b'0100'				; for length = 5 (dp.2)
	retlw	b'1000'				; for length = 6 (dp.3)
	retlw	b'0010'				; for length = 7 (dp.1)
	retlw	b'0010'				; for length = 8 (dp.2)
	retlw	0					; the remaining values are meaningless
	retlw	0					; and are added to prevent a jump
	retlw	0					; to a wrong place in the code
	retlw	0
	retlw	0
	retlw	0
	retlw	0

displaySymbol					; symbol is in WREG
	call 	encode7seg			; get 7-digit code
	movwf	temp				; and save it in temp
	movf	cnt, w				; select LCD digit to update
	sublw	4					; depending on the value of cnt
	addwf	PCL, f
	goto	displayDig1			; each of those jumps provides a return
	goto	displayDig2
	goto	displayDig3
	goto	displayDig4

main
	clrf	PORTA						; initialize the I/O potrs
	clrf	PORTB
	clrf	PORTC
	bsf		LT1719						; turn off LT1719					
	bsf		POWER						; turn on the unit
	bsf		MODBUT
	clrwdt
  	bsf    	STATUS, RP0        			; change to BANK 1
	movlw	7
	movwf	CMCON0						; comparators off
	clrf	ANSEL ^ 0x80				; all inputs digital
	clrf	OSCCON ^ 0x80				; oscillator configuration bits

    clrf	TRISA ^ 0x80				; STEP 1
  	clrf    TRISB ^ 0x80           		; setup all pins for output
	clrf	TRISC ^ 0x80				; with the only exception of the
	bsf		TRISC ^ 0x80, 5				; TMR1/CCP1 input pin (and PORTE:3)
	bcf		STATUS, RP0
  	bsf		STATUS, RP1					; change to BANK 2

	bsf		LCDCON ^ 0x100, VLCDEN		; STEP 2: enable LCD bias voltage pins
	bsf		LCDCON ^ 0x100, CS1			; STEP 3: select clock source (lfintosc)	
	bcf		LCDCON ^ 0x100, CS0
	bsf		LCDCON ^ 0x100, LMUX1		; STEP 4: select multiplex mode (1/3)
	bcf		LCDCON ^ 0x100, LMUX0

	bcf		LCDPS, 	WFT					; STEP 5: select waveform type (A)
	bcf		LCDPS,	BIASMD				; STEP 6: select Bias mode (1/3)
	bcf		LCDPS, 	LP3					; STEP 7: select refresh rate 
	bsf		LCDPS, 	LP2					;         prescaler 1:8 for 43Hz
	bsf		LCDPS,	LP1
	bsf		LCDPS,	LP0

	movlw	b'10011111'					; step 8: enable LCD segment lines
	movwf	LCDSE0
	movlw	b'11110011'
	movwf	LCDSE1
	clrf	LCDDATA0 ^ 0x100			; step 9: clear segment data registers
	clrf	LCDDATA1 ^ 0x100
	clrf	LCDDATA3 ^ 0x100
	clrf	LCDDATA4 ^ 0x100
	clrf	LCDDATA6 ^ 0x100
	clrf	LCDDATA7 ^ 0x100

	bsf		LCDCON ^ 0x100,	LCDEN		; step 10: turn on the LCD driver module
	bcf		LCDCON ^ 0x100, SLPEN		; enable LCD in sleep mode

	movlw	b'10100'			; set Watchdog prescaler 1:32768	
	movwf	WDTCON				; for a reset period of approx. 1 sec
	bsf		STATUS, RP0			; switch to BANK 4	
	bsf		OPTION_REG, PSA		; assign prescaler to watchdog
	movlw	0xF8
	andwf	OPTION_REG, f		; set WDT/TMR0 prescaler 1:2
	bsf		OPTION_REG, 0
		
WDTrecover
	bcf		STATUS, RP0			; back to BANK 0
	bcf		STATUS, RP1

	bcf		INTCON, GIE			; disable all interrupts
	clrf	CCP1CON				; turn off the CCP module
	bsf		INTCON, PEIE		; enable periferal interrupts
	clrf	PIR1				; clear all interrupt flags

	clrf	freq				; initialize freq with 0
	clrf	freq+1
	clrf	freq+2
	clrf	mode
	clrf	err
	clrf 	unit
	clrf	idle
	clrf	idle+1
	goto	entry2				; display initial 0

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
loop							; MAIN LOOP
	btfsc	MODBUT				; check the button status
	 goto 	entry1
	call	wait4Release		; wait for the button release
	movlw	1
	xorwf	mode, f				; toggle the mode status

entry1
	clrf	err					; reset all error flags
	clrf	unit				; initialize the unit (Hz, KHz, MHz)
	bcf		LT1719				; turn on LT1719
	call	delay1msec			; let LT1719 wake up
	call	countFreq			; get frequency count
	bsf		LT1719				; shut down LT1719 
	call 	countIdleCycles		; check if the counter is idle (freq=0)
entry2
	call	freq2BCD			; convert freq into BCD representation	
	call 	displayFreq			; send the data to LCD
	call	delay100msec
	btfsc	err, 4				; need a delay?
	 call	delay100msec			
	goto	loop

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
countFreq	 
	bsf		T1CON, TMR1CS		; set external clock for TMR1
	bsf		T1CON, T1SYNC		; no SYNC
	clrf	dp					; decimal point correction
	movlw	1
	movwf	cnt
	call	countPulses			; apply a 1msec interval first

analyseFreq						; select a correct counting interval	
	movlw	LOW 15000			; check if freq count >= 15000
	subwf	freq, w				; in this case report the error
	movlw	HIGH 15000			; because the real freq if above 15MHz
	movwf	temp	
	btfss	STATUS, C
	 incfsz	temp, w		
	  subwf	freq+1, w
	btfss 	STATUS, C
	 goto	below15MHz
	bsf		err, 4				; need a delay to slow down
	bsf		err, 1				; report a too high frequency
	movlw	5					; rounding constant
	bsf		unit, 2				; display MHz
	goto	roundFreq			; frequency has 5 decimal digits

below15MHz 		
	movlw	LOW 10000			; check if freq count >= 10000
	subwf	freq, w				; in this case the real freq is above 10MHz
	movlw	HIGH 10000			
	movwf	temp	
	btfss	STATUS, C
	 incfsz	temp, w		
	  subwf	freq+1, w
	btfss 	STATUS, C
	 goto	below10MHz
	bsf		err, 4				; need a delay to slow down
	movlw	5					; rounding constant
	bsf		unit, 2				; display MHz
	goto	roundFreq			; frequency has 5 decimal digits (above 10MHz)

below10MHz
	movlw	LOW 1000
	subwf	freq, w				; 16-bit subtract
	movlw	HIGH 1000
	movwf	temp	
	btfss	STATUS, C
	 incfsz	temp, w		
	  subwf	freq+1, w
	btfss 	STATUS, C
	 goto	below1MHz			; counted in 1msec frequency has 3 decimal digits	 

	movlw	2					; at this point freq has 4 decimal digits				
	movwf	cnt					; so is between 1Mhz and 10MHz
	call	countPulses			; apply a 10msec interval to make it 5-digits
	movlw	-1					; decimal point correction
	movwf	dp					; e.g. 1.2345
	bsf		err, 4				; need a delay to slow down
	movlw	5					; rounding constant
	bsf		unit, 2				; display MHz
	goto	roundFreq			; we know that freq count has 5 digits

below1MHz						; measuring freqiencies below 1MHz
	movlw	4					; apply a 100msec counting interval 
	movwf	cnt					
	call	countPulses
	movf	freq, w				; if the freq is 0 we are done and do not
	addwf	freq+1, w			; measure the freq below 10Hz
	btfsc	STATUS, Z
	 return
								; at this point freq has up to 5 decimal digits
	movlw	LOW 10000			; check if freq count >= 10000 (5 digits)
	subwf	freq, w				; in this case the rounding constant
	movlw	HIGH 10000			; is 50	and the decimal point correction =1
	movwf	temp				; and the input frequency is above 100KHz
	btfss	STATUS, C
	 incfsz	temp, w		
	  subwf	freq+1, w
	btfss 	STATUS, C
	 goto	below100KHz
	movlw	1					; here the freq count is 5-digit
	movwf	dp					; dp correction (in reality freq is 6-digit)
	movlw	50					; rounding constant
	bsf		unit, 1				; display KHz
	goto	roundFreq

below100KHz
	movlw	LOW 1000			; check if freq count >= 1000 (4 digits)
	subwf	freq, w				; in this case the rounding constant
	movlw	HIGH 1000			; is 0 and the decimal point correction =1
	movwf	temp				; and the input frequency is below 100KHz
	btfss	STATUS, C
	 incfsz	temp, w		
	  subwf	freq+1, w
	btfss 	STATUS, C
	 goto	below10KHz
	movlw	1					; here the freq count is 4-digit
	movwf	dp					; dp correction (in reality freq is 5-digit)
	clrw						; rounding constant
	bsf		unit, 1				; display KHz
	goto	roundFreq

below10KHz						; this is the range where we switch from counting
	btfss	mode, 0				; is 1-sec mode enabled?
	 goto	checkSwitching		; NO

	movlw	8					; otherwise, apply a 1sec counting interval 
	movwf	cnt					
	call	countPulses
	clrf	dp					; no dp corretion is needed
	movlw	1
	movwf	unit				; Hz
	movlw	LOW 1000			; check if freq count >= 1000 
	subwf	freq, w				; to correctly display the unit (Hz or KHz)
	movlw	HIGH 1000		
	movwf	temp			
	btfss	STATUS, C
	 incfsz	temp, w		
	  subwf	freq+1, w
	btfsc 	STATUS, C
	 incf	unit, f				; KHz
	return

checkSwitching
	movlw	switch0				; number of pulses to measuring the period
	subwf	freq, w				; check if the freq is above the switching point
	movlw	switch1				; given by switch1:switch0	
	movwf	temp	
	btfss	STATUS, C
	 incfsz	temp, w
	  subwf	freq+1, w
	btfss	STATUS, C
	 goto	measureFreq			; goto measuring the period

aboveSwitching
	bcf		STATUS, C			; in this case we need to multipy freq by 10
	rlf		freq, f				; to have 4 digits to display
	rlf		freq+1, f			; the input freq is below 10KHz and above the
	movf	freq, w				; switching point 7KHz, so it is only 3 digits
	movwf	bcd					; by counting it for 0.1sec
	movf	freq+1, w
	movwf	bcd+1				; bcd = freq*2 (2 bytes)
	rlf		freq, f				
	rlf		freq+1, f
	rlf		freq, f				
	rlf		freq+1, f			; freq *= 8

	movf	bcd, w	
	addwf	freq, f
	movf	bcd+1, w
	btfsc	STATUS, C
	 addlw	1
	addwf	freq+1, f			; freq *= 10
	clrw						; rounding constant
	bsf		unit, 1				; display KHz

roundFreq
	addwf	freq, f				; only 4 high-order digits will be shown
	clrw
	btfsc	STATUS, C
	 addlw	1					; correct C-bit is not needed
	addwf	freq+1, f
	return

;**************************************************************************
measureFreq						; measure the period of freq below 7KHz
	clrwdt
	clrf	dp
	bcf		T1CON, TMR1CS		; set internal clock 

	movlw	4					; turn on the CCP module and
	movwf	CCP1CON				; enable capture mode on falling edge
	clrf	PIR1				; clear CCP1 and TMR1 interrupt flags
	bsf		STATUS, RP0			; switch to BANK 1
	bsf		PIE1, CCP1IE		; enable CCP1 interrupt
	bsf		PIE1, TMR1IE		; enable TMR1 interrupt
	bcf		STATUS, RP0			; back to BANK 0
	clrf	freq+2

	bsf		T1CON, TMR1ON		; enable counting
	clrf	cnt
	clrf	PIR1				; clear all interrupt flags
	bsf		INTCON, GIE			; enable global interrupts

	btfss	cnt, 1				; wait for 2 input pulses
	 goto	$-1

	bcf		INTCON, GIE			; disable interrupts
	clrf	PIR1				; and clear all interrupt flags
	bcf		T1CON, TMR1ON		; disable counting
	bsf		STATUS, RP0			; switch to BANK 1
	bcf		PIE1, CCP1IE		; disable CCP1 interrupt
	bcf		PIE1, TMR1IE		; disable TMR1 interrupt
	bcf		STATUS, RP0			; back to BANK 0
	clrf	CCP1CON				; turn off the CCP module
	clrf	PIR1				; clear all interrupt flags

	movf	period1+0, w		; 24-bit subtract
	subwf	period+0, f		
	movf	period1+1, w	
	btfss	STATUS, C
	 incfsz	period1+1, w		
	  subwf	period+1, f
	movf	period1+2, w
	btfss	STATUS, C
	 incfsz	period1+2, w
	  subwf	period+2, f			; subtraction is completed

	movlw	b'01001100'			; load the devidend = 5*10^6
	movwf	devidend+2			; to perform the division of
	movlw	b'01001011'			; 5*10^6 over the measured period
	movwf	devidend+1			; to get the frequency value in Hz
	movlw	b'01000000'
	movwf	devidend
	clrf	freq+2				; clear the result
	clrf	freq+1
	clrf	freq

shift
	movlw	1
	movwf	cnt					; divide 5*10^6 / period
	btfsc	period+2, 6			; to get the quotient in freq 
	 goto	divide				; (at most 14 bits)
	bcf		STATUS, C
	rlf		period, f			; rotate the period to put its MSB
	rlf		period+1, f			; below the one in devidend
	rlf		period+2, f
	incf	cnt, f
	goto	shift+2

divide			
	movf	period, w			; 24-bit subtract
	subwf	devidend, f		
	movf	period+1, w	
	btfss	STATUS, C
	 incfsz	period+1, w		
	  subwf	devidend+1, f
	movf	period+2, w
	btfss	STATUS, C
	 incfsz	period+2, w
	  subwf	devidend+2, f		; subtraction is completed
	
	rlf		freq, f				; shift in the result (status bit)
	rlf		freq+1, f			; into the quotient (it is at most 14 bits)

	btfsc 	freq, 0
	 goto	checkCount

	movf	period, w			; restore the devidend
	addwf	devidend, f
	movf	period+1, w
	btfsc	STATUS, C
	 incfsz	period+1, w
	  addwf	devidend+1, f
	movf	period+2, w
	btfsc	STATUS, C
	 addlw	1					; here the correct C bit is not needed
	addwf	devidend+2, f	

checkCount
	decf	cnt, f
	btfsc	STATUS, Z
	 goto	round

	bcf		STATUS, C			; shift the devisor 1 bit to the right
	rrf		period+2, f
	rrf		period+1, f
	rrf		period, f
	goto	divide				

round
	bcf		STATUS, C			; check if the remainder is at least a
	rlf		devidend, f			; half of the period and add 1 to the
	rlf		devidend+1, f		; result in this case

	movf	period, w			; 16-bit subtract
	subwf	devidend, f		
	movf	period+1, w	
	btfss	STATUS, C
	 incfsz	period+1, w		
	  subwf	devidend+1, f

	btfss	STATUS, C			; if C=0 no rounding is needed
	 goto	checkUnit

	incf	freq, f				; increment the quotient (at most 14 bits)
	btfsc	STATUS, Z
	 incf	freq+1, f

checkUnit
	movlw	1
	movwf	unit
	movlw	LOW 1000
	subwf	freq, w				; check if the freq is above 1000
	movlw	HIGH 1000
	movwf	temp	
	btfss	STATUS, C
	 incfsz	temp, w
	  subwf	freq+1, w
	btfsc	STATUS, C
	 incf	unit, f	
	return

;-------------------------------------------------------------------------
countPulses						; bits 0-2 of cnt determine the delay
	clrwdt	
	clrf	TMR1L				; clear the TMR1 counter
	clrf	TMR1H				
	bsf		T1CON, TMR1ON		; enable TMR1 for counting

	btfss	cnt, 0				; select the delay
	 goto	$+3					; cnt=1:  delay=1msec
	call	delay1msec			; cnt=2:  delay=10msec
	goto	getCount			; cnt=3:  delay=100msec
	btfss	cnt, 1
	 goto	$+3
	call	delay10msec			
	goto	getCount
	btfss	cnt, 2
	 goto	$+3
	call	delay100msec
	goto	getCount
	call	delay1sec

getCount
	bcf		T1CON, TMR1ON		; disable TMR1 for counting
	movf	TMR1L, w			; get the TMR1 value in freq+1:freq
	movwf	freq				; and leave freq+2 = 0
	movf	TMR1H, w
	movwf	freq+1				; at this point the freq counter is set
	clrf	freq+2				; clear interrupt counter
	return

delay1msec
	movlw	111					; settings for a 0.001sec delay
	movwf	del
	movlw	3
	movwf	del+1

	movlw	1					; this fragment takes 5000 instruction
	subwf	del, f				; cycles and provides a 1msec delay 
	btfsc	STATUS, Z			; by running PIC at 20MHz
	 decf	del+1, f
	movf	del+1, w
	btfss	STATUS, Z			
	 goto	$-6			
	goto	$+1
	goto	$+1
	nop
	return

delay10msec
	movlw	104					; settings for a 0.01sec delay
	movwf	del
	movlw	25
	movwf	del+1

	movlw	1					; this fragment takes 50000 instruction
	subwf	del, f				; cycles and provides a 10msec delay 
	btfsc	STATUS, Z			; by running PIC at 20MHz
	 decf	del+1, f
	movf	del+1, w
	btfss	STATUS, Z			
	 goto	$-6			
	goto	$+1
	return

delay100msec
	movlw	33					; settings for a 0.1sec delay
	movwf	del
	movlw	245
	movwf	del+1

	movlw	1					; this fragment takes 500000 instruction
	subwf	del, f				; cycles and provides a 100msec delay 
	btfsc	STATUS, Z			; by running PIC at 20MHz
	 decf	del+1, f
	movf	del+1, w
	btfss	STATUS, Z			
	 goto	$-6		
	goto	$+1	
	goto	$+1
	goto	$+1
	nop
	return

delay1sec
	movlw	1					; settings for a 1sec delay
	movwf	del
	movlw	49
	movwf	del+1
	movlw	16
	movwf	temp

	movlw	1					; this fragment takes 5000000 instruction
	subwf	del, f				; cycles and provides a 1sec delay 
	btfss	STATUS, Z			; by running PIC at 20MHz
	 goto	$-3
	decf	del+1, f
	btfsc	STATUS, Z
	 decf	temp, f
	movf	temp, w
	btfss	STATUS, Z			
	 goto	$-9		
	goto	$+1	
	nop
	return

checkByte
	movlw	0xF0
	andwf	temp, w
	btfsc	del, 0
	 goto	$+4	
	btfsc	STATUS, Z
	 goto	nextNybble
	bsf		del, 0
	incf	length, f
	decf	cnt, f
	btfsc	cnt, 7
	 goto	nextNybble
	movwf	INDF
	swapf	INDF, f
	decf	FSR, f	

nextNybble
	movlw	0x0F
	andwf	temp, w
	btfsc	del, 0
	 goto	$+4	
	btfsc	STATUS, Z
	 return
	bsf		del, 0
	incf	length, f
	decf	cnt, f
	btfsc	cnt, 7
	 return
	movwf	INDF
	decf	FSR, f	
	return

freq2BCD
	clrwdt	
	movlw	24					; 24-bits
	movwf	temp				; make cycle counter
	clrf	bcd					; clear result area
	clrf	bcd+1
	clrf	bcd+2
	clrf	bcd+3
	
b2bcd2	
	movlw	bcd					; make pointer
	movwf	FSR
	movlw	4
	movwf	cnt

b2bcd3	
	movlw	0x33		
	addwf	INDF, f				; add to both nybbles
	btfsc	INDF, 3				; test if low result > 7
	 andlw	0xF0				; low result >7 so take the 3 out
	btfsc	INDF, 7				; test if high result > 7
	 andlw	0x0F				; high result > 7 so ok
	subwf	INDF, f				; any results <= 7, subtract back
	incf	FSR, f				; point to next
	decfsz	cnt, f
	 goto	b2bcd3
	
	rlf		freq+0, f			; get another bit
	rlf		freq+1, f
	rlf		freq+2, f

	rlf		bcd+3, f			; put it into bcd
	rlf		bcd+2, f
	rlf		bcd+1, f
	rlf		bcd+0, f
	decfsz	temp, f				; all done?
	 goto	b2bcd2				; no, loop
	return						; yes

processErrors					; display error messages
	btfss	err, 0				; too low freq?
	 goto 	tooHigh
	movlw	11					; code of L
	call 	encode7seg
	movwf	temp
	call	displayDig2
	movlw	12					; code of F
	call 	encode7seg
	movwf	temp
	call	displayDig3
	return

tooHigh
	btfss	err, 1				; too high freq?
	 goto	someError

	movlw	10					; code of H
	call	encode7seg
	movwf	temp
	call	displayDig2
	movlw	12					; code of F
	call 	encode7seg
	movwf	temp
	call	displayDig3
	return

someError						; display Err_		
	movlw	13
	call 	encode7seg
	movwf	temp				; code of E	
	call 	displayDig1
	movlw	14					; code of r
	call 	encode7seg
	movwf	temp
	call	displayDig2
	call	displayDig3
	return

displayFreq
	clrwdt
	bsf		STATUS, RP1			; switch to BANK 2
	clrf	LCDDATA0			; clear all used segment data regs
	clrf	LCDDATA1			; this will clear the display
	clrf	LCDDATA3
	clrf	LCDDATA4
	clrf	LCDDATA6
	clrf	LCDDATA7
	bcf		STATUS, RP1			; back to BANK 0

	movlw	b'1011'				; check for errors and display them
	andwf	err, w
	btfss	STATUS, Z
	 goto	processErrors		; ... and return from there

	clrf	length				; digits counter
	clrf	del
	clrf	lcd+3				; needed to display 0 if freq=0
	movlw	4
	movwf	cnt					; number of digits to copy
	movlw	lcd+3
	movwf	FSR					; set pointer to the lcd array
	movf	bcd+0, w
	movwf	temp
	call	checkByte
	movf	bcd+1, w
	movwf	temp
	call	checkByte
	movf	bcd+2, w
	movwf	temp
	call	checkByte
	movf	bcd+3, w
	movwf	temp
	call	checkByte

	movf	dp, w	
	addwf	length, w			
	call	setDecimalPoint
	movwf	dp
spaces
	movlw	lcd+3				; put indirect pointer to the first
	movwf	FSR					; lcd digit
	movlw	4					; cnt = # of symbols to display
	movwf	cnt	
	subwf	length, w			; >= 4 digits?
	btfsc	STATUS, C
	 goto	displayDigits 

	sublw	0					; w = number of spaces (4 - length) > 0
	movwf	length				; in this case we display spaces 
	decf	cnt, f

	decf	length, f
	btfsc	STATUS, Z
	 goto	displayDigits
	decf	cnt, f

	decf	length, f
	btfsc	STATUS, Z
	 goto	displayDigits
	decf	cnt, f

displayDigits					; take the next cnt digits from lcd
	movf	INDF, w
	decf	FSR, f	
	call	displaySymbol
	decf	cnt, f
	btfss	STATUS, Z
	 goto	displayDigits
	call 	showUnits
	return

displayDig1						; displaying the first digit (the leftmost one)
	bsf		STATUS, RP1			; switch to BANK 2
	btfsc	temp, 3 			; setup for COM0
	 bsf	LCDDATA0, 4
	btfsc	temp, 4
	 bsf	LCDDATA1, 0
	btfsc	temp, 5
	 bsf	LCDDATA1, 1

	btfsc	temp, 0				; setup for COM1
	 bsf	LCDDATA3, 4
	btfsc	temp, 1
	 bsf	LCDDATA4, 0
	btfsc	temp, 2
	 bsf	LCDDATA4, 1

	btfsc	temp, 6				; setup for COM2
	 bsf	LCDDATA7, 0
	btfsc	dp, 1				; decimal point?
	 bsf	LCDDATA6, 4

	bcf		STATUS, RP1			; switch to BANK 0
	return

displayDig2
	bsf		STATUS, RP1			; switch to BANK 2
	btfsc	temp, 3 			; setup for COM0
	 bsf	LCDDATA1, 7
	btfsc	temp, 4
	 bsf	LCDDATA0, 1
	btfsc	temp, 5
	 bsf	LCDDATA0, 0

	btfsc	temp, 0				; setup for COM1
	 bsf	LCDDATA4, 7
	btfsc	temp, 1
	 bsf	LCDDATA3, 1
	btfsc	temp, 2
	 bsf	LCDDATA3, 0

	btfsc	temp, 6				; setup for COM2
	 bsf	LCDDATA6, 1
	btfsc	dp, 2				; decimal point?
	 bsf	LCDDATA7, 7

	bcf		STATUS, RP1			; switch to BANK 0
	return

displayDig3
	bsf		STATUS, RP1			; switch to BANK 2
	btfsc	temp, 3 			; setup for COM0
	 bsf	LCDDATA0, 7
	btfsc	temp, 4
	 bsf	LCDDATA0, 3
	btfsc	temp, 5
	 bsf	LCDDATA0, 2

	btfsc	temp, 0				; setup for COM1
	 bsf	LCDDATA3, 7
	btfsc	temp, 1
	 bsf	LCDDATA3, 3
	btfsc	temp, 2
	 bsf	LCDDATA3, 2

	btfsc	temp, 6				; setup for COM2
	 bsf	LCDDATA6, 3
	btfsc	dp, 3				; decimal point?
	 bsf	LCDDATA6, 7

	bcf		STATUS, RP1			; switch to BANK 0
	return

displayDig4
	bsf		STATUS, RP1			; switch to BANK 2
	btfsc	temp, 3 			; setup for COM0
	 bsf	LCDDATA1, 4
	btfsc	temp, 4
	 bsf	LCDDATA1, 5
	btfsc	temp, 5
	 bsf	LCDDATA1, 6

	btfsc	temp, 0				; setup for COM1
	 bsf	LCDDATA4, 4
	btfsc	temp, 1
	 bsf	LCDDATA4, 5
	btfsc	temp, 2
	 bsf	LCDDATA4, 6

	btfsc	temp, 6				; setup for COM2
	 bsf	LCDDATA7, 5

	bcf		STATUS, RP1			; switch to BANK 0
	return

showUnits						; display Hz, KHz, or MGz
	btfss	unit, 0				; check for Hz
	 goto	$+6
	bsf		STATUS, RP0			; switch to BANK 1
	bcf		LEDport				; configure the pin for output
	bcf		STATUS, RP0			; back to BANK 0
	bcf		LEDpin				; turn on green LED
	return

	btfss	unit, 1				; check for KHz
	 goto	$+5					; and turn on both LEDs
	bsf		STATUS, RP0			; switch to BANK 1
	bsf		LEDport				; configure the pin for input
	bcf		STATUS, RP0			; back to BANK 0
	return

	bsf		STATUS, RP0			; switch to BANK 1
	bcf		LEDport				; configure the pin for output
	bcf		STATUS, RP0			; back to BANK 0
	bsf		LEDpin				; turn the red LED
	return

countIdleCycles
	movf	freq, w
	iorwf	freq+1, w
	iorwf	freq+2, w			; W=0 iff freq=0
	btfss	STATUS, Z
	 goto	resetIdle

	bsf		unit, 0				; set Hz
	incf	idle, f 			; increment idle counter
	btfsc	STATUS, Z
	 incf	idle+1, f
	btfsc	idle+1, 2
	 bcf	POWER
	return

resetIdle
	clrf	idle
	clrf	idle+1
	return

wait4Release
	clrf 	debounce			; wait for the button release
	clrf	debounce+1

	btfss	MODBUT				; button released?
	 goto 	wait4Release		; NO - wait for release
	incf	debounce, f
	btfsc	STATUS, Z
	 incf	debounce+1, f
	btfss	debounce+1, 6		; 30msec
	 goto	$-6
	return

 END
