;********************************************************************
;																	*
;	XTB-II Repeater													*
;																	*
;	This program is for the PIC used in the XTB-II.  This is a back *
;	converted version of the program developed for the XTB-IIR.  It	*
;	uses the 8-pin 12F683 PIC rather than the 16F684 used in the	*
;	XTB-IIR.  Functons needing the additional I/O capability have	*
;	been deleted.  The mode options now use the XTB-IIR numbers.	*
;																	*
;	Version 2.01 changes:											*
;		digital input is echoed back with collision abort disabled.	*
;		delete unused AGC code raising thrshold for receive errors.	*
;																	*
;********************************************************************
;																	*
;	Filename:		XTB-II.asm										*
; 	Date:			02/13/08										*
;	File Version:	rev 2.01										*
;																	*
;	Author:			Jeff Volp										*
;	Company:		JV Digital Engineering 							*
;																	*
;	This code is Copyright JV Digital Engineering 02/04//08			*
;																	*
;********************************************************************
;																	*
;	Files required:	12F683.lkr (with interrupts enabled)			*
;																	*
;																	*
;********************************************************************
;																	*
;   This is a major update based on the XTB-IIR code				*
;																	*
;********************************************************************

	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 


; I/O PIN DEFINITIONS

DRVINH	EQU		H'05'			; (GP5) Transmit Drive Inhibit - 1 for XTB driver OFF
TW523O	EQU		H'04'			; (GP4) TW523 Data Out - 0 for opto-isolator ON
TW523I	EQU		H'03'			; (GP3) TW523 Data In - 0 for opto-isolator LED ON
PWM120	EQU		H'02'			; (GP2) 120KHz Drive - 1 mS burst to XTB output driver
RCVIN	EQU		H'01'			; (GP1) 120KHz low level X10 sinewave input
ZCDIN	EQU		H'00'			; (GP0) Zero Crossing Detector - 50/60Hz reference input


; VARIABLE DEFINITIONS

			UDATA		0x20

startw		RES		1			; startup window for configuration reset (0 when over)
periodH		RES		1			; period measurement MSB
periodL		RES		1			; period measurement LSB
frqerr		RES		1			; 2's compliment frequency error (0 for no error)
cyccnt		RES		1			; received bit cycle count
adjcnt		RES		1			; received bit cycle count less background average count
posavgH		RES		1			; positive half cycle background average cycle count MSB
posavgL		RES		1			; positive half cycle background average cycle count LSB
negavgH		RES		1			; negative half cycle background average cycle count MSB
negavgL		RES		1			; negative half cycle background average cycle count LSB
rcvsr3		RES		1			; received data shift register byte 3 (output)
rcvsr2		RES		1			; received data shift register byte 2
rcvsr1		RES		1			; received data shift register byte 1 (input)
onezer		RES		1			; net total of one versus zero bits (error if not 1-3)
cmpcnt		RES		1			; count of comparator cycles received in the "pre-window"
rcvcnt		RES		1			; received bit count
onecnt		RES		1			; received "1" count
gapcnt		RES		1			; received gap length (255 max)
seccnt		RES		1			; second timer - counts 60 to 1 each second
x10cnt		RES		1			; X10 traffic density (+3 for command, -1 every second)
msgcnt		RES		1			; number of bits in message
rhouse		RES		1			; unpacked receive message house code (4-bit X10 code)
rkeycd		RES		1			; unpacked received message key code (5-bit X10 code)
oldhse		RES		1			; last house code received
oldkey		RES		1			; last key code received
modcnt		RES		1			; number of "982" XTB mode command digits accepted
modtim		RES		1			; XTB mode command window timer
modsel		RES		1			; valid 1-16 mode select (in X10 code)
xcmdcd		RES		1			; command code used to assemble a transmit message
xhouse		RES		1			; house code used to assemble a transmit message
xkeycd		RES		1			; key code used to assemble a transmit message
xmtsr3		RES		1			; transmit data shift register byte 3 (output)
xmtsr2		RES		1			; transmit data shift register byte 2
xmtsr1		RES		1			; transmit data shift register byte 1
xmtcnt		RES		1			; transmitted bit count
agcend		RES		1			; end of AGC sample window
thdset		RES		1			; comparator threshold setting
temp0		RES		1			; Bank 0 general purpose register
temp1		RES		1			; Bank 0 general purpose register (for mode command)
temp2		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
nxttune		RES		1			; next setting for OSCTUNE


stsflg		RES		1			; general status flags

cycpol		EQU		H'07'		; "1" indicates positive half cycle
rcverr		EQU		H'06'		; "1" indicates an error was detected in the data pattern
rcvbit		EQU		H'05'		; "1" indicates received bit was a logic "1" (22 bit delay from input)
inpbit		EQU		H'04'		; "1" indicates last received bit is a logic "1" (as received)
dimcmd		EQU		H'03'		; "1" indicates receiving a bright/dim command
bx10in		EQU		H'02'		; "1" indicates a X10 Boost input is being received
linein		EQU		H'01'		; "1" indicates a powerline input is being received
dgioin		EQU		H'00'		; "1" indicates a digital input is being received
allsrc		EQU		b'00001111'	; used to check if any X10 source flags are set (including dim)
clrsrc		EQU		b'11111000'	; used to clear all X10 source flags

xmtflg		RES		1			; transmitter status flags

shutdn		EQU		H'07'		; "1" indicates shutdown due to command storm
colisn		EQU		H'06'		; "1" indicates a collision was detected
outbit		EQU		H'05'		; "1" indicates next bit to be transmitted is a logic "1"
xmtmsg		EQU		H'04'		; "1" indicates transmitter is active (from 1st start bit to 6-bit gap)
rptmsg		EQU		H'03'		; "1" indicates a message is being repeated (also set for re-transmits)
xtbmsg		EQU		H'02'		; "1" indicates an internal message is being transmitted (inhibit repeat)
xmtcod		EQU		H'01'		; "1" indicates the keycode should be transmitted
xmtcmd		EQU		H'00'		; "1" indicates the command should be transmitted

modflg1		RES		1			; mode flags (copied from EEPROM)
  variable	moddef1=b'10000001'	; default to repeater enabled, sequential dims

rptena		EQU		H'07'		; 16 = 1 enables repeater function
realtm		EQU		H'06'		; 15 = 1 (TBD) Unit now handes Boost & TW523 without switching modes
xmt3ph		EQU		H'05'		; 14 = 1 enables transmit on all 3 phases (X10 boost, digital, & repeat)
xmtdly		EQU		H'04'		; 13 = 1 selects delayed transmit to reduce potential for "dimmer flicker"
xmtlow		EQU		H'03'		; 12 = 1 selects transmit at reduced power level (default for 3-phase xmit)
colrpt		EQU		H'02'		; 11 = 1 enables auto re-transmit of repeat following collision
colabt		EQU		H'01'		; 10 = 1 selects abort transmission when collision is detected
rptdim		EQU		H'00'		;  9 = 1 enables special handling when repeating bright/dim commands

modflg2		RES		1			; more mode flags (copied from EEPROM)
  variable	moddef2=b'00100000'	; default AGC on

onerpt		EQU		H'07'		;  8 = 1 selects only one repeat for each command (no ping-pong)
hitraf		EQU		H'06'		;  7 = 1 storm threshold 60/min. avg; 0 allows 20/min. avg.
agcena		EQU		H'05'		;  6 = 1 enables AGC, (0 for low gain fixed threshold)
thldhi		EQU		H'04'		;  5 = 1 selects high AGC threshold or MSB of fixed threshold
altagc		EQU		H'03'		;  4 = 1 selects alternate AGC window or LSB of fixed threshold
								;		 00 =   0 mV threshold
								;		 01 = 200 mV threshold
								;		 10 = 400 mV threshold
								;		 11 = 600 mV threshold
vardcd		EQU		H'02'		;  3 = 1 selects variable decode rather than 40 cycles
pgmhse		EQU		H'01'		;  2 = 1 is used to program housecode after ALL OFF reset
allbit		EQU		H'00'		;  1 = 1 selects return all bits received (no error checking)

hsemod		RES		1			; housecode accepted for mode commands
  variable	hsedef=b'00001100'	; default P, changed code used for 9-8-2-2-ON after ALL OFF reset

hseulk		EQU		H'07'		; unlock flag set at ALL OFF reset to allow house code to be changed

EEMODE		equ		H'0010'		; location of mode flags in EEPROM
EEHSE		equ		EEMODE+2	; location of housecode in EEPROM

;	******	LINE FREQUENCY SELECT FOR 50 or 60 Hz	******

ac50hz		equ		0			; set to zero for 60Hz, non zero for 50Hz

;		Note:  50 Hz operation needs more changes

;**********************************************************************

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


;		X10 -> DECIMAL CODE CONVERSION UTILITY  (locate at program page zero)

;		Convert X10 code to 0-15 decimal equivalent (offset by 1 count)

;		Enter with W = X10 code in 4 LSBs
;		Returns with W = decimal value of code (X10 1-16 -> decimal 0-15)

x10dec	clrf	PCLATH			; assume page zero
		andlw	b'00001111'		; make sure 4 MSBs are zero
		addwf	PCL,F			; adjust program counter LSB
		retlw	.12				; M or 13 = '0000'
		retlw	.4				; E or  5 = '0001'
		retlw	.2				; C or  3 = '0010'
		retlw	.10				; K or 11 = '0011'
		retlw	.14				; O or 15 = '0100'
		retlw	.6				; G or  7 = '0101'
		retlw	.0				; A or  1 = '0110'
		retlw	.8				; I or  9 = '0111'
		retlw	.13				; N or 14 = '1000'
		retlw	.5				; F or  6 = '1001'
		retlw	.3				; D or  4 = '1010'
		retlw	.11				; L or 12 = '1011'
		retlw	.15				; P or 16 = '1100'
		retlw	.7				; H or  8 = '1101'
		retlw	.1				; B or  2 = '1110'
		retlw	.9				; J or 10 = '1111'


;		DECIMAL -> X10 CODE CONVERSION UTILITY  (locate at program page zero)

;		Convert 0-15 decimal number to equivalent X10 code (offset by 1 count)

;		Enter with W = X10 code in 4 LSBs
;		Returns with W = 8-bit X10 equivalent (decimal 0-15 -> X10 1-16)

decx10	clrf	PCLATH			; assume page zero
		andlw	b'00001111'		; make sure 4 MSBs are zero
		addwf	PCL,F			; adjust program counter LSB
		retlw	b'01101001'		; A or  1 = b'0110'
		retlw	b'10101001'		; B or  2 = b'1110'
		retlw	b'01011001'		; C or  3 = b'0010'
		retlw	b'10011001'		; D or  4 = b'1010'
		retlw	b'01010110'		; E or  5 = b'0001'
		retlw	b'10010110'		; F or  6 = b'1001'
		retlw	b'01100110'		; G or  7 = b'0101'
		retlw	b'10100110'		; H or  8 = b'1101'
		retlw	b'01101010'		; I or  9 = b'0111'
		retlw	b'10101010'		; J or 10 = b'1111'
		retlw	b'01011010'		; K or 11 = b'0011'
		retlw	b'10011010'		; L or 12 = b'1011'
 		retlw	b'01010101'		; M or 13 = b'0000'
		retlw	b'10010101'		; N or 14 = b'1000'
		retlw	b'01100101'		; O or 15 = b'0100'
		retlw	b'10100101'		; P or 16 = b'1100'


;		EXPAND X10 CODE CONVERSION UTILITY  (locate at program page zero)

;		Convert 4-bit X10 code to 8-bit X10 transmission code

;		Enter with W = X10 code in 4 LSBs
;		Returns with W = 8-bit X10 equivalent (decimal 0-15 -> X10 1-16)

expx10	clrf	PCLATH			; assume page zero
		andlw	b'00001111'		; make sure 4 MSBs are zero
		addwf	PCL,F			; adjust program counter LSB
 		retlw	b'01010101'		; M or 13 = b'0000'
		retlw	b'01010110'		; E or  5 = b'0001'
		retlw	b'01011001'		; C or  3 = b'0010'
		retlw	b'01011010'		; K or 11 = b'0011'
		retlw	b'01100101'		; O or 15 = b'0100'
		retlw	b'01100110'		; G or  7 = b'0101'
		retlw	b'01101001'		; A or  1 = b'0110'
		retlw	b'01101010'		; I or  9 = b'0111'
		retlw	b'10010101'		; N or 14 = b'1000'
		retlw	b'10010110'		; F or  6 = b'1001'
		retlw	b'10011001'		; D or  4 = b'1010'
		retlw	b'10011010'		; L or 12 = b'1011'
		retlw	b'10100101'		; P or 16 = b'1100'
		retlw	b'10100110'		; H or  8 = b'1101'
		retlw	b'10101001'		; B or  2 = b'1110'
		retlw	b'10101010'		; J or 10 = b'1111'


;		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
		btfsc	PIR1,TMR1IF		; skip if not a Timer 1 overflow
		goto	timint			; handle timer interrupt if so
		btfss	INTCON,GPIF		; skip if it was a zero crossing interrupt
		goto	unknwn			; unidentified interrupt if not

;		Calculate line frequency period by adding each half cycle

		bcf		T1CON,TMR1ON	; stop Timer 1
		movfw	TMR1L			; get Timer 1 LSB
		addwf	periodL,F		; add to last half cycle LSB
		btfsc	STATUS,C		; skip if carry clear
		incf	periodH,F		; propagate carry into MSB
		movfw	TMR1H			; get Timer 1 MSB
		addwf	periodH,F		; add to last half cycle MSB
		movlw	.247			; preset Timer 1 to -1.17mS (at 7.68MHz clk)
		movwf	TMR1H			; set high byte
		movlw	.10				; adjust for Timer 1 being stopped
		movwf	TMR1L			; set low byte
		bsf		T1CON,TMR1ON	; restart Timer 1

;		Clear interrupt & watchdog timer

		bcf		stsflg,cycpol	; clear cycle polarity flag
		btfsc	GPIO,ZCDIN		; clear mismatch & skip if falling edge of AC input
		bsf		stsflg,cycpol	; set cycle polarity for positive half cycle
		bcf		INTCON,GPIF		; clear interrupt flag
		clrwdt					; clear watchdog timer

;		Clear source flags if no message is being processed

clrflg	movf	onecnt,F		; have there been any "1" bits received?
		btfss	STATUS,Z		; skip and clear X10 source flags if not
		goto	xmtnxt			; transmit next bit as required
		movlw	clrsrc			; to mask all X10 source flags
		andwf	stsflg,F		; clear all X10 source status flags

;		Turn on transmitter as required for local or repeated messages

xmtnxt	btfss	xmtflg,xtbmsg	; skip if transmitting an internal message
		goto	chkrpt			; check if repeating a message
		btfss	xmtflg,outbit	; skip & transmit if output bit is a "1"
		goto	chkpol			; just check polarity if a "0"
		goto	x10out			; transmit bit
chkrpt	btfsc	xmtflg,rptmsg	; skip if message is not being repeated
		btfss	stsflg,rcvbit	; skip & transmit if bit is a "1"
		goto	chkpol			; just check polarity if a "0"
x10out	call	xmton			; turn on transmitter

;		Determine which AC input zero crossing just passed

chkpol	btfsc	stsflg,cycpol	; skip if falling edge of AC input
		goto	adjclk			; adjust clock frequency on rising edge

;		Count 60Hz to check noise & command storm once a second

		decfsz	seccnt			; tick off one cycle (delay 4 seconds after powerup)
		goto	strtup			; skip storm processing except at zero count
		movlw	.60				; reload count for next second
		movwf	seccnt

;		Check busy count to recover from a command storm

chktrf	movf	x10cnt,F		; is traffic count already zero?
		btfsc	STATUS,Z		; skip if not
		goto	strtup			; check startup window
		decfsz	x10cnt,F		; decrement traffic count & skip if went to zero
		goto	strtup			; check startup window
		bcf		xmtflg,shutdn	; clear shutdown flag
		goto	intrtn			; return with interrupts

;		Check startup window

strtup	movf	startw,F		; is startup window is over?
		btfsc	STATUS,Z		; skip if not
		goto	chkmod			; check for mode command if so
		decfsz	startw,F		; decrement startup window & skip if just ended
		goto	intrtn			; return with interrups enabled
		
;		Retrieve mode configuration from EEPROM at end of startup window

getmod	bsf		STATUS,RP0		; select Bank 1
		movlw	EEMODE			; get mode flags address in EEPROM
		movwf	EEADR			; set EEPROM address to read
		bsf		EECON1,RD		; read EEPROM location
		movfw	EEDAT			; get 1st set of mode flags
		movwf	modflg1			; save in RAM for quick access
		incf	EEADR,F			; set next EEPROM address to read
		bsf		EECON1,RD		; read EEPROM location
		movfw	EEDAT			; get 2nd set of mode flags
		movwf	modflg2			; save in RAM for quick access
		incf	EEADR,F			; set next EEPROM address to read
		bsf		EECON1,RD		; read EEPROM location
		movfw	EEDAT			; get housecode used for mode programming
		movwf	hsemod			; save in RAM for quick access
modrtn	bcf		STATUS,RP0		; back to Bank 0
		call	pwmpwr			; set PWM power level
		goto	intrtn			; return with interrupts enabled

;		Check mode command timeout on falling edge of AC input

chkmod	movf	modtim,F		; check mode command timer
		btfsc	STATUS,Z		; skip if not zero
		goto	intrtn			; just return if so
		decf	modtim,F		; tick off one count
		btfsc	STATUS,Z		; skip if not zero yet
		goto	clrmod			; clear mode select if timeout

;		Enable transmit mode select acknowledge when receive has completed

		btfss	xmtflg,xmtcmd	; skip if waiting to transmit a command word
		goto	intrtn			; just return if not

chkgap	movlw	.241			; allow a gap of 15 half cycles
		addwf	gapcnt,W		; is the line clear?
		btfss	STATUS,C		; skip if so
		goto	intrtn			; just return if not
		bsf		xmtflg,xtbmsg	; set flag to begin transmit acknowledge
		clrf	modtim			; clear mode command timer
		goto	intrtn			; return

;		Clear mode select digits if timeout

clrmod	clrf	modcnt			; clear mode command digit count
		clrf	modsel			; clear mode select
		clrf	rkeycd			; clear received key code
		goto	intrtn			; return with interrupts enabled if falling edge

;		Using line frequency as a reference, adjust clock to 7.68MHz on rising edge

adjclk	bsf		STATUS,RP0		; select Bank 1
		movfw	OSCTUNE			; get present oscillator tune setting
		movwf	nxttune			; save it
		bcf		STATUS,RP0		; back to Bank 0
		btfsc	periodL,7		; round up MSB if LSB > 127
		incf	periodH,F		; MSB = 105 (60Hz) or 130 (50Hz) at 7.68MHz +/- .5%
		movfw	periodH			; get period MSB
		clrf	periodL			; clear period measurement
		clrf	periodH
		addlw	.149			; calculate error at 60Hz: W = 0 at 7.68MHz
		btfsc	STATUS,Z		; skip if clock needs adjusting
		goto	intrtn			; return with interrupts enabled if not
		addlw	.128			; add 128 to set C for negative error (freq too low)
		btfsc	STATUS,C		; skip if error is positive
		goto	adjpos			; increase frequency if negative error

adjneg	decf	frqerr,F		; decrease frequency error count
		btfss	frqerr,6		; adjust frequency if max count
		goto	intrtn			; return with interrupts enabled if not
		movfw	nxttune			; recall oscillator tune setting
		andlw	b'00011111'		; mask all except tune bits
		xorlw	b'00010000'		; check if already at min frequency
		btfss	STATUS,Z		; no adjust if already at minimum
		decf	nxttune,F		; decrease frequency (write at next T1 interrupt)
		goto	rsterr			; preset error word to mid range

adjpos	incf	frqerr,F		; increase frequency error count
		btfss	frqerr,6		; adjust frequency if max count
		goto	intrtn			; return with interrupts enabled if not
		movfw	nxttune			; recall oscillator tune setting
		andlw	b'00011111'		; mask all except tune bits
		xorlw	b'00001111'		; check if already at max frequency
		btfss	STATUS,Z		; no adjust if already at maximum
		incf	nxttune,F		; increase frequency (write at next T1 interrupt)

rsterr	bcf		STATUS,RP0		; select Bank 0
		movlw	b'00100000'		; preset frequency error to mid-range
		movwf	frqerr
		goto	intrtn			; return with interrupts enabled

;		Turn off transmitter for single-phase output 1mS after zero crossing

timint	bcf		PIR1,TMR1IF		; clear Timer 1 interrupt flag
		call	xmtoff			; turn off transmitter

;		Update oscillator tune 1mS after zero crossing

oscadj	bsf		STATUS,RP0		; select Bank 1
		movfw	nxttune			; recall next oscillator tune setting
		movwf	OSCTUNE			; set next frequency
		bcf		STATUS,RP0		; back to Bank 0
		goto	intrtn			; return with interrupts enabled

;		Disable driver for unresolved interrupt

unknwn	call	xmtoff			; make sure transmitter is off

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
;
;digpls	bcf		GPIO,TW523O		; set output pulse low
;		nop
;		nop
;		bsf		GPIO,TW523O		; set output pulse high
;		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'00001011'		; PWM120, DRVINH, TW523 Data Out are outputs
		movwf	TRISIO			; define I/O port directions
		movlw	b'00000001'		; enable interrupt on change for ZCD
		movwf	IOC				; define I/O port directions
		movlw	b'10111111'		; disable pull-ups, other options
		movwf	OPTION_REG		; set option register
		movlw	b'10100110'		; set comparator threshold to 1.25V
		movwf	VRCON
		movlw	b'00000001'		; enable Timer 1 overflow interrupt
		movwf	PIE1
		movlw	.15				; set Timer 2 period for 120KHz PWM
		movwf	PR2
		clrf	ANSEL			; all I/O pins are for digital signals
		bcf		STATUS,RP0		; select Bank 0
		movlw	b'00110000'		; Xmt Inhibit ON, TW523 Data Out LED off
		movwf	GPIO			; initialize I/O port bits
		movlw	b'00010111'		; start with comparator disabled
		movwf	CMCON0
		movlw	b'00000010'		; make sure comparator output is asynchronous
		movwf	CMCON1
		movlw	.10				; set Timer 2 duty cyclefor 120KHz PWM
		movwf	CCPR1L
		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 (120KHz PWM)
		clrf	CCP1CON			; disable PWM mode
		clrf	ADCON0			; disable A/D converter
		movlw	b'00001001'		; set WDT for 2 sec timeout (31KHz / 128x512)
		movwf	WDTCON
		movlw	b'11001000'		; enable peripheral interrupts & interrupt on change
		movwf	INTCON
		movlw	b'00100000'		; preset frequency error to mid-range
		movwf	frqerr
		clrf	stsflg			; clear general status flags
		clrf	xmtflg			; clear transmit status flags
		clrf	rcvsr1			; clear shift register, counters, & other registers
		clrf	rcvsr2			; (in-line code is faster if there is room)
		clrf	rcvsr3
		clrf	xmtsr1
		clrf	xmtsr2
		clrf	xmtsr3
		clrf	rcvcnt
		clrf	cmpcnt
		clrf	onezer
		clrf	onecnt
		clrf	gapcnt
		clrf	modcnt
		clrf	modsel
		clrf	cyccnt
		clrf	adjcnt
		clrf	msgcnt
		clrf	xmtcnt
		clrf	x10cnt
		clrf	seccnt
		clrf	periodH
		clrf	periodL
		clrf	posavgH
		clrf	posavgL
		clrf	negavgH
		clrf	negavgL
		clrf	thdset
		clrf	xhouse
		clrf	xkeycd
		clrf	rkeycd			; initialize received key & house codes
		clrf	oldkey
		clrf	oldhse
		movlw	.60				; 60 cycles = 1 sec.
		movwf	startw			; set startup window for configuration reset to 1 second
		movwf	modtim			; set mode command timer to transmit powerup @ end of window
		movlw	moddef1			; get first mode command default byte
		movwf	modflg1			; set defaults
		movlw	moddef2			; get second mode command default byte
		movwf	modflg2			; set defaults
		movlw	hsedef			; get default housecode
		movwf	hsemod			; set housecode
		movlw	b'00001100'		; housecode P
		movwf	rhouse			; save for power-on acknowledge
		movlw	b'00011011'		; status ON
		movwf	xcmdcd			; save for power-on acknowledge
		bsf		xmtflg,xmtcmd	; set flag to transmit power-on status
		retfie					; return from initialize & enable interrupts


;		SUBROUTINE TO PROCESS RECEIVED DATA
;
;		This routine is called after the end of the X10 reception window.
;		A count of 40 or more in cyccnt normally indicates a logic "1".

;		Process variable decode

rcvinp	bcf		stsflg,rcvbit	; set received bit status to "0"
		bcf		stsflg,inpbit	; also set input bit status to "0"

		btfss	stsflg,dgioin	; skip if a digital input is being processed
		goto	rcvin2			; process powerline signals if not
		btfsc	modflg1,colabt	; skip if collision abort is disabled
		goto	rcvin2			; process powerline signals if not
		btfsc	xmtflg,outbit	; skip if transmitted bit is "0"
		goto	bitone			; force a received "1"
		goto	bitzer			; force a received "0"	

rcvin2	btfss	modflg2,vardcd	; skip if variable decode is selected
		goto	fixdcd			; use fixed 40 cycle decode if not
		movfw	posavgH			; get positive half-cycle average MSB
		btfss	stsflg,cycpol	; skip if positive half-cycle of AC input
		movfw	negavgH			; get negative half-cycle average MSB
		subwf	cyccnt,W		; subtract from new cycle count
		btfss	STATUS,C		; skip if new count is greater than average	
		movlw	.0				; force a "0" if new count is less than average
		movwf	adjcnt			; save for diagnostics
		addlw	.232			; need only 24 more cycles for a "1"
		goto	chkdcd			; process bit

;		Process X10 40-cycle fixed decode

fixdcd	movfw	cyccnt			; get received bit cycle count
		addlw	.216			; C set if => 40 cycles of 120KHz in the window
chkdcd	btfsc	STATUS,C		; skip if not a logic "1"
		goto	bitone			; process the "1" bit

;		Count gap length up to 255 half cycles (clear line)

bitzer	incf	gapcnt,W		; increment gap count
		btfss	STATUS,Z		; skip if no overflow
		movwf	gapcnt			; save new gap count
		movlw	.252			; to check for a 2 cycle gap
		addwf	gapcnt,W		; has there been a 2 cycle gap?
		btfss	STATUS,C		; skip if gap is at least 2 cycles long
		goto	chk60p			; check 60Hz polarity if not
		bcf		xmtflg,colisn	; ensure collision is cleared
		bcf		xmtflg,xmtmsg	; ensure transmit message flag is cleared
		bcf		xmtflg,rptmsg	; ensure repeat message flag is cleared
		bcf		stsflg,dimcmd	; ensure bright/dim command flag is cleared

;		Maintain average cycle counts from background noise with no X10 burst detected.
;		Noise can be different for each-half cycle, and independent averages are kept.

chk60p	btfss	stsflg,cycpol	; skip if positive half-cycle of AC input
		goto	negavg			; maintain negative half-cycle average

posavg	movfw	posavgH			; get positive half-cycle average MSB
		subwf	posavgL,F		; subtract from LSB
		btfss	STATUS,C		; skip if no borrow
		decf	posavgH,F		; propogate borrow to MSB
		movfw	cyccnt			; get received cycle count
		addwf	posavgL,F		; add to positive average LSB
		btfsc	STATUS,C		; skip if no carry
		incf	posavgH,F		; propogate carry to MSB
		bcf		STATUS,C		; restore bit status to "0" for shift
		goto	bitsav			; save bit in shift register

negavg	movfw	negavgH			; get negative half-cycle average MSB
		subwf	negavgL,F		; subtract from LSB
		btfss	STATUS,C		; skip if no borrow
		decf	negavgH,F		; propogate borrow to MSB
		movfw	cyccnt			; get received cycle count
		addwf	negavgL,F		; add to negative average LSB
		btfsc	STATUS,C		; skip if no carry
		incf	negavgH,F		; propogate carry to MSB
		bcf		STATUS,C		; restore bit status to "0" for shift
		goto	bitsav			; save bit in shift register

bitone	bsf		stsflg,inpbit	; set input bit status to "1"
		incf	onecnt,F		; increment number of "1" bits
		clrf	gapcnt			; clear gap count for a "1"
		btfss	xmtflg,xmtmsg	; skip if a message is being transmitted
		bsf		stsflg,linein	; set flag for receiving a powerline signal
		btfss	xmtflg,xmtmsg	; skip if a message is being transmitted
		goto	bitone1			; restore bit status for shift
		btfsc	xmtflg,outbit	; skip if transmitter was off this half cycle
		goto	bitone1			; restore bit status for shift
		bsf		xmtflg,colisn	; indicate a collision was detected
		btfsc	modflg1,colrpt	; skip if no auto-repeat following a collision
		bsf		xmtflg,xmtcmd	; set flag to re-transmit when line is clear
bitone1	bsf		STATUS,C		; restore bit status to "1" for shift
bitsav	rlf		rcvsr1,F		; shift bit into receive shift register
		rlf		rcvsr2,F
		rlf		rcvsr3,F		; C=0 at 22 bits
		movf	rcvcnt,F		; test receive bit count
		btfss	STATUS,Z		; skip & check for start if zero
		goto	rcvchk			; check if received full word
		movfw	rcvsr1			; get most recent 8 bits
		andlw	b'00111111'		; mask all but last 6 bits
		btfsc	STATUS,Z		; skip if not 6 zeros in a row (full gap)
		goto 	rcvgap			; clear counts for a gap
		andlw	b'00001111'		; mask all but last 4 bits
		xorlw	b'00001110'		; check for "start" data pattern (1110)?
		btfss	STATUS,Z		; skip if a "start"?
		goto	rcvclr			; clear receive counts if not
		movlw	.4				; preset rcvcnt to 4 bits for valid "start"
		movwf	rcvcnt
		movlw	.2				; preset 1/0 ratio to 2 (for 1110 start)
		movwf	onezer
		clrf	msgcnt			; no message size determined yet
		clrf	onecnt			; clear count of "1" data bits
		return					; done with bit processing
		
;		Check if all data bits have been received

rcvchk	incf	rcvcnt,F		; increment received bit count
		movfw	rcvcnt			; get received bit count
		addlw	.234			; has it received at least 22 bits?
		btfsc	STATUS,C		; skip and check for errors if not
		goto	rcv22b			; process received data

;		Maintain 1/0 ratio when receiving first half of message

		btfss	rcvsr1,0		; skip if received bit was a "1"
		decf	onezer			; decrement 1/0 ratio if it was a "0"
		btfsc	rcvsr1,1		; skip if received bit was a "0"
		incf	onezer			; increment 1/0 ratio if it was a "1"

;		Check 1/0 pattern for an error (should always be 1-3 after start)

		btfsc	onezer,7		; skip if 1/0 ratio is positive
		goto	errset			; set error flag if negative overflow
		movfw	onezer			; get 1/0 ratio
		btfsc	STATUS,Z		; skip if > 0 (0 = not enough "1"s)
		goto	errset			; set error flag if not enough "1"s
		addlw	.252			; 4 + 252 -> carry set (too many "1"s)
		btfss	STATUS,C		; skip if too many "1"s
		return					; done with bit processing

errset	bsf		stsflg,rcverr	; set error flag
		return					; done with bit processing

;		Check if exactly 22 bits have been received

rcv22b 	btfss	STATUS,Z		; skip if it has received exactly 22 bits
 		goto	rcvout			; output data if not

;		Check for a X10 command storm

rpt22b	btfsc	xmtflg,shutdn	; skip if not already in shutdown
		goto	rcvs10			; set clear line recovery at 10 sec
		movlw	.1				; allow 60 commands per minute average
		btfss	modflg1,xmt3ph	; skip & force low threshold if 3-phase transmit
		btfss	modflg2,hitraf	; skip if high traffic threshold
		movlw	.3				; allow 20 commands per minute average
		addwf	x10cnt,W		; add to X10 traffic count
		btfss	STATUS,C		; skip if X10 transmit storm
		goto	rcvsct			; save X10 traffic count
		bsf		xmtflg,shutdn	; set shutdown flag for command storm

;		Substitue "P Status OFF" for command that tripped the storm threshold

		movlw	b'00111010'		; Start pattern & 1st bit of 1100 P housecode
		movwf	rcvsr3
		movlw	b'10010110'		; Remainder of house code & 1st bit "Status Off"
		movwf	rcvsr2
		movlw	b'10100110'		; Remainder of 11101 "Status Off"
		movwf	rcvsr1

rcvs10	movlw	.10				; set clear line recovery at 10 sec	
rcvsct	movwf	x10cnt			; save X10 traffic count

;		Check that no extra "1" bits were received due to a collision
	
		movfw	onecnt			; get number of "1" bits received
		addlw	.247			; were there 9 "1" bits in the data meassage?
		btfss	STATUS,Z		; skip if exactly 9 data bits
		goto	msgbad			; message is bad if not
		call	unpack			; unpack received word
		movfw	rkeycd			; get received key code
		btfss	xmtflg,xtbmsg	; skip if transmitting an internal message
		movwf	xcmdcd			; save for possible transmission
		xorwf	oldkey,W		; same as the prior key code?
		btfss	STATUS,Z		; skip if so
		goto	rcvcmd			; check for command if different key code
		movfw	rhouse			; get received house code
		xorwf	oldhse,W		; same as prior house code?
		btfss	STATUS,Z		; skip if so
rcvcmd	call	cmdchk			; check for mode command

;		Check for extended message codes (01111 or 11001)

		movlw	b'00001111'		; to check for extended code
		xorwf	rkeycd,W		; was it the extended code?
		btfsc	STATUS,Z		; skip if not
		movlw	b'00011001'		; to check for extended analog data
		xorwf	rkeycd,W		; was it the extended analog data code?
		btfsc	STATUS,Z		; skip if not extended data code
		goto	rcvout			; begin extended code output
		movlw	.44				; 22-bit fixed length message (repeated)
		movwf	msgcnt			; set length

;		Set repeat enable flag in repeat mode for standard 22-bit messages

		btfss	modflg1,rptena	; skip if repeater funtion is enabled
		goto	rcvout			; just set bit status
		btfss	stsflg,linein	; skip if receiving a powerline X10 command
		goto	rcvout			; just set bit status
		btfsc	stsflg,dimcmd	; skip if not a bright/dim sequence
		goto	rcvout			; bright/dim repeats use internal transmit
		movfw	rkeycd			; get received key code
		xorwf	oldkey,W		; same as the prior key code?
		btfsc	STATUS,Z		; skip and set flag to repeat message if not
		btfss	modflg2,onerpt	; skip if no repeat of already repeated message
		bsf		xmtflg,rptmsg	; enable repeat of standard 22-bit message

rcvdim	btfss	modflg1,rptdim	; skip if enable special handling of bright/dim commands
		goto	rcvout			; no special handling of bright/dim commands
		movlw	b'00001001'		; to check for "DIM" command
		xorwf	rkeycd,W		; is it "DIM"?
		btfsc	STATUS,Z		; skip if not "DIM"
		goto	dimset			; set bright/dim if "DIM"
		movlw	b'00001011'		; to check for "BRIGHT" command
		xorwf	rkeycd,W		; is it "BRIGHT"?
		btfsc	STATUS,Z		; skip if not "BRIGHT"
dimset	bsf		stsflg,dimcmd	; set flag indicating received a bright/dim command

rcvout	btfsc	rcvsr3,5		; skip if next bit is a "0"
		bsf		stsflg,rcvbit	; indicate received bit is a "1"

rcvlen	movf	msgcnt,F		; test message length
		btfss	STATUS,Z		; has length been determined?
		goto	chkend			; check end of message if so

		movfw	rcvsr1			; recall most recent 8 bits
		andlw	b'00111111'		; mask all but last 6 bits
		btfss	STATUS,Z		; is it a gap (6 zeros in a row)?
		goto	chknxt			; check for next "start" if not
		movfw	rcvcnt			; get received bit count
		addlw	.16				; 16 bits left in message
		movwf	msgcnt			; set message size
		return

chknxt	andlw	b'00001111'		; mask all but last 4 bits
		xorlw	b'00001110'		; check for next "start" data pattern (1110)?
		btfss	STATUS,Z		; skip if "start"
		return					; done with bit processing if not

		movfw	rcvcnt			; get received bit count
		addlw	.17				; 18 bits left in message (allow for trailing 0)
		movwf	msgcnt			; set message size
		return

msgbad	bsf		stsflg,rcverr	; indicate a receive error for a bad message
		goto	clrmsg			; clear message

chkend	movfw	msgcnt			; recall message size
		subwf	rcvcnt,W		; is message complete (C=1 if so)?
		btfss	STATUS,C		; clear message if so
		return

clrmsg	clrf	rcvsr1			; clear shift register
		clrf	rcvsr2
		clrf	rcvsr3
		btfsc	xmtflg,rptmsg	; skip if not repeating a message
		bcf		xmtflg,xmtmsg	; ensure transmit message flag is cleared
rcvgap	bcf		stsflg,rcverr	; clear error flag
rcvclr	clrf	rcvcnt			; clear counters
		clrf	onecnt
		clrf	onezer			; clear 1/0 ratio
		bcf		stsflg,rcvbit	; set received bit to "0"
		bcf		xmtflg,rptmsg	; ensure repeat message flag is cleared
		return					; done with bit processing


;		SUBROUTINE TO UNPACK RECEIVED DATA
;
;		Decodes received message into house code & key code
;
;		22-bit data in rcvsr3-rcvsr2-rcvsr1: 001110HH HHHHHHKK KKKKKKKK
;		Assumes bank 0 is selected at entry.

unpack	movfw	rhouse			; get prior house code
		movwf	oldhse			; save it
		movfw	rkeycd			; get prior key code
		movwf	oldkey			; save it
		clrf	rhouse			; clear prior house code
		clrf	rkeycd			; clear prior key code
		btfsc	rcvsr3,1		; skip if house code MSB is 0
		bsf		rhouse,3		; set house code MSB
		btfsc	rcvsr2,7		; skip if next house code bit is 0
		bsf		rhouse,2		; set next house code bit
		btfsc	rcvsr2,5		; skip if next house code bit is 0
		bsf		rhouse,1		; set next house code bit
		btfsc	rcvsr2,3		; skip if house code LSB is 0
		bsf		rhouse,0		; set house code LSB 
		btfsc	rcvsr2,1		; skip if key code MSB bit is 0
		bsf		rkeycd,4		; set key code MSB
		btfsc	rcvsr1,7		; skip if next key code bit is 0
		bsf		rkeycd,3		; set next key code bit
		btfsc	rcvsr1,5		; skip if next key code bit is 0
		bsf		rkeycd,2		; set next key code bit
		btfsc	rcvsr1,3		; skip if next key code bit is 0
		bsf		rkeycd,1		; set next key code bit
		btfsc	rcvsr1,1		; skip if key code LSB is 0
		bsf		rkeycd,0		; set key code LSB (function select bit)
		return


;		SUBROUTINE TO PROCESS XTB MODE COMMANDS

;		Requires 9-8-2-X-ON/OFF where X is mode setting to change.
;		Max interval between digits is 4 seconds.
;		An incorrect digit will cause an error.
;		Keys cannot be accepted faster than X10 transmit - about .5 sec each.
;
;			16 enables repeater function
;			15 (TBD) Unit now handes Boost & TW523 without switching modes
;			14 enables transmit on all 3 phases (X10 boost, digital, & repeat)
;			13 selects delayed transmit to reduce potential for "dimmer flicker"
;			12 selects transmit at reduced power level (default for 3-phase xmit)
;			11 enables auto re-transmit of repeat following collision
;			10 selects abort transmission when collision is detected
;			 9 enables special handling when repeating bright/dim commands
;			 8 selects only one repeat for each command to prevent repeater ping-pong
;			 7 sets storm threshold 60/min. avg; OFF allows 20/min. avg.
;			 6 enables AGC (OFF for low gain fixed threshold)
;			 5 selects high AGC threshold or MSB of fixed threshold
;			 4 selects alternate AGC window or LSB of fixed threshold
;			   fixed threshold:	5-OFF	4-OFF	  0 mV threshold
;								5-OFF	4-ON	200 mV threshold
;								5-ON	4-OFF	400 mV threshold
;								5-ON	4-ON	600 mV threshold
;			 3 selects variable decode rather than fixed 40 cycles
;			 2 used to program housecode after ALL OFF reset (9-8-2-2-ON)
;			 1 selects return all bits received (no error checking)


cmdchk	movf	startw,F		; is startup window is over?
		btfsc	STATUS,Z		; skip if not
		goto	cmdcnt			; check for mode command if so
		movlw	b'00000001'		; to test for "All Units Off"
		movwf	xcmdcd			; save it to transmit acknowledgement
		xorwf	rkeycd,W		; is it the correct command?
		btfss	STATUS,Z		; skip if so
		goto	cmdbad			; invalid command if not

		clrf	startw			; clear startup window
		bsf		hsemod,hseulk	; unlock housecode for change
		movlw	.240			; set mode command timeout for 4 seconds
		movwf	modtim			; start timer
		goto	resmod			; restore default to EEPROM

cmdcnt	movfw	modcnt			; get number of valid digits received
		movwf	temp1			; save in temp
		movlw	b'00001110'		; to check for "9"
		btfsc	STATUS,Z		; skip if at least one digit was received
		goto	chkdgt			; check first digit
		movlw	b'00011010'		; to check for "8"
		decf	temp1,F			; was just one digit received?
		btfsc	STATUS,Z		; skip if not
		goto	chkdgt			; check second digit
		movlw	b'00011100'		; to check for "2"
		decf	temp1,F			; were just two digits received?
		btfsc	STATUS,Z		; skip if not
		goto	chkdgt			; check third digit
		decf	temp1,F			; were just three digits received?
		btfss	STATUS,Z		; skip & get mode select if so
		goto	cmdset			; check for on/off set function
		btfss	hsemod,hseulk	; skip if housecode is not locked
		goto	cmdmod			; get mode select
		movlw	b'00000101'		; to check for "ON" command
		xorwf	rkeycd,W		; is it "ON"?
		btfsc	STATUS,Z		; skip if not "ON"
		goto	chgcod			; change housecode
		bcf		hsemod,hseulk	; lock housecode to prevent it from being changed
cmdmod	btfsc	rkeycd,0		; skip if keycode is a digit
		goto	cmdbad			; invalid code if not
		movfw	rkeycd			; get key code (1-16)
		movwf	modsel			; save as mode select
		goto	nxtkey			; digit accepted & start timeout
cmdset	movlw	.120			; set mode command timeout for 2 seconds
		movwf	modtim			; start timer
		movlw	b'00000101'		; to check for "ON" command
		xorwf	rkeycd,W		; is it "ON"?
		btfsc	STATUS,Z		; skip if not "ON"
		goto	modset			; set selected mode bit
		movlw	b'00000111'		; to check for "OFF" command
		xorwf	rkeycd,W		; is it "OFF"?
		btfsc	STATUS,Z		; skip if not "OFF"
		goto	modclr			; clear selected mode bit
		movlw	b'00011111'		; to check for "Status Request" command
		xorwf	rkeycd,W		; is it "Status Request"?
		btfsc	STATUS,Z		; skip if not
		goto	modack			; check and acknowledge mode status

cmdbad	clrf	modcnt			; clear mode command digit count
		clrf	modtim			; clear mode command timer
		clrf	modsel			; clear mode select
		return

chkdgt	xorwf	rkeycd,W		; is it the correct digit?
		btfss	STATUS,Z		; skip if so
		goto	cmdbad			; invalid mode command
		btfsc	hsemod,hseulk	; skip if housecode is locked
		goto	nxtkey			; accept all housecodes when unlocked
		movfw	rhouse			; get received housecode
		xorwf	hsemod,W		; is it accepted to program mode commands?
		btfss	STATUS,Z		; skip if so
		goto	cmdbad			; reject command if not
nxtkey	incf	modcnt,F		; indicate correct digit was received
		movlw	.240			; set mode command timeout for 4 seconds
		movwf	modtim			; start timer
		return


;		Change housecode accepted for ON and OFF mode commands

chgcod	movfw	rhouse			; get housecode (MSB is zero)
		movwf	hsemod			; set & lock housecode accepted for mode commands
		bsf		STATUS,RP0		; select Bank 1
		movlw	EEHSE			; point to housecode address in EEPROM
		movwf	EEADR			; set EEPROM address to write
		movfw	hsemod			; recall housecode from global RAM
		call	writee			; write housecode into EEPROM
		bcf		STATUS,RP0		; back to Bank 0
		clrf	modcnt			; clear mode command digit count
		movlw	b'00011011'		; to acknowledge "Status ON"
		movwf	xcmdcd			; save it to be transmitted
		bsf		xmtflg,xmtcmd	; set flag to transmit acknowledgement
		return


;		SUBROUTINE TO SET SELECTED MODE BIT

;		Enter with modsel = mode bit to set (0-15)

modset	rrf		modsel,W		; get mode select & shift right
		call	x10dec			; convert X10 code to decimal value
		movwf	temp0			; save in temporary workspace
		clrf	temp1			; clear bit pointer
		btfsc	temp0,3			; skip if decimal value is 0-7
		goto	modset2			; set flag in mode word 2 if 8-15
		incf	temp0,F			; adjust 0-7 to 1-8
		bsf		STATUS,C		; set carry bit to shift 1 into LSB
modset1	rlf		temp1,F			; shift bit pointer left
		decfsz	temp0,F			; decrement and skip if bit pointer is correct
		goto	modset1			; loop until correct bit
		movfw	temp1			; get bit to set
		iorwf	modflg2,F		; set flag bit
		goto	savmod			; write new mode status into EEPROM
		
modset2	movlw	.249			; add 249 to decimal	
		addwf	temp0,F			; adjust 8-15 to 1-8 (sets carry)
modset3	rlf		temp1,F			; shift bit pointer left
		decfsz	temp0,F			; decrement and skip if bit pointer is correct
		goto	modset3			; loop until correct bit
		movfw	temp1			; get bit to set
		iorwf	modflg1,F		; set flag bit
		goto	savmod			; write new mode status into EEPROM


;		SUBROUTINE TO CLEAR SELECTED MODE BIT

;		Enter with modsel = mode bit to clear (0-15)

modclr	rrf		modsel,W		; get mode select & shift right
		call	x10dec			; convert X10 code to decimal value
		movwf	temp0			; save in temporary workspace
		clrf	temp1			; clear bit pointer
		btfsc	temp0,3			; skip if decimal value is 0-7
		goto	modclr2			; set flag in mode word 2 if 8-15
		incf	temp0,F			; adjust 0-7 to 1-8
		bsf		STATUS,C		; set carry bit to shift 1 into LSB
modclr1	rlf		temp1,F			; shift bit pointer left
		decfsz	temp0,F			; decrement and skip if bit pointer is correct
		goto	modclr1			; loop until correct bit
		movfw	temp1			; get bit to clear
		xorlw	b'11111111'		; flip all bits
		andwf	modflg2,F		; clear flag bit
		goto	savmod			; write new mode status into EEPROM
		
modclr2	movlw	.249			; add 249 to decimal	
		addwf	temp0,F			; adjust 8-15 to 1-8 (sets carry)
modclr3	rlf		temp1,F			; shift bit pointer left
		decfsz	temp0,F			; decrement and skip if bit pointer is correct
		goto	modclr3			; loop until correct bit
		movfw	temp1			; get bit to clear
		xorlw	b'11111111'		; flip all bits
		andwf	modflg1,F		; clear flag bit
		goto	savmod			; write new mode status into EEPROM


;		Write mode flags & XTB housecode into EEPROM

savmod	bsf		xmtflg,xmtcod	; set flag to transmit keycode number
resmod	bsf		xmtflg,xmtcmd	; set flag to transmit status
		clrf	modcnt			; clear mode command digit count

		bsf		STATUS,RP0		; select Bank 1
		movlw	EEMODE			; get mode flags address in EEPROM
		movwf	EEADR			; set EEPROM address to read
		movfw	modflg1			; get 1st bank of mode flags in RAM
		call	writee			; write new mode select into EEPROM

		incf	EEADR,F			; set next EEPROM address to read
		movfw	modflg2			; get 2nd bank of mode flags in RAM
		call	writee			; write new mode select into EEPROM

		incf	EEADR,F			; set next EEPROM address to read
		movfw	hsemod			; get XTB housecode from RAM
		andlw	b'00001111'		; mask MSBs
		call	writee			; write housecode into EEPROM
		bcf		STATUS,RP0		; back to Bank 0

;		SET PWM POWER LEVEL

pwmpwr	btfsc	modflg1,xmtlow	; skip if not transmit at reduced power
		goto	lowpwm			; set low power PWM width for reduced power
		btfss	modflg1,xmt3ph	; skip if 3-phase transmit is selected
		goto	nrmpwm			; set normal PWM width if not
lowpwm	movlw	.12				; for 2.08uS low power PWM pulsewidth
		goto	setpwm			; set PWM pulsewith
nrmpwm	movlw	.10				; for 3.12uS default PWM pulsewidth
setpwm	movwf	CCPR1L			; set correct PWM duty cycle
		return					; done


;		Perform the actual write into EEPROM

;		Address must be set up in EEADR before called
;		Data passed in W is only written if EEPROM data must be changed

writee	bsf		EECON1,RD		; read EEPROM location
		xorwf	EEDAT,F			; does data have to be changed?
		btfsc	STATUS,Z		; skip & write data if so
		return					; no write is necessary

		movwf	EEDAT			; save data to write to EEPROM
		bsf		EECON1,WREN		; enable write
disint	bcf		INTCON,GIE		; disable interrupts
		btfsc	INTCON,GIE		; skip if they are disabled
		goto	disint			; try again
		movlw	h'55'			; write 55 AA pattern to unlock write
		movwf	EECON2
		movlw	h'AA'
		movwf	EECON2
		bsf		EECON1,WR		; start write
		bsf		INTCON,GIE		; enable interrupts

savdly	btfsc	EECON1,WR		; skip if write has completed
		goto	savdly			; wait for EEPROM write to complete
		bcf		EECON1,WREN		; disable write
		return


;		SUBROUTINE TO CHECK & ACKNOWLEDGE STATUS OF SELECTED MODE BIT

;		Enter with modsel = X10 keycode for mode bit to check
;		Returns xcmdcd = X10 keycode for Status ON or Status OFF

modack	rrf		modsel,W		; get mode select & shift right
		andlw	b'00001111'		; mask all but 4 LSBs
		call	x10dec			; convert X10 code to decimal value
		movwf	temp0			; save in temporary workspace
		btfsc	temp0,3			; skip if decimal value is 0-7
		goto	modack1			; check flag in mode word 2 if 8-15
		incf	temp0,F			; adjust 0-7 to 1-8
		movfw	modflg2			; get 2nd bank of mode flags
		goto	modack2			; check bit status
		
modack1	movlw	.249			; add 249 to decimal	
		addwf	temp0,F			; adjust 8-15 to 1-8
		movfw	modflg1			; get 1st bank of mode flags
modack2	movwf	temp1			; save in temp workspace
modack3	rrf		temp1,F			; shift LSB of workspace into C
		decfsz	temp0,F			; decrement and skip if C is correct status bit
		goto	modack3			; loop until correct bit

		movlw	b'00011011'		; to acknowledge "Status ON"
		btfss	STATUS,C		; skip if selected mode is ON
		movlw	b'00011101'		; to acknowledge "Status OFF"
		movwf	xcmdcd			; save it to be transmitted
		bsf		xmtflg,xmtcmd	; set flag to transmit status
		return


;		SUBROUTINE TO PROCESS TRANSMIT DATA
;
;		This routine is called to set up the next bit to be transmitted

xmtbit	btfss	xmtflg,xmtcod	; skip if keycode must be transmitted
		goto	xmtccd			; just transmit command code if not
		movf	xmtcnt,F		; check if message must be loaded
		btfss	STATUS,Z		; skip if so
		goto	xmtout			; output next bit if not

		call	modack			; check and acknowledge mode status
		movlw	.228			; for 28 bits left (22 data + 6 gap)
		addwf	xmtcnt,W		; check if 2nd half must be loaded
		btfsc	STATUS,Z		; skip if not
		goto	xmtnu2			; set up 2nd half
		movfw	rhouse			; get received house code
		movwf	xhouse			; save it for transmit
		movfw	modsel			; get mode select (X10 code)
		movwf	xkeycd			; save it for transmit
		movlw	.50				; transmit 50 bits (22 + 22 + 6 gap)
		movwf	xmtcnt			; set up transmit count
xmtnu2	call	packit			; assemble 1st half of transmit message
		goto	xmtout			; output first bit

xmtccd	movf	xmtcnt,F		; check if message must be loaded
		btfss	STATUS,Z		; skip if so
		goto	xmtout			; output next bit if not

		movlw	.228			; for 28 bits left (22 data + 6 gap)
		addwf	xmtcnt,W		; check if 2nd half must be loaded
		btfsc	STATUS,Z		; skip if not
		goto	xmtst2			; set up 2nd half
		movfw	rhouse			; get received house code
		movwf	xhouse			; save it for transmit
		movfw	xcmdcd			; get mode ON/OFF Status (X10 code)
		movwf	xkeycd			; save it for transmit
		movlw	.50				; transmit 50 bits (22 + 22 + 6 gap)
		movwf	xmtcnt			; set up transmit count
xmtst2	call	packit			; assemble 1st half of transmit message

xmtout	bcf		xmtflg,outbit	; make sure output bit has been cleared
		btfsc	xmtsr3,5		; skip if next bit is a "0"
		bsf		xmtflg,outbit	; set output bit to a logic "1"
		bcf		STATUS,C		; make sure "C" is zero
		rlf		xmtsr1,F		; shift "0" into transmit shift register
		rlf		xmtsr2,F
		rlf		xmtsr3,F
		decf	xmtcnt,F		; decrement transmit bit count
		btfss	STATUS,Z		; skip if end of message
		return					; just return if not

		btfsc	xmtflg,xmtcod	; skip if not transmit keycode
		goto	xmtnu0			; just clear acknowledge number flag
		bcf		xmtflg,xmtmsg	; clear transmit message flag
		bcf		xmtflg,xtbmsg	; clear internally generated message flag
		bcf		xmtflg,xmtcmd	; clear transmit command flag
xmtnu0	bcf		xmtflg,xmtcod	; clear transmit keycode flag
		return


;		SUBROUTINE TO ASSEMBLE SHIFT REGISTER DATA FOR TRANSMIT
;
;		Encodes house code and key code into transmit message
;			xhouse = house code for transmit
;			xkeycd = key code for transmit
;		22-bit data in xmtsr3-xmtsr2-xmtsr1: 001110HH HHHHHHKK KKKKKKKK
;		Assumes bank 0 is selected at entry.

packit	movlw	b'00001110'		; set up 4-bit start pattern
		movwf	xmtsr3			; save in MSB of transmit shift register
		movfw	xhouse			; get 4-bit house code
		call	expx10			; convert to expanded X10 code
		movwf	xmtsr2			; save in 2nd byte of transmit shift register
		rrf		xkeycd,W		; get 5-bit key code & shift off command/function bit
		call	expx10			; convert remaining 4 bits to expanded X10 code
		movwf	xmtsr1			; save in LSB of transmit shift register

		bcf		STATUS,C		; make sure carry bit is cleared
		btfss	xkeycd,0		; skip if command/function bit is "1"
		bsf		xmtsr3,7		; set MSB to roll a "0-1" into command/function bits
		btfsc	xkeycd,0		; skip if command/function bit "0"
		bsf		STATUS,C		; set carry to roll a "1-0" into command/function bits

		rlf		xmtsr1,F		; roll 1st command/function bit into LSB of shift register
		rlf		xmtsr2,F
		rlf		xmtsr3,F		; shifts 2nd command/function bit into carry
		rlf		xmtsr1,F		; and around into LSB of transmit shift register
		rlf		xmtsr2,F
		rlf		xmtsr3,F
		return


;		SUBROUTINES TO CONTROL 120KHZ PWM TRANSMITTER

;		Control 120KHz by TW523 Data Input

chkdin	btfsc	xmtflg,xtbmsg	; continue if not transmitting an internal message
		return
		btfsc	xmtflg,rptmsg	; continue if not repeating a message
		return
chkd3p	btfsc	GPIO,TW523I		; skip if TW523 input is ON (transmit a logic "1")
		goto	xmtoff			; ensure PWM transmitter is off
		goto	xmtond			; ensure PWM transmitter is on for digital input

;		Control 120KHz by X10 Boost Input

chkxin	btfsc	xmtflg,xtbmsg	; continue if not transmitting an internal message
		return
		btfsc	xmtflg,rptmsg	; continue if not repeating a message
		return
chkx3p	btfsc	CCP1CON,CCP1M2	; skip if PWM transmitter is not on yet
		return
		movlw	.10				; number of loops looking for a rising edge of 120KHz
		movwf	cmpcnt			; set comparator cycle count (.5uS per instruction)

chkx00	btfss	CMCON0,COUT		; skip if X10 comparator is one (2uS per loop)
		goto	chkx01			; check for rising edge if zero
		decfsz	cmpcnt			; tick off one cycle & skip if timeout
		goto	chkx00			; loop until timeout
		return

chkx01	btfsc	CMCON0,COUT		; skip if X10 comparator is zero (2uS per loop)
		goto	xmtonx			; ensure PWM transmitter is on for rising edge on X10 input
		decfsz	cmpcnt			; tick off one cycle & skip if timeout
		goto	chkx01			; loop until timeout
		return

;		Ensure flags are set to indicate source of X10 signal

xmtond	bsf		stsflg,dgioin	; make sure digital input flag is set
		goto	xmton			; make sure transmitter is on

xmtonx	bsf		stsflg,bx10in	; make sure X10 input flag is set

;		Turn on 120KHz PWM transmitter

xmton	movlw	b'00010111'		; disable comparator
		movwf	CMCON0
		btfsc	CCP1CON,CCP1M2	; skip if PWM transmitter is off
		goto	xmton1			; check if DRVINH should be switched off
		movlw	b'00001100'		; set PWM active high mode
		movwf	CCP1CON
		movlw	H'FF'			; set Timer 2 to 1 count before overflow
		movwf	TMR2
		bsf		T2CON,TMR2ON	; start PWM timer
		movlw	.3				; delay for PWM to stabilize
		movwf	temp0
x10ody	decfsz	temp0,F
		goto	x10ody
		bsf		xmtflg,outbit	; set flag indicating a one is being transmitted
		bsf		xmtflg,xmtmsg	; ensure flag indicates a message is being transmitted

;	PATCH TO DELAY TRANSMITTER ON

xmton1	btfss	modflg1,xmtdly	; skip if transmit burst should be delayed
		goto	xmton2			; no delay if not
		movfw	TMR1H			; get Timer 1 high byte
		addlw	.7				; at beginning of delayed transmit window?
		btfsc	STATUS,C		; skip if not
		goto	xmton2			; turn off drive inhibit
		addlw	.210			; before next zero crossing?
		btfsc	STATUS,C		; skip if so
		return					; just return if not

xmton2	btfsc	xmtflg,shutdn	; skip if no shutdown due to command storm
		return					; leave DRVINH on
		btfss	xmtflg,colisn	; skip if a collision has occurred
		goto	xmton3			; make sure DRVINH is off
		btfsc	modflg1,colabt	; skip if no abort on a collision
		return					; leave DRVINH on

xmton3	bcf		GPIO,DRVINH		; make sure drive inhibit is off
		return

;		Turn off 120KHz PWM transmitter

xmtoff	btfss	CCP1CON,CCP1M2	; skip if PWM transmitter is on
		return					; just return if already off
		bsf		GPIO,DRVINH		; turn on drive inhibit
		bcf		T2CON,TMR2ON	; stop PWM timer
		clrf	CCP1CON			; turn off PWM
		bsf		GPIO,PWM120		; set receiver detect output high
		return


; MAIN PROGRAM TIMING LOOP

; This loops through completely for each half cycle of the powerline

start	call	initiz			; call initialization sequence
		goto	main			; go to main timing loop

;		Disable Timer 1 and inhibit driver when zero-crossing is not detected

main00	bcf		T1CON,TMR1ON	; turn off Timer 1
		clrf	TMR1H			; clear Timer 1
		clrf	TMR1L

;		Main timing loop

main	bcf		STATUS,RP0		; ensure Bank 0 is selected
		bsf		GPIO,DRVINH		; ensure drive inhibit is on & 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	movfw	TMR1H			; get Timer 1 high byte
		btfsc	STATUS,Z		; skip if Timer 1 is running
		goto	mainzc			; loop & wait for zero-crossing if Timer 1 is not running

; -----	Stargate Patch begin ------

main01	btfss	T1CON,TMR1ON	; skip if Timer 1 is running
		goto	main			; stop and wait for next zero crossing if not
		movfw	TMR1H			; get Timer 1 high byte
		addlw	.204			; .2 mS before the next zero crossing? (at 7.68MHz clk)
		btfss	STATUS,C		; skip and output digital data if so
		goto	main01			; loop until until just before ZCD

;		Send input bit to digital I/O port early to accommodate Stargate

		bsf		GPIO,TW523O		; make sure TW523 output pulse is high
		btfss	modflg2,allbit	; skip if receiving all bits
		goto	outdig			; output error checked digital data if not
		btfsc	stsflg,inpbit	; skip if input bit status is "0"
		bcf		GPIO,TW523O		; set output pulse low for a "1"
		goto	main02			; done with digital output
outdig	btfsc	stsflg,rcvbit	; skip if received bit is a "0"
		bcf		GPIO,TW523O		; set output pulse low for a "1"

; -----	Stargate Patch end ------

main02	movfw	TMR1H			; get Timer 1 high byte
		addlw	.10				; check for timer preload
		btfsc	STATUS,C		; skip if not
		goto	main03			; begin processing next half cycle if so

		addlw	.178			; past next transmit window with no zero crossing?
		btfss	STATUS,C		; skip if so
		goto	main02			; loop while waiting for zero crossing
		goto	main00			; stop & disable transmitter if no zero crossing

;		If transmitter is off, enable comparator at beginning of the reception window

;		Start checking X10 Boost input, & switch to powerline at start of reception window
;		If repeating a bright/dim command, check powerline instead of X10 Boost input.

main03	btfsc	CCP1CON,CCP1M2	; skip if PWM transmitter is off
		goto	dlyxmt			; no pre-window source check if transmitting
		bsf		GPIO,DRVINH		; make sure drive inhibit is on
		bsf		STATUS,RP0		; select Bank 1
		movlw	b'10100110'		; set comparator threshold is 1.25V
		movwf	VRCON
		bcf		STATUS,RP0		; back to Bank 0

		btfsc	xmtflg,xtbmsg	; skip if not transmitting an internal message
		goto	dlyxmt			; no pre-window source check if transmitting a message
		movlw	b'00010011'		; insure comparator is enabled (inverting mode)
		movwf	CMCON0
		movlw	.8				; need 4 cycles during pre-window to recognize X10 boost input
		movwf	cmpcnt			; pre-set comparator count

;		Check source of X10 signal until reception window starts 266 uS after zero crossing

		movf	startw,F		; is startup window over?
		btfss	STATUS,Z		; skip if so
		goto	chkx10			; just check X10 boost input for ALL OFF during startup window
		movlw	allsrc			; to check if a X10 signal is already being processed
		andwf	stsflg,W		; check all receive flags
		btfsc	STATUS,Z		; skip if a X10 command is already being processed
		goto	chksrc			; check for a new X10 command prior to reception window

		btfsc	stsflg,bx10in	; skip if not already receiving a X10 Boost input
		goto	chkx10			; check for next X10 Boost input bit
		btfsc	stsflg,dgioin	; skip if not already receiving a Digital input
		goto	bgnwin			; no need to wait for window if digital input
		btfss	stsflg,dimcmd	; skip if receiving a bright/dim command
		goto	dlywin			; delay for reception window if normal powerline signal
		btfsc	xmtflg,rptmsg	; skip if dim command is not already being repeated
		goto	bgnwin			; no need to wait for window if processing a dim command
		movfw	thdset			; recall present threshold level
		bsf		STATUS,RP0		; select Bank 1
		movwf	VRCON			; set comparator threshold
		bcf		STATUS,RP0		; back to Bank 0
		movlw	b'00010011'		; insure comparator is enabled (inverting mode)
		movwf	CMCON0
		clrf	TMR0			; clear Timer 0 to check for early bright/dim burst

;		Check for early X10 burst if receiving a bright/dim sequence

chkdim	btfss	T1CON,TMR1ON	; skip if Timer 1 is running
		goto	main			; stop and wait for next zero crossing if not

		movfw	TMR1H			; get Timer 1 high byte
		addlw	.6				; end of pre-window check?
		btfsc	STATUS,C		; skip and check for early burst if not
		goto	bgnwin			; begin sampling powerline during window

		movfw	TMR0			; read Timer 0 count
		addlw	.244			; check for 12 burst cycles in the pre-window
		btfss	STATUS,C		; skip if received 10 burst cycles
		goto	chkdim			; loop looking for "pre-window" bright/dim start pattern

		call	xmton			; turn on transmitter
		bcf		GPIO,TW523O		; set output pulse low for a "1"
		movfw	rhouse			; get received house code
		movwf	xhouse			; save it for transmit
		movfw	xcmdcd			; get bright/dim code
		movwf	xkeycd			; save it for transmit
		call	packit			; assemble transmit message
		bcf		STATUS,C		; clear carry bit
		movfw	xmtsr1			; move dim command into receive shift register
		movwf	rcvsr1
		movfw	xmtsr2	
		movwf	rcvsr2
		movfw	xmtsr3	
		movwf	rcvsr3
		movlw	.22				; preset rcvcnt to 21 bits to process dim command when called
		movwf	rcvcnt
		movlw	.2				; set 1/0 ratio to 2 (for 1110 start)
		movwf	onezer
		movlw	.44				; set 44-bit standard message length
		movwf	msgcnt
		movlw	.9				; set 9 "1" bits for no error
		movwf	onecnt
		clrf	cyccnt			; clear cycle count to shift in a trailing zero
		call	rpt22b			; set up dim to be repeated
		bsf		xmtflg,rptmsg	; also set flag to indicate a repeat is in process
		goto	bgnwin			; no need to wait for window

;		Check for X10 Boost input if already processing a X10 Boost message

chkx10	movfw	TMR1H			; get Timer 1 high byte
		addlw	.6				; at beginning of X10 reception window?
		btfsc	STATUS,C		; skip and continue checking source if not
		goto	bgnwin			; switch to monitoring the powerline & begin reception window

		btfss	PIR1,CMIF		; skip if a change is detected on the X10 Boost input
		goto	chkx10			; loop if not
		movfw	CMCON0			; clear the change
		bcf		PIR1,CMIF		; clear the flag bit
		decfsz	cmpcnt			; decrement X10 Boost input change count & skip if zero
		goto	chkwin			; continue if not enough cycles to trigger a transmit
		goto	xxmton			; set flag for x10 boost input & turn on transmitter

;		Check source of X10 signal if not currently processing a message

chksrc	btfss	GPIO,TW523I		; skip if TW523 input is off (transmit a logic "0")
		goto	dxmton			; indicate source is data input port & turn on transmitter

chkwin	movfw	TMR1H			; get Timer 1 high byte
		addlw	.6				; at beginning of X10 reception window?
		btfsc	STATUS,C		; skip and continue checking source if not
		goto	bgnwin			; switch to monitoring the powerline & begin reception window

		btfss	PIR1,CMIF		; skip if a change is detected on the X10 Boost input
		goto	chksrc			; check for TW523 input if not
		movfw	CMCON0			; clear the change
		bcf		PIR1,CMIF		; clear the flag bit
		decfsz	cmpcnt			; decrement X10 Boost input change count & skip if zero
		goto	chkwin			; continue if not enough cycles to trigger a transmit

xxmton	call	xmtonx			; set flag for x10 boost input & turn on transmitter
		goto	dlyxmt			; turn off XMTINH for a delayed transmit burst

dxmton	call	xmtond			; set flag for digital input & turn on transmitter

; -----	Flicker Patch begin ------

;		Turn off XMTINH if transmitting

dlyxmt	btfss	CCP1CON,CCP1M2	; skip if PWM transmitter is on
		goto	dlywin			; leave XMTINH on if not transmitting
		btfss	modflg1,xmtdly	; skip if transmit burst should be delayed
		goto	bgnwin			; no need to wait for window if digital input
dlyxlp	movfw	TMR1H			; get Timer 1 high byte
		addlw	.7				; at beginning of delayed transmit window?
		btfss	STATUS,C		; skip if so
		goto	dlyxlp			; wait for begining of transmit window
		call	xmton2			; turn off XMTINH
		goto	bgnwin			; no need to wait for window if digital input

; -----	Flicker Patch end ------

;		Delay until beginning of X10 reception window

dlywin	movfw	TMR1H			; get Timer 1 high byte
		addlw	.6				; at beginning of X10 reception window?
		btfss	STATUS,C		; skip if so
		goto	dlywin			; wait for begining of reception window

;		Start X10 reception window

bgnwin	movfw	thdset			; recall present threshold level
		bsf		STATUS,RP0		; select Bank 1
		movwf	VRCON			; set comparator threshold
		bcf		STATUS,RP0		; back to Bank 0
		btfss	CCP1CON,CCP1M2	; skip if PWM transmitter is on
		bsf		GPIO,DRVINH		; make sure drive inhibit is on
		movlw	b'00010011'		; insure comparator is enabled (inverting mode)
		movwf	CMCON0
		clrf	TMR0			; clear Timer 0 at start of window
		clrf	cyccnt			; clear cycle count for next reception window

;		Wait for end of reception window 933uS after zero crossing

main05	btfss	T1CON,TMR1ON	; skip if Timer 1 is running
		goto	main			; stop and wait for next zero crossing if not

		btfsc	stsflg,linein	; skip if not receiving a powerline signal
		goto	main06			; only count powerline cycles
		btfss	stsflg,bx10in	; skip if receiving a X10 boost input
		call	chkdin			; check digital input

main06	movfw	TMR1H			; get Timer 1 high byte
		addlw	.2				; past the X10 reception window?
		btfss	STATUS,C		; skip if so
		goto	main05			; loop until end of reception window

		movfw	TMR0			; read Timer 0 count (=> 40 cycles for logic 1)
		movwf	cyccnt			; save count for later processing

;		Delay until timer overflow (end of X10 transmit window)

main07	btfss	T1CON,TMR1ON	; skip if Timer 1 is running
		goto	main			; stop and wait for next zero crossing if not
		btfsc	TMR1H,7			; check for timer overflow
		goto	main07			; loop until timer overflow

;		Shut off PWM & digital output at end of X10 window

		bsf		GPIO,DRVINH		; turn on drive inhibit at end of X10 window
		clrf	CCP1CON			; turn off PWM (also indicates xmtr is off)
		movlw	b'00010111'		; disable comparator
		movwf	CMCON0
		btfss	modflg2,allbit	; skip if receiving all bits
		bsf		GPIO,TW523O		; make sure TW523 output pulse is high

;		Check AGC sample point

		btfss	modflg2,agcena	; skip if AGC is enabled
		goto	main20			; check for 3-phase transmission
		btfss	modflg2,altagc	; skip if alternate AGC sample point
		goto	main20			; check for 3-phase transmission

;		Sample AGC (alternate window) 1.4 mS to 2.5 mS after zero crossing

main10	btfss	T1CON,TMR1ON	; skip if Timer 1 is running
		goto	main			; stop and wait for next zero crossing if not
		movfw	TMR1H			; get Timer 1 high byte
		addlw	.253			; is it 1.56 mS after the last zero crossing (at 7.68MHz clk)
		btfss	STATUS,C		; skip to begin AGC sample if so
		goto	main10			; loop if not

		movlw	.247			; sample AGC until 2.34 mS after the last zero crossing
		movwf	agcend			; set up AGC cycle end
		call	agcloop			; run AGC loop

;		Check if 3-phase transmission is enabled

main20	btfss	modflg1,xmt3ph	; skip if 3-phase transmit is selected
		goto	main40			; just check AGC window if no 3-phase is selected
		btfsc	xmtflg,rptmsg	; skip if a message is not being repeated
		goto	main22			; wait for beginning of 2nd 3-phase window

;		Set comparator to monitor X10 Boost input
	
		bsf		STATUS,RP0		; select Bank 1
		movlw	b'10100110'		; make sure comparator threshold is 1.25V
		movwf	VRCON
		bcf		STATUS,RP0		; back to Bank 0

;		Check X10 & Digital In during 2nd 3-Phase Window

main22	btfss	T1CON,TMR1ON	; skip if Timer 1 is running
		goto	main			; stop and wait for next zero crossing if not
		movfw	TMR1H			; get Timer 1 high byte
		addlw	.244			; past the start of the 2nd 3-phase window?
		btfss	STATUS,C		; skip if so
		goto	main22			; loop until the beginning of the window

		btfss	xmtflg,xtbmsg	; skip if transmitting an internal message
		goto	main23			; check if repeating
		btfsc	xmtflg,outbit	; skip if output bit is zero
		call	xmton			; turn on the transmitter
		goto	main25			; ignore inputs while transmitting

main23	btfss	xmtflg,rptmsg	; skip if repeating a message
		goto	main24			; check other inputs
		btfsc	stsflg,rcvbit	; skip if repeated bit is "0"
		call	xmton			; turn on the transmitter
		goto	main25			; ignore inputs while repeating
	
main24	btfsc	xmtflg,xtbmsg	; skip if not transmitting an internal message
		goto	main25			; ignore inputs while transmitting
		btfsc	xmtflg,rptmsg	; skip if not repeating a message
		goto	main25			; ignore inputs while repeating
		
		movlw	b'00010011'		; insure comparator is enabled (inverting mode)
		movwf	CMCON0
		btfsc	stsflg,dgioin	; skip if not receiving a digital input
		call	chkdin			; check digital input
		btfsc	stsflg,bx10in	; skip if not receiving a X10 boost input
		call	chkxin			; check X10 boost input

main25	btfss	T1CON,TMR1ON	; skip if Timer 1 is running
		goto	main			; stop and wait for next zero crossing if not
		movfw	TMR1H			; get Timer 1 high byte
		addlw	.236			; past the end of the 2nd 3-phase window?
		btfss	STATUS,C		; skip if so
		goto	main24			; loop until end of the window

		bsf		GPIO,DRVINH		; ensure drive inhibit is on end of 2nd 3-phase window
		clrf	CCP1CON			; turn off PWM (also indicates xmtr is off)

;		Check X10 & Digital In during 3rd 3-Phase Window

main32	btfss	T1CON,TMR1ON	; skip if Timer 1 is running
		goto	main			; stop and wait for next zero crossing if not
		movfw	TMR1H			; get Timer 1 high byte
		addlw	.223			; past the start of the 3rd 3-phase window?
		btfss	STATUS,C		; skip if so
		goto	main32			; loop until the beginning of the window

		btfss	xmtflg,xtbmsg	; skip if transmitting an internal message
		goto	main33			; check if repeating
		btfsc	xmtflg,outbit	; skip if output bit is zero
		call	xmton			; turn on the transmitter
		goto	main35			; ignore inputs while transmitting

main33	btfss	xmtflg,rptmsg	; skip if repeating a message
		goto	main34			; check other inputs
		btfsc	stsflg,rcvbit	; skip if repeated bit is "0"
		call	xmton			; turn on the transmitter
		goto	main35			; ignore inputs while repeating
	
main34	btfsc	xmtflg,xtbmsg	; skip if not transmitting an internal message
		goto	main35			; ignore inputs while transmitting
		btfsc	xmtflg,rptmsg	; skip if not repeating a message
		goto	main35			; ignore inputs while repeating
		
		movlw	b'00010011'		; insure comparator is enabled (inverting mode)
		movwf	CMCON0
		btfsc	stsflg,dgioin	; skip if not receiving a digital input
		call	chkdin			; check digital input
		btfsc	stsflg,bx10in	; skip if not receiving a X10 boost input
		call	chkxin			; check X10 boost input

main35	btfss	T1CON,TMR1ON	; skip if Timer 1 is running
		goto	main			; stop and wait for next zero crossing if not
		movfw	TMR1H			; get Timer 1 high byte
		addlw	.215			; past the end of the 3rd 3-phase window?
		btfss	STATUS,C		; skip if so
		goto	main34			; loop until end of the window

		bsf		GPIO,DRVINH		; ensure drive inhibit is on end of 2nd 3-phase window
		clrf	CCP1CON			; turn off PWM (also indicates xmtr is off)
		movlw	b'00010111'		; disable comparator
		movwf	CMCON0

;		Process received data and set up next bit to be transmitted

main40	call	rcvinp			; process received data
		bcf		xmtflg,outbit	; make sure prior output bit status was cleared
		btfsc	xmtflg,xtbmsg	; skip if not transmitting an internal message
		call	xmtbit			; set up next transmit bit

;		Check for fixed AGC threshold - set level if so & use 40 count decode

		btfsc	modflg2,agcena	; skip if fixed threshold (AGC disabled)
		goto	main41			; run AGC loop if not alternate AGC sample point
		movfw	modflg2			; get mode flags (fixed threshold are middle 2 bits)
		andlw	b'00011000'		; mask all but threshold bits
		movwf	thdset			; hold threshold value
		rrf		thdset			; shift AGC bits to LSBs
		rrf		thdset
		rrf		thdset,w
		iorlw	b'10100000'		; to set comparator reference on at low range
		movwf	thdset			; save threshold value
		bsf		STATUS,RP0		; select Bank 1
		movwf	VRCON			; set comparator threshold
		bcf		STATUS,RP0		; back to Bank 0
		movlw	b'00010011'		; insure comparator is enabled (inverting mode)
		movwf	CMCON0
		goto	main			; loop and wait for next zero crossing

main41	btfsc	modflg2,altagc	; skip if not alternate AGC sample point
		goto	main			; loop and wait for next zero crossing

;		Sample AGC (primary window) 1.4 mS to 0.4 mS before zero crossing

main42	btfss	T1CON,TMR1ON	; skip if Timer 1 is running
		goto	main			; stop and wait for next zero crossing if not
		movfw	TMR1H			; get Timer 1 high byte
		addlw	.212			; 1.26 mS before the next zero crossing? (at 7.68MHz clk)
		btfss	STATUS,C		; skip and begin AGC if so
		goto	main42			; loop until beginning of normal AGC window
		movlw	.206			; sample AGC until .47 mS before the next zero crossing
		movwf	agcend			; set up AGC cycle end
		call	agcloop			; measure AGC level
		goto	main			; loop and wait for next zero crossing


;		AGC LOOP

;		Uses internal comparator reference to measure noise

agcloop	bsf		GPIO,DRVINH		; make sure drive inhibit is on during AGC sample
		movlw	b'10100000'		; start with comparator threshold at 0V
		bsf		STATUS,RP0		; select Bank 1
		movwf	VRCON			; set comparator threshold
		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	btfss	T1CON,TMR1ON	; is Timer 1 running?
		return					; done if not
		movlw	.224			; 32 pulses per normal AGC increment
		btfsc	modflg2,thldhi	; skip if high threshold is not selected
		movlw	.240			; 16 pulses per increment for high AGC threshold
		btfsc	modflg2,vardcd	; skip if variable decode is not selected
		movlw	.208			; 48 pulses per increment for variable decode
		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
		btfsc	modflg2,thldhi	; skip if not high threshold
		call	incthd			; increase threshold one more step
		bsf		STATUS,RP0		; select Bank 1
		movfw	VRCON			; get present threshold level
		bcf		STATUS,RP0		; back to Bank 0
		movwf	thdset			; save threshold setting
		return					; done

incthd	bsf		STATUS,RP0		; select Bank 1
		movfw	VRCON			; get present threshold level
		andlw	b'00000011'		; mask all but the 2 LSBs
		xorlw	b'00000011'		; invert the 2 LSBs
		btfss	STATUS,Z		; check if threshold is already 600mV (Z=1 if so)
		incf	VRCON,F			; increase threshold if not
		bcf		STATUS,RP0		; back to Bank 0
		clrf	TMR0			; clear Timer 0 to check next threshold level
		return


; initialize eeprom locations

EE		CODE	0x2100
		DE		"XTB-II V2.01"	; program name and version
		if		ac50hz == .0	; if not for 50Hz AC powerline
		  DE	"   "			; standard 60Hz version
		else					; must be for 50Hz
		  DE	"-50"			; 50Hz version
		endif


		CODE	0x2110
		DE		moddef1			; EEPROM storage for first mode flag byte
		DE		moddef2			; EEPROM storage for second mode flag byte
		DE		hsedef			; EEPROM storage for selected housecode

		END						; directive 'end of program'

