;********************************************************************
;																	*
;	XTB-ANR Active Noise Reducer									*
;																	*
;	This program is for the PIC used in the XTB-ANR					*
;																	*
;********************************************************************
;																	*
;	Filename:		XTB-ANR.asm										*
; 	Date:			02/15/21										*
;	File Version:	rev 2.00										*
;																	*
;	Author:			Jeff Volp										*
;	Company:		JV Digital Engineering 							*
;																	*
;	This code is Copyright JV Digital Engineering 02/15/21			*
;																	*
;	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			*
;	Version 2.00:													*
;		Program converted from PIC12F683 to PIC12F1822				*
;		Clock doubled to 16MH to tighten up 120KHz window			*
;																	*
;********************************************************************
;																	*
;	Files required:	12F1822.lkr (with interrupts enabled)			*
;																	*
;********************************************************************

	list      p=12F1822			; list directive to define processor
	#include <p12F1822.inc>		; processor specific variable definitions

	errorlevel  -302			; suppress message 302 from list file

    __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_ON & _CPD_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF
    __CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_ON & _BORV_25 & _LVP_OFF

; 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

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 (.156 X 0-31)
ledcnt		RES		1			; LED flash count indicating background noise level
frqbgn		RES		1			; frequency measurement T1 begin time
frqend		RES		1			; frequency measurement T1 end time
frqcnt		RES		1			; time count for 4 cycles (.5uS resolution)
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

PROGRAM			ORG		0x0008	; program code

;		INTERRUPT PROCESSING

;		Identify the source of the interrupt

intrpt
		banksel	IOCAF			; select BANK7 for interrupt on change
		btfss	IOCAF,IOCAP3	; skip if it was a zero crossing interrupt
		goto	unknwn			; unidentified interrupt if not
		bcf		IOCAF,IOCAF3	; clear interrupt flag
		clrf	BSR				; back to BANK0
		bcf		INTCON,IOCIE	; disable ZCD interrupt on change

;		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		PORTA,TSTOUT	; set test point output high to mark zero crossing
		nop						; make it wider to distinguish from others
		nop
		nop
		bcf		PORTA,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	PORTA,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,F		; decrement 2-second timer & skip if zero
		goto	intclr			; just clear interrupt flag
		movlw	.120			; 120 cycles
		movwf	twosec			; reset 2-second timer
		decf	thdset,W		; get noise threshold -1 (minimum threshold is 1)
		btfsc	stsflg,bkgnse	; skip if attenuator is off
		addlw	.1				; ensure at least 1 flash if attenuator is on	
		movwf	ledcnt			; save for LED flash count
		addlw	.248			; check if > 7 flashes (248 + 8 = 256)
		movlw	.7				; 7 flashes max
		btfsc	STATUS,C		; skip if <= 7 flashes
		movwf	ledcnt			; set max 7 flashes

intclr	bcf		INTCON,IOCIF	; 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		PORTA,LEDOUT	; set LED output low
		goto	intrtn			; return from interrupt

;		Unidentified interrupt

unknwn	clrf	BSR				; select BANK0
		call	initiz			; re-initialize if timer interrupt

;		Return from interrupt

intrtn	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		PORTA,TSTOUT	; set test point output high
		nop
		bcf		PORTA,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		PORTA,LEDOUT	; turn on LED
		clrwdt					; clear watchdog timer
		return


; INITIALIZATION SUBROUTINE
;
; Note: Clock Oscillator starts at 16MHz internal by default

initiz	clrf	BSR				; select BANK0
		clrf	PORTA			; Set all outputs to "0"
		movlw	b'01001000'		; enable peripheral interrupts & interrupt on change
		movwf	INTCON
		clrf	T1GCON			; disable Timer 1 gate function
		movlw	b'00010000'		; disable T1, Fosc/4, prescale=2,(LSB overflow at 128uS)
		movwf	T1CON			; set up timer 1 (time ref)
		clrf	T2CON			; disable Timer 2 with prescale=0
		clrf	CPSCON0			; disable capacitive sensing oscillator
		clrf	TMR0			; clear Timer 0
		clrf	TMR1H			; clear Timer 1
		clrf	TMR1L
		clrf	TMR2			; clear Timer 2
		clrf	PR2

		banksel	OSCCON			; select BANK1
		movlw	b'01111000'		; use internal osc at 16MHz
		movwf	OSCCON			; set oscillator control
		clrf	OSCTUNE			; clear oscillator tune setting
		movlw	b'00001011'		; T1CLKO, LEDOUT, ATTNON are outputs
		movwf	TRISA			; define I/O port directions
		movlw	b'00111000'		; enable weak pull-ups, Comparator -> T0, no T0 prescale
		movwf	OPTION_REG		; set option register
		movlw	b'00000001'		; enable Timer 1 overflow interrupt
		movwf	PIE1
		clrf	PIE2			; disable other interrupts
		clrf	ADCON0			; disable A/D converter
		clrf	ADCON1			;
		movlw	b'00010100'		; set WDT for 1 sec timeout (31KHz/32768)
		movwf	WDTCON

		banksel	APFCON			; select BANK2 for comparator and D/A
		clrf	APFCON			; set all standard pin functions
		clrf	SRCON0			; make sure S-R latch is disabled
		clrf	SRCON1
		clrf	FVRCON			; disable fixed voltage reference
		movlw	b'10000000'		; enable D/A with VDD reference, no output
		movwf	DACCON0
		movlw	b'00001000'		; set D/A converter to 1.25V  (8/32) x 5V
		movwf	DACCON1
		clrf	CM1CON0			; start with comparator disabled
		movlw	b'00010000'		; no interrupts, DAC reference, use C1IN0- input
		movwf	CM1CON1
		movlw	b'10000000'		; to enable brown-out protection
		movwf	BORCON			; set brown-out control

		banksel	ANSELA			; select BANK3 for A/D & UART control
		clrf	ANSELA			; all I/O pins are for digital signals
		clrf	TXSTA			; make sure the serial transmitter is disabled
		clrf	RCSTA			; make sure the serial receiver is disabled

		banksel	WPUA			; select BANK4 for weak pull-up control
		movlw	b'00000001'		; to enable weak pull-up on TSTOUT
		movwf	WPUA			; set weak pull-up on TSTOUT

		banksel	CCP1CON			; select BANK5 for PWM control
		clrf	CCP1CON			; PWM disabled
		clrf	PSTR1CON		; PWM output disabled

		banksel	IOCAF			; select BANK7 for interrupt on change
		movlw	b'00001000'		; to enable interrupt on change for ZCD
		movwf	IOCAP			; enable positive interrupt on change for ZCD
		movwf	IOCAN			; enable negative interrupt on change for ZCD
		clrf	IOCAF			; clear any existing IOC flag bits
		clrf	CLKRCON			; disable clock reference module

		clrf	BSR				; back to BANK0
		clrf	stsflg			; clear general status flags
		clrf	ledtim
		clrf	cyccnt
		clrf	bkgcnt
		clrf	prezcd
		clrf	agcend
		clrf	thdset
		clrf	ledcnt
		movlw	.60				; preset 2 second timer
		movwf	twosec
		btfss	PORTA,TSTOUT	; skip if TP/INST jumper is not installed
		retfie					; return from initialize & enable interrupts

		banksel	TRISA			; select BANK1
		bcf		TRISA,TSTOUT	; switch TSTOUT to an output
		clrf	BSR				; back to BANK0
		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

main	clrf	BSR				; select BANK0
		bcf		PORTA,TSTOUT	; make sure test point output is low
		btfss	stsflg,inston	; skip if an Insteon command is being detected
		bsf		PORTA,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		PORTA,ATTNON	; turn off attenuator & clear any mismatch condition
		bcf		INTCON,IOCIF	; clear any prior ZCD interrupt on change flag
		bsf		INTCON,IOCIE	; 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
		banksel	DACCON1			; select BANK2
		movwf	DACCON1			; set comparator threshold
		clrf	BSR				; back to BANK0
		addlw	.227			; check if at max 4.5V (29/32 x 5.0V)
		btfsc	STATUS,C		; skip and check for X10 signal not at max
		goto	main05			; leave attenuator on if excessive noise
		banksel	CM1CON0			; select BANK2
		movlw	b'10110100'		; insure comparator is enabled (inverting mode)
		movwf	CM1CON0
		clrf	BSR				; back to BANK0

;		Must be continuous noise if on before ZCD transition

		movfw	prezcd			; get pre-ZCD window pulse count
		addlw	.244			; check if 12 pulses in 128uS 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 128uS to 192uS measuring time for 4 pulses.

main01	btfss	TMR1H,0			; skip when Timer 1 LSB overflows (128uS)
		goto	main01			; loop until it does

		clrwdt					; clear watchdog timer
		bsf		PORTA,TSTOUT	; set test point high at start of X10 sample
		bsf		PORTA,LEDOUT	; turn on LED to indicate power is on
		clrf	TMR0			; clear Timer 0 to check for X10 burst

main02	btfsc	TMR0,0			; skip until Timer 0 has counted a pulse
		goto	main03			; save begin time, and count up to 4 pulses
		btfss	TMR1L,7			; skip if timeout (128+64 = 192uS)
		goto	main02			; loop until 192uS timeout
		goto	nosgnl			; no signal burst within 128-192uS window

main03	movfw	TMR1L			; get Timer 1 LSB
		movwf	frqbgn			; save begin time
		clrf	TMR0			; clear Timer 0 to count pulses
main04	btfsc	TMR0,2			; skip until Timer 0 has counted 4 pulses
		goto	chk120			; check if frequency is near 120KHz
		btfss	TMR1L,7			; skip if timeout (128+64 = 192uS)
		goto	main04			; loop until 192uS timeout
		goto	nosgnl			; no signal burst within 128-192uS window

chk120	movfw	TMR1L			; get Timer 1 LSB
		movwf	frqend			; save end time
		bcf		PORTA,TSTOUT	; set test point output low at end of X10 of sample
		movfw	frqbgn			; get begin time
		subwf	frqend,w		; subtract begin time from end time
		movwf	frqcnt			; save time count for 4 cycles (.5uS per count)
		addlw	.183			; check if below 110KHz (36.5uS) 73+183=256 
		btfsc	STATUS,C		; skip if > 110KHz (<73)
		goto	main05			; must be noise if < 110KHz
		addlw	.11				; check if above 130KHz (31.0uS) 62+11+183=256
		btfss	STATUS,C		; skip if < 130KHz (<62)
		goto	main05			; must be noise > 130KHz
		bcf		PORTA,ATTNON	; make sure attenuator is off
		lsrf	frqcnt,W		; recall time count for 4 cycles in uS
		call	wpulse			; output pulse chain for period of 4 cycles

main05	call	ledx10			; turn on LED for X10 signal (or high noise)

;		Wait past end of X10 transmit window to turn on attenuator again

nosgnl	;bcf		PORTA,TSTOUT	; set test point output low at end of X10 of sample
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	ledcnt,f		; check if LED count is already zero
		btfsc	STATUS,Z		; skip if not
		goto	main07			; turn LED off if so
		decf	ledcnt,f		; decrement LED count

		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		PORTA,LEDOUT	; turn off LED

main08	btfsc	stsflg,bkgnse	; skip if no background noise
		bsf		PORTA,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 16MHz 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 16MHz clk)
		movwf	agcend			; set up AGC cycle end
		call	agcloop			; measure AGC level

		movfw	thdset			; get AGC threshold
		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 16MHz clk)
		btfss	STATUS,C		; skip and begin turn off attenuator if so
		goto	main10			; loop until 6.78mS
		bcf		PORTA,ATTNON	; turn off attenuator

;		Measure low-threshold background noise from 6.78mS to 7.04mS

		banksel	DACCON1			; select BANK2
		movlw	.1				; to set comparator threshold for .15V
		movwf	DACCON1			; set comparator threshold to .15V
		movlw	b'10110100'		; insure comparator is enabled (inverting mode)
		movwf	CM1CON0
		clrf	BSR				; back to BANK0
		clrf	TMR0			; clear Timer 0 to measure background noise
		bsf		PORTA,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 16MHz clk)
		btfss	STATUS,C		; skip and get noise count
		goto	main20			; loop until 7.04mS
		movfw	TMR0			; get background noise count
		bcf		PORTA,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		PORTA,TSTOUT	; set test point output high to check for TP/INST jumper
		nop						; add a bit of delay for output to slew
		btfss	PORTA,TSTOUT	; skip & check for Insteon if no TP/INST jumper
		goto	main40			; skip to end of cycle noise check if no Insteon check
		bcf		PORTA,TSTOUT	; set test point output low

;		Measure pre-Insteon background noise from 7.04mS to 7.30mS (230uS before Insteon window)

		movlw	.28				; to set comparator threshold for 4.5V
		banksel	DACCON1			; select BANK3
		movwf	DACCON1			; set comparator threshold to 4.5V
		clrf	BSR				; back to BANK0
		clrf	TMR0			; clear Timer 0 to measure pre-Insteon background noise
		bsf		PORTA,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 16MHz 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		PORTA,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 16MHz 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		PORTA,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 16MHz 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		PORTA,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		PORTA,ATTNON	; turn on attenuator
		btfss	stsflg,bkgnse	; skip and leave attenuator on if there is no background noise
		bcf		PORTA,ATTNON	; turn off attenuator

		movfw	thdset			; get present threshold
		banksel	DACCON1			; select BANK2
		movwf	DACCON1			; restore current AGC threshold
		movlw	b'10110100'		; insure comparator is enabled (inverting mode)
		movwf	CM1CON0
		clrf	BSR				; back to BANK0

main41	movfw	TMR1H			; get Timer 1 high byte
		addlw	.193			; 8.06mS after the zero crossing? (63 at 16MHz 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		PORTA,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 16MHz clk)
		btfss	STATUS,C		; skip and check for noise
		goto	main42			; loop until 8.19mS
		movfw	TMR0			; get noise cycle count
		movwf	prezcd			; save noise count before zero crossing
		bcf		PORTA,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	movf	thdset,F		; check if threshold is already zero
		btfsc	STATUS,Z		; skip if not
		incf	thdset,F		; set threshold to 1
		decf	thdset,W		; decrement present threshold
		btfsc	STATUS,Z		; skip if not zero
		movlw	.1				; insure minimum threshold is 1 (.15V)

		banksel	DACCON1			; select BANK2
		movwf	DACCON1			; start with comparator threshold at 1 below prior value
		movlw	b'10110100'		; insure comparator is enabled (inverting mode)
		movwf	CM1CON0
		clrf	BSR				; back to BANK0
		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
		banksel	CM1CON0			; select BANK2
		movlw	b'00010100'		; disable comparator
		movwf	CM1CON0
		movfw	DACCON1			; get present threshold setting
		clrf	BSR				; back to BANK0
		movwf	thdset			; save threshold setting
		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	.4				; set AGC to 4 counts with attenuator switched off
		banksel	DACCON1			; select BANK2
		movwf	DACCON1			; set the new comparator threshold
		clrf	BSR				; back to BANK0
		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	.3				; set AGC to 3 counts with attenuator switched on
		banksel	DACCON1			; select BANK2
		movwf	DACCON1			; set the new comparator threshold
		clrf	BSR				; back to BANK0
		movwf	thdset			; save new threshold setting
		return					; done

incthd
		banksel	DACCON1			; select BANK2
		movfw	DACCON1			; get present threshold reading
		addlw	.227			; check if at max 4.5V (29/32 x 5.0V)
		btfss	STATUS,C		; skip if already at max
		incf	DACCON1,F		; increase threshold if not
		clrf	BSR				; back to BANK0
		bsf		PORTA,TSTOUT	; set test point output high
		nop						; output a pulse each time threshold is increased
		bcf		PORTA,TSTOUT	; set test point output low
		clrf	TMR0			; clear Timer 0 to check next threshold level
		return


;		Initialize EEPROM locations

EE		CODE	0xF000

		DE		"XTB-ANR V2.00"	; program name and version

		END						; directive 'end of program'

