;********************************************************************
;																	*
;	XTB-ANR Active Noise Reducer									*
;																	*
;	This program is for the PIC used in the XTB-ANR					*
;																	*
;********************************************************************
;																	*
;	Filename:		XTB-ANR.asm										*
; 	Date:			07/04/16										*
;	File Version:	rev 1.10										*
;																	*
;	Author:			Jeff Volp										*
;	Company:		JV Digital Engineering 							*
;																	*
;	This code is Copyright JV Digital Engineering 07/04/16			*
;																	*
;	Version 1.00:													*
;		Watchdog timeout = 1 sec & reset moved to LED turn on		*
;		Disables Insteon window if TP/INST jumper is in place	 	*
;	Version 1.10:													*
;		Added "Stealth" mode (no attnuation if very low noise)		*
;		Added crude noise level indicaton							*
;		Check noise before ZCD to reject continuous noise			*
;																	*
;********************************************************************
;																	*
;	Files required:	12F683.lkr (with interrupts enabled)			*
;																	*
;********************************************************************

	list      p=12F683			; list directive to define processor
	#include <p12F683.inc>		; processor specific variable definitions

	errorlevel  -302			; suppress message 302 from list file


	__CONFIG   _FCMEN_ON & _IESO_OFF & _CP_ON & _CPD_OFF & _BOD_ON & _MCLRE_OFF & _WDT_ON & _PWRTE_ON & _INTRC_OSC_NOCLKOUT 
;	__CONFIG   _FCMEN_ON & _IESO_OFF & _CP_ON & _CPD_OFF & _BOD_ON & _MCLRE_OFF & _WDT_ON & _PWRTE_ON & _INTRC_OSC_CLKOUT 


; I/O PIN DEFINITIONS

ATTNON	EQU		H'05'			; (GP5) Attenuator ON - 1 to turn on attenuator
LEDOUT	EQU		H'04'			; (GP4) LED output - 1 to turn on LED
ZCDIN	EQU		H'03'			; (GP3) Zero Crossing Detector - 50/60Hz reference input
T0CLKI	EQU		H'02'			; (GP2) Comparator output -> T1 clock input
RCVIN	EQU		H'01'			; (GP1) 120KHz low level X10 sinewave input
TSTOUT	EQU		H'00'			; (GP0) Test point output / Insteon disable (jumper in place)


; VARIABLE DEFINITIONS

			UDATA		0x20

periodH		RES		1			; period measurement MSB
periodL		RES		1			; period measurement LSB
ledtim		RES		1			; countdown timer for LED flashing
twosec		RES		1			; two second timer for LED noise flash
cyccnt		RES		1			; pulse cycle count
bkgcnt		RES		1			; background noise count
prezcd		RES		1			; pulse count before the zero crossing
agcend		RES		1			; end of AGC sample window
thdset		RES		1			; comparator threshold setting
nselvl		RES		1			; background noise level with atteuation off
temp0		RES		1			; Bank 0 general purpose register (for AGC loop)

			UDATA_SHR	0x70

tmp_wrk		RES		1			; variable used for context saving
tmp_sts		RES		1			; variable used for context saving

stsflg		RES		1			; general status flags

zcross		EQU		H'07'		; "1" indicates zero crossing flag
cycpol		EQU		H'06'		; "1" indicates positive half cycle
inston		EQU		H'05'		; "1" indicates an Insteon signal was detected
rcverr		EQU		H'04'		; "1" indicates an error was detected in the data pattern
inpbit		EQU		H'03'		; "1" indicates last received bit is a logic "1" (as received)
dimcmd		EQU		H'02'		; "1" indicates receiving a bright/dim command
bkgnse		EQU		H'01'		; "1" indicates background noise sensed, turn attenuator on

;**********************************************************************

RESET_VECTOR	CODE	0x000	; processor reset vector

		goto    start			; go to beginning of program

INT_VECTOR	CODE	0x004		; interrupt vector location

		goto	intrpt			; process interrupt routine


;		INTERRUPT PROCESSING

;		Identify the source of the interrupt

intrpt	movwf   tmp_wrk			; save current W register contents
		swapf	STATUS,W		; swap status register into W register
		movwf	tmp_sts			; save contents of STATUS register
		bcf		STATUS,RP0		; select Bank 0
		btfss	INTCON,GPIF		; skip if it was a zero crossing interrupt
		goto	unknwn			; unidentified interrupt if not

;		Reset Timer 1 at each zero crossing

		bcf		T1CON,TMR1ON	; stop Timer 1
		clrf	TMR1H			; clear high byte
		clrf	TMR1L			; clear low byte
		bsf		T1CON,TMR1ON	; restart Timer 1
		bsf		stsflg,zcross	; indicate a 60Hz zero cross occured
		bsf		GPIO,TSTOUT		; set test point output high to mark zero crossing
		nop						; make it wider to distinguish from others
		nop
		nop
		bcf		GPIO,TSTOUT		; set test point output low

;		Clear interrupt & turn off LED after delay if being flashed

		bcf		stsflg,cycpol	; clear cycle polarity for negative half cycle
		btfss	GPIO,ZCDIN		; clear mismatch & skip if rising edge of AC input
		goto	intclr			; nothing further for negative half cycle

		bsf		stsflg,cycpol	; set cycle polarity flag for positive half cycle
		decfsz	twosec			; decrement 2-second timer & skip if zero
		goto	intclr			; just clear interrupt flag
		movlw	.120			; 120 cycles
		movwf	twosec			; reset 2-second timer
		movfw	thdset			; get AGC threshold (noise level)
		andlw	b'00001111'		; mask all but AGC threshold bits
		movwf	nselvl			; save noise level

intclr	bcf		INTCON,GPIF		; clear interrupt flag
		movf	ledtim,F		; check if LED flash time is already zero
		btfsc	STATUS,Z		; skip if not zero
		goto	ledoff			; make sure LED is off
		decfsz	ledtim,F		; count off 8.3mS & turn off LED if done
		goto	intrtn			; return from interrupt
ledoff	bcf		GPIO,LEDOUT		; set LED output low
		goto	intrtn			; return from interrupt

;		Unidentified interrupt

unknwn	call	 initiz			; re-initialize if timer interrupt

;		Return from interrupt

intrtn	swapf   tmp_sts,W		; retrieve copy of STATUS register
		movwf	STATUS			; without changing Z status bit
		swapf   tmp_wrk,F		; restore pre-isr W register
		swapf   tmp_wrk,W		; without effecting status bits
		retfie					; return with interrupts enabled


;		Diagnostic Pulse Chain (W pulses)

wpulse	movwf	temp0			; save to output diagnostic
		incf	temp0,F			; insure 1 pulse minimum
wplslp	bsf		GPIO,TSTOUT		; set test point output high
		nop
		bcf		GPIO,TSTOUT		; set test point output low
		decfsz	temp0,F
		goto	wplslp
		return

;		Flash LED for X10 or Insteon signal

ledins	movlw	.10				; to turn on LED for 5 cycles
		goto	ledon
ledx10	movlw	.44				; to turn on LED for 22 cycles
ledon	movf	ledtim,F		; check if LED is already on
		btfss	STATUS,Z		; skip if not
		return
		movwf	ledtim			; set duration of LED flash
		bsf		GPIO,LEDOUT		; turn on LED
		clrwdt					; clear watchdog timer
		return


; INITIALIZATION SUBROUTINE
;
; Note: Clock Oscillator starts at 4MHz internal by default

initiz	bsf		STATUS,RP0		; select Bank 1
		movlw	b'01111000'		; use internal osc at 8MHz
		movwf	OSCCON			; set oscillator control
		movlw	b'00000000'		; start with all outputs at zero
		movwf	GPIO			; initialize I/O port bits
		movlw	b'00001011'		; T1CLKO, LEDOUT, ATTNON are outputs
		movwf	TRISIO			; define I/O port directions
		movlw	b'00001000'		; enable interrupt on change for ZCD
		movwf	IOC				; define I/O port directions
		movlw	b'00111110'		; enable pull-ups, T0 CLK from pin, WDT prescale 64
		movwf	OPTION_REG		; set option register
		movlw	b'00000001'		; to enable weak pull-up on TSTOUT
		movwf	WPU				; set weak pull-up on TSTOUT
		movlw	b'10100110'		; set comparator threshold to 1.25V
		movwf	VRCON
		movlw	b'00000001'		; enable Timer 1 overflow interrupt
		movwf	PIE1
		clrf	ANSEL			; all I/O pins are for digital signals
		bcf		STATUS,RP0		; select Bank 0
		movlw	b'00010111'		; start with comparator disabled
		movwf	CMCON0
		movlw	b'00000010'		; make sure comparator output is asynchronous
		movwf	CMCON1
		clrf	TMR0			; clear Timer 0
		clrf	TMR1H			; clear Timer 1
		clrf	TMR1L
		clrf	T1CON			; disable Timer 1 with prescale=0 (time ref)
		clrf	T2CON			; disable Timer 2 with prescale=0
		clrf	CCP1CON			; disable PWM mode
		clrf	ADCON0			; disable A/D converter
		movlw	b'00001001'		; set WDT for 1 sec timeout (31KHz / 64x512)
		movwf	WDTCON
		movlw	b'11001000'		; enable peripheral interrupts & interrupt on change
		movwf	INTCON
		clrf	stsflg			; clear general status flags
		clrf	periodH
		clrf	periodL
		clrf	ledtim
		clrf	cyccnt
		clrf	bkgcnt
		clrf	prezcd
		clrf	agcend
		clrf	thdset
		clrf	nselvl
		movlw	.60				; preset 2 second timer
		movwf	twosec
		btfss	GPIO,TSTOUT		; skip if TP/INST jumper is not installed
		retfie					; return from initialize & enable interrupts

		bsf		STATUS,RP0		; select Bank 1
		bcf		TRISIO,0		; switch TSTOUT to an output
		bcf		STATUS,RP0		; select Bank 0
		retfie					; return from initialize & enable interrupts


;		MAIN PROGRAM TIMING LOOP

;		This loops through completely for each half cycle of the powerline

start	call	initiz			; call initialization sequence

;		Main timing loop

;		Update oscillator tune before zero crossing

main	bcf		STATUS,RP0		; Select Bank 0
		bcf		GPIO,TSTOUT		; make sure test point output is low
		btfss	stsflg,inston	; skip if an Insteon command is being detected
		bsf		GPIO,ATTNON		; ensure attenuator is on & clear any mismatch condition
		btfss	stsflg,inston	; skip if an Insteon command is being detected
		btfss	stsflg,bkgnse	; skip and leave attenuator on if there is background noise
		bcf		GPIO,ATTNON		; turn off attenuator & clear any mismatch condition
		bcf		INTCON,GPIF		; clear any prior ZCD interrupt on change flag
		bsf		INTCON,GPIE		; ensure ZCD interrupt on change is enabled

mainzc	btfss	stsflg,zcross	; skip if 60Hz zero cross has occurred
		goto	mainzc			; loop & wait for zero-crossing if not

		movfw	thdset			; recall present threshold level
		bsf		STATUS,RP0		; select Bank 1
		movwf	VRCON			; set comparator threshold
		bcf		STATUS,RP0		; back to Bank 0
		andlw	b'00001111'		; mask all but the threshold bits
		xorlw	b'00001111'		; check if at max 3.1V
		btfsc	STATUS,Z		; skip and check for X10 signal not at max
		goto	main05			; leave attenuator on if excessive noise
		movlw	b'00010011'		; insure comparator is enabled (inverting mode)
		movwf	CMCON0

;		Must be continuous noise if on before ZCD transition

		movfw	prezcd			; get pre-ZCD window pulse count
		addlw	.244			; check if 12 pulses in 128 pre-ZCD window
		btfsc	STATUS,C		; skip & check for X10 signal if < 12 pulses (no noise)
		goto	main09			; no X10 signal if noise before ZCD transition

;		Per X10 spec, 120KHz burst must start within 100uS of zero crossing
;		So we sample from 160uS to 224uS & expect 7 to 8 pulses.

main01	btfss	TMR1H,0			; skip when Timer 1 MSB overflows (128uS)
		goto	main01			; loop until it does
main02	btfss	TMR1L,6			; skip if beginning of pre-window check (160uS)
		goto	main02			; loop until end of pre-window
		clrf	TMR0			; clear Timer 0 to check for X10 burst
		bsf		GPIO,TSTOUT		; set test point high at start of X10 sample

main03	btfss	TMR1L,7			; skip when Timer 1 LSB MSB increments (192uS)
		goto	main03			; loop until it does
main04	btfss	TMR1L,6			; skip if end of pre-window check (224uS)
		goto	main04			; loop until end of pre-window

		movfw	TMR0			; get 120KHz cycle count
		bcf		GPIO,TSTOUT		; set test point output low at end of X10 of sample
		movwf	cyccnt			; save cycle count
		bsf		GPIO,LEDOUT		; turn on LED to indicate power is on
		clrwdt					; clear watchdog timer
		addlw	.247			; 9 + 247 = 256
		btfsc	STATUS,C		; skip and check if in range if < 9 pulses
		goto	main06			; must be noise if > 8 pulses (> 132KHz)
		addlw	.2				; 7 + 247 + 2 = 256  (C = 1 for 7-8 pulses)
		btfss	STATUS,C		; skip if 7-8 pulses
		goto	main06			; no X10 signal
		bcf		GPIO,ATTNON		; make sure attenuator is off
		movfw	cyccnt			; recall cycle count
		call	wpulse			; output pulse chain for # of cycles in X10 sample

main05	call	ledx10			; turn on LED for X10 signal (or high noise)

;		Wait past end of X10 transmit window to turn on attenuator again

main06	movfw	TMR1H			; get Timer 1 high byte
		addlw	.246			; end of X10 transmit window check (1.28mS)?
		btfss	STATUS,C		; skip and turn on attenuator if so
		goto	main06			; loop until end of transmit window
		bcf		stsflg,zcross	; clear zero crossing flag
		bcf		stsflg,inston	; clear Insteon flag
		movf	ledtim,F		; check if LED is being flashed
		btfss	STATUS,Z		; skip if not
		goto	main08			; no noise monitor if LED is being flashed

		movfw	twosec			; get 2-second timer
		andlw	b'00001111'		; check for 266mS increment
		btfss	STATUS,Z		; skip if time for next flash
		goto	main07			; just turn off LED
		movf	nselvl,f		; check if noise level already counted down to zero
		btfsc	STATUS,Z		; skip if not
		goto	main07			; turn LED off if so
		decf	nselvl,f		; decrement noise level

		movlw	.2				; to turn on LED for just 2 cycle
		btfsc	stsflg,bkgnse	; skip if background noise is low (attenuation off)
		movlw	.5				; to turn on LED for 5 cycles
		movf	ledtim,F		; check if LED is already on
		btfsc	STATUS,Z		; skip if so
		movwf	ledtim			; set duration of LED flash
		goto	main08			; leave LED on to flash this noise level count

main07	bcf		GPIO,LEDOUT		; turn off LED

main08	btfsc	stsflg,bkgnse	; skip if no background noise
		bsf		GPIO,ATTNON		; turn on attenuator

;		Sample AGC 1.54 mS to 2.56 mS after zero crossing

main09	movfw	TMR1H			; get Timer 1 high byte
		addlw	.244			; 1.5 mS after the zero crossing? (12 at 8MHz clk)
		btfss	STATUS,C		; skip and begin AGC if so
		goto	main09			; loop until beginning of normal AGC window
		movlw	.236			; sample AGC until 2.5mS (20 at 8MHz clk)
		movwf	agcend			; set up AGC cycle end
		call	agcloop			; measure AGC level

		movfw	thdset			; get AGC threshold
		andlw	b'00001111'		; mask all but D/A bits
		call	wpulse			; output pulse chain indicating D/A level

;		Turn off attenuator at 6.78mS (after the 3rd X10 transmit burst)

main10	movfw	TMR1H			; get Timer 1 high byte
		addlw	.203			; 6.78mS after the zero crossing? (53 at 8MHz clk)
		btfss	STATUS,C		; skip and begin turn off attenuator if so
		goto	main10			; loop until 6.78mS
		bcf		GPIO,ATTNON		; turn off attenuator

;		Measure low-threshold background noise from 6.78mS to 7.04mS

		movlw	b'10100001'		; to set comparator threshold for .2V
		bsf		STATUS,RP0		; select Bank 1
		movwf	VRCON			; set comparator threshold to .2V
		bcf		STATUS,RP0		; back to Bank 0
		movlw	b'00010011'		; insure comparator is enabled (inverting mode)
		movwf	CMCON0
		clrf	TMR0			; clear Timer 0 to measure background noise
		bsf		GPIO,TSTOUT		; set test point high at start of background noise sample

main20	movfw	TMR1H			; get Timer 1 high byte
		addlw	.201			; 7.04mS after the zero crossing? (55 at 8MHz clk)
		btfss	STATUS,C		; skip and get noise count
		goto	main20			; loop until 7.04mS
		movfw	TMR0			; get background noise count
		bcf		GPIO,TSTOUT		; set test point low at end of background noise sample
		movwf	bkgcnt			; save background noise count

;		No Insteon check if TP/INST jumper is in place

		bsf		GPIO,TSTOUT		; set test point output high to check for TP/INST jumper
		nop						; add a bit of delay for output to slew
		btfss	GPIO,TSTOUT		; skip & check for Insteon if no TP/INST jumper
		goto	main40			; skip to end of cycle noise check if no Insteon check
		bcf		GPIO,TSTOUT		; set test point output low

;		Measure pre-Insteon background noise from 7.04mS to 7.30mS (230uS before Insteon window)

		movlw	b'10101111'		; to set comparator threshold for 3.1V
		bsf		STATUS,RP0		; select Bank 1
		movwf	VRCON			; set comparator threshold to 3.1V
		bcf		STATUS,RP0		; back to Bank 0
		clrf	TMR0			; clear Timer 0 to measure pre-Insteon background noise
		bsf		GPIO,TSTOUT		; set test point high at start of pre-Insteon noise sample
		movfw	bkgcnt			; recall background noise count
		call	wpulse			; output pulse chain for background noise count

main22	movfw	TMR1H			; get Timer 1 high byte
		addlw	.199			; 7.30mS after the zero crossing? (57 at 8MHz clk)
		btfss	STATUS,C		; skip and check for noise
		goto	main22			; loop until end of Pre-Insteon noise sample window
		movfw	TMR0			; get background noise cycle count (131.7KHz = 33.7 pulses)
		bcf		GPIO,TSTOUT		; set test point low at end of Pre-Insteon noise sample
		call	wpulse			; output pulse chain for # of cycles in pre-Insteon noise sample

;		No Insteon check if high noise near the Insteon band (121KHz to 141KHz)

		addlw	.219			; 37 + 219 = 256
		btfsc	STATUS,C		; skip and check if in Insteon band if < 37 pulses
		goto	main30			; check for Insteon signal if noise is above Insteon band
		addlw	.6				; 31 + 219 + 6 = 256  (C = 1 for 31-36 pulses)
		btfsc	STATUS,C		; skip and check if for Insteon signal
		goto	main			; end cycle and turn on attenuator if high in-band noise

;		Check for 131.7KHz Insteon signal from 7.68mS to 7.94mS

;		First wait until 7.68mS

main30	movfw	TMR1H			; get Timer 1 high byte
		addlw	.196			; 7.68mS after the zero crossing? (60 at 8MHz clk)
		btfss	STATUS,C		; skip and begin sample if so
		goto	main30			; loop until 7.68mS
		clrf	TMR0			; clear Timer 0 to check for Insteon signal
		bsf		GPIO,TSTOUT		; set test point high at start of Insteon sample

;		Next wait until 7.94mS

main32	movfw	TMR1H			; get Timer 1 high byte
		addlw	.194			; 7.94mS after the zero crossing? (62 at 8MHz clk)
		btfss	STATUS,C		; skip and check for Insteon command
		goto	main32			; loop until 7.94uS
		movfw	TMR0			; get potential Insteon cycle count (131.7KHz = 33.7 pulses)
		bcf		GPIO,TSTOUT		; set test point low at end of Insteon sample
		call	wpulse			; output pulse chain for # of cycles in Insteon sample

;		Now check for an Insteon signal (125KHz to 137KHz)

		addlw	.220			; 36 + 220 = 256
		btfsc	STATUS,C		; skip and check for an Insteon signal if < 36 pulses
		goto	main			; not an Insteon signal if noise is above Insteon band
		addlw	.4				; 32 + 220 + 4 = 256  (C = 1 for 32-35 pulses)
		btfss	STATUS,C		; skip if receiving an Insteon signal
		goto	main40			; loop and wait for next zero crossing
		bsf		stsflg,inston	; set flag to indicate receiving an Insteon command
		call	ledins			; turn on LED to indicate Insteon signal
		goto	main			; no noise check if receiving an Insteon signal

;		Check for noise from 8.06mS to 8.19mS

;		First wait until 8.06mS

main40	btfsc	stsflg,bkgnse	; skip if there is no background noise
		bsf		GPIO,ATTNON		; turn on attenuator
		btfss	stsflg,bkgnse	; skip and leave attenuator on if there is no background noise
		bcf		GPIO,ATTNON		; turn off attenuator

		movfw	thdset			; get present threshold
		andlw	b'00001111'		; mask all but threshold bits
		iorlw	b'10100000'		; set bits that control voltage reference are set

		bsf		STATUS,RP0		; select Bank 1
		movwf	VRCON			; restore current AGC threshold
		bcf		STATUS,RP0		; back to Bank 0
		movlw	b'00010011'		; insure comparator is enabled (inverting mode)
		movwf	CMCON0

main41	movfw	TMR1H			; get Timer 1 high byte
		addlw	.193			; 8.06mS after the zero crossing? (63 at 8MHz clk)
		btfss	STATUS,C		; skip and begin sample if so
		goto	main41			; loop until 8.06mS
		clrf	TMR0			; clear Timer 0 to check for powerline noise
		bsf		GPIO,TSTOUT		; set test point high at start of noise sample

;		Next wait until 8.19mS

main42	movfw	TMR1H			; get Timer 1 high byte
		addlw	.192			; 8.19mS after the zero crossing? (64 at 8MHz clk)
		btfss	STATUS,C		; skip and check for noise
		goto	main42			; loop until 7.94uS
		movfw	TMR0			; get noise cycle count
		movwf	prezcd			; save noise count before zero crossing
		bcf		GPIO,TSTOUT		; set test point low at end of noise sample
		call	wpulse			; output pulse chain indicating noise before zero crossing
		goto	main			; loop and wait for next zero crossing
		

;		AGC LOOP

;		Uses internal comparator reference to measure noise

agcloop	decf	thdset,W		; decrement present threshold
		andlw	b'00001111'		; mask all but voltage select bits
		btfsc	STATUS,Z		; skip if not zero
		movlw	.1				; insure minimum threshold is 1 (200mV)
		iorlw	b'10100000'		; set bits that control voltage reference

		bsf		STATUS,RP0		; select Bank 1
		movwf	VRCON			; start with comparator threshold at 1 below prior value
		bcf		STATUS,RP0		; back to Bank 0
		movlw	b'00010011'		; insure comparator is enabled (inverting mode)
		movwf	CMCON0
		clrf	TMR0			; clear Timer 0 to monitor noise
	
agclp1	movlw	.248			; 8 pulses per normal AGC increment
		addwf	TMR0,W			; add to Timer 0 pulse count
		btfsc	STATUS,C		; skip if not enough noise to raise threshold
		call	incthd			; increase threshold level
		movfw	TMR1H			; get Timer 1 high byte
		addwf	agcend,W		; check for end of AGC window
		btfss	STATUS,C		; done with AGC sample window if so
		goto	agclp1			; loop if not
		movlw	b'00010111'		; disable comparator
		movwf	CMCON0
		bsf		STATUS,RP0		; select Bank 1
		movfw	VRCON			; get present threshold level
		bcf		STATUS,RP0		; back to Bank 0
		movwf	thdset			; save threshold setting
		andlw	b'00001111'		; mask all but voltage select bits
		btfss	stsflg,bkgnse	; skip if background noise flag is set
		goto	agcnse			; check if still low noise

		movfw	bkgcnt			; get background noise count
		addlw	.253			; check if noise is 2 counts or below
		btfsc	STATUS,C		; skip and turn off attenuation if so
		return					; done

		bcf		stsflg,bkgnse	; clear background noise flag
		movlw	b'10100100'		; set AGC to 4 counts with attenuator switched off
		bsf		STATUS,RP0		; select Bank 1
		movwf	VRCON			; set the new comparator threshold
		bcf		STATUS,RP0		; back to Bank 0
		movwf	thdset			; save new threshold setting
		return					; done

agcnse	addlw	.250			; check if noise is 6 counts or above
		btfss	STATUS,C		; skip and turn on attenuation if so
		return					; done

		bsf		stsflg,bkgnse	; set background noise flag
		movlw	b'10100011'		; set AGC to 3 counts with attenuator switched on
		bsf		STATUS,RP0		; select Bank 1
		movwf	VRCON			; set the new comparator threshold
		bcf		STATUS,RP0		; back to Bank 0
		movwf	thdset			; save new threshold setting
		return					; done

incthd	bsf		STATUS,RP0		; select Bank 1
		movfw	VRCON			; get present threshold reading
		andlw	b'00001111'		; mask all but the threshold bits
		xorlw	b'00001111'		; check if already at max 3.1V
		btfss	STATUS,Z		; skip if already at max
		incf	VRCON,F			; increase threshold if not
		bcf		STATUS,RP0		; back to Bank 0
		bsf		GPIO,TSTOUT		; set test point output high
		nop						; output a pulse each time threshold is increased
		bcf		GPIO,TSTOUT		; set test point output low
		clrf	TMR0			; clear Timer 0 to check next threshold level
		return


;		Initialize EEPROM locations

EE		CODE	0x2100
		DE		"XTB-ANR V1.10"	; program name and version

		END						; directive 'end of program'

