;********************************************************************
;																	*
;	XTBM-Pro / XTBM													*
;																	*
;	This program is for the PIC used in the XTBM Pro X10 Signal		*
;	Analyzer and the XTBM as of September 2016. The Pro version	 	*
;	contains many features beyond those of the basic XTBM.			*
;																	*
;********************************************************************
;																	*
;	Filename:		XTBM-Pro.asm / XTBM								*
; 	Date:			04/30/19										*
;	File Version:	rev 1.09/E / rev 2.02/E							*
;																	*
;	Author:			Jeff Volp										*
;	Company:		JV Digital Engineering 							*
;																	*
;	This code is Copyright JV Digital Engineering 04/30/19			*
;																	*
;********************************************************************
;																	*
;	Version 1.00: first version of XTBM-Pro firmware				*
;	Version 1.01: corrects bug in status monitor pre-set dim level	*
;				  locks the bitmap when curser moves off zero		*
;				  display max X10 signal level in line monitor mode	*
;				  delay High Noise warning for 1/2 second			*
;				  allow Collision error to override other errors	*
;	Version 1.02: modify timing for more contiguous noise sampling	*
;				  adjust ZCD interrupt for positive/negative match	*
;				  display configuration status after self test		*
;				  display small x for receiving just 1/2 doublet	*
;				  display cycle count for intermittent noise		*
;				  identify transients and random noise				*
;				  ignore 2mS+ bursts from X10 & Leviton repeaters	*
;				  blank signal level bargraph after 2 seconds		*
;				  display voltages & use pseudo log scale bargraph	*
;				  correct miscellaneous minor bugs					*
;	Version 1.03: display x,X for both halves of extended doublet	*
;				  blank "over" for 2nd line of extended bit voltage	*
;				  clear X reading when blanking X bargraph			*
;				  slow up bargraph noise voltage readout			*
;				  reduce noise bar max from 2.5V to 1.0V full scale	*
;				  clean up self-test error reporting				*
;	Version 1.04: fix error in logging timestamp day count			*
;				  clear prior readings for receiving BSC or noise	*
;				  suppress high noise warning for a collision		*
;	Version 1.05: fix bug in gain change causing 2.55V max readings	*
;	Version 1.06: fix LCD not clearing on return to monitor mode	*
;				  fix bit 4 being updated when pattern is locked	*
;				  extended startup mode sample window to 1 second	*
;				  included "if" statements to share code with XTBM	*
;	Version 1.07: single noise bursts do not cause BSC errors		*
;				  display noise level during high noise warning		*
;				  no gain change during noise error					*
;	Version 1.08  correct 4x measurement jump at gain transition	*
;	Version 1.09  last valid command is not logged again for error	*
;																	*
;********************************************************************
;																	*
;	XTBM VolpMeter													*
;																	*
;	Version 1.10: new version based on Pro code						*
;	Version 2.02: update based on Pro verison 1.07 code				*
;	Version 2.03: update based on Pro version 1.09 code
;																	*
;********************************************************************

;	******	SELECT VERSION AND LINE FREQUENCY 50/60 Hz	******

pro			equ		.1			; set to zero for XTBM, non zero for XTBM-Pro
ac50hz		equ		.0			; set to zero for 60Hz, non zero for 50Hz


	if			pro == .1		; if Pro version
		list      p=16F1825			; list directive to define processor
		#include <p16F1825.inc>		; processor specific variable definitions
	else
		list      p=16F1824			; list directive to define processor
		#include <p16F1824.inc>		; processor specific variable definitions
	endif

	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

LCDE	EQU		H'05'			; (RA5) LCD Enable
VLEVEL	EQU		H'04'			; (RA4) Voltage level input (was RA0)
ZCDIN	EQU		H'03'			; (RA3) Zero Crossing Detector - 50/60Hz reference (was RA2)
SWINPT	EQU		H'02'			; (RA2) Switch input - analog voltage (was RA3)
X10XIN	EQU		H'01'			; (RA1) 120KHz powerline X10 input
LCDDS	EQU		H'00'			; (RA0) LCD data select (was RC4)
PWMOUT	EQU		H'05'			; (RC5) 120KHz PWM output
RANGE	EQU		H'04'			; (RC4) Signal level range select (0 = 2.56V, 1 = 10.24V) (was RA4)
LCDDB7	EQU		H'03'			; (RC3) LCD data bit 7
LCDDB6	EQU		H'02'			; (RC2) LCD data bit 6
LCDDB5	EQU		H'01'			; (RC1) LCD data bit 5
LCDDB4	EQU		H'00'			; (RC0) LCD data bit 4


; VARIABLE DEFINITIONS

BANK0		UDATA		0x20	; Page 0 RAM workspace
pg0ram							; beginning of page 0 RAM workspace

periodH		RES		1			; period measurement MSB
periodL		RES		1			; period measurement LSB
nxttune		RES		1			; next setting for OSCTUNE
frqerr		RES		1			; 2's compliment frequency error (0 for no error)
cyccnt		RES		1			; received cycle count in X10 sample window
x10cnt		RES		1			; received cycles for potential "1" bit
pstcnt		RES		1			; post X10 window cycle count
frqcnt		RES		1			; frequency count for X10 "1" bit
rcvsrg		RES		8			; 8 byte received data shift register (LSB is last bit received)
strtrg		RES		1			; start pattern shift register
presr1		RES		1			; pre X10 sample window shift register (for Insteon check)
pstsr1		RES		1			; post X10 sample window shift register (for Insteon check)
onezer		RES		1			; net total of one versus zero bits (error if not 1-3)
rcvcnt		RES		1			; receive routine received bit count (4-22 from end of start)
bitcnt		RES		1			; bit pattern routine received bit count (4-44 from end of start)
onecnt		RES		1			; received "1" count from end of start (1-9 or 1-29)
bsccnt		RES		1			; bad start code count (> 1 per .5 sec for 10 max for high noise)
hincnt		RES		1			; high noise count - used to delay warning to allow for Insteon
gapcnt		RES		1			; received gap length (255 max)
rhouse		RES		1			; unpacked received house code (4-bit X10 code)
rkeycd		RES		1			; unpacked received key code (5-bit X10 code)
rhunit		RES		1			; unpacked received house & unit code (4-bit X10 code)
rcomnd		RES		1			; unpacked received command code (4-bit X10 code in 4 LSBs)
cmdbad		EQU		H'07'		; "1" indicates no valid command
cmdrpt		EQU		H'06'		; "1" indicates a repeater check
rxdat1		RES		1			; unpacked first extended data byte
rxdat2		RES		1			; unpacked second extended data byte
xhouse		RES		1			; house code used to assemble a transmit message
xkeycd		RES		1			; key code used to assemble a transmit message
xmtsrg		RES		3			; 3-byte transmit data shift register (MSB is output)
xmtcnt		RES		1			; transmitted bit count
x10avg		RES		1			; X10 signal level average for 8 bits of this command
x10cpr		RES		1			; X10 compare value to select maximum (8 MSBs with 40mV resolution)
x10max		RES		1			; X10 maximum signal level for both halves of doublet (0-255)
x10ech		RES		1			; X10 status check echo voltage
frqavg		RES		1			; average frequency for 8 bits of this command
adcsum		RES		1			; A/D conversion sum MSB
adcx10		RES		1			; A/D conversion in X10 sample window MSB
adcpst		RES		1			; A/D conversion post X10 sample window sum MSB
adcins		RES		1			; A/D value of Insteon bit in X10 sample window
maxx10		RES		1			; A/D maximum value in X10 sample window
maxpst		RES		1			; A/D maximum value in post X10 sample window
pknlta		RES		1			; peak noise long-term average MSB
siglvl		RES		1			; signal level for X10 logic "1" bit check
x10thd		RES		1			; signal detection threshold for X10 windows (adjusted for hirang)
seccnt		RES		1			; minute counter to delay re-transmit of noise ON/OFF (120->0)
dspcnt		RES		1			; display update counter - counts 63 to 1 each half second
strptr		RES		1			; string pointer
lcdnib		RES		1			; LCD nibble for output
bcdlsd		RES		1			; LSD 2 digits for binary to BCD conversion
bcdmsd		RES		1			; MSD for binary to BCD conversion
hldcnt		RES		1			; switch duration at present selection (+1 each half cycle)
logwpt		RES		1			; log write pointer
logrbf		RES		8			; log read buffer
logrpt		RES		1			; log read pointer
logcnt		RES		1			; number of commands in log file
modptr		RES		1			; mode pointer for log or bit pattern buffers
ahouse		RES		1			; archived house code
ahunit		RES		1			; archived house & unit code
acomnd		RES		1			; archived command code
ax10mx		RES		1			; archived X10 maximum signal level
axfreq		RES		1			; archived X10 frequency
anoise		RES		1			; archived noise level (noise = 0-127)
ahrang		EQU		H'07'		; "1" indicates high range
test1		RES		1
pg0end							; end of page 0 RAM workspace (2 left)

BANK1		UDATA	0xA0		; Page 1 RAM workspace
pg1ram							; beginning of page 1 RAM workspace

	if			pro == .1		; if Pro version

onesec		RES		1			; one-second time tick
logsec		RES		1			; log time reference seconds (0-59)
logmin		RES		1			; log time reference minutes (0-59)
loghr		RES		1			; log time reference hours (0-23)
logday		RES		1			; log time reference days (0-7)

	endif

sumcnt		RES		1			; number of "1" bits summed
adcsumH		RES		1			; A/D conversion sum MSB
adcsumL		RES		1			; A/D conversion sum LSB
x10sumH		RES		1			; X10 signal level sum MSB
x10sumL		RES		1			; X10 signal level sum LSB
frqcntH		RES		1			; frequency count for 8 bits of this command MSB
frqcntL		RES		1			; frequency count for 8 bits of this command LSB
pkncyp		RES		1			; peak noise for positive half cycle
pkncyn		RES		1			; peak noise for negative half cycle
ncyctp		RES		1			; noise cycle count for positive half cycle
ncyctn		RES		1			; noise cycle count for negative half cycle
bgncnt		RES		1			; received cycle count at beginning of X10 transmission window
endcnt		RES		1			; received cycle count at end of X10 transmission window
adcbgn		RES		1			; A/D conversion at beginning of X10 transmission window 
maxbgn		RES		1			; A/D maximum value at beginning of X10 transmission window
adcend		RES		1			; A/D conversion at end of X10 transmission window
maxend		RES		1			; A/D maximum value at end of X10 transmission window
nsestaH		RES		1			; noise level short-term average MSB
nsestaL		RES		1			; noise level short-term average LSB
nseltaH		RES		1			; noise level long-term average MSB
nseltaL		RES		1			; noise level long-term average LSB
pknstaH		RES		1			; peak noise short-term average MSB
pknstaL		RES		1			; peak noise short-term average LSB
pknltaH		RES		1			; peak noise long-term average MSB
pknltaL		RES		1			; peak noise long-term average LSB

	if			pro == .1		; if Pro version

bgnstaH		RES		1			; beginning of transmit window noise short-term average MSB
bgnstaL		RES		1			; beginning of transmit window noise short-term average LSB
endstaH		RES		1			; end of transmit window noise short-term average MSB
endstaL		RES		1			; end of transmit window noise short-term average LSB

	endif

ncycntH		RES		1			; noise cycle count average MSB
ncycntL		RES		1			; noise cycle count average LSB
nseavg		RES		1			; present background noise level short-term average
bgnnse		RES		1			; peak noise at beginning of X10 transmit window
endnse		RES		1			; peak noise at end of X10 transmit window
ncycnt		RES		1			; noise cycle count average
agcthd		RES		1			; signal detection threshold for pre X10 sample window

	if			pro == .1		; if Pro version

shftct		RES		1			; receive shift register shift count (for bit pattern display)
bitsrg		RES		6			; 6 byte bit-pattern shift register (LSB is last bit received)

	endif

pg1end							; end of page 1 RAM workspace

	if			pro == .1		; if Pro version

;			Logging buffer uses 800 bytes from BANK2 to BANK11

BANK12		UDATA	0x620		; Page 12 RAM workspace (48 bytes)
pgbram							; beginning of page 12 RAM workspace
stbufr		RES		.4			; 4 byte buffer for start pattern voltage readings
bvbufr		RES		.44			; bit voltage buffer for individual bit readings

	endif

			UDATA_SHR	0x70

adcmax		RES		1			; A/D conversion maximum value in sample window
pknois		RES		1			; peak noise short-term average
pkncyc		RES		1			; peak noise level for this half cycle
bkgnse		RES		1			; background noise level
temp0		RES		1			; global register (LCD delay, temp, LCD nibble, A/D, mode SW)
temp1		RES		1			; global register (LCD delay, temp, start pattern, A/D)
temp2		RES		1			; global register (LCD, temp, freq display, diagnostic pulse)
temp3		RES		1			; global register (LCD character count, ZCD startup pattern test)
temp4		RES		1			; global register

stsflg		RES		1			; general status flags

archok		EQU		H'07'		; "1" indicates received a valid X10 command that is OK to archive
rcving		EQU		H'06'		; "1" indicates received at least one "1" bit after a gap
rcvbit		EQU		H'05'		; "1" indicates last received bit is a logic "1"
bitxtd		EQU		H'04'		; "1" indicates the last bit pattern was for an extended command
spare3		EQU		H'03'		;
spare2		EQU		H'02'		;
spare1		EQU		H'01'		;
cycpol		EQU		H'00'		; AC cycle polarity - "1" for positive half cycle

x10vld		EQU		H'07'		; "1" indicates a valid X10 command has been received

rcvflg		RES		1			; receive status flags

x10vld		EQU		H'07'		; "1" indicates a valid X10 command has been received
rcvdbl		EQU		H'06'		; "1" indicates received both halves of X10 doublet
rptvld		EQU		H'05'		; "1" indicates received a valid repeater check command
spare4		EQU		H'04'		;
hirang		EQU		H'03'		; "1" indicates receiving a high-level X10 signal
h2rang		EQU		H'02'		; "1" indicates 2nd bit pattern was sampled in high range mode
h1rang		EQU		H'01'		; "1" indicates 1st bit pattern was sampled in high range mode
maxrng		EQU		H'00'		; "1" indicates the last x10max was stored in high range mode

dspflg		RES		1			; display status flags

x10rng		EQU		H'07'		; "1" indicates the last x10avg was stored in high range mode
bitlok		EQU		H'06'		; "1" indicates the bit pattern display is locked
bitstl		EQU		H'05'		; "1" indicates the bit pattern data is stale
dsphld		EQU		H'04'		; "1" indicates hold display until next dspcnt 0
moddes		EQU		H'03'		; "1" indicates mode description is being displayed
bitnew		EQU		H'02'		; "1" indicates a new bit pattern has been stored
errmsg		EQU		H'01'		; "1" indicates an error message is displayed
nsefrq		EQU		H'00'		; "1" indicates a noise frequency is displayed
lokstl		EQU		H'60'		; for bit test of stale locked data

xmtflg		RES		1			; transmitter status flags

rptchk		EQU		H'07'		; "1" indicates the repeater check is in process
clrlin		EQU		H'06'		; "1" indicates the line is clear and ready for transmit
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)
xhinse		EQU		H'03'		; "1" indicates a high noise level warning was transmitted
xmtvlt		EQU		H'02'		; "1" indicates transmit X10 voltage measurement
xmtucd		EQU		H'01'		; "1" indicates transmit a X10 unit code
strtup		EQU		H'00'		; "1" indicates 1 second startup powerline monitor is in process

cmdflg		RES		1			; command status flags (cleared by a gap)

rcvx10		EQU		H'07'		; "1" indicates receiving a new X10 start pattern
xtdcmd		EQU		H'06'		; "1" indicates receiving an extended command
xtddat		EQU		H'05'		; "1" indicates receiving an extended data command
inston		EQU		H'04'		; "1" indicates receiving an Insteon command
errbsc		EQU		H'03'		; "1" indicates a bad start code error
errcol		EQU		H'02'		; "1" indicates a collision error
errrcv		EQU		H'01'		; "1" indicates a receive data pattern error
errnse		EQU		H'00'		; "1" indicates a high noise error

modflg		RES		1			; XTBM Pro operating mode flags

bothsw		EQU		H'07'		; "1" indicates both switches are pressed
leftsw		EQU		H'06'		; "1" indicates left switch is pressed
rghtsw		EQU		H'05'		; "1" indicates right switch is pressed
relese		EQU		H'04'		; "1" indicates both switches were released
modchg		EQU		H'03'		; "1" indicates mode has been changed (to display new mode)
modsel2		EQU		H'02'		; mode select bit
modsel1		EQU		H'01'		; mode select bit ("1" for expanded noise or bargraph modes)
modsel0		EQU		H'00'		; mode select bit

cfgflg		RES		1			; configuration flags (4 MSBs copied from EEPROM)

tested		EQU		H'07'		; "1" indicates the self test was completed (EEPROM)
nsemon		EQU		H'06'		; "1" indicates noise monitor alert is enabled (EEPROM)
stsmon		EQU		H'05'		; "1" indicates status echo voltage transmission is enabled (EEPROM)

  variable	cfgdef=b'00000000'	; Default is noise monitor disabled

EEFIRM		equ		H'0000'		; location of firmware version in EEPROM
EECONF		equ		H'0010'		; location of configuration flags in EEPROM
EEARCH		equ		H'0014'		; location of archived data in EEPROM

pg2ram		UDATA	0x120		; start of age 2 logging RAM
pg2end		UDATA	0x16F		; top of page 2 logging general purpose RAM

;**********************************************************************

RESET_VECTOR	CODE	0x000	; processor reset vector

		goto    start			; go to beginning of program

INT_VECTOR	CODE	0x004		; interrupt vector location

		goto	jmpint			; process interrupt routine

jmpint

	if			pro == .1		; if Pro version
		bsf		PCLATH,4		; select ROM Page 2 for Interrupt routine
	else
		bsf		PCLATH,3		; select ROM Page 1 for Interrupt routine
	endif

		call	intrpt			; call interrupt service routine
		clrf	PCLATH			; back to ROM Page 0
		retfie					; return with interrupts enabled


;		X10 -> HEXIDECIMAL CODE CONVERSION UTILITY  (locate at program page zero)

;		Convert offset X10 code to 0-F hex equivalent

;		Enter with W = X10 code in 4 LSBs where "1" = 0000
;		Returns with W = hex value of code (X10 "1-16" -> hex 0-F)

x10hex	brw
		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'


;		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	brw
 		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'


;		LCD CONTROL CODE

;		LCD Initialization

;		Set up LCD function byte for 4-bit operation, 2x16 format, 5x7 font
;		Set cursor to advance after each write
;		Turn on LCD display with no cursor
;		Clear the entire display

lcdinit	clrf	BSR				; make sure BANK0 is selected
		bcf		PORTA,LCDDS		; data select must be low for control word
		movlw	b'00000011'		; for initialization
		movwf	lcdnib			; save nibble
		call	nibout			; output first initialization nibble
		movlw	.17				; 17 x 250uS delay (must be greater than 4.1mS)
		call	dlyset			; delay for 4.25mS
		call	nibout			; output second initialization nibble
		movlw	.1				; 1 x 250uS delay (must be greater than 100uS)
		call	dlyset			; delay for 250uS
		call	nibout			; output third initialization nibble	
		call	dly50us			; delay 50uS

		movlw	b'00000010'		; 4 LSBs for 1st nibble
		movwf	lcdnib			; save nibble
		call	nibout			; the first nibble is sent twice for 4 bit operation
		call	dly50us			; 50uS delay for LCD to process command
	
		movlw	b'00101000'		; function byte [4-bit, 2-line, no ext reg, 5x7]		
		call	lcdout			; output control byte
		call	dly50us			; 50uS delay for LCD to process command

		movlw	b'00000110'		; command word to turn on cursor movement
		call	lcdout			; output control byte
		call	dly50us			; 50uS delay for LCD to process command

		movlw	b'00001100'		; command word to turn on display with no cursor
		call	lcdout			; output control byte
		call	dly50us			; 50uS delay for LCD to process command

		call	dnldcc			; download custom characters to LCD

;		Clear the entire display

lcdclr	movlw	b'00000001'		; command word to clear display & home cursor
		bcf		PORTA,LCDDS		; data select must be low for control word
		call	lcdout			; output control byte
		bsf		PORTA,LCDDS		; restore data select for data
		goto	dly2ms			; delay 2mS & return
		clrw					; start at beginning of display

;		Set address - sets the LCD address to that passed in W

adrout	iorlw	b'10000000'		; set bit flag to indicate it is an address
		bcf		PORTA,LCDDS		; data select must be low for control word
		call	lcdout			; output address byte
		bsf		PORTA,LCDDS		; restore data select for data
		return


;		Download custom characters to LCD

;		Set character generator address - sets address to character generator RAM

dnldcc	clrf	BSR				; make sure BANK0 is selected
		movlw	b'01000000'		; start at beginning of character generator RAM
		bcf		PORTA,LCDDS		; data select must be low for control word
		call	lcdout			; output address byte
		bsf		PORTA,LCDDS		; restore data select for data

		clrf	strptr			; start at beginning of custom characters
dnldc0	movfw	strptr			; get pointer

	if			pro == .1		; if Pro version
		bsf		PCLATH,4		; select ROM Page 2 for text strings
	else
		bsf		PCLATH,3		; select ROM Page 1 for text strings
	endif

		call	cuschr			; get custom character bit pattern
		clrf	PCLATH			; back to ROM Page 0
		movwf	temp2			; save bit pattern to output
		andlw	b'01111111'		; mask MSB (end of string flag)
		call	lcdout			; output to LCD
		incf	strptr,F		; point to next bit pattern
		btfss	temp2,7			; skip if that was the last bit pattern
		goto	dnldc0			; loop for next bit pattern if not
		return					; done


;		Output a text string to the LCD display

;		Enter with W pointing to the first character in the string
;		Last character in the string must have the MSB set

lcdstr	clrf	BSR				; make sure BANK0 is selected	
		movwf	strptr			; save pointer
lcdst0	movfw	strptr			; get pointer

	if			pro == .1		; if Pro version
		bsf		PCLATH,4		; select ROM Page 2 for text strings
	else
		bsf		PCLATH,3		; select ROM Page 1 for text strings
	endif

		call	strngs			; get character
		clrf	PCLATH			; back to ROM Page 0
		movwf	temp2			; save character to output
		andlw	b'01111111'		; mask MSB (end of string flag)
		call	lcdout			; output to LCD
		incf	strptr,F		; point to next character
		btfss	temp2,7			; skip if that was the last character
		goto	lcdst0			; loop for next character if not
		return					; done


;		Output decoded command text string to the LCD display

;		Enter with W pointing to the first character in the string
;		Last character in the string must have the MSB set

lcddcd	clrf	BSR				; make sure BANK0 is selected
		movwf	strptr			; save pointer
lcddc0	movfw	strptr			; get pointer

	if			pro == .1		; if Pro version
		bsf		PCLATH,4		; select ROM Page 2 for text strings
	else
		bsf		PCLATH,3		; select ROM Page 1 for text strings
	endif

		call	strdcd			; get character
		clrf	PCLATH			; back to ROM Page 0
		movwf	temp2			; save character to output
		andlw	b'01111111'		; mask MSB (end of string flag)
		call	lcdout			; output to LCD
		incf	strptr,F		; point to next character
		btfss	temp2,7			; skip if that was the last character
		goto	lcddc0			; loop for next character if not
		return					; done


;		Output a hexadecimal character to the LCD display

;		Enter with W being the hex value to output

lcdhex	andlw	b'00001111'		; mask upper bits
		addlw	H'30'			; adjust to output 0-9
		movwf	temp0			; hold at temp register
		addlw	.198			; check if A-F
		movfw	temp0			; recall adjusted value
		btfsc	STATUS,C		; skip if 0-9
		addlw	.7				; adjust to output A-F
		goto	lcdout			; output hex digit


;		Output BCD Digits

;		Enter with bcdmsd = 0,0 to 0,9 and bcdlsd = 0,0 to 9,9
;		Also the LCD address should be pointing to the first character location

;		outbcd outputs a 2-digit number with a decimal point x.x or .xx
;		outlsd outputs 2 digits in the form .xx
;		outbcd3 outputs 3 digits in the form x.xx
;		outbcd3 displays "over" if msb of bcdmsd is set (8,x)
;		outbcd2 outputs just 2 digits xx
;		lcddgt converts a 4-bit value 0-9 to an ASCII character & outputs to LCD

outbcd	movfw	bcdmsd			; check if MSD is a zero
		btfsc	STATUS,Z		; skip & output MSD if > zero
		goto	outlsd			; output just LSDs
		call	lcddgt			; output MS digit
		movlw	"."				; decimal point
		call	lcdout			; output character
		swapf	bcdlsd,W		; move BCD middle digit to LSD position
		goto	lcddgt			; output digit

outbcd3	movlw	strovr-1		; -> "over" to indicate overrange
		btfsc	bcdmsd,7		; skip if not overrange
		goto	lcdstr			; output "over" string
		movfw	bcdmsd			; get MSD
		call	lcddgt			; output MS digit
outlsd	movlw	"."				; decimal point
		call	lcdout			; output character
outbcd2	swapf	bcdlsd,W		; move BCD middle digit to LSD position
		call	lcddgt			; output digit
		movfw	bcdlsd			; get BCD LSD

;		Convert a 4-bit value to an ASCII digit

lcddgt	andlw	b'00001111'		; mask all but the hex value
		iorlw	b'00110000'		; convert to ASCII digit	

;		LCD Output sends one complete byte of data to the LCD
;		Data is passed in W

lcdout	movwf	temp0			; save byte to be written
		swapf	temp0,W			; swap LCD nibbles
		movwf	lcdnib			; set upper nibble of data byte for output
		call	nibout			; output upper nibble (nibout masks upper bits)
		movfw	temp0			; get lower nibble
		movwf	lcdnib			; set lower nibble of data byte for output
		call	nibout			; output lower nibble (nibout masks upper bits)

;		Delay 50 microseconds for LCD to respond

dly50us	movlw	.65				; for 50uS delay (65 x .76uS)
		movwf	temp0
dlylp2	decfsz	temp0,F			; tick off .76uS
		goto	dlylp2			; loop for 50uS
		return


;		Send one nibble to LCD

nibout	movfw	lcdnib			; get nibble to output
		andlw	b'00001111'		; make sure all but nibble bits are cleared
		movwf	PORTC			; output new LCD data (with PWM=0)
		bsf		PORTA,LCDE		; set LCD enable high
		nop						; delay for 1.01uS pulse
		nop
		nop
		bcf		PORTA,LCDE		; return LCD enable low
		return

;		Output a series of spaces to the LCD

;		space1 outputs a single space
;		spacen outputs a series of spaces where W = number

space1	movlw	.1				; one space character
spacen	movwf	temp3			; save character count
spcout	movlw	" "				; ASCII space
		call	lcdout			; output space character to LCD display
		decfsz	temp3,F			; count off one character
		goto	spcout			; loop and output another space
		return

;		LCD Test - Display a series of boxes on second LCD line

lcdtst	movlw	H'40'			; point to beginning of message line
		call	adrout			; output address
		movlw	.16				; 16 box characters
		movwf	temp3			; save character count
boxout	movlw	H'FF'			; "box" character
		call	lcdout			; output box character to LCD display
		decfsz	temp3,F			; count off one character & skip if done
		goto	boxout			; loop and output another space
		return

;		LCD Header - Display XTBM PRO & firmware version on first LCD line

lcdhdr	clrw					; point to LCD 1st character of line 1
		call	adrout			; output address
		call	space1			; output one space
		movlw	.16				; to output 16 characters
		movwf	temp3			; save character count	

		banksel EEADRL			; select BANK3
		movlw	EEFIRM			; get firmware version address in EEPROM
		movwf	EEADRL			; set EEPROM address to read
		clrf	EEADRH
lcdfrm
		banksel	EEADRL			; select BANK3
		bsf		EECON1,RD		; read EEPROM location
		movfw	EEDAT			; get next character
		incf	EEADRL,F		; increment EEPROM address pointer
		clrf	BSR				; back to BANK0
		call	lcdout			; output character to LCD
		decfsz	temp3,F			; count off one character & skip if done
		goto	lcdfrm			; loop and continue firmware display		
		return


;		LCD Delay routines

;		Delay 20 milliseconds

dly20ms	movlw	.80				; 80 x 250us = 20mS
		goto	dlyset			; set delay interval & delay

;		Delay 2 milliseconds

;		Alternate entry (dlyset) for W x 250uS delay

dly2ms	movlw	.8				; 8 x 250uS = 2mS
dlyset	movwf	temp1			; set delay interval to N x 250uS
dlylp0	movlw	.246			; for 250uS delay (246 x 1.01uS)
		movwf	temp0
dlylp1	nop						; add a cycle for 4 instruction cycle loop
		decfsz	temp0,F			; tick off 1.01uS
		goto	dlylp1			; loop for 250uS
		decfsz	temp1,F			; tick off 250uS
		goto	dlylp0			; loop for N x 250uS
		return


;		Binary to BCD conversion

;		Enter with W = 8-bit binary number
;			binbcdx scales 2.55 max or 9.99 max based on x10rng (1 = 9.99)
;			binbcd range is 256 max
;			binbcd9	range is 999 max
;		Returns bcdmsd = 00 to 09 (BCD)
;				bcdlsd = 00 to 99 (BCD)

binbcdx	btfsc	dspflg,x10rng	; skip & process 2.55 max if low range
		goto	binbcd9			; convert high range 9.99 max

binbcd	clrf	bcdmsd			; clear MS BCD digit
		clrf	bcdlsd			; clear 2 LSD BCD digits
bin2x1	addlw	-.100			; subtract 100 & check for underflow
		btfss	STATUS,C		; skip if no underflow
		goto	bin2dg			; restore 100 & convert 2 LSDs if underflow
		incf	bcdmsd,F		; increment MSD
		goto	bin2x1
		
bin2dg	addlw	.100			; restore underflow
bin2x2	addlw	-.10			; subtract 10 & check for underflow
		btfss	STATUS,C		; skip if no underflow
		goto	bin2x3			; next digit
		incf	bcdlsd,F		; increment BCD digit
		goto	bin2x2			; loop

bin2x3	addlw	.10				; restore underflow
		swapf	bcdlsd,F		; move MSD to proper location
		iorwf	bcdlsd,F		; add in the LSD directly from W
		return

;		Binary to BCD conversion with max number 999

binbcd9	clrf	bcdmsd			; clear MS BCD digit
		clrf	bcdlsd			; clear 2 LSD BCD digits
		movwf	temp0			; save for conversion
		addlw	.6				; 250 + 6 -> overflow
		btfss	STATUS,C		; skip & set 999 if overflow
		goto	bin9x1			; begin conversion
		movlw	H'09'			; set MBD = 9
		movwf	bcdmsd
		movlw	H'99'			; set LSDs = 99
		movwf	bcdlsd
		return

bin9x1	movlw	.25				; to subtract 25
		subwf	temp0,F			; subtract 25 & check for underflow
		btfss	STATUS,C		; skip if no underflow
		goto	bin9dg			; restore 25 & convert 2 LSDs if underflow
		incf	bcdmsd,F		; increment MSD
		goto	bin9x1
		
bin9dg	addwf	temp0,F			; restore underflow
		lslf	temp0,F			; multiply temp by 2 to subtract 5 instead of 2.5
bin9x2	movlw	.5				; to subtract 5
		subwf	temp0,F			; subtract 5 & check for underflow
		btfss	STATUS,C		; skip if no underflow
		goto	bin9x3			; next digit
		incf	bcdlsd,F		; increment BCD digit
		goto	bin9x2			; loop

bin9x3	addwf	temp0,F			; restore underflow
		swapf	bcdlsd,F		; move MSD to proper location
		lslf	temp0,W			; multiply temp by 2 to get proper LSB scale factor
		iorwf	bcdlsd,F		; add in the LSD directly from W
		return


;		Output X10 voltage measurement to LCD 

;		x10lcd outputs reading in the form Xx.xx
;		bsclcd outputs readubg ub the form ?x.xx for unidentified source

x10lcd	btfss	cmdflg,inston	; skip if an Insteon transmission
		goto	x10vlt			; display X10 voltage
		movlw	.10				; point to noise readout
		call	adrout			; output address
		movlw	.6				; for 6 spaces
		call	spacen			; blank noise readout
		clrw					; start at first character on display
		call	adrout			; output address
		movlw	"I"				; I for Insteon amplitude
		call	lcdout			; output character
		movfw	adcins			; get A/D reading of Insteon bit
		goto	dspvlt			; display Insteon voltage

x10vlt	clrw					; start at first character on display
		call	adrout			; output address
		movlw	"X"				; X for X10 amplitude of doublet
		btfss	rcvflg,rcvdbl	; skip if received second half of doublet
		movlw	"x"				; x for X10 amplitude of 1/2 of doublet
		btfsc	cmdflg,errbsc	; skip if not a bad start code
bsclcd	movlw	"?"				; for unidentified source
		call	lcdout			; output character
		movfw	x10max			; get maximum X10 signal
		bcf		dspflg,x10rng	; set display range flag to 2.55V
		btfsc	rcvflg,maxrng	; skip if sample was in low range
		bsf		dspflg,x10rng	; set display range flag to 9.99V

dspvlt	call	binbcdx			; convert to BCD (extended range)
		call	outbcd3			; output X10 signal level in format x.xx
		call	space1			; add a trailing space
		movfw	frqavg			; get average frequency for command


;		Output frequency to LCD

;		frequency is passed in W
;		range is 100Khz to 140Khz
;		displays "F -- " if W = 0

frqlcd	movwf	temp3			; save it for frequency display
		movlw	.6				; point to beginning of X10 frequency field		
		call	adrout			; output address
		movlw	"F"
		call	lcdout			; output character

frqnse	movfw	temp3			; recall frequency to display
		btfss	STATUS,Z		; skip & display " -- " if zero
		goto	frqlim			; check frequency limits
		movlw	strfno			; " --  " no frequency to display
		goto	lcdstr			; output string & return

frqlim	addlw	.115			; 141KHz + 115 -> overflow
		btfsc	STATUS,C		; skip if frequency < 141KHz
		goto	frqmax			; display "140>"
		addlw	.41				; 100KHz + 115 + 41 -> overflow
		btfsc	STATUS,C		; skip if frequency < 100KHz
		goto	frqnml			; display normal frequency

frqmin	movlw	"<"				; "less than"
		movwf	temp4			; set frequency character
		movlw	.100			; to display minimum frequency
		goto	frqout			; output frequency

frqmax	movlw	">"				; "greater than"
		movwf	temp4			; set frequency character
		movlw	.140			; to display maximum frequency
		goto	frqout			; output frequency

frqnml	movlw	"k"				; to display in-range frequency
		movwf	temp4			; preset frequency character
		movfw	temp3			; recall frequency to display

frqout	call	binbcd			; convert to BCD (255 range)
		movfw	bcdmsd			; get MSD
		call	lcddgt			; output to LCD
		call	outbcd2			; output frequency 2 LSDs
		movfw	temp4			; get frequency character
		call	lcdout			; output character
		call	space1			; add a trailing space
		return


;		Output noise frequency or cycle count to LCD

;		This subroutine outputs either noise frequency or cycle count to the LCD

;		Enter with W = LCD location for the noise display

nselcd	btfsc	dspflg,nsefrq	; skip if noise is not high enough for frequency display
		goto	nsedsp			; disply noise frequency or cycle count
		movlw	H'0C'			; point to beginning of noise type field
		call	adrout			; output LCD address
		movlw	strnty-1		; -> "TYPE"
		call	lcdstr			; output noise type string
		movlw	H'4C'			; point to beginning of noise data field
		call	adrout			; output LCD address
		movlw	strnon-1		; -> "----"
		call	lcdstr			; output string
		return

nsedsp
		banksel	pg1ram			; select BANK1 RAM
		movfw	bgncnt			; get begin window cycle count in 144uS window
		movwf	temp0			; save begin cycle count
		movfw	endcnt			; get end window cycle count in 144uS window
		clrf	BSR				; back to BANK0

		addlw	.235			; was it => 20 (>140KHz)
		btfsc	STATUS,C		; skip if < 140KHz
		goto	nsernd			; display "N" or "RNDM" if cycle count > 140KHz
		addlw	.7				; was it =>	14 (100KHz)
		btfss	STATUS,C		; skip if > 100KHz
		goto	nsecyc			; check further if cycle count < 100KHz

		movfw	temp0			; recall begin window cycle count
		addlw	.235			; was it => 20 (>140KHz)
		btfsc	STATUS,C		; skip if < 140KHz
		goto	nsernd			; display "N" or "RNDM" if cycle count > 140KHz
		addlw	.7				; was it =>	14 (100KHz)
		btfss	STATUS,C		; skip if > 100KHz
		goto	nsernd			; display "R" or "RNDM" if cycle count < 100KHz

		movfw	frqcnt			; get noise frequency in X10 sample window
		movwf	temp3			; save for frequency display

		btfss	modflg,modsel1	; skip if expanded noise display
		goto	nsedef			; display default mode 0 noise frequency

		movlw	H'0C'			; point to beginning of noise type field
		call	adrout			; output LCD address
		movlw	strfrq-1		; -> "FREQ"
		call	lcdstr			; output noise type string
		movlw	H'4C'			; point to beginning of noise data field
		call	adrout			; output LCD address
		goto	frqnse			; output noise frequency		
	
nsedef	movlw	H'06'			; point to beginning of X10 frequency field		
		call	adrout			; output address
		movlw	"N"				; use "N" for noise frequency
		call	lcdout			; output character
		goto	frqnse			; output noise frequency

nsecyc	addlw	.8				; was it > 1 (some activity)
		btfsc	STATUS,C		; skip and display "T" if no activity in end window
		goto	nsernd			; display "R" or "RNDM"	for random noise

		movlw	"T"				; to display "T" for just a transient
		movwf	temp4			; save at temp workspace
		movlw	strntr-1		; -> "TRAN"
		movwf	strptr			; initialize string pointer
		goto	nsetyp			; display noise type

nsernd	movlw	"R"				; to display "R" for random noise
		movwf	temp4			; save at temp workspace
		movlw	strnrd-1		; -> "RNDM"
		movwf	strptr			; initialize string pointer

nsetyp	btfss	modflg,modsel1	; skip if expanded noise display
		goto	nsetcy			; output noise type and cycle count

		movlw	H'0C'			; point to beginning of noise type field
		call	adrout			; output LCD address
		call	lcdst0			; output noise type string
		movlw	H'4C'			; point to beginning of noise data field
		call	adrout			; output LCD address
		goto	nsecct			; output noise cycle count

nsetcy	movlw	.6				; point to beginning of X10 frequency field
		call	adrout			; output LCD address
		movfw	temp4			; recall noise type type character
		call	lcdout			; output character

nsecct
		banksel	pg1ram			; select BANK1 RAM
		movfw	ncycnt			; get average noise cycle count
		clrf	BSR				; back to BANK0
		call	binbcd			; convert to BCD (2.55V range)
		movlw	H'99'			; to force 99 if => 100
		movf	bcdmsd,F		; check if BCD MSB > 0
		btfss	STATUS,Z		; skip and use cycle count if not
		movwf	bcdlsd			; set BCD LSD to read 99
		call	outbcd2			; output 2 digit noise level
		movlw	"C"				; to display "C"
		call	lcdout			; output character
		movlw	"Y"				; to display "Y"
		call	lcdout			; output character
		clrf	BSR				; back to BANK0
		return


;		Output pre-set dim level to LCD

;		This subroutine converts the preset dim to a 0-99 & outputs to LCD

;		The 5-bit preset dim value encoded in rhouse and rkey is passed in W

dimlcd	movwf	temp0			; save to flip the bit order
		clrf	temp1			; start with register cleared
		rrf		temp0,F			; flip bit order
		rlf		temp1,F
		rrf		temp0,F
		rlf		temp1,F
		rrf		temp0,F
		rlf		temp1,F
		rrf		temp0,F
		rlf		temp1,F
		rrf		temp0,F
		rlf		temp1,W			; moved re-ordered bits into W
		movwf	temp0			; save for conversion
		lslf	temp0,W			; multiply by 2 (0-62 range)
		addwf	temp0,W			; total multiply by 3 (0-93 range)
		lsrf	temp0,F			; divide by 4 (0-7 range)
		lsrf	temp0,F
		addwf	temp0,W			; add for total range of 0-100
		
		call	binbcd			; convert to BCD (2.55V range)
		movlw	H'99'			; to force 99 if => 100
		movf	bcdmsd,F		; check if BCD MSB > 0
		btfss	STATUS,Z		; skip and use actual value if not
		movwf	bcdlsd			; set BCD LSD to read 99
		call	outbcd2			; output 2 digit noise level
		movlw	"%"				; to output % character
		call	lcdout			; output "%"
		call	space1			; add a trailing space
		return

	if			pro == .1		; if Pro version

;		Output bit pattern to LCD

;		Bit pattern is stored in the bit pattern shift register:

;		44-bit data in bitsrg5-0: xxxx1110 HHHHHHHH KKKKKKKK KK1110HH HHHHHHKK KKKKKKKK
;		Assumes bank 0 is selected at entry

bitptn	call	lcdclr			; start with LCD completely cleared
		banksel bitsrg			; select BANK1
		movlw	.48				; 48-bit shift to restore original data pattern
		movwf	shftct			; save shift count
		movlw	.0				; point to LCD 1st line 1st character
		goto	bitadr			; start output on 1st line	

bitnxt	bcf		STATUS,C		; clear carry bit
		btfsc	bitsrg+5,7		; skip if MSB of shift register is 0
		bsf		STATUS,C		; set carry bit
		rlf		bitsrg,F		; shift received bits for display
		rlf		bitsrg+1,F
		rlf		bitsrg+2,F
		rlf		bitsrg+3,F
		rlf		bitsrg+4,F
		rlf		bitsrg+5,F

		decf	shftct,F		; tick off one shift cycle
		btfsc	STATUS,Z		; skip if not done
		goto	bitdon			; done with bit pattern

		btfsc	shftct,0		; skip and check for further processing if even bit pair
		goto	bitnxt			; shift again
		movfw	shftct			; get shift count
		sublw	.4				; past data? (4 bits left to shift)
		btfsc	STATUS,C		; skip & output data if not
		goto	bitnxt			; finish restoring original data pattern

		movfw	shftct			; get shift count
		sublw	.26				; is it start of 2nd half of command?
		btfss	STATUS,Z		; skip and set up 2nd half if so
		goto	bitout			; just output bit pattern
		movlw	H'40'			; point to LCD 2nd line 1st character
bitadr	clrf	BSR				; back to BANK0
		call	adrout			; output address
		banksel bitsrg			; select BANK1

bitout	movfw	bitsrg+5		; get bits into W bits 3&2
		clrf	BSR				; back to BANK0
		movwf	temp3			; save for shift
		lsrf	temp3,F			; shift bits into W bits 1&0
		lsrf	temp3,W
		andlw	b'00000011'		; mask all but 2 LSBs
		call	lcdout			; output character
		banksel bitsrg			; select BANK1
		goto	bitnxt			; loop for next bit

bitdon	clrf	BSR				; back to BANK0
		return					; done

	endif


;		Diagnostic to output a series of pulses equal to the value passed in W

;diag	movwf	temp2
;		incf	temp2,F			; insure 1 pulse minimum
;diaglp	bcf		PORTA,LCDDS		; output a negative going pulse to LCD Data Select
;		bsf		PORTA,LCDDS
;		decfsz	temp2,F			; done?
;		goto	diaglp			; loop if not
;		return


;		Check signal level & cycle count for a received logic "1"
;
;		Enter with:
;			W = signal detection threshold
;			siglvl = signal level during sample window
;			cyccnt = number of comparator cycles during sample of window
;		Returns with C = bit status

chkone	clrf	BSR				; make sure BANK0 is selected
		subwf	siglvl,W		; subtract threshold from this A/D sample
		btfss	STATUS,C		; continue if reading => threshold (C=1)
		return					; return with C=0 if reading < threshold

		movfw	siglvl			; recall the signal level
		addlw	.228			; check if the level => 280mV
		movlw	.208			; 48 cycle "1" detection threshold if => 280mV
		btfsc	STATUS,C		; skip if < 280mV
		goto	chkcnt			; use 48 cycle detection threshold
		movfw	siglvl			; recall the signal level (must be < 28)
		sublw	.236			; calculate 20-47 detection threshold depending on signal level

chkcnt	addwf	cyccnt,F		; C set if => 20 to 48 cycles of 120KHz in the window
		return					; return with C=1 for received logic "1"


;		COMMAND DECODE

;		Decode & Display valid X10 commands

cmddec	movlw	H'40'			; point to LCD 2nd line 1st character
		call	adrout			; output LCD address
		movlw	strrpt-1		; -> "RPT_CHK"
		btfsc	rcomnd,cmdrpt	; skip if not a repeated command
		goto	cmdlcd			; output command string to LCD
		movfw	rkeycd			; get keycode
		andlw	b'11111101'		; mask possible pre-set dim MSB
		xorlw	b'00010101'		; check for pre-set dim command
		btfss	STATUS,Z		; skip & process pre-set dim command differently
		goto	cmdnml			; display a normal X10 command
		movlw	b'00101000'		; select Pdim message
		call	lcddcd			; output command string to LCD
		call	space1			; add a space
		bcf		STATUS,C		; to shift in "0" for the MSB
		btfsc	rkeycd,1		; skip if pre-set dim MSB is 0
		bsf		STATUS,C		; to shift in 1 for the MSB
		rlf		rhouse,W		; assemble complete pre-set dim command
		andlw	b'00011111'		; mask extraneous bits
		call	dimlcd			; output pre-set dim level to LCD
		goto	cmdvld			; indicate valid command

cmdnml	movfw	rhouse			; get current command house code
		andlw	b'00001111'		; make sure 4 MSBs are zero
		call	x10hex			; convert X10 "A-P" to hex 0-F
		addlw	H'41'			; convert hex 0-F to ASCII A-P
		call	lcdout			; output housecode to LCD
		btfsc	rkeycd,0		; skip if a unit code
		goto	cmdftn			; process a function code
		movfw	rhunit			; get 4-bit unit code
		andlw	b'00001111'		; make sure 4 MSBs are zero
		call	x10hex			; convert X10 "1-16" to hex 0-F
		addlw	.1				; adjust for X10 digits starting at "1"
		call	binbcd			; convert to BCD (255 range)
		call	outbcd2			; output 2-digit unit code to LCD
		movlw	.6				; to output 6 spaces
		call	spacen			; output 6 spaces to blank the last command
		goto	cmdvld			; indicate valid command

cmdftn	movlw	H'41'			; point to LCD 2nd line 2nd character
		call	adrout			; output LCD address
		btfsc	cmdflg,xtdcmd	; skip if not an extended command
		goto	cmdout			; output unit code extracted from extended code
		swapf	rhunit,W		; get house code for last unit code into LSBs
		andlw	b'00001111'		; mask MSBs
		subwf	rhouse,W		; compare with house code for the command
		btfsc	STATUS,Z		; skip if command uses a different house code 
		goto	cmdout			; display command
cmducx	movlw	"x"				; indicate unit code is not valid for this command
		call	lcdout
		movlw	"x"
		call	lcdout
		goto	cmdous			; output command function string

cmdout	movfw	rhunit			; get 4-bit unit code
		andlw	b'00001111'		; make sure 4 MSBs are zero
		call	x10hex			; convert X10 "1-16" to hex 0-F
		addlw	.1				; adjust for X10 digits starting at "1"
		call	binbcd			; convert to BCD (255 range)
		call	outbcd2			; output 2-digit unit code to LCD
cmdous	call	space1			; leave a space between the codes and the function
		rlf		rkeycd,W		; multiply command key code by 4
		andlw	b'00111100'		; mask all but command bits
		addlw	strdcd			; point to command decode strings
cmdlcd	call	lcddcd			; output command string to LCD
		call	space1			; make sure the last character is blank

cmdvld	movlw	H'49'			; point to message field
		call	adrout			; output address
		movlw	strvld-1		; -> "-VALID-"
		call	lcdstr			; output string
		return


;		Write configuration flags into EEPROM

savcfg
		banksel EEADRL			; select BANK3
		movlw	EECONF			; get configuration flags address in EEPROM
		movwf	EEADRL			; set EEPROM address to write
		clrf	EEADRH
		movfw	cfgflg			; get configuration flags in RAM
		call	writee			; write new configuration into EEPROM
		clrf	BSR				; back to BANK0
		return

;		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

	if			pro == .1		; if Pro version

;		ARCHIVE DATA

;		This routine saves the last X10 reading into EEPROM when power is interrupted

archiv	movfw	rhouse			; get unpacked received house code (4-bit X10 code)
		movwf	temp0			; save to write into EEPROM
		movfw	rhunit			; get unpacked received house & unit code (4-bit X10 code)
		movwf	temp1			; save to write into EEPROM
		movfw	rcomnd			; get unpacked received command code (4-bit X10 code & flags)
		movwf	temp2			; save to write into EEPROM
		movfw	x10max			; get X10 maximum signal level
		movwf	temp3			; save to write into EEPROM
		movfw	frqavg			; get average frequency
		movwf	temp4			; save to write into EEPROM

		banksel EEADRL			; select BANK3
		movlw	EEARCH			; get address for archive data in EEPROM
		movwf	EEADRL			; set EEPROM address to write
		clrf	EEADRH

		movfw	temp0			; recall unpacked received house code 
		call	writee			; save in EEPROM
		incf	EEADRL,F		; point to next EEPROM address
		movfw	temp1			; recall unpacked received house & unit code
		call	writee			; save in EEPROM
		incf	EEADRL,F		; point to next EEPROM address
		movfw	temp2			; recall unpacked received command code
		call	writee			; save in EEPROM
		incf	EEADRL,F		; point to next EEPROM address
		movfw	temp3			; recall X10 signal level average
		call	writee			; save in EEPROM
		incf	EEADRL,F		; point to next EEPROM address
		movfw	temp4			; recall average frequency 
		call	writee			; save in EEPROM
		incf	EEADRL,F		; point to next EEPROM address
		movfw	pknois			; get peak noise level short-term average
		btfsc	pknois,7		; skip if less than 128
		movlw	.127			; set noise = 127 max & leave MSB clear for gain flag
		btfsc	rcvflg,maxrng	; skip if X10 maximum signal level was measured in low range
		iorlw	b'10000000'		; set range flag for archive data
		call	writee			; save in EEPROM

		clrf	BSR				; back to BANK0
		return

	endif


;		SUBROUTINE TO PROCESS TRANSMIT DATA
;
;		This routine is called to set up the next bit to be transmitted

xmtbit	btfss	xmtflg,clrlin	; skip and continue if line is clear
		return					; wait for clear line
		btfss	xmtflg,xmtucd	; skip if unit code must be transmitted
		goto	xmtccd			; just transmit command code if not

		movf	xmtcnt,F		; check if new message must be loaded
		btfsc	STATUS,Z		; skip if not
		goto	xmthse			; set up first half
		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
		goto	xmtout			; output next bit

xmthse	movlw	b'00001100'		; "P" housecode for transmit
		movwf	xhouse			; save it for transmit
		lslf	rhunit,W		; get received unit code for status echo
		andlw	b'00011110'		; mask all but unit code bits
		btfss	xmtflg,xmtvlt	; skip if transmitting status echo voltage
		movlw	b'00001100'		; "1" unit code for noise alert transmit
		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 new message must be loaded
		btfsc	STATUS,Z		; skip if not
		goto	xmtcod			; set up first half
		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
		goto	xmtout			; output next bit

xmtcod	btfss	xmtflg,xmtvlt	; skip if transmitting status echo voltage
		goto	xmtphc			; transmit P housecode
		movfw	x10ech			; get X10 status echo voltage
		bsf		PCLATH,3		; select ROM Page 1 for data compression routine
		call	comprs			; compress voltage measurement into 5 bits (0-31)
		clrf	PCLATH			; back to ROM Page 0
		movwf	temp0			; save to flip the bit order
		clrf	temp1			; start with register cleared
		rrf		temp0,F			; flip bit order
		rlf		temp1,F
		rrf		temp0,F
		rlf		temp1,F
		rrf		temp0,F
		rlf		temp1,F
		rrf		temp0,F
		rlf		temp1,W			; move 4 re-ordered LSBs into W
		movwf	xhouse			; save for pre-set dim command
		rrf		temp0,F			; move MSB into C
		movlw	b'00010101'		; pre-set dim command
		btfsc	STATUS,C		; skip if voltage measurement MSB is 0
		iorlw	b'00000010'		; set voltage MSB bit in pre-set dim command
		goto	xmtkkd			; save pre-set dim keycode & set up transmit

xmtphc	movlw	b'00001100'		; "P" housecode for transmit
		movwf	xhouse			; save it for transmit
		movlw	b'00000101'		; "ON" command
		btfss	xmtflg,xhinse	; skip if high noise flag is set
		movlw	b'00000111'		; "OFF" command
		btfsc	xmtflg,rptchk	; skip if repeater check is not in process
		movlw	b'00010011'		; "Hail Acknowledge" for repeater check
xmtkkd	movwf	xkeycd			; save it for transmit
		movlw	.50				; transmit 50 bits (22 + 22 + 6 gap)
		btfsc	xmtflg,rptchk	; skip if repeater check is not in process
		movlw	.23				; only transmit first 22 bits for repeater check
		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	xmtsrg+2,5		; skip if next bit is a "0"
		bsf		xmtflg,outbit	; set output bit to a logic "1"

		lslf	xmtsrg,F		; shift "0" into transmit shift register
		rlf		xmtsrg+1,F
		rlf		xmtsrg+2,F
deccnt	decfsz	xmtcnt,F		; decrement transmit bit count & skip if end of message
		return					; just return if not

		btfsc	xmtflg,xmtucd	; skip if not transmit unit code
		goto	xmtnu0			; just clear the unit code flag
		bcf		xmtflg,xmtmsg	; clear transmit message flag
		bcf		xmtflg,xmtvlt	; clear transmit voltage flag
xmtnu0	bcf		xmtflg,xmtucd	; 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	xmtsrg+2		; save in MSB of transmit shift register
		movfw	xhouse			; get 4-bit house code
		andlw	b'00001111'		; make sure 4 MSBs are zero
		call	expx10			; convert to expanded X10 code
		movwf	xmtsrg+1		; save in 2nd byte of transmit shift register
		lsrf	xkeycd,W		; get 5-bit key code & shift off command/function bit
		andlw	b'00001111'		; make sure 4 MSBs are zero
		call	expx10			; convert remaining 4 bits to expanded X10 code
		movwf	xmtsrg			; 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		xmtsrg+2,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		xmtsrg,F		; roll 1st command/function bit into LSB of shift register
		rlf		xmtsrg+1,F
		rlf		xmtsrg+2,F		; shifts 2nd command/function bit into carry
		rlf		xmtsrg,F		; and around into LSB of transmit shift register
		rlf		xmtsrg+1,F
		rlf		xmtsrg+2,F
		return


;		MAIN PROGRAM TIMING LOOP

;		This loops through completely for each half cycle of the powerline

start	bsf		PCLATH,3		; select ROM Page 1 for Initialization routine
		call	initiz			; call initialization routine
		clrf	PCLATH			; back to ROM Page 0
		call	dly20ms			; long delay for LCD startup
		call	lcdinit			; initialize & clear the LCD display
		call	lcdhdr			; output XTBM PRO version header
		btfss	cfgflg,tested	; skip if startup self-test has already passed
		call	lcdtst			; display firmware version & boxes to 2nd LCD line
		movlw	.128			; for 1 second startup delay
		movwf	dspcnt
		bsf		xmtflg,strtup	; indicate startup powerline monitor is in process

strtlp	decfsz	dspcnt,W		; skip if startup interval is over
		goto	mainlp			; monitor powerline for 1st 1/2 second
		bcf		xmtflg,strtup	; clear startup flag
		banksel	TRISA			; select BANK1 for port direction registers
		bcf		TRISA,LCDDS		; make sure LCDDS (C1I+) is an output
		banksel	ANSELA			; select BANK3
		bcf		ANSELA,LCDDS	; make sure LCDDS (C1I+) is configured for digitial out
		clrf	BSR				; back to BANK0

		movlw	H'40'			; point to message field
		call	adrout			; output address
		movlw	b'00001100'		; "P" housecode
		xorwf	rhouse,W		; check if received P houscode during startup
		btfss	STATUS,Z		; skip & continue check if so

	if			pro == .1		; if Pro version
		goto	strt00			; check for complete reset if not
	else
		goto	strt02			; no "double switch" reset check for basic XTBM
	endif

		movfw	rkeycd			; get keycode
		movlw	b'00000101'		; "ON" command
		xorwf	rkeycd,W		; check if received P ON command during startup
		btfsc	STATUS,Z		; skip & continue check if not
		goto	nsemn1			; turn noise monitor on
		movlw	b'00000111'		; "OFF" command
		xorwf	rkeycd,W		; check if received P OFF command during startup
		btfsc	STATUS,Z		; skip if not
		goto	nsemn0			; turn noise monitor off
		movlw	b'00001011'		; "BRIGHT" command
		xorwf	rkeycd,W		; check if received P BRIGHT command during startup
		btfsc	STATUS,Z		; skip & continue check if not
		goto	echmn1			; turn status echo monitor on
		movlw	b'00001001'		; "DIM" command
		xorwf	rkeycd,W		; check if received P DIM command during startup
		btfsc	STATUS,Z		; skip if not
		goto	echmn0			; turn status echo monitor off
		movlw	b'00000001'		; "ALL OFF" command
		xorwf	rkeycd,W		; check if received P ALL OFF command during startup
		btfss	STATUS,Z		; skip and do a "factory reset" if so
		goto	strt02			; no configuration change if not
		clrf	cfgflg			; clear noise alarm enable & status report enable
		goto	strt01			; perform a complete reset

strt00	btfss	modflg,bothsw	; skip if both switches are pressed
		goto	strt02			; no configuration change if not
		bcf		cfgflg,tested	; clear just the self-test flag to force re-test
strt01	clrf	rhouse			; clear received house code
		clrf	rhunit			; clear received unit code
		clrf	rcomnd			; clear received command code
		clrf	x10max			; clear maximum signal level
		clrf	x10cpr			; clear compare signal level
		clrf	frqavg			; clear average frequency

	if			pro == .1		; if Pro version
		call	archiv			; clear archived data
	endif

		movlw	strrst-1		; -> "!COMPLETE RESET!"
		goto	cfgnew			; output string & save new configuration

nsemn1	bsf		cfgflg,nsemon	; set noise monitor flag
		movlw	straon-1		; -> "ON "
		movwf	temp4			; save for report
		movlw	strnal-1		; -> " NOISE ALERT "
		goto	cfgsts			; report configuration status	
nsemn0	bcf		cfgflg,nsemon	; clear noise monitor flag
		movlw	straof-1		; -> "OFF"
		movwf	temp4			; save for report
		movlw	strnal-1		; -> " NOISE ALERT "
		goto	cfgsts			; report configuration status	
echmn1	bsf		cfgflg,stsmon	; set status echo monitor flag
		movlw	straon-1		; -> "ON "
		movwf	temp4			; save for report
		movlw	strsts-1		; -> " STATUS LEVEL "
		goto	cfgsts			; report configuration status	
echmn0	bcf		cfgflg,stsmon	; clear status echo monitor flag
		movlw	straof-1		; -> "OFF"
		movwf	temp4			; save for report
		movlw	strsts-1		; -> " STATUS LEVEL "

cfgsts	call	lcdstr			; output configuration string
		movfw	temp4			; recall ON/OFF status
cfgnew	call	lcdstr			; output string
		call	savcfg			; write new configuration into EEPROM

		movlw	.50				; for 1 second delay
		movwf	temp2			; set delay loop count
cfgdly	call	dly20ms			; delay 20mS
		decfsz	temp2,F			; count off 20mS
		goto	cfgdly			; display configuration status for 1 second

strt02	bsf		xmtflg,rptchk	; set flag to indicate repeater check is in process
		bsf		xmtflg,xmtmsg	; set flag to transmit repeater check message
		bsf		PCLATH,3		; select ROM Page 1
		call	rstflt			; reset all filters before startup repeater check
		clrf	PCLATH			; back to ROM Page 0
		movlw	.63				; allow .5 sec for repeater check before flag is cleared
		movwf	dspcnt			; set dispcnt
		goto	mainlp			; 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
		banksel	WDTCON			; select BANK1 for watchdog timer
		movlw	b'00010010'		; set WDT for 1/2 sec timeout (31KHz/16384)
		movwf	WDTCON			; no WD timeout during power down
		clrf	BSR				; back to BANK0

	if			pro == .1		; if Pro version
		btfsc	stsflg,archok	; skip if no data is available to archive
		call	archiv			; archive present X10 data into EEPROM
		bcf		stsflg,archok	; indicate data has been archived
	endif

;		Main timing loop

mainlp
		banksel CCP1CON			; select BANK5 for PWM
		clrf	CCP1CON			; ensure PWM is off
		banksel CM1CON0			; select BANK2 for comparators
		movlw	b'00100100'		; comparator 1 disabled, C1OUT, fast asynchronous, no hysteresis
		movwf	CM1CON0
		clrf	CM1CON1			; no interrupts, C1I+ reference, C1IN0- input (X10XIN)
		banksel	IOCAF			; select BANK7 for interrupt on change
		bcf		IOCAF,IOCAF3	; make sure IOC flag is cleared for PCB version 1.2
		clrf	BSR				; back to BANK0
		bsf		INTCON,IOCIE	; enable ZCD interrupt on change

mainzc	movfw	TMR1H			; get Timer 1 high byte
		btfsc	STATUS,Z		; skip if Timer 1 is running
		goto	mainzc			; loop & wait for zero-crossing or WDT if Timer 1 is not running

main01	movfw	TMR1H			; get Timer 1 high byte
		addlw	.20				; check for timer preload (239 -> 255)
		btfsc	STATUS,C		; skip if not
		goto	main02			; begin processing next half cycle if so
		addlw	.40				; past next transmit window with no zero crossing?
		btfss	STATUS,C		; skip if so
		goto	main01			; loop while waiting for zero crossing
		goto	main00			; stop & disable transmitter if no zero crossing

;		Set up Timer 0 to count comparator transitions

main02	bcf		PORTC,PWMOUT	; make sure PWM output is a logic 0 for off
		banksel	TRISA			; select BANK1 for port direction registers
		movlw	b'00011011'		; C1I+, CI0-, ZCD, VLEVEL are all inputs
		movwf	TRISA			; SWINPT is output to route C1OUT -> T0CLK
		bcf		TRISC,PWMOUT	; make sure PWM output is enabled
		banksel	ANSELA			; select BANK3
		movlw	b'00010011'		; LCDDS (for C1I+), X10XIN, VLEVEL are analog inputs
		movwf	ANSELA			; disable SWINPT analog input (for C1OUT -> T0CLK)
		banksel CM1CON0			; select BANK2 for comparators
		movlw	b'10100100'		; comparator 1 enabled, C1OUT, fast asynchronous, no hysteresis
		movwf	CM1CON0
		clrf	BSR				; back to BANK0

;		Reduce analog gain if in high range mode

		btfsc	rcvflg,hirang	; skip if not high range
		bsf		PORTC,RANGE		; switch to high range input (9.99V max)

;		Delay until 100 uS after the zero crossing

dly060	movfw	TMR1H			; get Timer 1 high byte
		addlw	.16				; is it 60uS after the zero crossing?
		btfss	STATUS,C		; skip if so
		goto	dly060			; loop if not

dly100	movfw	TMR1L			; get Timer 1 high byte
		addlw	.64			; is it 100uS after the zero crossing?
		btfss	STATUS,C		; skip if so
		goto	dly100			; loop if not

;		Count cycles from and sample level 100uS to 244uS after the zero crossing

		clrf	TMR0			; clear Timer 0 at start of window
		bcf		INTCON,T0IF		; clear Timer 0 overflow flag

	bsf	PORTC,3

		bcf		INTCON,GIE		; disable interrupts
		bsf		PCLATH,3		; select ROM Page 1
		call	adsamp			; sample signal/noise at beginning of X10 sample window
		clrf	PCLATH			; back to ROM Page 0
		bsf		INTCON,GIE		; enable interrupts	

	bcf	PORTC,3

		movfw	TMR0			; read Timer 0 count
		movwf	temp0			; save it
		movfw	adcsum			; get A/D conversion sum MSB
		banksel	pg1ram			; switch to BANK1
		movwf	adcbgn			; save A/D average at beginning of X10 transmission window
		movfw	adcmax			; get A/D maximum value
		movwf	maxbgn			; save A/D max at beginning of X10 transmission window
		movfw	temp0			; recall Timer 0 count
		movwf	bgncnt			; save beginning cycle count
		clrf	BSR				; back to BANK0

;		Measure signal level during the X10 sample window from 248uS to 924uS after zero crossing

		clrf	TMR0			; clear Timer 0 at start of window
		bcf		INTCON,T0IF		; clear Timer 0 overflow flag

	bsf	PORTC,3

		bcf		INTCON,GIE		; disable interrupts
		bsf		PCLATH,3		; select ROM Page 1
		call	admeas			; sample signal/noise voltage for 660 uS
		clrf	PCLATH			; back to ROM Page 0
		bsf		INTCON,GIE		; enable interrupts	

	bcf	PORTC,3

		movfw	TMR0			; read Timer 0 count (=> 48 cycles for logic 1)
		btfsc	INTCON,T0IF		; skip if Timer 0 did not overflow
		movlw	.255			; set max count = 255
		movwf	x10cnt			; save for X10 bit processing
		movfw	adcmax			; get A/D maximum value during X10 sample window
		movwf	maxx10			; save it
		movfw	adcsum			; get A/D conversion sum MSB
		movwf	adcx10			; save A/D reading for X10 sample window

;		Count cycles and sample level 928uS to 1070uS after the zero crossing

		clrf	TMR0			; clear Timer 0 at start of window
		bcf		INTCON,T0IF		; clear Timer 0 overflow flag

	bsf	PORTC,3

		bcf		INTCON,GIE		; disable interrupts
		bsf		PCLATH,3		; select ROM Page 1
		call	adsamp			; sample signal/noise at end of X10 sample window
		clrf	PCLATH			; back to ROM Page 0
		bsf		INTCON,GIE		; enable interrupts	

	bcf	PORTC,3

		movfw	TMR0			; read Timer 0 count
		movwf	temp0			; save it
		movfw	adcsum			; get A/D conversion sum MSB
		banksel	pg1ram			; switch to BANK1
		movwf	adcend			; save A/D average at end of X10 transmission window
		movfw	adcmax			; get A/D maximum value
		movwf	maxend			; save A/D max at end of X10 transmission window
		movfw	temp0			; recall Timer 0 count
		movwf	endcnt			; save end cycle count
		clrf	BSR				; back to BANK0

;		Shut off PWM at end of X10 window

		banksel	CCP1CON			; select BANK5 for PWM control registers
		clrf	CCP1CON			; turn off PWM
		clrf	BSR				; back to BANK0
		bcf		PORTC,PWMOUT	; make sure PWM output is a "0"

;		Sample noise level 1076uS to 1220uS after zero crossing

	bsf	PORTC,3

		bcf		INTCON,GIE		; disable interrupts
		bsf		PCLATH,3		; select ROM Page 1
		call	adsamp			; sample signal/noise at end of X10 transmission window
		clrf	PCLATH			; back to ROM Page 0
		bsf		INTCON,GIE		; enable interrupts	

	bcf	PORTC,3

		banksel	pg1ram			; switch to BANK1
		movfw	adcmax			; get A/D maximum value
		subwf	maxend,W		; compare with max at end of X10 sample window
		movfw	adcmax			; recall A/D maximum value
		btfss	STATUS,C		; skip if < than end of X10 sample window value
		movwf	maxend			; update value to new higher level
		clrf	BSR				; back to BANK0

;		Measure signal level in the post X10 transmission window 1230 to 1896uS after zero crossing

		clrf	TMR0			; clear Timer 0 at start of window
		bcf		INTCON,T0IF		; clear Timer 0 overflow flag

	bsf	PORTC,3

		bcf		INTCON,GIE		; disable interrupts
		bsf		PCLATH,3		; select ROM Page 1
		call	admeas			; sample signal/noise voltage for 660 uS
		clrf	PCLATH			; back to ROM Page 0
		bsf		INTCON,GIE		; enable interrupts	

	bcf	PORTC,3

		movfw	TMR0			; read Timer 0 count (=> 48 cycles for logic 1)
		btfsc	INTCON,T0IF		; skip if Timer 0 did not overflow
		movlw	.255			; set max count = 255
		movwf	cyccnt			; save count for later processing
		movwf	pstcnt			; save post X10 window cycle count

		banksel	TRISA			; select BANK1 for port direction registers
		movlw	b'00011110'		; CI0-, SWINPT, ZCD, VLEVEL are all inputs
		movwf	TRISA			; change LCDDS (C1I+) back to output
		banksel	ANSELA			; select BANK3
		movlw	b'00010110'		; X10XIN, SWINPT, VLEVEL are analog inputs
		movwf	ANSELA
		banksel CM1CON0			; select BANK2 for comparators
		movlw	b'00100100'		; comparator 1 disabled, C1OUT, fast asynchronous, no hysteresis
		movwf	CM1CON0
		clrf	BSR				; back to BANK0

		bcf		PORTC,RANGE		; switch back to low-range input (2.5V max)

		movfw	adcmax			; get A/D maximum value during post X10 sample window
		movwf	maxpst			; save it
		movfw	adcsum			; get A/D conversion sum MSB
		movwf	adcpst			; save A/D conversion post X10 window sum MSB
		movwf	siglvl			; save for logic "1" check
		movfw	x10thd			; X10 sample window threshold (range adjusted)
		call	chkone			; check for a logic "1" in post X10 window
		rlf		pstsr1,F		; shift detected bit into post window shift register

;		Process measurements inside X10 sample window

		lsrf	x10cnt,W		; divide by 2 (N pulses in 667uS window x 1.5 = frequency)
		addwf	x10cnt,W		; add to multiply by 1.5 (convert to frequency)
		btfsc	STATUS,C		; skip if no overflow
		movlw	.255			; set max frequency
		movwf	frqcnt			; save frequency measurement
		movfw	adcx10			; recall A/D reading for X10 sample window
		movwf	temp0			; save in global to store into bit voltage buffer

	if			pro == .1		; if Pro version

;		Store X10 sample reading in the bit voltage buffer

		movf	bitcnt,F		; check if past start pattern (bitcnt >=4)
		btfss	STATUS,Z		; skip & save start pattern voltages if not
		goto	bitvlt			; save bit voltage

		banksel	pgbram			; switch to BANK12
		movfw	stbufr+1		; save start pattern voltage readings start buffer
		movwf	stbufr
		movfw	stbufr+2
		movwf	stbufr+1
		movfw	stbufr+3
		movwf	stbufr+2
		movfw	temp0			; recall new A/D reading in X10 sample window
		movwf	stbufr+3		; save in bit voltage buffer
		clrf	BSR				; back to BANK0
		goto	main03			; continue with noise processing

bitvlt	btfsc	dspflg,bitlok	; skip if bit pattern display is not locked
		goto	main03			; no bit pattern processing if locked

		movfw	bitcnt			; get number of samples stored in bit voltage buffer
		addlw	.212			; check if buffer is full (>=44)
		btfsc	STATUS,C		; skip & store X10 voltage if buffer is not full yet
		goto	main03			; continue with noise processing

		movlw	bvbufr			; get beginning of bit voltage buffer
		addwf	bitcnt,W		; add bit count offset
		movwf	FSR0L			; set indirect pointer 0 to bit voltage buffer
		movlw	.6				; for bank 12
		movwf	FSR0H
		movfw	adcx10			; recall A/D reading in X10 sample window
		movwf	INDF0			; write into bit voltage buffer

	endif

;		Adjust noise measurement when sampled in high range (9.9V)

main03	clrf	pkncyc			; begin with peak noise cleared for this half cycle
		btfss	rcvflg,hirang	; skip if X10 window was sampled at high range (9.9V)
		goto	main04			; no noise adjustment for normal range

		banksel	pg1ram			; switch to BANK1
		movfw	adcsumL			; get A/D conversion sum LSB
		clrf	BSR				; back to BANK0
		movwf	temp0			; save A/D conversion sum LSB
		rlf		temp0,F			; multiply background noise by 4 when at low gain
		rlf		bkgnse,F
		rlf		temp0,F
		rlf		bkgnse,F

main04	movfw	x10cnt			; recall x10 pulse count
		movwf	cyccnt			; save for logic "1" check
		movfw	adcx10			; recall A/D conversion for X10 sample window
		movwf	siglvl			; save for logic "1" check
		movfw	x10thd			; X10 sample window threshold (range adjusted)
		call	chkone			; check for a logic "1"

		movfw	adcx10			; recall A/D reading for X10 sample window
		btfss	STATUS,C		; skip if bit was a "1"
		movwf	bkgnse			; save A/D reading for background noise average

		bsf		PCLATH,3		; select ROM Page 1 for Receive routine
		call	rcvinp			; process received bit
		clrf	PCLATH			; back to ROM Page 0

;		Perform self-test during repeater check at power-up

		btfsc	cfgflg,tested	; skip to perform self-test if it has not passed yet
		goto	x10chk			; check receive bit status
		btfss	xmtflg,rptchk	; skip if doing a repeater check
		goto	x10chk			; check receive bit status if not
		btfss	xmtflg,xmtmsg	; skip if transmitting
		goto	x10chk			; check receive bit status if not
		btfss	xmtflg,outbit	; skip if output bit is a "1"
		goto	endcyc			; no display 

		clrw					; point to beginning of first line
		call	adrout			; output address
		movfw	adcx10			; get A/D reading in X10 sample window
		addlw	.56				; it should be over 8.0V (800/4 + 56 = 256)
		btfsc	STATUS,C		; skip if less than 8.0V
		goto	stpass			; self test passed if over 8.0V
		addlw	.50				; check if over 6.0V (600/4 + 26 = 256)
		btfss	STATUS,C		; skip and give low level warning if 6-8V
		goto	stfail			; process self-test failure
		movlw	strlow-1		; -> "LOW OUTPUT LEVEL"
		goto 	stoutp			; output string

stpass	bsf		cfgflg,tested	; indicate self-test passed
		call	savcfg			; save self-test status
		movlw	strslf-1		; -> "SELF-TEST "
		call	lcdstr			; output string
		movlw	strpas-1		; -> "PASSED"
		call	lcdstr			; output string

		movlw	H'40'			; point to beginning of message line
		call	adrout			; output address
		movlw	stralr-1		; -> "ALRT "
		call	lcddcd			; output string
		movlw	straon-1		; -> "ON "
		btfss	cfgflg,nsemon	; skip if noise monitor alert is enabled
		movlw	straof-1		; -> "OFF"
		call	lcdstr			; output string
		call	space1			; add 1 space
		movlw	strstm-1		; -> "STS "
		call	lcddcd			; output string
		movlw	straon-1		; -> "ON "
		btfss	cfgflg,stsmon	; skip if status monitor alert is enabled
		movlw	straof-1		; -> "OFF"
		call	lcdstr			; output string

		bcf		xmtflg,xmtmsg	; make sure transmitter is disabled
		movlw	.100			; for 2 second delay
		movwf	temp2			; set delay loop count

stloop	bsf		INTCON,IOCIE	; enable ZCD interrupt on change
		call	dly20ms			; delay 20mS
		decfsz	temp2,F			; count off 20mS
		goto	stloop			; display self-test status for 1 second
		reset					; restart after self-test has completed

stfail	movlw	strtst-1		; -> " FAIL SELF TEST "
stoutp	call	lcdstr			; output string
		bcf		xmtflg,rptchk	; cancel repeater check
		bcf		xmtflg,xmtmsg	; cancel transmit

		movfw	adcx10			; get the self-test readback voltage
		call	binbcd9			; convert to BCD (9.99V range)
		movlw	H'40'			; point to beginning of message line
		call	adrout			; output address
		movlw	"X"				; X for X10 amplitude
		call	lcdout			; output character
		call	outbcd3			; output X10 signal level in format x.xx
		movlw	strrbk-1		; -> "V READBACK"
		call	lcdstr			; output string

		bsf		dspflg,errmsg	; set flag indicating an error message is displayed
		clrf	dspcnt			; leave error message displayed for 2 seconds
		goto	dsplay			; done

;		Process potential X10 command

x10chk	btfsc	stsflg,rcvbit	; skip if bit was a "0"
		goto	x10rck			; check gain and process a "1"
		btfss	stsflg,rcving	; skip if receiving a possible X10 command
		goto	nseck1			; check noise level in X10 window if not receiving anything
		goto	chkcmd			; no noise processing for "0" if receiving a possible command

x10rck	btfsc	rcvflg,hirang	; skip if not already high range
		goto	x10bit			; just process X10 averages
		btfsc	xmtflg,xmtmsg	; skip if not transmitting
		goto	x10hrg			; set high range (9.99V) during transmission

chkbct	movfw	rcvcnt			; get receive bit count
		btfss	STATUS,Z		; allow gain switch only during start pattern
		goto	x10bit			; process X10 averages
		movfw	adcx10			; get A/D reading during X10 window
		sublw	.200			; was it over 200 (2.00V)
		btfsc	STATUS,C		; skip if reading was over 2.0V
		goto	x10bit			; process X10 averages
		btfsc	cmdflg,errnse	; switch to high range if no noise error
		goto	x10bit			; process X10 averages

x10hrg	bsf		rcvflg,hirang	; set high range (9.99V max)
		bsf		PCLATH,3		; select ROM Page 1
		call	x10clr			; clear X10 counters & sum next 8 bits (out of 12 total)
		clrf	PCLATH			; back to ROM Page 0
		goto	dsplay			; skip further processing of this sample

x10bit	btfsc	cmdflg,errbsc	; skip if no bad start code error
		goto	nseck1			; process noise for a bad start code
		btfsc	cmdflg,errrcv	; skip if no receive error
		goto	nseck1			; process noise for a receive error
		btfsc	cmdflg,errnse	; skip if no noise error
		goto	nseck1			; process noise for a noise error

		bsf		PCLATH,3		; select ROM Page 1
		call	x10sum			; sum X10 signal level & frequency
		clrf	PCLATH			; back to ROM Page 0
		goto	chkcmd			; no noise processing if a valid "1" bit

;		Determine peak noise over X10 transmission window

nseck1	btfsc	xmtflg,xmtmsg	; skip if not transmitting
		goto	chkcmd			; no noise processing when transmitting
		btfsc	xmtflg,rptchk	; skip if not doing a repeater check
		goto	chkcmd			; no noise processing during a repeater check

		movfw	maxx10			; get peak noise value inside X10 reception window
		subwf	pkncyc,W		; compare with present peak noise value
		movfw	maxx10			; recall peak noise value inside X10 reception window
		btfss	STATUS,C		; skip if not higher than present peak noise value
		movwf	pkncyc			; set peak noise to value inside X10 reception window

		banksel	pg1ram			; select BANK1 RAM
		movfw	maxbgn			; get peak noise value at beginning of X10 transmission window
		subwf	pkncyc,W		; compare with present peak noise value
		movfw	maxbgn			; recall peak noise value at beginning of X10 transmission window
		btfss	STATUS,C		; skip if not higher than present peak noise value
		movwf	pkncyc			; set peak noise to value at beginning of X10 transmission window

		movfw	maxend			; get peak noise value at end of X10 transmission window
		subwf	pkncyc,W		; compare with present peak noise value
		movfw	maxend			; recall peak noise value at end of X10 transmission window
		btfss	STATUS,C		; skip if not higher than present peak noise value
		movwf	pkncyc			; set peak noise to value at end of X10 transmission window

		movfw	pkncyc			; get peak noise for this half cycle
		btfsc	stsflg,cycpol	; skip if negative half cycle
		movwf	pkncyp			; set peak noise for positive half cycle
		btfss	stsflg,cycpol	; skip if positive half cycle
		movwf	pkncyn			; set peak noise for negative half cycle

		movfw	pkncyp			; get peak noise for positive half cycle
		subwf	pkncyc,W		; compare with present peak noise value
		movfw	pkncyp			; recall peak noise for positive half cycle
		btfss	STATUS,C		; skip if not higher than present peak noise value
		movwf	pkncyc			; set peak noise to value of positive half cycle
				
		movfw	pkncyn			; get peak noise for negative half cycle
		subwf	pkncyc,W		; compare with present peak noise value
		movfw	pkncyn			; recall peak noise for negative half cycle
		btfss	STATUS,C		; skip if not higher than present peak noise value
		movwf	pkncyc			; set peak noise to value of negative half cycle
		clrf	BSR				; back to BANK0

		movfw	x10cnt			; get cycle count in X10 reception window
		banksel	pg1ram			; select BANK1 RAM
		btfsc	stsflg,cycpol	; skip if negative half cycle
		movwf	ncyctp			; set noise cycle count for positive half cycle
		btfss	stsflg,cycpol	; skip if positive half cycle
		movwf	ncyctn			; set noise cycle count for negative half cycle
		clrf	BSR				; back to BANK0
		
avgnse	bsf		PCLATH,3		; select ROM Page 1
		call	nselta			; calculate background noise long-term average
		call	nsesta			; calculate background noise short-term average
		call	pkavgs			; calculate peak noise averages

	if			pro == .1		; if Pro version
		call	bgnsta			; calculate beginning of transmit window noise short-term average
		call	endsta			; calculate end of transmit window noise short-term average
	endif

		call	nctavg			; calculate noise cycle count average
		clrf	PCLATH			; back to ROM Page 0
chkcmd	btfsc	xmtflg,strtup	; skip if startup flag is cleared
		goto	endcyc			; skip noise calculations & display during startup

;		Monitor noise & issue a warning for a sudden increase

cknois	movfw	pknois			; get peak noise short-term average
		sublw	.9				; check if noise => 100mV
		btfsc	STATUS,C		; skip & check for high noise if => 100mV
		goto	nselow			; check for transmit low noise message
		bsf		dspflg,nsefrq	; set flag indicating noise frequency is to be displayed
		btfsc	cmdflg,errcol	; skip if a collision
		goto	updnse			; no high noise warning for a collision
		movfw	bsccnt			; get BSC count
		addlw	.251			; have a burst of at least 5 BSCs been counted?
		btfsc	STATUS,C		; skip if not
		goto	ckhnse			; indicate high noise if so
		lsrf	pknois,W		; get 1/2 the peak noise short term average
		subwf	pknlta,W		; subtract 1/2 noise short-term average from long-term average
		btfsc	STATUS,C		; skip if peak noise short-term avg exceeds 2X long-term avg
		goto	cklnse			; check for decrease if not

ckhnse	btfsc	xmtflg,xhinse	; skip if high noise has not yet been transmitted
		goto	dsplay			; just update display if noise warning has been issued
		movlw	.196			; allow 60 half cycles (1/2 second) to ignore transients & Insteon
		addwf	hincnt,W		; check if high noise has lasted for 60 half cycles
		btfsc	STATUS,C		; skip & just increment high noise count if not
		goto	ckhnsw			; issue high noise warning
		incf	hincnt,F		; increment high noise count
		goto	dsplay			; update the display
ckhnsw	bsf		xmtflg,xhinse	; set flag indicating a high noise warning is being transmitted
		bcf		rcvflg,x10vld	; clear valid command flag
		movlw	strnup-1		; -> "HIGH NOISE LEVEL"
		goto	nsexmt			; output message to LCD & transmit noise message

cklnse	lsrf	pknlta,W		; get 1/2 the peak noise long term average
		subwf	pknois,W		; subtract 1/2 noise long-term average from short-term average
		btfsc	STATUS,C		; skip if 2X peak noise short-term avg is less than long-term avg
		goto	dsplay			; just update display if not

nselow	btfsc	dspflg,errmsg	; skip if an error message is not displayed
		goto	dsplay			; don't check for low noise if an error is currently displayed
		btfss	xmtflg,xhinse	; skip if a high noise warning has been transmitted
		goto	dsplay			; just update display if low noise and no warning message
		movfw	bsccnt			; get BSC count
		btfss	STATUS,Z		; skip & indicate low noise if zero
		goto	dsplay			; just update display if still receiving BSCs
		movf	hincnt,F		; check if high noise count is already 0
		btfss	STATUS,Z		; skip if already 0
		decf	hincnt,F		; decrement high noise count
		btfss	STATUS,Z		; skip if zero
		goto	dsplay			; just update display until noise count is zero
		bcf		xmtflg,xhinse	; clear flag indicating a high noise warning was transmitted
		bcf		rcvflg,x10vld	; clear valid command flag
		movlw	strndn-1		; -> " NOISE DECREASE "

nsexmt	movwf	temp4			; save string pointer
		movfw	modflg			; get mode select
		andlw	b'00000111'		; mask all but mode select bits
		btfss	STATUS,Z		; skip & output message in the default display mode
		goto	nsexck			; just check if warning should be transmitted

		clrw					; point to beginning of first line
		call	adrout			; output address
		movlw	strint-1		; -> "X-.-- F -- " initial setup string
		call	lcdstr			; clear existing X10 signal level
		movlw	H'40'			; point to message field
		call	adrout			; output address
		movfw	temp4			; recall noise message string pointer
		call	lcdstr			; output noise message string
		bsf		dspflg,errmsg	; set flag indicating an error message is displayed
		clrf	dspcnt			; leave error message displayed for 2 seconds
nsexck	btfss	cfgflg,nsemon	; skip if noise monitor transmitter is enabled
		goto	updnse			; no transmit if disabled
		bsf		xmtflg,xmtucd	; set flag to transmit unit code
		bsf		xmtflg,xmtmsg	; set flag to transmit message

updnse	movfw	pknois			; get peak noise short-term average
		banksel	pg1ram			; select BANK1 RAM
		movwf	pknltaH			; set peak noise long-term average to new noise level
		movfw	nsestaH			; get noise short-term average
		movwf	nseltaH			; set long-term average to new noise level
		clrf	BSR				; back to BANK0


;		DISPLAY ROUTINES

;		The present operating mode is stored in the 3 LSBs of modflg:

;			000 = normal default mode (same as XTBM)
;			001 = prior display mode (normal, but using prior data)
;			010 = expanded noise display
;			011 = bargraph
;			100	= expanded data pattern mode
;			101 = logging mode

;		Choose the appropriate display routine

dsplay

	if			pro == .1		; if Pro version

		movfw	modflg			; get mode flags
		andlw	b'11100000'		; check if any switches are pressed
		btfsc	STATUS,Z		; skip if so
		goto	moddsp			; display selected mode
		movfw	hldcnt			; get switch hold count
		addlw	.196			; pressed for .5 second?
		btfss	STATUS,C		; skip if so
		goto	moddsp			; display data until pressed at least .5 second

		btfsc	modflg,bothsw	; skip if both switches are not pressed
		goto	modlbl			; output mode label if both switches are pressed
		btfss	modflg,leftsw	; skip if left mode change switch
		goto	moddsp			; normal display if not mode change

;		Show mode label in all modes at mode change

modlbl	btfsc	xmtflg,rptchk	; skip if a repeater check is not in process
		goto	moddsp			; no mode labels while doing a repeater check
		movfw	modflg			; get mode flags
		andlw	b'00000111'		; mask all but mode selection bits
		movwf	temp0			; save to determine mode
		movlw	strmd0-1		; -> point to label for mode 0
		movf	temp0,F			; check if mode 0
		btfsc	STATUS,Z		; skip if not mode 0
		goto	modlcd			; output mode label to LCD
		movlw	strmd1-1		; -> label for mode 1
		decf	temp0,F			; check if mode 1
		btfsc	STATUS,Z		; skip if not mode 1
		goto	modlcd			; output mode label to LCD
		movlw	strmd2-1		; -> label for mode 2
		decf	temp0,F			; check if mode 2
		btfsc	STATUS,Z		; skip if not mode 2
		goto	modlcd			; output mode label to LCD
		movlw	strmd3-1		; -> label for mode 3
		decf	temp0,F			; check if mode 3
		btfsc	STATUS,Z		; skip if not mode 3
		goto	modlcd			; output mode label to LCD

		movfw	modptr			; get mode pointer
		andlw	b'01111111'		; mask the step through enable bit
		btfss	STATUS,Z		; skip & display mode label if cursor is at zero
		goto	moddsp			; normal mode display if not

		movlw	strmd4-1		; -> label for mode 4
		decf	temp0,F			; check if mode 4
		btfsc	STATUS,Z		; skip if not mode 4
		goto	modlcd			; output mode label to LCD
		movlw	strmd5-1		; -> label for mode 5

modlcd	movwf	strptr			; save string ponter
		clrw					; point to beginning of first line
		call	adrout			; output address
		call	lcddc0			; output mode string
		bsf		dspflg,moddes	; set display mode description flag

;		Show switch prompt in modes 0-3 & 4-5 if cursor is at zero
		
modswp	movfw	modflg			; get mode flags
		andlw	b'00000111'		; mask all but mode selection bits
		movwf	temp0			; save to determine mode
		movlw	strsw0-1		; -> switch label for mode 0
		movf	temp0,F			; check if mode 0
		btfsc	STATUS,Z		; skip if not mode 0
		goto	swplcd			; output mode label to LCD
		movlw	strsw1-1		; -> label for mode 1
		decf	temp0,F			; check if mode 1
		btfsc	STATUS,Z		; skip if not mode 1
		goto	swplcd			; output mode label to LCD
		movlw	strsw1-1		; -> label for mode 2
		decf	temp0,F			; check if mode 2
		btfsc	STATUS,Z		; skip if not mode 2
		goto	swplcd			; output mode label to LCD
		movlw	strsw1-1		; -> label for mode 3
		decf	temp0,F			; check if mode 3
		btfsc	STATUS,Z		; skip if not mode 3
		goto	swplcd			; output mode label to LCD
		movlw	strsw4-1		; -> switch label for modes 4&5

swplcd	movwf	strptr			; save string pointer
		movlw	H'40'			; point to beginning of second line
		call	adrout			; output address
		call	lcddc0			; output mode string
		goto	endcyc			; no data display

moddsp	btfsc	dspflg,moddes	; skip if mode description flag is cleared
		bsf		modflg,modchg	; set mode change flag to refresh display
		bcf		dspflg,moddes	; make sure the mode description flag is cleared
		movfw	modflg			; get mode select
		andlw	b'00000111'		; mask all but mode select bits
		btfsc	STATUS,Z		; skip if not the basic mode
		goto	md0dsp			; run mode zero display (XTBM default)
		addlw	.255			; subtract 1 to check for mode 1
		btfsc	STATUS,Z		; skip if not mode 1
		goto	md1dsp			; run mode 1 display (prior data)
		addlw	.255			; subtract 1 to check for mode 2
		btfsc	STATUS,Z		; skip if not mode 2
		goto	md2dsp			; run mode 2 display (expanded noise)
		addlw	.255			; subtract 1 to check for mode 3
		btfsc	STATUS,Z		; skip if not mode 3
		goto	md3dsp			; run mode 3 display (bargraph)
		addlw	.255			; subtract 1 to check for mode 4
		btfsc	STATUS,Z		; skip if not mode 4
		goto	md4dsp			; run mode 4 display (bit pattern)
		goto	md5dsp			; must be mode 5 (logging history)

	endif


;		MODE 0 DEFAULT LCD DISPLAY ROUTINE

;		This subroutine displays the normal XTBM data in default mode 0

md0dsp

	if			pro == .1		; if Pro version

		btfss	modflg,modchg	; skip if mode was just changed
		goto	md0rck			; check if transmitting
		bcf		dspflg,bitlok	; make sure bit pattern display is not locked
		bcf		PORTA,LCDDS		; data select must be low for control word
		movlw	b'00001100'		; command word to turn off cursor
		call	lcdout			; output control byte
		call	dly50us			; 50uS delay for LCD to process command
		bsf		PORTA,LCDDS		; switch data select high for data
		clrw					; point to beginning of first line
		call	adrout			; output address
		movlw	strint-1		; -> "X-.-- F -- " initial setup string
		call	lcdstr			; clear existing X10 signal level

	endif

md0rck	btfsc	xmtflg,strtup	; skip if not doing the startup sequence
		goto	md0end			; no default display during startup
		btfss	xmtflg,rptchk	; skip if repeater check is in process
		goto	md0xmt			; check if transmitting
		clrw					; point to beginning of first line
		call	adrout			; output address
		movlw	strint-1		; -> "X-.-- F -- " initial setup string
		call	lcdstr			; clear existing X10 signal level
		movlw	H'40'			; point to beginning of second line
		call	adrout			; output address
		movlw	strrpt-1		; -> "RPT CHK"
		call	lcddcd			; output decode string
		movlw	strxmt-1		; -> "  -CHECK-"
		call	lcdstr			; output string
		goto	updisp			; no errors for repeater check

md0xmt	movf	xmtcnt,F		; check if transmitting
		btfss	STATUS,Z		; skip & update LCD if not transmitting
		goto	md0end			; no display update while transmitting

;		Receiving error definitions:

;		Bad Start Code - anything other than 1110 or 1111 start pattern
;		Insteon - 1111 start pattern with the Insteon signature
;		Noise - 11111111 or 1111 and not Insteon
;		Collision - more than 12 or 32 "1" bits after a valid start code
;		Receiving error - fewer than 12 or 32 "1" bits after a valid start code
;						  OR a complimentary pair mismatch after a valid start code

		movlw	strins-1		; -> "INSTEON"
		btfsc	cmdflg,inston	; skip if not an Insteon command
		goto	errlcd			; output Insteon report to LCD
		movlw	strcol-1		; -> "ERR COL"
		btfsc	cmdflg,errcol	; skip if not a collision error
		goto	errlcd			; output error report to LCD
		movlw	strrcv-1		; -> "ERR RCV"
		btfsc	cmdflg,errrcv	; skip if not a receive data pattern error
		goto	errlcd			; output error report to LCD
		movlw	strbsc-1		; -> "ERR BSC"
		btfsc	cmdflg,errbsc	; skip if not a bad start code error
		goto	errlcd			; output error report to LCD
		movlw	strnhi-1		; -> "^NOISE^"
		btfss	cmdflg,errnse	; skip & output to LCD if a high noise error
		goto	updisp			; no error - update display

errlcd	btfsc	xmtflg,strtup	; skip if startup flag is cleared
		goto	norept			; no error report during startup
		btfsc	dspflg,errmsg	; skip if an error message is not displayed
		goto	norept			; don't update message until prior error message is cleared

		movwf	strptr			; save pointer
		bsf		dspflg,errmsg	; set flag indicating an error message is displayed
		movlw	H'40'			; point to beginning of 2nd line
		call	adrout			; output address
		call	lcdst0			; output error string (pointer was saved)
		movlw	.2				; to output 2 spaces
		call	spacen			; make sure middle of message line is cleared
		movlw	strerr-1		; -> "!ERROR!"
		btfsc	cmdflg,inston	; skip if not an Insteon command
		movlw	strmon-1		; -> "MONITOR"
		call	lcdstr			; output string
		clrf	dspcnt			; leave error message displayed for 2 seconds

		btfsc	cmdflg,errnse	; skip if not noise
		clrf	frqavg			; clear previous frequency average
		btfss	cmdflg,errbsc	; skip if a bad start code
		goto	norept			; display measured value
		clrf	x10max			; clear previous X10 voltage
		clrf	frqavg			; clear previous frequency average
		goto	dspnse			; just display noise level

norept	movlw	.12				; point to beginning of LCD noise field				
		call	adrout			; output address
		movlw	.4				; to output 4 spaces
		call	spacen			; clear the LCD noise field
		call	x10lcd			; send X10/Insteon reading to LCD
		btfsc	cmdflg,inston	; skip if not an Insteon command
		goto	dspmon			; display "MONITOR" & finish up

;		Display noise or X10 signal frequency

updisp	btfsc	cmdflg,rcvx10	; skip if not receiving a X10 command
		goto	x10one			; check for a X10 "1" bit
		decfsz	dspcnt,W		; skip & clear message if display interval is over (dspcnt=1)
		goto	updis2			; check if time to update display info
		btfsc	rcvflg,x10vld	; skip & clear message if a valid X10 command is not displayed
		goto	updis1			; don't blank valid X10 command
		btfsc	dspflg,nsefrq	; skip & clear message if noise frequency is not displayed
		goto	updis0			; don't blank the noise frequency

updclr	clrw					; select LCD 1st character of line 1
		call	adrout			; output address
		movlw	strxno-1		; -> "X-.-- " no X10 signal
		call	lcdstr			; output string to clear displayed error voltage
		movlw	strfno-1		; -> "F --  " no frequency
		btfss	dspflg,nsefrq	; skip if noise frequency is being displayed
		call	lcdstr			; clear frequency display
updis0	movlw	H'40'			; point to beginning of message line
		call	adrout			; output address
		movlw	.9				; to output 9 spaces
		call	spacen			; clear message line

updis1	movlw	H'48'			; point to message field
		call	adrout			; output address
		call	space1			; output one space
		movlw	strnhi-1		; -> "^NOISE^"
		btfss	xmtflg,xhinse	; skip if high noise flag is set
		movlw	strmon-1		; -> "MONITOR"
		call	lcdstr			; output string
		bcf		dspflg,errmsg	; indicate the error message was cleared

updis2	movfw	dspcnt			; get display update timer
		andlw	b'00011111'		; check if time to update display
		btfss	STATUS,Z		; skip if so
		goto	md0end			; no display if not

		btfss	dspflg,nsefrq	; skip if noise frequency to display
		goto	x10one			; display X10 bit frequency
		call	nselcd			; display noise frequency or cycle count to LCD
		goto	dspnse			; display noise amplitude

x10one	btfss	stsflg,rcvbit	; skip & display frequency if received a X10 "1" bit
		goto	updx10			; display signal level if not
dspfrq	btfsc	rcvflg,x10vld	; skip & output frequency if no valid command displayed
		goto	updx10			; clean up display if not
		movfw	frqcnt			; get frequency for this sample
		btfss	xmtflg,xmtmsg	; skip frequency update while transmitting
		call	frqlcd			; output frequency to LCD display

updx10	btfss	rcvflg,x10vld	; skip if a valid X10 command has been received
		goto	dspnse			; display noise level
		btfss	rcomnd,cmdrpt	; skip if it was a repeater check
		goto	vldx10			; display valid X10 command if not
		btfss	rcomnd,cmdbad	; skip if no repeated command received
		goto	vldx10			; display valid repeated command
		bcf		rcvflg,x10vld	; clear valid flag if no repeater found
		goto	dspnse			; display noise level

vldx10	call	cmddec			; decode & display the command
		call	x10lcd			; send X10 reading to LCD
		btfsc	xmtflg,xhinse	; skip if "^NOISE^" is not displayed
		goto	dspnse			; display noise level
		movlw	H'48'			; point to message field
		call	adrout			; output address
		call	space1			; output one space
		movlw	strvld-1		; -> "-VALID-"
		btfss	dspflg,dsphld	; skip & display VALID for hold interval
dspmon	movlw	strmon-1		; -> "MONITOR"
		call	lcdstr			; output string
		bcf		cmdflg,rcvx10	; indicate X10 command was received & processed
		bcf		dspflg,errmsg	; indicate the error message was cleared
		goto	dspnse			; display noise level
		movfw	frqavg			; get average frequency for command
		call	frqlcd			; output frequency to LCD display

;		Update LCD noise display

dspnse	movlw	.12				; point to beginning of LCD noise field
		call	adrout			; output address
		movlw	"N"
		call	lcdout			; output character
		movlw	"."
		call	lcdout			; output character
		movfw	pknois			; get peak noise short term average
		call	binbcd			; convert to BCD (2.55V range)
		movlw	H'99'			; to force 99 if => 100
		movf	bcdmsd,F		; check if BCD MSB > 0
		btfss	STATUS,Z		; skip and use actual value if not
		movwf	bcdlsd			; set BCD LSD to read 99
		call	outbcd2			; output 2 digit noise level

md0end	bcf		modflg,modchg	; make sure mode change flag is cleared

	if			pro == .1		; if Pro version

		goto	endcyc			; done with display - process end of cycle


;		MODE 1 DISPLAY PRIOR DATA (command archived at last powerdown)

md1dsp	btfss	modflg,modchg	; skip if mode was just changed
		goto	endcyc			; no update needed after initial display

 		clrw					; start at first character on display
		call	adrout			; output address
		movlw	"X"				; X for X10 amplitude
		call	lcdout			; output character
		movfw	ax10mx			; get archived maximum X10 signal
		btfss	anoise,ahrang	; skip if X10 reading was in high range
		goto	md1255			; convert X10 reading in low range
		call	binbcd9			; convert to BCD (999 range)
		goto	md1x10			; output X10 reading
md1255	call	binbcd			; convert to BCD (255 range)
md1x10	call	outbcd3			; output X10 signal level in format x.xx
		call	space1			; add a trailing space

		movfw	axfreq			; get archived average frequency
		call	frqlcd			; output frequency

		movlw	"N"
		call	lcdout			; output character
		movlw	"."
		call	lcdout			; output character
		movfw	anoise			; get short term average peak noise level
		andlw	b'01111111'		; mask X10 range flag
		call	binbcd			; convert to BCD (2.55V range)
		movlw	H'99'			; to force 99 if => 100
		movf	bcdmsd,F		; check if BCD MSB > 0
		btfss	STATUS,Z		; skip and use actual value if not
		movwf	bcdlsd			; set BCD LSD to read 99
		call	outbcd2			; output 2 digit noise level

		movlw	H'40'			; point to beginning of message line
		call	adrout			; output address
		movlw	.9				; to output 9 spaces	
		btfss	acomnd,cmdrpt	; skip if not a repeated command
		goto	md1clr			; clear command portion of message line
		movlw	strrpt-1		; -> "RPT_CHK"
		call	lcddcd			; output string to LCD
		movlw	.1				; to output 2 spaces
md1clr	call	spacen			; clear first portion of message line
		movlw	strpri-1		; -> "*PRIOR*"
		call	lcdstr			; output string
md1end	bcf		modflg,modchg	; make sure mode change flag is cleared
		goto	endcyc			; done


;		MODE 2 EXPANDED NOISE READOUT LCD DISPLAY ROUTINE

;		This display routine expands the display of noise data

md2dsp	btfss	modflg,modchg	; skip if mode was just changed
		goto	md2nse			; output a new bit pattern
		clrw					; point to 1st character of 1st line
		call	adrout			; set LCD address
		movlw	strexn-1		; -> " < NOISE >  FREQ"
		call	lcdstr			; output string

md2nse	movfw	dspcnt			; get display update timer
		andlw	b'00011111'		; check if time to update display
		btfss	STATUS,Z		; skip if so
		goto	md2end			; no display if not

		movlw	H'40'			; point to 1st character of 2nd line
		call	adrout			; set LCD address

		banksel	pg1ram			; select BANK1 RAM
		movfw	bgnnse			; get peak noise at beginning of X10 transmit window
		clrf	BSR				; back to BANK0
		call	binbcd			; convert to BCD (2.55V range)
		movlw	H'99'			; to force 99 if => 100
		movf	bcdmsd,F		; check if BCD MSB > 0
		btfss	STATUS,Z		; skip and use actual value if not
		movwf	bcdlsd			; set BCD LSD to read 99
		call	outlsd			; output noise short-term average in form .xx
		call	space1			; add one space

		movfw	pknois			; get peak noise short term average
		call	binbcd			; convert to BCD (2.55V range)
		movlw	H'99'			; to force 99 if => 100
		movf	bcdmsd,F		; check if BCD MSB > 0
		btfss	STATUS,Z		; skip and use actual value if not
		movwf	bcdlsd			; set BCD LSD to read 99
		call	outlsd			; output noise long-term average in form .xx
		call	space1			; add one space

		banksel	pg1ram			; select BANK1 RAM
		movfw	endnse			; get peak noise at end of X10 transmit window
		clrf	BSR				; back to BANK0
		call	binbcd			; convert to BCD (2.55V range)
		movlw	H'99'			; to force 99 if => 100
		movf	bcdmsd,F		; check if BCD MSB > 0
		btfss	STATUS,Z		; skip and use actual value if not
		movwf	bcdlsd			; set BCD LSD to read 99
		call	outlsd			; output peak noise short-term average in form .xx
		call	space1			; add one space
		call	nselcd			; output noise frequency to LCD display
md2end	bcf		modflg,modchg	; make sure mode change flag is cleared
		goto	endcyc			; done with display - process end of cycle


;		MODE 3 BARGRAPH DISPLAY

md3dsp	btfss	modflg,modchg	; skip if mode was just changed
		goto	md3x00			; no reset of display hold time if not
		btfsc	rcvflg,x10vld	; skip if a valid X10 is not displayed
		clrf	dspcnt			; to hold decoded command bargraph for 2 seconds

md3x00	clrw					; point to 1st character of 1st line
		call	adrout			; set LCD address
		movlw	"X"				; X for X10 amplitude of doublet
		btfss	rcvflg,rcvdbl	; skip if received second half of doublet
		movlw	"x"				; x for X10 amplitude of 1/2 of doublet
		call	lcdout			; output character
		movfw	x10max			; get maximum X10 signal
		bcf		dspflg,x10rng	; set display range flag to 2.55V
		btfsc	rcvflg,maxrng	; skip if sample was in low range
		bsf		dspflg,x10rng	; set display range flag to 9.99V
		btfss	dspflg,dsphld	; skip if display hold time is not over
		clrw					; clear X10 bargraph if hold time is over
		call	binbcdx			; convert to BCD (extended range)
		call	outbcd3			; output X10 signal level in format x.xx
		movlw	":"				; ":"
		call	lcdout			; output character

		movfw	x10max			; get X10 maximum signal level
		btfss	dspflg,dsphld	; skip if display hold time is not over
		clrw					; clear X10 bargraph if hold time is over
		movwf	temp3			; save X10 maximum signal level to adjust
		btfsc	rcvflg,maxrng	; skip if maximum X10 signal was measured in low range
		goto	md3cvt			; display X10 bargraph without adjustment
		lsrf	temp3,F			; divide low range by 4 for 0-15 range
		lsrf	temp3,F
md3cvt	call	cvtlog			; convert value in temp3 to a pseudo log value

md3x01	movlw	.5				; 5 columns per character
		subwf	temp3,F			; check if >= 5
		btfss	STATUS,C		; output a box if >= 5
		goto	md3x02			; display X10 remainder
		movlw	H'FF'			; "box" character
		call	lcdout			; output box character to LCD display
		goto	md3x01			; loop

md3x02	movlw	.5				; to check for 0 remainder
		addwf	temp3,W			; check if remainder was zero
		btfsc	STATUS,Z		; skip if not
		goto	md3x03			; no additional bars
		addlw	.3				; adjust for custom character offset
		call	lcdout			; output last bargraph character
md3x03	movlw	.14				; make sure remainder of line is blank
		call	spacen			; output spaces to clear remainder of line

md3n00	movfw	dspcnt			; get display update timer
		andlw	b'00011111'		; check if time to update display
		btfss	STATUS,Z		; skip if so
		goto	md3n01			; no update to noise value if not

		movlw	H'40'			; point to 1st character of 2nd line
		call	adrout			; set LCD address
		movlw	"N"				; "N" for N amplitude
		call	lcdout			; output character
		movfw	pknois			; get peak noise level
		call	binbcd			; convert to BCD (2.55V range)
		call	outbcd3			; output X10 signal level in format x.xx
		movlw	":"				; ":"
		call	lcdout			; output character
	
md3n01	movlw	H'46'			; point to 5th character of 2nd line
		call	adrout			; set LCD address
		movlw	.156			; to check if noise level is over .99V
		addwf	pknois,W		; check if noise level is over .99V
		movlw	.255			; for maximum bargraph
		btfsc	STATUS,C		; skip if noise < 1.0V
		goto	md3n02			; set bargraph to max
		lslf	pknois,W		; multiply peak noise by 2
		movwf	temp3			; save at temp
		lsrf	pknois,W		; divide peak noise by 2
		addwf	temp3,W			; add to multiply noise level by 2.5
md3n02	movwf	temp3			; save X10 maximum signal level to adjust
		call	cvtlog			; convert value in temp3 to a pseudo log value

md3n03	movlw	.5				; 5 columns per character
		subwf	temp3,F			; check if >= 5
		btfss	STATUS,C		; output a box if >= 5
		goto	md3n04			; display noise remainder
		movlw	H'FF'			; "box" character
		call	lcdout			; output box character to LCD display
		goto	md3n03			; loop

md3n04	movlw	.5				; to check for 0 remainder
		addwf	temp3,W			; check if remainder was zero
		btfsc	STATUS,Z		; skip if not
		goto	md3n05			; no additional bars
		addlw	.3				; adjust for custom character offset
		call	lcdout			; output last bargraph character
md3n05	movlw	.14				; make sure remainder of line is blank
		call	spacen			; output spaces to clear remainder of line
		bcf		modflg,modchg	; make sure mode change flag is cleared
		goto	endcyc			; done with display - process end of cycle

;		Pseudo Log conversion for bargraph display

;		Enter with temp3 = 0-255 value to be converted
;		Returns temp3 = 0-47 pseudo log value

cvtlog	movfw	temp3			; get value to be converted
		andlw	H'F0'			; mask LSBs to check if value is > 15
		movlw	.0				; add no offset
		btfsc	STATUS,Z		; skip if > 15
		goto	cvtadj			; return just LSBs
		lsrf	temp3,F			; divide value by 2
		movfw	temp3			; get adjusted value
		andlw	H'F0'			; mask LSBs to check if value was > 31
		movlw	.9				; add 8 offset to LSBs (8-15) -> (16-23)
		btfsc	STATUS,Z		; skip if > 31
		goto	cvtadj			; return LSBs + 8
		lsrf	temp3,F			; divide value by 2
		movfw	temp3			; get adjusted value
		andlw	H'F0'			; mask LSBs to check if value was > 63
		movlw	.17				; add 17 offset to LSBs (8-15) -> (25-32)
		btfsc	STATUS,Z		; skip if > 63
		goto	cvtadj			; return LSBs + 17
		lsrf	temp3,F			; divide value by 2
		movfw	temp3			; get adjusted value
		andlw	H'F0'			; mask LSBs to check if value was > 127
		movlw	.26				; add 26 offset to LSBs (8-15) -> (34-41)
		btfsc	STATUS,Z		; skip if > 127
		goto	cvtadj			; return LSBs + 26
		lsrf	temp3,F			; divide value by 2
		movlw	.35				; add 35 offset to LSBs (8-15) -> (43-50)

cvtadj	addwf	temp3,F			; add offset to LSBs
		return


;		MODE 4 EXPANDED DISPLAY OF INDIVIDUAL BITS

;		This display routine expands the display of individual bits received

md4dsp	btfss	modflg,modchg	; skip if mode was just changed
		goto	md4out			; output a new bit pattern
		bcf		PORTA,LCDDS		; data select must be low for control word
		movlw	b'00001110'		; command word to turn on display with cursor
		call	lcdout			; output control byte
		call	dly50us			; 50uS delay for LCD to process command
		bsf		PORTA,LCDDS		; switch data select high for data
		bsf		dspflg,bitnew	; set flag to display existing bit pattern

md4out	btfss	dspflg,bitnew	; skip if a new bit pattern has been stored
		goto	md4loc			; check if bit pattern display is locked
		call	bitptn			; output bit pattern to display
		bcf		dspflg,bitnew	; indicate bit pattern has been displayed

md4loc	clrw					; to point to first character of line 1
		call	adrout			; set LCD address
		movfw	dspflg			; get display flags
		andlw	lokstl			; mask all but locked and stale bits
		xorlw	lokstl			; check if both locked and stale (Z=1 if so)
		movlw	"+"				; to indicate locked and stale
		btfsc	STATUS,Z		; skip if not locked and stale
		goto	md4sts			; output "-" for locked and stale
		movlw	.3				; for 11 start pattern
		btfsc	dspflg,bitstl	; skip if bit pattern display is not stale
		movlw	":"				; stale character
		btfsc	dspflg,bitlok	; skip if bit pattern display is not locked
		movlw	"*"				; lock character
md4sts	call	lcdout			; output character

md4vlt	lslf	modptr,W		; get 2X bit voltage buffer pointer (0-42)
		addlw	bvbufr			; add offset to beginning of bit voltage buffer
		movwf	FSR0L			; set indirect pointer 0 to bit voltage buffer
		movlw	.6				; for bank 12
		movwf	FSR0H
		movlw	H'0C'			; point to first line voltage location
		call	adrout			; set LCD address
		movfw	modptr			; get bit voltage buffer pointer (0-21)
		andlw	b'01111111'		; mask the mode change step-through flag
		addlw	.245			; check if >11 (C=1 for 2nd line of LCD)
		movfw	INDF0			; read first bit voltage from buffer
		btfsc	STATUS,C		; skip if first half of doublet
		goto	md4120			; check range for 2nd half
		btfsc	rcvflg,h1rang	; skip if reading was done in low range
		goto	md4199			; convert to 9.99 range
		goto	md4125			; convert to BCD (2.55 range)
md4120	btfsc	rcvflg,h2rang	; skip if reading was done in low range
		goto	md4199			; convert to 9.99 range
md4125	call	binbcd			; convert to BCD (2.55 range)
		goto	md41vo			; output voltage without adjustment
md4199	call	binbcd9			; convert to BCD (9.99 range)
		movfw	modptr			; is it pointing to 1st bit pair of 1st line?
		andlw	b'01111111'		; mask the mode change step-through flag
		btfsc	STATUS,Z		; skip if not
		bsf		bcdmsd,7		; set flag for overrange
		addlw	.245			; is it pointing to 1st bit pair of 2nd line?
		btfsc	STATUS,Z		; skip if not
		btfss	STATUS,Z		; skip if so
		goto	md41vo			; no overrange if not
		btfss	stsflg,bitxtd	; skip & no 2nd half overrange if an extended command
		bsf		bcdmsd,7		; set flag for overrange
md41vo	call	outbcd3			; output X10 signal level in format x.xx

		movlw	H'4C'			; point to second line voltage location
		call	adrout			; set LCD address
		incf	FSR0L,F			; increment to next bit voltage buffer address
		movfw	modptr			; get bit voltage buffer pointer (0-21)
		andlw	b'01111111'		; mask the mode change step-through flag
		addlw	.245			; check if >11 (C=1 for 2nd line of LCD)
		movfw	INDF0			; read first bit voltage from buffer
		btfsc	STATUS,C		; skip if first half of doublet
		goto	md4220			; check range for 2nd half
		btfsc	rcvflg,h1rang	; skip if reading was done in low range
		goto	md4299			; convert to 9.99 range
		goto	md4225			; convert to BCD (2.55 range)
md4220	btfsc	rcvflg,h2rang	; skip if reading was done in low range
		goto	md4299			; convert to 9.99 range
md4225	call	binbcd			; convert to BCD (2.55 range)
		goto	md42vo			; output voltage without adjustment
md4299	call	binbcd9			; convert to BCD (9.99 range)
md42vo	call	outbcd3			; output X10 signal level in format x.xx

		movfw	modptr			; get bit voltage buffer pointer (0-21)
		andlw	b'01111111'		; mask the mode change step-through flag
		movwf	temp0			; save bit pointer
		addlw	.245			; check if >11 (2nd line of LCD)
		movfw	temp0			; recall bit pointer
		btfsc	STATUS,C		; skip if not second line of LCD
		addlw	.53				; select second line of LCD (11+53=64)
		call	adrout			; output cursor address
		bcf		modflg,modchg	; make sure mode change flag is cleared
		goto	endcyc			; done with display - process end of cycle


;		MODE 5 LOGGING HISTORY

;		This display routine accesses a log of the  last N commands received

;		logrbf[0-7]:  RNEDCxxx DDDHHHHH MMMMMMSS SSSSHHHH UUUUCCCC VVVVVVVV XXXXXXXX XXXXXXXX

;			R = range flag (1 = 9.99V, 0 = 2.55V)
;			N = missing unit code
;			E = extended command flag (1 for extended command)
;			D = extended command/data flag (1 for extended data)
;			C = valid command code
;			D = day from 0 to 7 (7 is overflow at 8th day)
;			H = hours (0-23)
;			M = minutes (0-59)
;			S = seconds (0-59)
;			H = house code
;			U = unit code
;			C = command code
;			V = 8 bit X10 level
;			X = extended data

md5dsp	movfw	modptr			; get mode pointer (0-99)
		subwf	logwpt,W		; calculate current position in logging buffer
		btfss	STATUS,C		; skip if no overflow
		addlw	.100			; adjust for wraparound
		movwf	logrpt			; save log read pointer

		btfss	modflg,modchg	; skip if mode was just changed
		goto	md5out			; output current logging data
		bcf		dspflg,bitlok	; make sure bit pattern display is not locked
		bcf		PORTA,LCDDS		; data select must be low for control word
		movlw	b'00001100'		; command word to turn off cursor
		call	lcdout			; output control byte
		call	dly50us			; 50uS delay for LCD to process command
		bsf		PORTA,LCDDS		; switch data select high for data
		call	lcdclr			; start with LCD completely cleared

;		output first line to LCD

md5out	bsf		PCLATH,3		; select ROM Page 1
		call	readlg			; read selected record into log read buffer
		clrf	PCLATH			; back to ROM Page 0

		clrw					; point to first line first character
		call	adrout			; output LCD address
		movfw	modptr			; get mode pointer 0-99 (0 at start of logging mode)
		call	binbcd			; convert to 2 digits
		call	outbcd2			; output 2 digit log index
		call	space1			; output a space
		swapf	logrbf+1,W		; get day into W 4 LSBs
		andlw	b'00001110'		; mask all but day bits
		movwf	temp0			; save for shift
		lsrf	temp0,W			; shift day into 3 LSBs
		btfss	STATUS,Z		; skip if still < 24 hours
		goto	md5day			; output day
		movlw	.2				; for two spaces
		call	spacen			; output two spaces
		goto	md5hr			; output hours
md5day	addlw	H'30'			; convert 0-7 to ascii character
		movwf	temp0			; save it
		addlw	.201			; is it overflow (7) C=1 if so
		movfw	temp0			; recall day ascii character
		btfsc	STATUS,C		; skip if no day overflow
		movlw	"^"				; "^" to indicate overflow
		call	lcdout			; output character to LCD
		movlw	":"				; ":" to separate days and hours
		call	lcdout			; output character
md5hr	movfw	logrbf+1		; get log hour count
		andlw	b'00011111'		; mask all but 0-23
		call	binbcd			; convert to 2 digits
		call	outbcd2			; output 2 digit hour count
		movlw	"h"				; "h" for Hours
		call	lcdout			; output "h" to LCD
		movlw	":"				; ":" to separate hours and minutes
		call	lcdout			; output character
		lsrf	logrbf+2,W		; shift log minute count right 1 bit into W
		movwf	temp0			; save for another shift
		lsrf	temp0,W			; shift log minute count to 6 LSBs
		andlw	b'00111111'		; mask all but 0-59
		call	binbcd			; convert to 2 digits
		call	outbcd2			; output 2 digit hour count
		movlw	"m"				; "m" for Minutes
		call	lcdout			; output "m" to LCD
		movlw	":"				; ":" to separate minutes and seconds
		call	lcdout			; output character
		movfw	logrbf+2		; get second count 2 MSBs
		movwf	temp1			; save for shift
		movfw	logrbf+3		; get second count LSBs
		movwf	temp0			; save for shift
		lsrf	temp1,F			; shift to MSBs into LSB byte
		rrf		temp0,F
		lsrf	temp1,F
		rrf		temp0,F
		lsrf	temp0,F			; shift second count into 6 LSBs
		lsrf	temp0,W
		call	binbcd			; convert to 2 digits
		call	outbcd2			; output 2 digit second count
		movlw	"s"				; "s" for Seconds
		call	lcdout			; output "s" to LCD

;		output second line to LCD

		movlw	H'40'			; point to second line first character
		call	adrout			; output LCD address
		btfss	logwpt,7		; skip if logging buffer is empty
		goto	md5hcd			; start with the house code
		movlw	.16				; to output 16 spaces
		call	spacen			; clear the second line
		goto	md5end			; done

md5hcd	btfss	logrbf,3		; skip if this is a command code
		goto	md5ohc			; output house code  
		movfw	logrbf+4		; get house code and command code
		andlw	b'00001110'		; mask house code & possible pre-set dim MSB
		xorlw	b'00001010'		; check for pre-set dim command
		btfss	STATUS,Z		; skip & process pre-set dim command differently
		goto	md5ohc			; display house code for a normal X10 command
		movlw	b'00101000'		; select Pdim message
		call	lcddcd			; output command string to LCD
		call	space1			; make sure the last character is blank

		rrf		logrbf+4,W		; shift pre-set dim MSB into C
		rlf		logrbf+3,W		; assemble complete pre-set dim command into W
		andlw	b'00011111'		; mask extraneous bits
		call	dimlcd			; output pre-set dim level to LCD
		goto	md5x10			; finish up with X10 voltage reading

md5ohc	movfw	logrbf+3		; get 4-bit house code into W LSBs
		andlw	b'00001111'		; make sure 4 MSBs are zero
		call	x10hex			; convert X10 "A-P" to hex 0-F
		addlw	H'41'			; convert hex 0-F to ASCII A-P
		call	lcdout			; output house code to LCD

		btfss	logrbf,6		; skip if unit code is not defined 
		goto	md5ucd			; display unit code
		movlw	"x"				; indicate unit code is not valid for this command
		call	lcdout
		movlw	"x"
		call	lcdout
		goto	md5ftn			; output command function string

md5ucd	swapf	logrbf+4,W		; get 4-bit unit code into W LSBs
		andlw	b'00001111'		; make sure 4 MSBs are zero
		call	x10hex			; convert X10 "1-16" to hex 0-F
		addlw	.1				; adjust for X10 digits starting at "1"
		call	binbcd			; convert to BCD (255 range)
		call	outbcd2			; output 2-digit unit code to LCD

md5ftn	call	space1			; leave a space between the codes and the function
		btfsc	logrbf,5		; skip if not an extended command
		goto	md5xtd			; output extended command
		movlw	.7				; for 7 total spaces
		btfss	logrbf,3		; skip if valid command
		goto	md5spc			; leave 7 spaces blank for invalid command

md5cmd	lslf	logrbf+4,W		; multiply command code by 2
		movwf	temp0			; save at temp
		lslf	temp0,W			; multiply command key code again by 2
		andlw	b'00111100'		; mask all but command bits
		addlw	strdcd			; point to command decode strings
		call	lcddcd			; output command string to LCD
		movlw	.3				; to leave 3 spaces
md5spc	call	spacen			; output 3 spaces
		goto	md5x10			; output X10 voltage

md5xtd  movlw	"X"				; "X" for extended command
		call	lcdout			; output "X" to LCD
		movlw	"c"				; "c" for extended command
		btfsc	logrbf,4		; skip if extended command
		movlw	"d"				; "d" for extended data
		call	lcdout			; output type of extended command to LCD
		swapf	logrbf+6,W		; get first extended byte MS nibble
		call	lcdhex			; mask MS nibble and output hex digit
		movfw	logrbf+6		; get first extended byte LS nibble
		call	lcdhex			; mask MS nibble and output hex digit
		swapf	logrbf+7,W		; get second extended byte MS nibble
		call	lcdhex			; mask MS nibble and output hex digit
		movfw	logrbf+7		; get second extended byte LS nibble
		call	lcdhex			; mask MS nibble and output hex digit
		call	space1			; leave one space

md5x10	movlw	H'4A'			; point to second line 11th character
		call	adrout			; output LCD address
		call	space1			; output one space
		movfw	logrbf+5		; read X10 voltage from buffer
		btfsc	logrbf,7		; skip & process 2.55 max if low range
		goto	md5x99			; convert to 9.99 range
		call	binbcd			; convert to 2.55 range
		goto	md5xvo			; output X10 voltage
md5x99	call	binbcd9			; convert to 9.99 range
md5xvo	call	outbcd3			; output X10 signal level in format x.xx
		movlw	"V"				; "V" for Volts
		call	lcdout			; output "V" to LCD
md5end	bcf		modflg,modchg	; make sure mode change flag is cleared
		goto	endcyc			; done with display - sample noise at end of cycle

	endif

;		Done with display routines - process end of cycle

endcyc	bcf		dspflg,nsefrq	; make sure noise frequency flag is cleared

;		Diagnostic display routines for testing

;	goto	endcy1

;	movlw	H'06'
;	movlw	H'40'
;	call	adrout
;	call	space1

;	swapf	cmdflg,W
;	call	lcdhex
;	movfw	cmdflg
;	call	lcdhex
;	call	space1

;	swapf	rcvflg,W
;	call	lcdhex
;	movfw	rcvflg
;	call	lcdhex
;	call	space1

;	swapf	dspflg,W
;	call	lcdhex
;	movfw	dspflg
;	call	lcdhex
;	call	space1

;	swapf	x10max,W
;	call	lcdhex
;	movfw	x10max
;	call	lcdhex
;	call	space1

;	banksel	agcthd
;	movfw	agcthd
;	clrf	BSR
;	movfw	x10avg
;	call	binbcd
;	call	outbcd2
;	call	space1

;	movfw	bkgnse
;	movfw	x10max
;	call	binbcd
;	call	outbcd2
;	call	space1

;	movfw	x10thd
;	movfw	pknois
;	movfw	maxend
;	movfw	test1
;	call	binbcd
;	call	outbcd2
;	call	space1

;endcy1

;		Switch back to low range at end of first half of normal doublet

		btfsc	cmdflg,xtdcmd	; skip if not receiving an extended command
		goto	endnxt			; no gain change in middle of an extended command
		movfw	bitcnt			; get received bit count
		addlw	.234			; check if exactly 22 bits have been received
		btfsc	STATUS,Z		; skip & continue with present gain if not
		bcf		rcvflg,hirang	; switch back to low range

;		Set up next bit to be transmitted

endnxt	btfsc	xmtflg,xmtmsg	; skip if not transmitting a message
		call	xmtbit			; set up next transmit bit

;		Sample mode switches

	if			pro == .1		; if Pro version
		bsf		PCLATH,3		; select ROM Page 1 for Check Mode Switch routine
		call	chkmod			; check mode switches
		clrf	PCLATH			; back to ROM Page 0
	endif

;		No pre-zero crossing sample during startup

		btfsc	xmtflg,strtup	; skip if startup flag is cleared
		goto	strtlp			; loop for first second while waiting for P ON/OFF command

;		No pre-zero crossing sample for Insteon on 50Hz system

	  if		ac50hz == 1		; if for 50Hz AC powerline
		goto	mainlp			; loop and wait for next zero crossing
	  endif

;		Switch LCDDS back to an analog reference for the comparator

		banksel	TRISA			; select BANK1 for port direction registers
		bsf		TRISA,LCDDS		; switch LCDDS to input for comparator reference
		clrf	BSR				; back to BANK0

;		Sample noise 900uS to 200uS before zero crossing

dlyend	btfss	T1CON,TMR1ON	; skip if Timer 1 is running
		goto	mainlp			; stop and wait for next zero crossing if not
		movfw	TMR1H			; get Timer 1 high byte
		addlw	.157			; 900uS before the next zero crossing? (at 7.68MHz clk)
		btfss	STATUS,C		; skip and begin sampling noise if so
		goto	dlyend			; loop until beginning of sample window

		banksel	TRISA			; select BANK1 for port direction registers
		movlw	b'00011011'		; C1I+, CI0-, ZCD, VLEVEL are all inputs
		movwf	TRISA			; SWINPT is output to route C1OUT -> T0CLK
		bcf		TRISC,PWMOUT	; make sure PWM output is enabled
		banksel	ANSELA			; select BANK3
		movlw	b'00010011'		; LCDDS (for C1I+), X10XIN, VLEVEL are analog inputs
		movwf	ANSELA			; disable SWINPT analog input (for C1OUT -> T0CLK)
		banksel CM1CON0			; select BANK2 for comparators
		movlw	b'10100100'		; comparator 1 enabled, C1OUT, fast asynchronous, no hysteresis
		movwf	CM1CON0
		clrf	BSR				; back to BANK0

		clrf	TMR0			; clear Timer 0 at start of window
		bcf		INTCON,T0IF		; clear Timer 0 overflow flag

	bsf	PORTC,3

		bsf		PCLATH,3		; select ROM Page 1
		call	admeas			; sample signal/noise voltage for 660 uS
		clrf	PCLATH			; back to ROM Page 0

	bcf	PORTC,3

		movfw	TMR0			; read Timer 0 count (=> 48 cycles for logic 1)
		btfsc	INTCON,T0IF		; skip if Timer 0 did not overflow
		movlw	.255			; set max count = 255
		movwf	cyccnt			; save pre zero-crossing count for later processing
		banksel CM1CON0			; select BANK2 for comparators
		movlw	b'00100100'		; comparator 1 disabled, C1OUT, fast asynchronous, no hysteresis
		movwf	CM1CON0
		clrf	BSR				; back to BANK0

		movfw	adcsum			; get A/D conversion sum MSB
		movwf	siglvl			; save for logic "1" check
		banksel	pg1ram			; select BANK1 RAM
		movfw	agcthd			; get pre X10 sample window threshold
		clrf	BSR				; back to BANK0
		call	chkone			; check for a logic "1" in pre zero-crossing window
		rlf		presr1,F		; shift detected bit into pre window shift register

		goto	mainlp			; loop and wait for next zero crossing


;		***************************************************************

;		ROM PAGE 1

		org		0x800			; Beginning of ROM Page 1


;		 INITIALIZATION SUBROUTINE
;
;		 Note: Clock Oscillator starts at 16MHz internal by default

initiz	clrf	BSR				; select BANK0
		clrf	PORTA			; set LCDDS and LCDE outputs low
		clrf	PORTC			; set LCD, RANGE, & PWM outputs low
		movlw	.32				; set Timer 2 period for 120KHz PWM (15.84MHz/(4*33)
		movwf	PR2
		clrf	INTCON			; disable interrupts for now
		clrf	T1GCON			; disable Timer 1 gate function
		movlw	b'00000000'		; disable Timer 1 (Fosc/4 = 3.96MHz Clk, prescale = 0)
		movwf	T1CON			; initialize Timer 1
		clrf	T2CON			; disable Timer 2 with prescale=0 (120KHz PWM)

		banksel	OSCCON			; select BANK1 for osc, interrupts, & A/D converter
		movlw	b'01111010'		; use internal osc at 16MHz
		movwf	OSCCON			; set oscillator control
		movlw	b'00111000'		; to start with frequency close to 15.84MHz
		movwf	OSCTUNE			; set oscillator tune setting
		movlw	b'00011010'		; LCDE, SWINPT (for C1OUT), and LCDDS are outputs
		movwf	TRISA			; define I/O port A directions
		movlw	b'00000000'		; LCD DB4-7, RANGE, & PWM are all outputs
		movwf	TRISC			; define I/O port C directions
		movlw	b'10111000'		; disable weak pull-ups, T0 Clk is C1Out\, no T0 prescale
		movwf	OPTION_REG		; set option register
		movlw	b'00000001'		; enable just Timer 1 overflow interrupt
		movwf	PIE1
		clrf	PIE2			; no other interrupts enabled
		clrf	PIE3
		movlw	b'00001101'		; set WDT for 64mS sec timeout (31KHz/2048)
		movwf	WDTCON
		movlw	b'11010000'		; A/D clk = Fosc/16 (1.018uS), right justified, Ref VDD
		movwf	ADCON1			; set A/D clock (667uS for 32-sample sum)
		movlw	b'00001101'		; enable A/D with channel AN3 on RA4 (VLEVEL) selected
		movwf	ADCON0			; configure A/D converter
		
		banksel	APFCON0			; select BANK2 for comparator and D/A
		clrf	APFCON0			; set all standard pin functions
		clrf	APFCON1
		clrf	SRCON0			; make sure S-R latch is disabled
		clrf	SRCON1
		clrf	FVRCON			; disable fixed voltage reference
		clrf	DACCON0			; disable D/A converter and the output test point
		clrf	DACCON1

		movlw	b'00100100'		; comparator 1 disabled, C1OUT, fast asynchronous, no hysteresis
		movwf	CM1CON0
		clrf	CM1CON1			; no interrupts, C1I+ reference, C1IN0- input (X10XIN)
		clrf	CM2CON0			; make sure comparator 2 is disabled
		clrf	CM2CON1
		movlw	b'10000000'		; to enable brown-out protection
		movwf	BORCON			; set brown-out control

		banksel	ANSELA			; select BANK3
		movlw	b'00010010'		; X10XIN, VLEVEL are always analog inputs
		movwf	ANSELA			; disable SWINPT analog input (for C1OUT -> T0CLK)
		clrf	ANSELC			; all port C pins are digital outputs

		banksel	WPUA			; select BANK4 for weak pull-up control
		clrf	WPUA			; no port A weak pull-ups
		clrf	WPUC			; no port C weak pull-ups

		banksel	CCPR1L			; select BANK5 for PWM control
		movlw	.10				; for 2.08uS pulsewidth
		movwf	CCPR1L			; set default PWM duty cycle
		clrf	CCP1CON			; make sure PWM is off
		clrf	PWM1CON			; no PWM delay between phases
		clrf	PSTR1CON		; keep PWM output disabled for now

		banksel	T4CON			; select BANK8 for Timer 4 & 6 control
		clrf	T4CON			; make sure Timer 4 is disabled
		clrf	T6CON			; make sure Timer 6 is disabled
		clrf	BSR				; back to BANK0
		movlw	pg0ram			; get start of BANK0 workspace
		movwf	FSR0L			; set indirect pointer to BANK0 workspace
		clrf	FSR0H
		movlw	pg0end-pg0ram	; get the number of bytes to clear
		call	clrram			; clear BANK0 workspace

		movlw	pg1ram			; get start of BANK1 workspace
		movwf	FSR0L			; set indirect pointer to BANK1 workspace
		movlw	pg1end-pg1ram	; get the number of bytes to clear
		call	clrram			; clear BANK1 workspace

		movlw	H'20'			; get start of BANK2 logging RAM
		movwf	FSR0L			; set indirect pointer to BANK2 workspace
		movlw	H'01'
		movwf	FSR0H
		movlw	.80				; get the number of bytes to clear
		call	clrram			; clear first page of logging RAM

		clrf	stsflg			; clear status flags
		clrf	rcvflg			; clear receive flags
		clrf	dspflg			; clear display flags
		clrf	xmtflg			; clear transmit flags
		clrf	modflg			; clear mode flags
		clrf	cmdflg			; clear command flags
		movlw	H'80'			; for logging buffer empty
		movwf	logwpt			; preset logging buffer write pointer

		movlw	b'00100000'		; preset frequency error to mid-range
		movwf	frqerr
		movlw	b'00011000'		; OSCTUNE was preset close to 15.84MHz
		movwf	nxttune			; also save at RAM workspace

		banksel EEADRL			; select BANK3
		movlw	EECONF			; get configuration flags address in EEPROM
		movwf	EEADRL			; set EEPROM address to configuration flags
		clrf	EEADRH
		bsf		EECON1,RD		; read EEPROM location
		movfw	EEDAT			; get configuration flags
		movwf	cfgflg			; save in RAM for quick access

	if			pro == .1		; if Pro version

		movlw	EEARCH			; get archive data address
		movwf	EEADRL			; set EEPROM to archive data
		bsf		EECON1,RD		; read EEPROM
		movfw	EEDAT			; read archived house code
		movwf	temp0			; save it
		incf	EEADRL,F		; point to next EEPROM address
		bsf		EECON1,RD		; read EEPROM
		movfw	EEDAT			; read archived house & unit code
		movwf	temp1			; save it
		incf	EEADRL,F		; point to next EEPROM address
		bsf		EECON1,RD		; read EEPROM
		movfw	EEDAT			; read archived command code
		movwf	temp2			; save it
		incf	EEADRL,F		; point to next EEPROM address
		bsf		EECON1,RD		; read EEPROM
		movfw	EEDAT			; read archived X10 signal level
		movwf	temp3			; save it
		incf	EEADRL,F		; point to next EEPROM address
		bsf		EECON1,RD		; read EEPROM
		movfw	EEDAT			; read archived average frequency
		movwf	temp4			; save it
		incf	EEADRL,F		; point to next EEPROM address
		bsf		EECON1,RD		; read EEPROM
		movfw	EEDAT			; read archived noise level

	endif

		clrf	BSR				; back to BANK0

	if			pro == .1		; if Pro version

		movwf	anoise			; save archived noise level
		movfw	temp0			; recall archived house code
		movwf	ahouse			; save it
		movfw	temp1			; recall archived house & unit code
		movwf	ahunit			; save it
		movfw	temp2			; recall archived command code
		movwf	acomnd			; save it
		movfw	temp3			; recall archived X10 maximum signal level
		movwf	ax10mx			; save it
		movfw	temp4			; recall archived X10 frequency
		movwf	axfreq			; save it

	endif

		clrf	TMR0			; clear Timer 0
		clrf	TMR1H			; clear Timer 1
		clrf	TMR1L
		bcf		PIR1,TMR1IF		; clear Timer 1 interrupt flag
		clrf	TMR2			; clear Timer 2
		bsf		T2CON,TMR2ON	; start PWM timer

		banksel	IOCAF			; select BANK7 for interrupt on change
		movlw	b'00001000'		; to enable interrupt on change for RA3 ZCD
		movwf	IOCAP			; set interrupt enable for ZCD positive edge
		movwf	IOCAN			; set interrupt enable for ZCD negative edge
		clrf	IOCAF			; clear any existing IOC flag bits
		clrf	BSR				; back to BANK0
		movlw	b'11001000'		; enable peripheral interrupts & interrupt on change
		movwf	INTCON			; enable interrupts
		return					; return from initialize

;		CLEAR RAM

;		This subroutine clears W bytes of RAM
;		The indirect pointer FSR0H & FSR0L must be set to the first RAM address

clrram	movwf	temp0			; save for loop counter
clrrlp	clrf	INDF0			; clear RAM
		incf	FSR0L,F			; next address
		decfsz	temp0,F			; skip if done
		goto	clrrlp			; loop for W bytes
		return

	if			pro == .1		; if Pro version

;		CHECK MODE SWITCHES

;		This subroutine selects the operating mode and control functions
;		The present operating mode is stored in the 3 LSBs of modflg:

;			000 = normal default mode (same as XTBM)
;			001 = prior display mode (normal, but using prior data)
;			010 = expanded noise display
;			011 = bargraph
;			100	= expanded data pattern mode
;			101 = logging mode

;			.05 second press on left switch increments to next mode
;			.05 second press on right switch to transmit in normal display mode
;			.05 second press on either switch toggles back & forth between normal and archived data
;			.05 second press on either switch scrolls back & forth in logging or data pattern modes
;			.05 first second press on right switch in bit pattern mode locks the bit pattern display
;			.05 second press on both switches in bit pattern returns cursor to zero & unlocks the display
;			0.5 second press on either switch jumps back & forth 5 steps in logging mode
;			0.5 second press on both switches returns to normal

;			bothsw	EQU	H'07'	"1" indicates both switches are pressed
;			leftsw	EQU	H'06'	"1" indicates right switch is pressed
;			rghtsw	EQU	H'05'	"1" indicates left switch is pressed
;			releas	EQU	H'04'	"1" indicates both switches were released
;			modchg	EQU	H'03'	"1" indicates mode has been changed (to display new mode)
;			modsel	3 LSBs

chkmod
		banksel	ANSELA			; select BANK3
		movlw	b'00010111'		; LCDDS (for C1I+), X10XIN, VLEVEL are analog inputs
		movwf	ANSELA			; enable SWINPT analog input
		banksel	ADCON0			; select BANK1 for A/D converter
		movlw	b'00001001'		; enable A/D with channel AN2 on RA2 (SWINPT) selected
		movwf	ADCON0			; configure A/D converter
		movlw	b'01010000'		; A/D clk = Fosc/16 (1.01uS), left justified, Ref VDD
		movwf	ADCON1			; set A/D clock
		movlw	.20				; set delay to allow input to stabilize
		movwf	temp0			; save delay count
addly2	decfsz	temp0,F			; tick off one delay cycle
		goto	addly2			; loop until done
		bsf		ADCON0,1		; begin A/D conversion
		movlw	.20				; set delay for conversion
		movwf	temp0			; save delay count
addly3	decfsz	temp0,F			; tick off one delay cycle
		goto	addly3			; loop until done
		movfw	ADRESH			; get ADC 8 MSBs
		movwf	temp0			; save mode switch reading
		clrf	BSR				; back to BANK0

		movlw	.32				; to check if over 4.4V (224) for both switches released 
		addwf	temp0,W			; are both released? (C=1 if so)
		btfsc	STATUS,C		; skip if not
		goto	modeno			; no mode switch is pressed
		movlw	.113			; to check if over 2.8V (143) for just S2 pressed
		addwf	temp0,W			; is S2 (left switch) pressed? (C=1 if so)
		btfsc	STATUS,C		; skip if not
		goto	modes2			; process S2 pressed
		movlw	.143			; to check if over 2.2V (113) for just S1 pressed
		addwf	temp0,W			; is S1 (right switch) pressed? (C=1 if so)
		btfsc	STATUS,C		; skip if not
		goto	modes1			; process S1 pressed

		btfss	modflg,bothsw	; skip if both switches flag is already set
		clrf	hldcnt			; reset hold count for the change
		movlw	b'00011111'		; to clear all switch status flags
		andwf	modflg,F		; clear all switch status flags
		bsf		modflg,bothsw	; indicate both switches are pressed
		goto	modchk			; check mode command

modes1	btfss	modflg,rghtsw	; skip if right switch flag is already set
		clrf	hldcnt			; reset hold count for the change
		movlw	b'00011111'		; to clear all switch status flags
		andwf	modflg,F		; clear all switch status flags
		bsf		modflg,rghtsw	; indicate right switch is pressed
		goto	modchk			; check mode command

modes2	btfss	modflg,leftsw	; skip if left switch flag is already set
		clrf	hldcnt			; reset hold count for the change
		movlw	b'00011111'		; to clear all switch status flags
		andwf	modflg,F		; clear all switch status flags
		bsf		modflg,leftsw	; indicate left switch is pressed
		goto	modchk			; check mode command

modeno	movlw	b'11100000'		; to check switch status flags	
		andwf	modflg,W		; check if any switch status flags were set
		btfss	STATUS,Z		; skip if not
		clrf	hldcnt			; reset hold count for the change
		movlw	b'00011111'		; to clear all switch status flags
		andwf	modflg,F		; clear all switch status flags
		incf	hldcnt,W		; increment switch hold count -> W
		btfss	STATUS,Z		; skip if overflow
		movwf	hldcnt			; save new switch hold count
		addlw	.250			; check both released for at least 50mS
		btfsc	STATUS,C		; skip if not
		bsf		modflg,relese	; indicate both switches have been released
		goto	nomode			; no mode change

modchk	incf	hldcnt,W		; increment switch hold count
		btfss	STATUS,Z		; skip if overflow
		movwf	hldcnt			; save new switch hold count
		btfss	modflg,relese	; skip if switches have been released
		goto	nomode			; no mode change if not

;		Check both switches

		btfss	modflg,bothsw	; skip if both switches are pressed
		goto	chklsw			; check left switch if not
		movfw	hldcnt			; recall switch hold count
		addlw	.196			; check if pressed at least .5 second
		btfss	STATUS,C		; skip if so
		goto	bitulk			; check for bit pattern unlock if not
defmod	movlw	b'11111000'		; to clear mode select bits
		andwf	modflg,F		; reset mode flag to normal default
		bsf		modflg,modchg	; indicate mode has been changed
		goto	nomode			; done

bitulk	addlw	.54				; check if pressed exactly .05 second
		btfss	STATUS,Z		; skip if so
		goto	nomode			; done
		movfw	modflg			; get mode status
		andlw	b'00000111'		; mask all but the mode select bits
		addlw	.252			; check if bit pattern mode (4+252 -> 256)
		btfss	STATUS,Z		; skip if modes 4
		goto	nomode			; done
		clrf	modptr			; clear mode pointer to return cursor to zero
		bcf		dspflg,bitlok	; unlock the bit pattern display
		goto	nomode			; done

;		Check left switch

chklsw	btfss	modflg,leftsw	; skip if left switch is pressed
		goto	chkrsw			; check right switch if not
		movfw	hldcnt			; recall switch hold count
		addlw	.250			; check if pressed exactly .05 second
		btfss	STATUS,Z		; skip if so
		goto	chkls5			; check for a half second press if not
		movfw	modflg			; get mode status
		andlw	b'00000111'		; mask all but the mode select bits
		addlw	.252			; check if logging or bit pattern modes (4+252 -> 256)
		btfss	STATUS,C		; skip if modes 4 or 5
		goto	chkl03			; increment mode for modes 0-3
		btfss	STATUS,Z		; skip if the bit pattern mode (mode 4)
		goto	chkscr			; process scrolling for logging mode
		btfss	modptr,7		; skip if just stepping through modes
		goto	chkscr			; process scrolling for bit pattern mode
		clrf	modptr			; clear mode pointer for modes 4 & 5
		goto	chklnx			; change mode for left switch
chkl03	movlw	H'80'			; clear mode pointer & set flag indicating stepping through modes
		movwf	modptr			; save mode pointer
chklnx	incf	modflg,F		; next mode
		bsf		modflg,modchg	; indicate mode has been changed
		bcf		modflg,relese	; clear released flag
		goto	nomode			; done

chkls5	movfw	hldcnt			; recall switch hold count
		addlw	.196			; check if pressed at least .5 second
		btfss	STATUS,C		; skip if so
		goto	nomode			; no mode change until pressed long enough
		movfw	modflg			; get mode status
		andlw	b'00000111'		; mask all but the mode select bits
		addlw	.252			; check if logging or bit pattern modes (4+252 -> 256)
		btfss	STATUS,C		; skip if so
		goto	nomode			; no action for holding down left switch in modes 0-3
		btfss	STATUS,Z		; skip if bit pattern mode (mode 4)
		goto	chkscr			; process scrolling for logging mode (mode 5)
		movf	modptr,F		; check if mode pointer is at zero
		btfsc	STATUS,Z		; skip if not
		goto	chklnx			; increment to mode 5 if so
		clrf	modptr			; clear mode pointer to return the cursor to zero
		bcf		modflg,relese	; clear released flag
		goto	nomode			; done

;		Check right switch

chkrsw	btfss	modflg,rghtsw	; skip if right switch is pressed
		goto	nomode			; done if not
		movfw	hldcnt			; recall switch hold count
		addlw	.250			; check if pressed exactly .05 second
		btfss	STATUS,Z		; skip if so
		goto	chkrs5			; check for a half second press if not
		movfw	modflg			; get mode status
		andlw	b'00000111'		; mask all but the mode select bits
		btfsc	STATUS,Z		; skip if not the default mode 0
		goto	nomode			; nothing to do in mode zero for .05 second press
		addlw	.252			; check if logging or bit pattern modes (4+252 -> 256)
		btfsc	STATUS,Z		; skip if not the bit pattern mode
		bsf		dspflg,bitlok	; lock the bit pattern display
		btfsc	STATUS,C		; skip if not modes 4 or 5
		goto	chkscr			; process scrolling for logging or bit pattern modes
		decf	modflg,F		; back to previous mode
		bsf		modflg,modchg	; indicate mode has been changed
		bcf		modflg,relese	; clear released flag
		goto	nomode			; done

chkrs5	movfw	hldcnt			; recall switch hold count
		addlw	.196			; check if pressed at least .5 second
		btfss	STATUS,C		; skip if so
		goto	nomode			; nothing to do until pressed long enough
		movfw	modflg			; get mode status
		andlw	b'00000111'		; mask all but the mode select bits
		btfsc	STATUS,Z		; skip if not the default mode 0
		goto	chkrpt			; trigger repeater check for mode 0
		addlw	.252			; check if logging or bit pattern modes (4+252 -> 256)
		btfss	STATUS,C		; skip if modes 4 or 5
		goto	nomode			; nothing to do for long press in modes 0-3
		btfss	STATUS,Z		; skip if bit pattern mode (mode 4)
		goto	chkscr			; process scrolling for logging mode
		movfw	modptr			; get the mode pointer
		addlw	.246			; check if >10
		movlw	.11				; to skip to start of second line
		btfss	STATUS,C		; skip if already on second line
		movwf	modptr			; jump to start of second line
		bcf		modflg,relese	; clear released flag
		goto	nomode			; done

chkrpt	bsf		xmtflg,rptchk	; set flag to run repeater check
		bsf		xmtflg,xmtmsg	; set flag to transmit repeater check message
		movlw	.63				; allow .5 sec for repeater check before flag is cleared
		movwf	dspcnt			; set dispcnt
		bcf		modflg,relese	; clear released flag
		goto	nomode			; done

chkscr	bcf		modptr,7		; clear mode stepping flag
		movfw	modflg			; get mode status
		andlw	b'00000111'		; mask all but the mode select bits
		addlw	.252			; check if data pattern mode (4+252 -> overflow)
		btfss	STATUS,Z		; skip if data pattern mode (increment to right)
		goto	chklgs			; check logging mode scroll (increment to left)
		btfss	modflg,rghtsw	; skip if right switch is pressed
		goto	chkbtl			; check left switch
		incf	modptr,F		; increment mode pointer for right switch
		goto	chkrng			; check log pointer range

chkbtl	btfss	modflg,leftsw	; skip if left switch is pressed
		goto	nomode			; no change if neither switch is pressed
		decf	modptr,F		; decrement mode pointer for left switch
		btfsc	STATUS,Z		; skip if not at the zero position
		bcf		modflg,relese	; clear released flag for no mode change until re-press
		goto	chkrng			; check log pointer range

chklgs	movfw	hldcnt			; recall switch hold count
		addlw	.250			; check if being held for exactly 50mS
		btfss	STATUS,Z		; skip and check which switch if so
		goto	chkjmp			; check for jump @ .5 sec
		btfss	modflg,rghtsw	; skip if right switch is pressed
		goto	chklgl			; check left switch

		movf	modptr,F		; check if mode pointer is at zero
		btfsc	STATUS,Z		; skip and decrement if not
		goto	nomode			; no change at .05 second if at zero
		decf	modptr,F		; decrement mode pointer for right switch
		goto	chkrng			; check log pointer range

chklgl	btfss	modflg,leftsw	; skip if left switch is pressed
		goto	nomode			; no change if neither switch is pressed

		movf	logcnt,F		; check end of logged data
		btfsc	STATUS,Z		; skip if not zero
		goto	nomode			; no scroll if no data
		decf	logcnt,W		; get number of last entry (starting at 0)
		subwf	modptr,W		; check if at end of logged data (Z=1 if so)
		btfsc	STATUS,Z		; skip and increment pointer if not
		goto	nomode			; no change at .05 second if at limit
		incf	modptr,F		; increment mode pointer for left switch
		goto	chkrng			; check log pointer range

chkjmp	movfw	hldcnt			; recall switch hold count
		addlw	.196			; check if being pressed at least .5 second
		btfss	STATUS,C		; skip and check which switch if so
		goto	nomode			; no jump if not
		clrf	hldcnt			; clear hold count for 1 jump every .5 second
		btfss	modflg,leftsw	; skip if left switch is pressed
		goto	jmprgh			; check right switch

		movf	logcnt,F		; check end of logged data
		btfsc	STATUS,Z		; skip if not zero
		goto	nomode			; no jump if no data
		decf	logcnt,W		; get number of last entry (starting at 0)
		subwf	modptr,W		; check if at end of logged data (Z=1 if so)
		btfss	STATUS,Z		; skip and set to beginning if so
		goto	jmplft			; jump left if not to end yet
		clrw					; to reset pointer to beginning
		goto	setptr			; set pointer

jmplft	movfw	modptr			; get mode pointer		
		addlw	.4				; add 4 (5 total jump) for left switch
		movwf	modptr			; save pointer
		goto	chkrng			; check log pointer range

jmprgh	btfss	modflg,rghtsw	; skip if right switch is pressed
		goto	nomode			; no change if neither switch is pressed

		movf	logcnt,F		; check end of logged data
		btfsc	STATUS,Z		; skip if not zero
		goto	nomode			; no scroll if no data
		decf	logcnt,W		; to set pointer to the end (starting at 0)
		movf	modptr,F		; check if mode pointer is at zero
		btfsc	STATUS,Z		; skip if not at zero
		goto	setptr			; set pointer to the end
		movfw	modptr			; get mode pointer
		addlw	.252			; subtract 4 (5 total jump) for right switch
		movwf	modptr			; save pointer

chkrng	btfsc	modptr,7		; skip if not negative overflow	of mode pointer
		clrf	modptr			; reset mode pointer to zero
		movfw	modflg			; get mode status
		andlw	b'00000111'		; mask all but the mode select bits
		addlw	.252			; check if bit pattern mode (4+252 -> 256)
		btfss	STATUS,Z		; skip if bit pattern mode
		goto	chklog			; check end of logging buffer

		movlw	.21				; 22 bit pairs in bit pattern buffer (0 to 21)
		subwf	modptr,W		; check if past end of bit pattern buffer (C=1 if so)
		btfss	STATUS,C		; skip and set to end if so
		goto	nomode			; done
		movlw	.21				; point to last bit pair in bit pattern buffer
		goto	setptr			; set mode pointer

chklog	movfw	logcnt			; get end of logged data
		btfsc	STATUS,Z		; skip if not zero
		goto	setptr			; keep mode pointer at 0 if buffer is empty
		movf	modptr,F		; check if mode pointer is at 0
		btfsc	STATUS,Z		; skip and check for max if not
		goto	clrrls			; clear release flag
		decf	logcnt,W		; get number of commands in buffer (starting at 0)
		subwf	modptr,W		; get number of last entry (C=1 if so)
		btfsc	STATUS,Z		; skip if not the same (at the end)
		goto	clrrls			; clear release flag at the end
		btfss	STATUS,C		; skip and set to end if so
		goto	nomode			; done
		decf	logcnt,W		; get number of last entry (starting at 0)
setptr	movwf	modptr			; set mode pointer to end of logged data
clrrls	bcf		modflg,relese	; clear released flag
nomode	return

	endif


;		SUBROUTINE TO PROCESS RECEIVED DATA
;
;		This routine processes data received during the X10 reception window.
;		Enter with C = bit status

;		Receiving error definitions:

;		Bad Start Code - anything other than 1110 or 1111 start pattern
;		Insteon - 1111 start pattern with the Insteon signature
;		Noise - 11111111 or 1111 and not Insteon
;		Collision - more than 12 or 32 "1" bits after a valid start code
;		Receiving error - fewer than 12 or 32 "1" bits after a valid start code
;						  OR a complimentary pair mismatch after a valid start code

;		Insteon signature:  1111 ZCD 1111  0000 & 128-135KHz

rcvinp	btfsc	STATUS,C		; skip if received a logic "0" (C=0)
		goto	bitone			; process bit as a logic "1"

;		Count gap length up to 255 half cycles (clear line)

bitzer	bcf		stsflg,rcvbit	; set received bit status to "0"
		incf	gapcnt,W		; increment gap count
		btfss	STATUS,Z		; skip if overflow
		movwf	gapcnt			; save new gap count
		btfsc	cmdflg,xtdcmd	; skip if not an extended command
		goto	chkgap			; no check for 2nd start if extended command
		movfw	bitcnt			; get bit count
		addlw	.234			; is it a missing start after receiving 22 bits?
		btfss	STATUS,Z		; skip & process just 1/2 of doublet if so
		goto	chkgap			; check for gap if not

;		Received just 1/2 of doublet due to missing start pattern at 23rd bit

;		Received data rcvsrg5-0: xxxxxxxx xxxxxxxx xxxxxxxx xx1110HH HHHHHHKK KKKKKKKK
;		Transfer to bitsrg5-0:   xxxx1110 HHHHHHHH KKKKKKKK KK000000 00000000 00000000

		btfss	xmtflg,rptchk	; skip if not doing a repeater check
		goto	xfrbts			; transfer received bits to bit pattern register
		clrf	x10avg			; clear X10 transmitted voltage
		clrf	x10max			; clear X10 maximum signal level

xfrbts

	if			pro == .1		; if Pro version

		movlw	bitsrg			; get address of bit pattern shift register
		movwf	FSR0L			; set indirect pointer 1 to bit pattern shift register
		clrf	FSR0H
		movfw	rcvsrg			; transfer received data to global temp registers
		movwf	temp1
		movfw	rcvsrg+1
		movwf	temp2
		movfw	rcvsrg+2
		movwf	temp3
		clrf	temp0
		lsrf	temp3,F			; shift data pattern two bits to the right
		rrf		temp2,F
		rrf		temp1,F
		rrf		temp0,F
		lsrf	temp3,F			; shift data pattern two bits to the right
		rrf		temp2,F
		rrf		temp1,F
		rrf		temp0,F
		clrf	INDF0			; clear 1st two bytes of bit pattern register
		incf	FSR0L,F
		clrf	INDF0
		incf	FSR0L,F
		movfw	temp0			; transfer shifted data into bit pattern register			
		movwf	INDF0
		incf	FSR0L,F
		movfw	temp1			
		movwf	INDF0
		incf	FSR0L,F
		movfw	temp2			
		movwf	INDF0
		incf	FSR0L,F
		movfw	temp3			
		movwf	INDF0
		goto	xfrb44			; indicate a new bit pattern is ready to be displayed

	endif

chkgap	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	bitsav			; just process bit if not
		btfss	STATUS,Z		; skip if exactly 2 cycles long
		goto	gapclr			; clear registers to get ready for next command

	if			pro == .1		; if Pro version
		movfw	bitcnt			; get bit count
		addlw	.234			; has it received at least 22 bits?  234+22=0+C
		btfss	STATUS,C		; check for valid data if so
		goto	clrchk			; no logging for fewer than 22 bits
		btfsc	rcvflg,x10vld	; skip if not a valid X10 command
		call	logdat			; save received data into logging buffer
	endif

clrchk	bcf		xmtflg,rptchk	; make sure repeater check flag is cleared
		btfsc	xmtflg,xmtvlt	; skip if not ready to transmit a status echo voltage
		bsf		xmtflg,xmtmsg	; set flag to transmit message

gapclr	clrf	cmdflg			; clear all command error and status bits
		bcf		rcvflg,hirang	; make sure low range is selected (2.5V max)
		bcf		stsflg,rcving	; indicate not receiving a command
		clrf	rcvcnt			; clear receive routine received bit count 
		clrf	bitcnt			; clear bit pattern received bit count 
		clrf	onecnt			; clear number of "1" bits received
		clrf	onezer			; clear 1/0 ratio
		clrf	adcins			; also clear any A/D Insteon reading

		movlw	.244			; to check for 6 cycle gap
		addwf	gapcnt,W		; is the gap at least 6 cycles long?
		btfsc	STATUS,C		; skip if not
		bsf		xmtflg,clrlin	; indicate line is clear for transmit
		bcf		STATUS,C		; restore bit status to "0" for shift
		goto	bitsav			; process bit
		
bitone	bsf		stsflg,rcvbit	; set received bit status to "1"
		bsf		stsflg,rcving	; indicate receiving a possible X10 command
		incf	onecnt,F		; increment number of "1" bits
		clrf	gapcnt			; clear gap count for a "1"
		btfss	xmtflg,xmtmsg	; skip if transmitting a message
		bcf		xmtflg,clrlin	; indicate the line is not clear
		bsf		STATUS,C		; restore bit status to "1" for shift
bitsav	rlf		rcvsrg,F		; shift bit into receive shift register
		rlf		rcvsrg+1,F
		rlf		rcvsrg+2,F
		rlf		rcvsrg+3,F
		rlf		rcvsrg+4,F
		rlf		rcvsrg+5,F
		rlf		rcvsrg+6,F
		rlf		rcvsrg+7,F

		bcf		STATUS,C		; clear carry bit
		btfsc	stsflg,rcvbit	; skip if received bit is a "0"
		bsf		STATUS,C		; set carry bit
		rlf		strtrg,F		; shift received bit into start pattern register

;		Process received bit

		movf	bitcnt,F		; check if a start pattern has been received (bitcnt >= 4)
		btfsc	STATUS,Z		; skip if so
		goto	ckstrt			; check for start pattern if not

		btfsc	dspflg,bitlok	; skip if bit pattern display is not locked
		goto	ckstrt			; no bit pattern processing if locked
		incf	bitcnt,F		; increment bit pattern received bit count
		movfw	bitcnt			; get bit pattern received bit count
		addlw	.212			; check if received exactly 44 bits since first start pattern
		btfss	STATUS,Z		; skip if so
		goto	ckstrt			; continue processing

	if			pro == .1		; if Pro version

;		Transfer received bits into bit pattern register

xfrcmd	movlw	rcvsrg			; get address of receive shift register
		movwf	FSR0L			; set indirect pointer 0 to receive shift register
		clrf	FSR0H
		movlw	bitsrg			; get address of bit pattern shift register
		movwf	FSR1L			; set indirect pointer 1 to bit pattern shift register
		clrf	FSR1H
		movlw	.6				; 6 bytes to transfer
		movwf	temp0			; save loop count
xfrbit	movfw	INDF0			; read received bits
		movwf	INDF1			; transfer into bit pattern register
		incf	FSR0L,F			; increment indirect pointers
		incf	FSR1L,F
		decfsz	temp0,F			; tick off one loop
		goto	xfrbit			; loop for 6 bytes

xfrb44	bsf		dspflg,bitnew	; indicate a complete new bit pattern has been received
		movlw	.44				; to lock bitcnt at 44 max
		movwf	bitcnt			; lock bitcnt at 44 until cleared
		bcf		stsflg,bitxtd	; clear prior extended command flag
		btfsc	cmdflg,xtdcmd	; skip if new pattern is not an extended command
		bsf		stsflg,bitxtd	; set extended command flag for new bit pattern

	endif	

;		Check for noise or a gap

ckstrt	movf	rcvcnt,F		; test receive bit count
		btfss	STATUS,Z		; skip & check for start if zero
		goto	rcvchk			; check if received full word
		incf	strtrg,W		; check if last 8 bits have been "1"
		btfsc	STATUS,Z		; skip and check for start if not
		goto	rcvnse			; indicate high noise
		movfw	strtrg			; 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 	clrmsg			; clear counts for a gap
		andlw	b'00001111'		; mask all but last 4 bits

;		Check for a valid start pattern

		btfsc	STATUS,Z		; skip if at least one bit is set
		goto	rcvclr			; make sure counters are cleared
		movwf	temp1			; save potential start pattern
		clrf	onecnt			; make sure count of "1" data bits is cleared
		xorlw	b'00001110'		; check for "start" data pattern (1110)?
		btfsc	STATUS,Z		; skip if not a valid start pattern
		goto	rcvbgn			; begin receive for a valid start pattern
		btfsc	xmtflg,strtup	; skip if startup flag is cleared
		goto	rcvclr			; ignore anything but valid start pattern during startup

;		Check for a bad start pattern

		movfw	temp1			; recall potential start pattern
		xorlw	b'00000010'		; to check for just one "1" bit
		btfsc	STATUS,Z		; skip if not "0010"
		goto	rcvclr			; just clear count for one signal burst
		movfw	temp1			; recall potential start pattern
		xorlw	b'00000110'		; to check for just two "1" bits
		btfsc	STATUS,Z		; skip if not "0110"
		goto	rcvbsc			; indicate receive bad start code error for "0110"
		movfw	temp1			; recall potential start pattern
		xorlw	b'00001111'		; to check for just four "1" bits with no "0"
		btfss	STATUS,Z		; skip if "1111"
		goto	rcvclr			; just clear counts for other patterns

;		Check for possible Insteon command

		movfw	presr1			; get pre zero-crossing shift register
		andlw	b'00001111'		; mask all but last 4 bits
		xorlw	b'00001111'		; check for all for "1" bits
	  if		ac50hz == 0		; if not for 50Hz AC powerline
		btfss	STATUS,Z		; skip if possible Insteon signal (at 60Hz ONLY)
	  endif
		goto	rcvbsc			; indicate receive bad start code error for "1111"
		movfw	pstsr1			; get post X10 window shift register
		andlw	b'00001111'		; mask all but last 4 bits
		btfss	STATUS,Z		; skip if post window was all zeros (not noise)
		goto	rcvnse			; indicate receive noise error if not "0000" (onecnt = 0 for BSC)
		movfw	frqcnt			; get frequency count for this bit (Insteon = 131.625)
		addlw	.120			; is it > 135KHz? (136 + 120 = 256)
		btfsc	STATUS,C		; skip <= 135KHz
		goto	rcvnse			; indicate receive noise error if > 135KHz
		addlw	.8				; is frequency < 127KHz? (128 + 120 + 8 = 255)
		btfss	STATUS,C		; skip if frequency is >= 127KHz
		goto	rcvnse			; indicate receive noise error if < 127KHz

		btfsc	cmdflg,inston	; skip if Insteon flag is not already set
		goto	insclr			; clear X10 data if Insteon command
		movfw	adcx10			; get present A/D sum in X10 window
		movwf	adcins			; save Insteon value
		movfw	frqcnt			; get present frequency count in X10 window
		movwf	frqavg			; save at average frequency for display
		bcf		dspflg,x10rng	; to indicate this sample was in low-range mode
		btfsc	rcvflg,hirang	; skip if X10 voltage was measured in low range
		bsf		dspflg,x10rng	; to indicate this sample was in high-range mode
		bsf		cmdflg,inston	; ensure Insteon flag is set

;		Clear noise numbers when receiving an Insteon command

insclr	clrf	bsccnt			; clear bad start code count
		banksel	pg1ram			; select BANK1 RAM
		call	rstflt			; reset noise filter data
		clrf	BSR				; back to BANK0
		bcf		xmtflg,xhinse	; clear high noise flag

	if			pro == .1		; if Pro version

		btfsc	dspflg,bitlok	; skip if bit pattern display is not locked
		goto	rcvclr			; do not clear bit pattern if locked

		banksel	bitsrg			; select BANK1 RAM
		clrf	bitsrg			; clear 6 byte bit-pattern shift register
		clrf	bitsrg+1
		clrf	bitsrg+2
		clrf	bitsrg+3
		clrf	bitsrg+4
		clrf	bitsrg+5
		clrf	BSR				; back to BANK0

		movlw	stbufr			; point to start of voltage data
		movwf	FSR0L			; set indirect pointer LSB
		movlw	.6				; select BANK12 workspace
		movwf	FSR0H			; set indirect pointer MSB
		movlw	.48				; to clear all bit voltages
		movwf	temp0			; save for loop counter
clrvlt	clrf	INDF0			; clear RAM
		incf	FSR0L,F			; next address
		decfsz	temp0,F			; skip if done
		goto	clrvlt			; loop for 48 bytes

		bsf		dspflg,bitnew	; set flag to flush bit pattern display

	endif

		goto	rcvclr			; clear receive counters

rcvbgn	bsf		cmdflg,rcvx10	; set flag to indicate receiving a X10 command
		bcf		rcvflg,x10vld	; ensure valid flag for prior command is cleared
		bcf		rcvflg,rcvdbl	; ensure doublet flag for prior command is cleared
		movlw	.2				; preset 1/0 ratio to 2 (for 1110 start)
		movwf	onezer
		movlw	.4				; preset rcvcnt to 4 bits for valid "start"
		movwf	rcvcnt
		movf	bitcnt,F		; is it the first start pattern after a gap?
		btfss	STATUS,Z		; skip if so
		goto	rcvbv2			; process second start pattern

;		Set X10 max voltage range flag for first half of doublet

		movwf	bitcnt			; preset bitcnt to 4 bits for new command
		bcf		rcvflg,maxrng	; clear X10max signal level high-range bit
		btfsc	rcvflg,hirang	; skip if X10 signal was not measured in high range
		bsf		rcvflg,maxrng	; set X10max signal level high-range bit
		btfsc	xmtflg,rptchk	; skip if not doing a repeater check
		bcf		rcvflg,maxrng	; ignore 1st half range when doing a repeater check
		clrf	x10max			; clear maximum value for previous X10 command
		clrf	x10cpr			; clear compare value for previous X10 command
		bcf		rcvflg,rptvld	; make sure the repeat valid flag is cleared

	if			pro == .1		; if Pro version

;		Set bit voltage buffer range flag for first half of doublet

		btfsc	dspflg,bitlok	; skip if bit pattern display is not locked
		return					; no update for ranges or voltages when bit pattern is locked

		bcf		rcvflg,h1rang	; clear first bit pattern high-range bit
		bcf		rcvflg,h2rang	; clear second bit pattern high-range bit
		btfsc	rcvflg,hirang	; skip if X10 signal was not measured in high range
		bsf		rcvflg,h1rang	; set first bit pattern high-range bit

;		Copy start buffer voltages into bit voltage buffer

startv
		banksel	pgbram			; switch to BANK12
		movfw	stbufr			; copy 4 start voltage readings into bit voltage buffer
		movwf	bvbufr
		movfw	stbufr+1
		movwf	bvbufr+1
		movfw	stbufr+2
		movwf	bvbufr+2
		movfw	stbufr+3
		movwf	bvbufr+3
		clrf	BSR				; back to BANK0

		movlw	bvbufr+4		; get next bit voltage buffer address
		movwf	FSR0L			; set indirect pointer 0 to bit voltage buffer
		movlw	.6				; for bank 12
		movwf	FSR0H
		movlw	.40				; to clear remainder of 44-bit bit voltage buffer
		movwf	temp0			; save loop count
clrbvb	clrf	INDF0			; clear old bit voltage buffer readings
		incf	FSR0L,F
		decfsz	temp0,F			; count off one byte
		goto	clrbvb			; loop for 40 bytes

	endif

		return

;		Set bit voltage buffer range flag for second half of doublet

rcvbv2	bsf		rcvflg,rcvdbl	; set flag to indicate receiving both halves of doublet
;		btfsc	rcvflg,hirang	; skip if X10 signal was not measured in high range
;		bsf		rcvflg,maxrng	; set X10max signal level high-range bit
		btfsc	xmtflg,rptchk	; skip if not a repeater check
		bsf		rcvflg,rptvld	; indicate receiving a repeated 2nd half of doublet
		btfsc	dspflg,bitlok	; skip if bit pattern display is not locked
		return					; no update for range when bit pattern is locked

		btfsc	rcvflg,hirang	; skip if X10 signal was not measured in high range
		bsf		rcvflg,h2rang	; set second bit pattern high-range bit
		return

;		Maintain 1/0 ratio when receiving first half of message

rcvchk	btfss	rcvsrg,0		; skip if received bit was a "1"
		decf	onezer,F		; decrement 1/0 ratio if it was a "0"
		btfsc	rcvsrg,1		; skip if received bit was a "0"
		incf	onezer,F		; increment 1/0 ratio if it was a "1"

;		Check if all data bits have been received

		incf	rcvcnt,F		; increment receive routine received bit count
		movfw	rcvcnt			; get received bit count
		btfss	cmdflg,xtdcmd	; skip if receiving an extended command
		addlw	.40				; adjust to check for 22-bit standard message
		addlw	.193			; extended: 62+193=255; normal 22+40+193=255
		btfsc	STATUS,C		; skip and check for errors until received 22 or 62 bits
		return					; no error check after receiving all of first half

;		Check for an error in first 22 or 62 bits (should always be 1-3 after start)

errchk	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)
		btfsc	STATUS,C		; skip if not too many "1"s
errset	bsf		cmdflg,errrcv	; set error flag

;		Check if exactly 22 bits have been received

rcv22b	movfw	rcvcnt			; get received bit count
		addlw	.194			; check for receiving exactly 62 bits
		btfsc	STATUS,Z		; skip if not
		goto	chk62b			; process 62-bit pattern
		addlw	.40				; check for receiving exactly 22 bits
		btfss	STATUS,Z		; skip if received exactly 22 bits
		return					; done

;		Check that no extra "1" bits were received in 22-bit command due to a collision

chk22b	btfsc	cmdflg,errrcv	; skip if no bit pair mismatch
		goto	msgbad			; message is bad if mismatch
		movfw	onecnt			; get number of "1" bits received
		addlw	.247			; were there 9 "1" bits in the data message?
		btfss	STATUS,Z		; skip if exactly 9 data bits
		goto	msgbad			; message is bad if not

;		Decode the standard command

		call	unpack			; unpack received word

;		Check for extended command codes (01111 or 11001)

		movlw	b'00001111'		; to check for extended code
		xorwf	rkeycd,W		; was it the extended code?
		btfsc	STATUS,Z		; skip & check for extended data if not
		goto	rcvxtm			; set flag for receiving an extended command
		movlw	b'00011001'		; to check for extended data
		xorwf	rkeycd,W		; was it the extended data code?
		btfss	STATUS,Z		; skip if extended data code
		goto	chksts			; check for status echo if not extended command
		bsf		cmdflg,xtddat	; set flag to indicate receiving an extended data command
rcvxtm	bsf		cmdflg,xtdcmd	; set flag to indicate receiving an extended command
		btfsc	rcvflg,h1rang	; skip if first half was measured in low range
		bsf		rcvflg,h2rang	; set high range for 2nd half of extended command
		return					; done

;		Indicate VALID & set stale data flag if bit pattern display is locked

chksts	bsf		rcvflg,x10vld	; indicate a valid X10 command has been received
		btfss	dspflg,bitlok	; skip if bit pattern display is locked
		bcf		dspflg,bitstl	; clear bit pattern display stale flag
		btfsc	dspflg,bitlok	; skip if bit pattern display is not locked
		bsf		dspflg,bitstl	; set bit pattern display stale flag
		btfss	xmtflg,strtup	; no delay during startup
		clrf	dspcnt			; to hold decoded command "VALID" display for 2 seconds
		bsf		dspflg,dsphld	; set display hold flag
		bsf		stsflg,archok	; indicate data is available to archive
		btfss	cfgflg,stsmon	; skip if status echo monitor is enabled
		goto	clrmsg			; done with 22-bit message

;		Check for STATUS_ON or STATUS_OFF echos (11011 or 11101)

		movlw	b'00011011'		; to check for STATUS_ON
		xorwf	rkeycd,W		; was it a STATUS_ON?
		btfsc	STATUS,Z		; skip if not
		goto	stsvlt			; report STATUS_ON echo voltage
		movlw	b'00011101'		; to check for STATUS_OFF
		xorwf	rkeycd,W		; was it a STATUS_OFF?
		btfsc	STATUS,Z		; skip & finish up if not
		goto	stsvlt			; report STATUS_OFF echo voltage
		goto	clrmsg			; done with standard 22-bit message

;		Check that no extra "1" bits were received in 62-bit command due to a collision

chk62b	btfsc	cmdflg,errrcv	; skip if no bit pair mismatch
		goto	msgbad			; message is bad if mismatch
		movfw	onecnt			; get number of "1" bits received
		addlw	.227			; were there 29 "1" bits in the data message?
		btfss	STATUS,Z		; skip if exactly 29 data bits
		goto	msgbad			; message is bad if not

;		Decode the extended command

		call	unpakx			; unpack received word
		bsf		rcvflg,x10vld	; indicate a valid X10 command has been received
		btfss	dspflg,bitlok	; skip if bit pattern display is locked
		bcf		dspflg,bitstl	; clear bit pattern display stale flag
		btfsc	dspflg,bitlok	; skip if bit pattern display is not locked
		bsf		dspflg,bitstl	; set bit pattern display stale flag
		clrf	dspcnt			; to hold decoded command "VALID" display for 2 seconds
		bsf		dspflg,dsphld	; set display hold flag
		bsf		stsflg,archok	; indicate data is available to archive
		goto	clrmsg			; done with 62-bit message

;		Transmit STATUS_ON or STATUS_OFF echo voltage measurement if so enabled

stsvlt	movfw	bitcnt			; get total received bit count
		addlw	.234			; check if exactly 22 bits
		btfss	STATUS,Z		; skip and save voltage for first half of doublet if so
		goto	clrmsg			; just clear message if not
		movfw	x10avg			; get X10 average voltage for this half of the doublet
		btfsc	rcvflg,hirang	; skip if measured in low range
		goto	stsvsv			; save value without adjustment
		movwf	temp0			; save for adjustment
		lsrf	temp0,F			; divide low range by 4 for 0-63 range
		lsrf	temp0,W
stsvsv	movwf	x10ech			; save adjusted X10 status echo voltage		

		bsf		xmtflg,xmtvlt	; set flag to transmit status echo voltage
		bsf		xmtflg,xmtucd	; set flag to transmit unit code for status echo voltage
		goto	clrmsg			; done with 22-bit message

;		Process a received error

msgbad	bsf		cmdflg,errrcv	; make sure receive error flag is set
		movfw	onecnt			; check number of 1's received (zero for BSC)
		btfss	cmdflg,xtdcmd	; skip if an extended command
		addlw	.20				; 20 bits more expected for extended command
		addlw	.226			; 9 bits expected for standard command (226+9+20=255)
		btfss	STATUS,C		; skip if extra bits were received (collision)
		goto	rcvclr			; clean up after error bit count error
		bsf		cmdflg,errcol	; set collision error flag if more than 12/32 bits
		bcf		dspflg,errmsg	; clear error message flag to display collision error
		goto	rcvclr			; clean up after error bit count error

rcvbsc	btfsc	cmdflg,errbsc	; skip if bad start code flag is not already set
		goto	rcvclr			; just clean up after error if already set
		bsf		cmdflg,errbsc	; set bad start code error flag
		bcf		rcvflg,x10vld	; clear valid if BSC during high noise

chkbsc	movfw	bsccnt			; get BSC count
		addlw	.250			; check if > 6 (decrements each half second)
		btfss	STATUS,C		; skip if already received 6 BSCs
		incf	bsccnt,F		; increment bad start code count
		goto	rcvclr			; clean up after bad start code error

rcvnse	bsf		cmdflg,errnse	; set flag to indicate noise
		goto	rcvclr			; clean up after high noise error

clrmsg	bcf		cmdflg,errrcv	; clear receive error flag
		clrf	presr1
		clrf	pstsr1
		clrf	strtrg			; clear start bit pattern register
rcvclr	clrf	rcvcnt			; clear receive bit count to receive 2nd half
		clrf	onecnt			; clear number of "1" bits received
		clrf	onezer			; clear 1/0 ratio
		return					; done with bit processing


;		SUBROUTINE TO UNPACK RECEIVED DATA @ 22 BITS
;
;		This subroutine decodes received message into house code & key code
;
;		22-bit data in rcvsr2-rcvsr1-rcvsr0: 001110HH HHHHHHKK KKKKKKKK
;		Assumes bank 0 is selected at entry.

unpack	clrf	rhouse			; clear prior house code
		clrf	rkeycd			; clear prior key code
		btfsc	rcvsrg+2,1		; skip if house code MSB is 0
		bsf		rhouse,3		; set house code MSB
		btfsc	rcvsrg+1,7		; skip if next house code bit is 0
		bsf		rhouse,2		; set next house code bit
		btfsc	rcvsrg+1,5		; skip if next house code bit is 0
		bsf		rhouse,1		; set next house code bit
		btfsc	rcvsrg+1,3		; skip if house code LSB is 0
		bsf		rhouse,0		; set house code LSB 
		btfsc	rcvsrg+1,1		; skip if key code MSB bit is 0
		bsf		rkeycd,4		; set key code MSB
		btfsc	rcvsrg,7		; skip if next key code bit is 0
		bsf		rkeycd,3		; set next key code bit
		btfsc	rcvsrg,5		; skip if next key code bit is 0
		bsf		rkeycd,2		; set next key code bit
		btfsc	rcvsrg,3		; skip if next key code bit is 0
		bsf		rkeycd,1		; set next key code bit
		btfsc	rcvsrg,1		; skip if key code LSB is 0
		bsf		rkeycd,0		; set key code LSB (function select bit)
		lsrf	rkeycd,W		; convert 5-bit key code to 4-bit unit code
		btfss	rkeycd,0		; skip if key code is a function code
		goto	savunt			; save unit code
		movwf	rcomnd			; save command code
		btfss	xmtflg,rptchk	; skip if a repeater check
		return

;		Check for valid repeat

		bsf		rcomnd,cmdrpt	; set flag indicating this is a repeater check
		btfss	rcvflg,rptvld	; skip if received 2nd half of the doublet
		goto	setbad			; set no valid command for first half
		movfw	rhouse			; get received housecode
		xorwf	xhouse,W		; check if matched transmitted housecode
		btfss	STATUS,Z		; skip & continue check if so
		goto	setbad			; indicate bad repeat if no match
		movfw	rkeycd			; get received keycode
		xorwf	xkeycd,W		; check if matched transmitted keycode
		btfss	STATUS,Z		; skip & indicate valid command if match
		goto	setbad			; indicate bad repeat if no match
		bcf		rcomnd,cmdbad	; clear invalid command flag
		return
	
savunt	movwf	rhunit			; save unit code
		swapf	rhouse,W		; get house code into W 4 MSBs
		iorwf	rhunit,F		; combine with unit code
		clrf	rcomnd			; clear prior command code
setbad	bsf		rcomnd,cmdbad	; set flag indicating command code is invalid
		return


;		SUBROUTINE TO UNPACK EXTENDED DATA 62 BITS
;
;		Ths subroutine decodes received message unit code and extended data
;
;		62-bit data in rcvsr7-rcvsr0:
;		001110HH HHHHHHCC CCCCCCCC UUUUUUUU XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
;		Assumes bank 0 is selected at entry.

unpakx	movfw	rcvsrg+4		; get unit code byte
		call	unpakb			; unpack byte to W
		movwf	rhunit			; save unpacked unit code
		swapf	rhouse,W		; get house code into W 4 MSBs
		iorwf	rhunit,F		; combine with unit code
		movfw	rcvsrg+3		; get first half of first extended code byte
		call	unpakb			; unpack first half to W
		movwf	rxdat1			; save first half
		swapf	rxdat1,F		; swap to MS nibble
		movfw	rcvsrg+2		; get second half of first extended code byte
		call	unpakb			; unpack second half to W
		iorwf	rxdat1,F		; assemble first extended code byte
		movfw	rcvsrg+1		; get first half of second extended code byte
		call	unpakb			; unpack first half to W
		movwf	rxdat2			; save first half
		swapf	rxdat2,F		; swap to MS nibble
		movfw	rcvsrg			; get second half of second extended code byte
		call	unpakb			; unpack second half to W
		iorwf	rxdat2,F		; assemble second extended code byte
		return
		
unpakb	movwf	temp0			; save byte for decode
		clrw					; start with unpacked byte cleared
		btfsc	temp0,7			; skip if MSB is 0
		iorlw	b'00001000'		; set decoded bit MSB
		btfsc	temp0,5			; skip if next bit is 0
		iorlw	b'00000100'		; set next decoded bit
		btfsc	temp0,3			; skip if next bit is 0
		iorlw	b'00000010'		; set next decoded bit
		btfsc	temp0,1			; skip if LSB is 0
		iorlw	b'00000001'		; set decoded LSB
		return


;		A/D MEASUREMENT

;		This routine takes 32 samples of the selected analog input and sums the readings

;		The 12-bit sum is returned left-justified in adcmsb and adclsb

admeas
		banksel	ADCON0			; select BANK1 for A/D converter
		movlw	b'11010000'		; A/D clk = Fosc/16 (1.01uS), right justified, Ref VDD
		movwf	ADCON1			; set A/D clock (660uS for 32-sample sum)
		movlw	b'00001101'		; enable A/D with channel AN3 on RA4 (VLEVEL) selected
		movwf	ADCON0			; configure A/D converter
		clrf	adcsumH			; clear A/D conversion sum registers (BANK1)
		clrf	adcsumL
		clrf	adcmax			; clear A/D sample maximum (global)
		movlw	.32				; make 32 conversions over the sample window
		movwf	temp1			; initialize A/D sample count

admslp	bsf		ADCON0,1		; begin A/D conversion
		movlw	.20				; set delay for 660uS sample window
		movwf	temp0			; save delay count
admsdy	decfsz	temp0,F			; tick off one delay cycle
		goto	admsdy			; loop until done
		nop						; add more delay to tweak tuning
		nop
		movfw	ADRESL			; get ADC 8 LSBs
		movwf	temp0			; save for maximum value check
		addwf	adcsumL,F		; add into A/D conversion sum LSB
		movfw	ADRESH			; get ADC 2 MSBs
		andlw	b'00000011'		; mask unused bits
		addwfc	adcsumH,F		; add into A/D conversion sum MSB (leaves C=0)

		lsrf	ADRESH,F		; shift A/D conversion 8 MSBs into temp register
		rrf		temp0,F
		lsrf	ADRESH,F
		rrf		temp0,F
		movfw	temp0			; get A/D conversion 8 MBSs
		subwf	adcmax,W		; compare with A/D maximum value for this sample window
		movfw	temp0			; recall A/D conversion 8 MSBs
		btfss	STATUS,C		; skip if new value <= maximum
		movwf	adcmax			; update A/D maximum value
		decfsz	temp1,F			; count off one A/D conversion cycle & skip if done
		goto	admslp			; make another A/D reading

		lslf	adcsumL,F		; shift LSB left
		rlf		adcsumH,F		; into MSB (A/D conversion is now left justified)
		movfw	adcsumH			; get ADC sum MSB
		clrf	BSR				; back to BANK0
		movwf	adcsum			; save ADC sum MSB in BANK0
		movf	adcmax,F		; check if maximum value is already zero
		btfss	STATUS,Z		; skip if so
		decf	adcmax,F		; reduce maximum value by 10mV
		return


;		A/D SAMPLE

;		This routine takes 8 samples of the selected analog input and sums the readings

;		The 12-bit sum is returned left-justified in adcmsb and adclsb

adsamp
		banksel	ADCON0			; select BANK1 for A/D converter
		movlw	b'11010000'		; A/D clk = Fosc/16 (1.01uS), right justified, Ref VDD
		movwf	ADCON1			; set A/D clock (140uS for 8-sample sum)
		movlw	b'00001101'		; enable A/D with channel AN3 on RA4 (VLEVEL) selected
		movwf	ADCON0			; configure A/D converter
		clrf	adcsumH			; clear A/D conversion sum registers (BANK1)
		clrf	adcsumL
		clrf	adcmax			; clear A/D sample maximum (global)
		movlw	.8				; make 8 conversions over the sample window
		movwf	temp1			; initialize A/D sample count

adsalp	bsf		ADCON0,1		; begin A/D conversion
		movlw	.16				; set delay for conversion window
		movwf	temp0			; save delay count
adsady	decfsz	temp0,F			; tick off one delay cycle
		goto	adsady			; loop until done
		movfw	ADRESL			; get ADC 8 LSBs
		movwf	temp0			; save for maximum value check
		addwf	adcsumL,F		; add into A/D conversion sum LSB
		movfw	ADRESH			; get ADC 2 MSBs
		andlw	b'00000011'		; mask unused bits
		addwfc	adcsumH,F		; add into A/D conversion sum MSB (leaves C=0)

		lsrf	ADRESH,F		; shift A/D conversion 8 MSBs into temp register
		rrf		temp0,F
		lsrf	ADRESH,F
		rrf		temp0,F
		movfw	temp0			; get A/D conversion 8 MBSs
		subwf	adcmax,W		; compare with A/D maximum value for this sample window
		movfw	temp0			; recall A/D conversion 8 MSBs
		btfss	STATUS,C		; skip if new value <= maximum
		movwf	adcmax			; update A/D maximum value
		decfsz	temp1,F			; count off one A/D conversion cycle & skip if done
		goto	adsalp			; make another A/D reading

		lslf	adcsumL,F		; shift to left justify 12-bit sum
		rlf		adcsumH,F
		lslf	adcsumL,F
		rlf		adcsumH,F
		lslf	adcsumL,F
		rlf		adcsumH,F
		btfss	rcvflg,hirang	; skip and multiply by 4 if high range
		goto	adssav			; save present value
		lslf	adcsumL,F		; multiply by 4 to adjust for high range
		rlf		adcsumH,F
		lslf	adcsumL,F
		rlf		adcsumH,F
adssav	movfw	adcsumH			; get ADC sum MSB
		clrf	BSR				; back to BANK0
		movwf	adcsum			; save ADC sum MSB in BANK0
		movf	adcmax,F		; check if maximum value is already zero
		btfss	STATUS,Z		; skip if so
		decf	adcmax,F		; reduce maximum value by 10mV
		return


;		X10 Command Average - sums 8 "1" bits in a X10 command after start pattern

;		Results are placed in x10avg & frqavg when the 8th bit is received

x10sum	movfw	rcvcnt			; get receive bit count
		btfsc	STATUS,Z		; skip & sum bits if past start pattern
		goto	x10clr			; make sure sum is cleared for start

		banksel pg1ram			; switch to BANK1
		movfw	sumcnt			; get number of "1" bits summed
		addlw	.248			; check if 8 bits have already been summed
		btfsc	STATUS,C		; skip and sum if not
		goto	x10smr			; sum only first 8 bits in transmission
		incf	sumcnt,F		; count summation

		clrf	BSR				; back to BANK0
		movfw	adcx10			; get present X10 signal level
		banksel	pg1ram			; switch to BANK1
		addwf	x10sumL,F		; add to X10 average LSB
		btfsc	STATUS,C		; skip if no carry
		incf	x10sumH,F		; propagate carry to MSB

		clrf	BSR				; back to BANK0
		movfw	frqcnt			; get frequency count
		banksel	pg1ram			; switch to BANK1
		addwf	frqcntL,F		; add to frequency count sum LSB
		btfsc	STATUS,C		; skip if no carry
		incf	frqcntH,F		; propagate carry to MSB

		movfw	sumcnt			; get number of "1" bits summed
		addlw	.248			; check if exactly 8 bits have been summed
		btfsc	STATUS,Z		; skip & return if not
		goto	x10smx			; transfer sum to X10 avg at 8th bit

x10smr	clrf	BSR				; back to BANK0
		return

x10smx	lsrf	x10sumH,F		; shift X10 signal sum right 3 bits to divide by 8
		rrf		x10sumL,F
		lsrf	x10sumH,F
		rrf		x10sumL,F
		lsrf	x10sumH,F
		rrf		x10sumL,W
		clrf	BSR				; back to BANK0
		movwf	x10avg			; save X10 average for this half of the doublet
		movwf	temp0			; save voltage for calculation
		movf	x10max,F		; check if X10 max is zero
		btfsc	STATUS,Z		; skip if not
		movwf	x10max			; set X10 max to X10 average
		bsf		dspflg,x10rng	; to indicate this sample was in high-range mode
		btfsc	rcvflg,hirang	; skip if X10 voltage was measured in low range
		goto	x10mx2			; no adjustment for high range
		bcf		dspflg,x10rng	; indicate this sample was in low-range mode
		lsrf	temp0,F			; divide reading by 4 for low range
		lsrf	temp0,F
		movfw	temp0			; get value adjusted for low range

x10mx2	subwf	x10cpr,W		; compare with previous value for this command
		movfw	temp0			; recall adjusted value
		btfsc	STATUS,C		; skip if new voltage => comparison value
		goto	x10mx3			; use prior reading as max value
		bcf		rcvflg,maxrng	; indicate this sample was in low-range mode
		btfsc	rcvflg,hirang	; skip if X10 voltage was measured in low range
		bsf		rcvflg,maxrng	; indicate this sample was in high-range mode
		movwf	x10cpr			; save new comparison value
		movfw	x10avg			; recall new X10 measurement
		movwf	x10max			; save new X10 maximum value

x10mx3	btfsc	xmtflg,rptchk	; skip if not doing a repeater check
		movwf	x10max			; use 2nd half value when doing a repeater check

		banksel	pg1ram			; switch to BANK1
		lsrf	frqcntH,F		; shift frequency count sum right 3 bits to divide by 8
		rrf		frqcntL,F
		lsrf	frqcntH,F
		rrf		frqcntL,F
		lsrf	frqcntH,F
		rrf		frqcntL,W
		clrf	sumcnt			; no bits have been summed
		clrf	BSR				; back to BANK0
		movwf	frqavg			; save average frequency for this command

x10clr
		banksel pg1ram			; make sure BANK1 is selected
		clrf	x10sumH			; clear X10 signal sum for next command
		clrf	x10sumL
		clrf	frqcntH			; clear frequency count sum for next command
		clrf	frqcntL
		clrf	sumcnt			; no bits have been summed
		clrf	BSR				; back to BANK0
		return


;		Noise Long-Term Average - 256 sample exponential filter

nselta
		banksel	pg1ram			; select BANK1 RAM
		movfw	nseltaH			; get noise level long-term average MSB
		subwf	nseltaL,F		; subtract from LSB
		btfss	STATUS,C		; skip if no borrow
		decf	nseltaH,F		; propagate borrow to MSB
		movfw	bkgnse			; get present background noise level
		addwf	nseltaL,F		; add to noise level long-term average LSB
		btfsc	STATUS,C		; skip if no carry
		incf	nseltaH,F		; propagate carry to MSB

;		Noise Short-Term Average - 16 sample exponential filter

nsesta
		banksel	pg1ram			; select BANK1 RAM
		swapf	nsestaL,W		; get upper nibble of noise short-term average into W LSBs
		andlw	b'00001111'		; mask upper nibble
		movwf	nseavg			; save in working register
		swapf	nsestaH,W		; get lower nibble of noise short-term average into W MSBs
		andlw	b'11110000'		; mask lower nibble
		iorwf	nseavg,F		; assemble present noise short-term average
		movfw	nseavg			; get present noise short-term average
		subwf	nsestaL,F		; subtract from LSB
		btfss	STATUS,C		; skip if no borrow
		decf	nsestaH,F		; propagate borrow to MSB
		movfw	bkgnse			; get present background noise level
		addwf	nsestaL,F		; add to noise level short-term average LSB
		btfsc	STATUS,C		; skip if no carry
		incf	nsestaH,F		; propagate carry to MSB
		clrf	BSR				; back to BANK0
		return

;		Peak Noise Averages

;		Peak Noise Long-Term Average - 256 sample exponential filter

pkavgs
		banksel	pg1ram			; select BANK1 RAM
		movfw	pknltaH			; get peak noise long-term average MSB
		subwf	pknltaL,F		; subtract from LSB
		btfss	STATUS,C		; skip if no borrow
		decf	pknltaH,F		; propagate borrow to MSB
		movfw	pkncyc			; get peak noise for this half cycle
		addwf	pknltaL,F		; add to peak noise long-term average LSB
		btfsc	STATUS,C		; skip if no carry
		incf	pknltaH,F		; propagate carry to MSB
		movfw	pknltaH			; recall long-term average MSB
		clrf	BSR				; back to BANK0
		movwf	pknlta			; save in BANK0

;		Peak Noise Short-Term Average - 16 sample exponential filter

;		Also maintains AGC detection threshold at 1.5 times the peak short-term average

		banksel	pg1ram			; select BANK1 RAM
		swapf	pknstaL,W		; get upper nibble of peak noise short-term average into W LSBs
		andlw	b'00001111'		; mask upper nibble
		movwf	pknois			; save in working register
		swapf	pknstaH,W		; get lower nibble of peak noise short-term average into W MSBs
		andlw	b'11110000'		; mask lower nibble
		iorwf	pknois,F		; assemble present peak noise short-term average
		movfw	pknois			; get present peak noise short-term average
		subwf	pknstaL,F		; subtract from LSB
		btfss	STATUS,C		; skip if no borrow
		decf	pknstaH,F		; propagate borrow to MSB
		movfw	pkncyc			; get peak noise for this half cycle
		addwf	pknstaL,F		; add to peak noise short-term average LSB
		btfsc	STATUS,C		; skip if no carry
		incf	pknstaH,F		; propagate carry to MSB

		movfw	pknois			; get present peak noise short-term average
		movwf	agcthd			; save at AGC threshold
		lsrf	agcthd,W		; calculate 1/2 long-term average
		btfsc	STATUS,Z		; skip if > 0
		movlw	.1				; make sure minimum threshold is at least 10mV
		addwf	agcthd,W		; add to value in AGC threshold to multiply by 1.5
		btfsc	pknois,7		; skip if noise short-term average < 127
		movlw	.192			; set maximum threshold to 192 (2 MSBs set)
		movwf	agcthd			; set AGC threshold for pre X10 sample window
		clrf	BSR				; back to BANK0
		movwf	x10thd			; also use for X10 & post window threshold
		btfss	rcvflg,hirang	; skip if high range
		return					; use same threshold for all windows
		lsrf	x10thd,F		; divide X10 & post window threshold by 4 when in high range
		lsrf	x10thd,F		
		return


	if			pro == .1		; if Pro version

;		Beginning of Transmit Window Noise Short-Term Average - 16 sample exponential filter

bgnsta
		banksel	pg1ram			; select BANK1 RAM
		swapf	bgnstaL,W		; get upper nibble of short-term average into W LSBs
		andlw	b'00001111'		; mask upper nibble
		movwf	temp0			; save in working register
		swapf	bgnstaH,W		; get lower nibble of short-term average into W MSBs
		andlw	b'11110000'		; mask lower nibble
		iorwf	temp0,F			; assemble present beginning of window noise short-term average
		movfw	temp0			; get present begining of window noise short-term average
		movwf	bgnnse			; save it
		subwf	bgnstaL,F		; subtract from LSB
		btfss	STATUS,C		; skip if no borrow
		decf	bgnstaH,F		; propagate borrow to MSB
		movfw	maxbgn			; get maximum A/D reading at beginning of transmit window
		addwf	bgnstaL,F		; add to beginning of window noise level short-term average LSB
		btfsc	STATUS,C		; skip if no carry
		incf	bgnstaH,F		; propagate carry to MSB
		clrf	BSR				; back to BANK0
		return

;		End of Transmit Window Noise Short-Term Average - 16 sample exponential filter

endsta

		banksel	pg1ram			; select BANK1 RAM
		swapf	endstaL,W		; get upper nibble of short-term average into W LSBs
		andlw	b'00001111'		; mask upper nibble
		movwf	temp0			; save in working register
		swapf	endstaH,W		; get lower nibble of short-term average into W MSBs
		andlw	b'11110000'		; mask lower nibble
		iorwf	temp0,F			; assemble present end of window noise short-term average
		movfw	temp0			; get present end of window noise short-term average
		movwf	endnse			; save it
		subwf	endstaL,F		; subtract from LSB
		btfss	STATUS,C		; skip if no borrow
		decf	endstaH,F		; propagate borrow to MSB
		movfw	maxend			; get maximum A/D reading at end of transmit window
		addwf	endstaL,F		; add to end of window noise level short-term average LSB
		btfsc	STATUS,C		; skip if no carry
		incf	endstaH,F		; propagate carry to MSB
		clrf	BSR				; back to BANK0
		return

	endif

;		Noise Cycle Count Short-Term average - 16 sample exponential filter

nctavg
		banksel	pg1ram			; select BANK1 RAM
		swapf	ncycntL,W		; get upper nibble of noise cycle count short-term avg LSB into W LSBs
		andlw	b'00001111'		; mask upper nibble
		movwf	ncycnt			; save in working register
		swapf	ncycntH,W		; get lower nibble of noise cycle count short-term avg MSB into W MSBs
		andlw	b'11110000'		; mask lower nibble
		iorwf	ncycnt,F		; assemble present noise cycle count short-term average
		movfw	ncycnt			; get present noise cycle count short-term average
		subwf	ncycntL,F		; subtract from LSB
		btfss	STATUS,C		; skip if no borrow
		decf	ncycntH,F		; propagate borrow to MSB
		movfw	ncyctn			; get noise cycle count for negative half cycle
		subwf	ncyctp,W		; compare with noise cycle count for positive half cycle
		movfw	ncyctn			; recall noise cycle count for negative half cycle
		btfsc	STATUS,C		; skip if higher than noise cycle count for positive half cycle
		movfw	ncyctp			; use noise cycle count for positive half cycle
		addwf	ncycntL,F		; add to noise cycle count short-term average LSB
		btfsc	STATUS,C		; skip if no carry
		incf	ncycntH,F		; propagate carry to MSB
		clrf	BSR				; back to BANK0
		return

;		Reset all filters

rstflt
		banksel	bgnnse			; select BANK1 for filter data
		clrf	bgnnse			; clear peak noise at beginning of X10 transmit window
		clrf	pknois			; clear peak noise in X10 reception window
		clrf	endnse			; clear peak noise at end of X10 transmit window
		clrf	nsestaH			; clear noise level short-term average MSB
		clrf	nsestaL
		clrf	nseltaH			; clear noise level long-term average MSB
		clrf	nseltaL
		clrf	pknstaH			; clear peak noise short-term average MSB
		clrf	pknstaL
		clrf	pknltaH			; clear peak noise long-term average MSB
		clrf	pknltaL

	if			pro == .1		; if Pro version
		clrf	bgnstaH			; clear beginning of transmit window noise short-term average MSB
		clrf	bgnstaL
		clrf	endstaH			; clear end of transmit window noise short-term average MSB
		clrf	endstaL
	endif

		clrf	ncycntH			; clear noise cycle count average MSB
		clrf	ncycntL
		clrf	BSR				; back to BANK0
		return

	if			pro == .1		; if Pro version

;		LOG DATA IN MEMORY

;		This subroutine saves certain X10 data in the logging memory.

;		Data is stored in logbuf comprised of 8 bytes per logged entry:

;		20 bits:	time stamp in days, hours, minutes, seconds (8 days max)
;		9 bits:		X10 voltage (8 bit value + range select)
;		15 bits:	housecode, unit code, command, 3 flags
;		16 bits:	2 bytes of optional extended data

;		(The logging history index is calculated from the offset in the table.)

;		logrbf[0-7]:  RNEDCxxx DDDHHHHH MMMMMMSS SSSSHHHH UUUUCCCC VVVVVVVV XXXXXXXX XXXXXXXX

;			R = range flag (1 = 9.99V, 0 = 2.55V)
;			N = missing unit code
;			E = extended command flag (1 for extended command)
;			D = extended command/data flag (1 for extended data)
;			C = valid command code
;			D = day from 0 to 7 (7 is overflow at 8th day)
;			H = hours (0-23)
;			M = minutes (0-59)
;			S = seconds (0-59)
;			H = house code
;			U = unit code
;			C = command code
;			V = 8 bit X10 level
;			X = extended data


logdat	btfsc	xmtflg,strtup	; continue if not during startup
		return					; no logging during startup
		btfsc	xmtflg,rptchk	; continue if not doing a repeater check
		return					; no logging of repeater check

		clrf	BSR				; make sure BANK0 is selected
		clrf	temp1			; clear workspace used to calculate buffer address
		incf	logwpt,F		; increment log write pointer
		movfw	logwpt			; get log buffer write pointer
		addlw	.156			; check for log buffer overflow @ 100
		btfsc	STATUS,C		; skip if no overflow
		clrf	logwpt			; reset write pointer to recycle overwrite oldest data
		movfw	logwpt			; get log buffer write pointer
		movwf	temp0			; save for multiply
		lslf	temp0,F			; multiply by 2
		lslf	temp0,F			; multiply by 2 again
		rlf		temp1,F			; propagate into MSB
		lslf	temp0,F			; multiply by 2
		rlf		temp1,F			; propagate into MSB
		movlw	.160			; to start at BANK2
		addwf	temp0,F			; add BANK2 offset to LS byte
		movlw	H'20'			; point to linear data memory
		addwfc	temp1,F			; add BANK2 offset to MS byte

		movfw	temp0			; get log buffer RAM address LSB
		movwf	FSR0L			; set indirect pointer 0 LSB
		movfw	temp1			; get log buffer RAM address MSB
		movwf	FSR0H			; set indirect pointer 0 MSB

;		Save flags in logging buffer

		clrf	INDF0			; start with all flags set to zero
		btfsc	rcvflg,maxrng	; skip if x10max was not sampled in high range mode
		bsf		INDF0,7			; set range flag for logging buffer
		swapf	rhunit,W		; get house code for last unit code into LSBs
		andlw	b'00001111'		; mask MSBs
		subwf	rhouse,W		; compare with house code for the command
		btfss	STATUS,Z		; skip if command uses the same house code 
		bsf		INDF0,6			; set missing unit code flag for logging buffer
		btfsc	cmdflg,xtdcmd	; skip if not an extended command
		bsf		INDF0,5			; set extended command flag
		btfsc	cmdflg,xtddat	; skip if not an extended data command
		bsf		INDF0,4			; set extended data command flag
		btfss	rcomnd,cmdbad	; skip if command code is invalid
		bsf		INDF0,3			; set valid command code flag
		incf	FSR0L,F			; increment logging buffer pointer LSB
		btfsc	STATUS,Z		; skip if no overflow
		incf	FSR0H,F			; increment logging buffer pointer MSB

;		Save time stamp & house code in logging buffer

		banksel	logsec			; select BANK1 for log time reference data
		movfw	logsec			; get log second count
		movwf	temp0			; save for shift
		movfw	logmin			; get log minute count
		movwf	temp1			; save for shift
		swapf	logday,W		; get log day count into W 4 MSBs
		movwf	temp2			; save for shift
		lslf	temp2,W			; shift day count to 3 MSBs
		andlw	b'11100000'		; make sure 5 LSBs are cleared
		movwf	temp2			; save again
		movfw	loghr			; get log hour count
		clrf	BSR				; back to BANK0

		andlw	b'00011111'		; make sure 3 MSBs are clear
		iorwf	temp2,W			; combine days & hours
		movwf	INDF0			; save log day & hour counts in logging buffer
		incf	FSR0L,F			; increment logging buffer pointer LSB
		btfsc	STATUS,Z		; skip if no overflow
		incf	FSR0H,F			; increment logging buffer pointer MSB	

		lslf	temp0,F			; left justify second count
		lslf	temp0,F
		lslf	temp0,F			; shift 2 MSB of second count into minute count LSBs
		rlf		temp1,F
		lslf	temp0,F
		rlf		temp1,W			; get minute/second counts
		movwf	INDF0			; save in logging buffer
		incf	FSR0L,F			; increment logging buffer pointer LSB
		btfsc	STATUS,Z		; skip if no overflow
		incf	FSR0H,F			; increment logging buffer pointer MSB	

		movfw	rhouse			; get house code
		andlw	b'00001111'		; make sure 4 MSBs are clear
		iorwf	temp0,W			; combine second count LSBs with house code
		movwf	INDF0			; save in logging buffer
		incf	FSR0L,F			; increment logging buffer pointer LSB
		btfsc	STATUS,Z		; skip if no overflow
		incf	FSR0H,F			; increment logging buffer pointer MSB

;		Save unit code and command code in logging buffer

		movfw	rcomnd			; get received command code
		andlw	b'00001111'		; mask any flag bits
		movwf	temp0			; save to combine with unit code
		swapf	rhunit,W		; get unit code into W MBSs
		andlw	b'11110000'		; make sure unit code LSBs are clear
		iorwf	temp0,W			; combine unit code with command code
		movwf	INDF0			; save in logging buffer
		incf	FSR0L,F			; increment logging buffer pointer LSB
		btfsc	STATUS,Z		; skip if no overflow
		incf	FSR0H,F			; increment logging buffer pointer MSB

;		Save X10 voltage in logging buffer

		movfw	x10max			; get X10 maximum voltage for this command
		movwf	INDF0			; save X10 voltage in logging buffer
		incf	FSR0L,F			; increment logging buffer pointer LSB
		btfsc	STATUS,Z		; skip if no overflow
		incf	FSR0H,F			; increment logging buffer pointer MSB

;		Save optional extended data
		
		movfw	rxdat1			; get first extended data word
		btfss	cmdflg,xtdcmd	; skip if an extended command
		clrw					; clear extended bits
		movwf	INDF0			; save in command buffer
		incf	FSR0L,F			; increment logging buffer pointer LSB
		btfsc	STATUS,Z		; skip if no overflow
		incf	FSR1L,F			; increment logging buffer pointer MSB

		movfw	rxdat2			; get second extended data word
		btfss	cmdflg,xtdcmd	; skip if an extended command
		clrw					; clear extended bits
		movwf	INDF0			; save optional extended bits in command buffer

		movfw	logcnt			; get number of records in the logging buffer
		addlw	.156			; check if reached limit of 100 records (0-99)
		btfss	STATUS,C		; skip if so
		incf	logcnt,F		; increment log count

		movfw	modflg			; get mode status
		andlw	b'00000111'		; mask all but the mode select bits
		addlw	.251			; check if logging mode (5+251 -> 256)
		btfss	STATUS,Z		; skip & adjust mode pointer if so
		return

		movf	modptr,F		; check if mode pointer is 0
		btfsc	STATUS,Z		; skip and increment pointer if not
		return					; stay at most recent command
		
		incf	modptr,F		; increment mode pointer
		goto	chklog			; check for end of buffer


;		READ DATA LOG

;		This routine transfers one record from the log file to the log read buffer

readlg	clrf	BSR				; make sure BANK0 is selected
		clrf	temp1			; clear workspace used to calculate buffer address
		movfw	logrpt			; get log buffer read pointer
		addlw	.156			; check for log buffer overflow @ 100
		btfsc	STATUS,C		; skip if no overflow
		clrf	logrpt			; reset read pointer to recycle
		movfw	logrpt			; get log buffer read pointer
		movwf	temp0			; save for multiply
		lslf	temp0,F			; multiply by 2
		lslf	temp0,F			; multiply by 2 again
		rlf		temp1,F			; propagate into MSB
		lslf	temp0,F			; multiply by 2
		rlf		temp1,F			; propagate into MSB
		movlw	.160			; to start at BANK2
		addwf	temp0,F			; add BANK2 offset to LS byte
		movlw	H'20'			; point to linear data memory
		addwfc	temp1,F			; add BANK2 offset to MS byte
		
		movfw	temp0			; get log buffer RAM address LSB
		movwf	FSR0L			; set indirect pointer 0 LSB
		movfw	temp1			; get log buffer RAM address MSB
		movwf	FSR0H			; set indirect pointer 0 MSB
		movlw	logrbf			; get log read buffer RAM address LSB
		movwf	FSR1L			; set indirect pointer 1 LSB
		clrf	FSR1H			; set indirect pointer 1 MSB to BANK0

		movlw	.8				; 8 bytes to transfer
		movwf	temp0			; save loop count
rdlglp	movfw	INDF0			; read data from log buffer
		movwf	INDF1			; save in read buffer
		incf	FSR0L,F			; increment logging buffer pointer LSB
		btfsc	STATUS,Z		; skip if no overflow
		incf	FSR0H,F			; increment logging buffer pointer MSB
		incf	FSR1L,F			; increment read buffer pointer
		decfsz	temp0,F			; tick off one cycle & skip it done
		goto	rdlglp			; loop if not
		return

	endif


;		COMPRESS READING

;		This subroutine compresses a 0-255 variable passed in W to a 0-31 pseudo log variable

comprs	movwf	temp0			; save original variable for calculation
		andlw	b'11000000'		; check if 64 or higher
		btfss	STATUS,Z		; skip if < 64
		goto	cmpr64			; compress value 64-255
		movfw	temp0			; recall the original value
		btfsc	temp0,5			; skip if value < 32
		goto	cmpr32			; compress value 32-63
		btfsc	temp0,4			; skip if value < 16
		goto	cmpr16			; compress value 16-31
		btfss	temp0,3			; skip & compress value 8-15
		return					; return original value if 0-7

		lsrf	temp0,W			; convert 8-15 to 4-7
		addlw	.4				; add offset to compress 8-15 to 8-11
		return

cmpr16	lsrf	temp0,F			; convert 16-31 to 4-7
		lsrf	temp0,W
		addlw	.8				; add offset to compress 16-31 to 12-15
		return

cmpr32	lsrf	temp0,F			; convert 32-63 to 4-7
		lsrf	temp0,F
		lsrf	temp0,W
		addlw	.12				; add offset to compress 32-63 to 16-19
		return

cmpr64	swapf	temp0,W			; convert 64-255 to 4-15
		andlw	b'00001111'		; mask upper nibble
		addlw	.16				; add offset to compress 64-255 to 20-31
		return

;		***************************************************************

	if			pro == .1		; if Pro version

;		ROM PAGE 2

		org		0x1000			; Beginning of ROM Page 2

	endif

;		INTERRUPT PROCESSING

;		Identify the source of the interrupt

intrpt	clrf	BSR				; also make sure BANK0 is selected

	bsf	PORTC,3

		btfsc	PIR1,TMR1IF		; skip if not a Timer 1 overflow
		goto	timint			; handle timer interrupt if so
		btfss	INTCON,IOCIF	; skip if it was a zero crossing interrupt
		goto	intrtn			; just return for unidentified interrupt

;		Calculate line frequency period by adding each half cycle

		banksel	IOCAF			; select BANK7 for interrupt on change
		bcf		IOCAF,IOCAF3	; clear interrupt flag for PCB version 1.2
		clrf	BSR				; back to BANK0
		bcf		INTCON,IOCIE	; disable ZCD interrupt on change
		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
		movlw	.231			; to subtract 25 from MSB
		addwf	periodH,F		; adjust MSB so no overflow at 50Hz (183.8 or 235.4)
		movfw	TMR1H			; get Timer 1 MSB
		addwf	periodH,F		; add to last half cycle MSB
		movlw	.239			; preset Timer 1 to -1.1 mS (at 15.84MHz clk)
		movwf	TMR1H			; set high byte
		clrw					; use the full LSB count for negative zero crossing
		btfsc	PORTA,ZCDIN		; skip if falling edge of AC input
		movlw	.160			; need less delay for positive zero crossing
		movwf	TMR1L			; set low byte
		bsf		T1CON,TMR1ON	; restart Timer 1
		clrwdt					; clear watchdog timer

;		Decrement 1/2 second display update timer

;	btfsc	xmtflg,strtup	; skip if startup interval is over
;	goto	chkpol			; no display update or xmit until startup is over

		decfsz	dspcnt,F		; tick off one cycle & skip if zero
		goto	xmtnxt			; check transmitter status

		movlw	.63				; reload count for next 1/2 second
		movwf	dspcnt
		bcf		dspflg,dsphld	; clear display hold flag
		bcf		xmtflg,rptchk	; make sure repeater check flag is cleared

		btfsc	xmtflg,xhinse	; skip if a high noise warning has not been issued
		bcf		rcvflg,x10vld	; clear valid after display interval if high noise

		movf	bsccnt,F		; check if bad start code is already zero
		btfss	STATUS,Z		; skip if already zero
		decf	bsccnt,F		; decrement bad start code count

;		Turn on transmitter as required

xmtnxt	btfss	xmtflg,xmtmsg	; skip if transmitting a message
		goto	chkpol			; skip to next section not
		btfsc	xmtflg,outbit	; skip transmit if output bit is a "0"
		call	xmton			; turn on transmitter

;		Determine which AC input zero crossing just passed

chkpol	bcf		stsflg,cycpol	; clear cycle polarity for negative half cycle
		btfss	PORTA,ZCDIN		; skip if rising edge of AC input
		goto	logtim			; increment log time if falling edge
		bsf		stsflg,cycpol	; set cycle polarity for positive half cycle

;		Using line frequency as a reference, adjust clock to 8.16MHz on rising edge

adjclk	btfsc	periodL,7		; round up MSB if LSB > 127
		incf	periodH,F		; MSB = 173.8 (60Hz) at 15.84MHz (with 239 preset)
		movfw	periodH			; get period MSB
		clrf	periodL			; clear period measurement
		clrf	periodH
		if		ac50hz == 0		; if not for 50Hz AC powerline
		  addlw	.82				; calculate error at 60Hz: W = 0 at 15.84MHz
		else					; must be for 50Hz
		  addlw	.31				; calculate error at 50Hz: W = 0 at 15.84MHz
		endif
		btfsc	STATUS,Z		; skip if clock needs adjusting
		goto	intrtn			; done 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			; done if not
		movfw	nxttune			; recall oscillator tune setting
		btfss	STATUS,Z		; no adjust if already at minimum frequency (0)
		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			; done if not
		incf	nxttune,F		; increase frequency (write at next T1 interrupt)
		movfw	nxttune			; recall oscillator tune setting
		addlw	.192			; check if beyond max (64 + 192 = 256)
		movlw	.63				; for maximum = 63
		btfsc	STATUS,C		; skip if within range (0 - 63)
		movwf	nxttune			; set to maximum if beyond maximum
rsterr	movlw	b'00100000'		; preset frequency error to mid-range
		movwf	frqerr

;		Process timer interrupt 1mS after zero crossing

timint	bcf		PIR1,TMR1IF		; clear Timer 1 interrupt flag

;		Update oscillator tune 1mS after zero crossing

oscadj	movfw	nxttune			; recall next oscillator tune setting
		xorlw	b'00100000'		; convert to offset binary for OSCTUNE
		banksel	OSCTUNE			; select BANK1
		movwf	OSCTUNE			; set next frequency
		goto	intrtn			; done

;		Increment log time reference once a second

logtim

	if			pro == .1		; if Pro version

		banksel	onesec			; switch to BANK1 for log time counts
		incf	onesec,F		; increment log second count
		if		ac50hz == 0		; if not for 50Hz AC powerline
		  movlw	.196			; to check for one second overflow (206+60=256)
		else					; must be for 50Hz
		  movlw	.206			; to check for one second overflow (196+50=256)
		endif
		addwf	onesec,W		; check if one second (C=1 for one second)
		btfsc	STATUS,C		; skip if < one second
		clrf	onesec			; clear one second count
		clrw					; to propagate one second overflow into second count
		addwfc	logsec,F		; increment second count at each one-second overflow
		movlw	.196			; to check for 60 second overflow (196+60=256)
		addwf	logsec,W		; check if 60 seconds (C=1 for 60 seconds)
		btfsc	STATUS,C		; skip if < 60 seconds
		clrf	logsec			; clear log second count
		clrw					; to propagate 60 second carry into minutes
		addwfc	logmin,F		; increment minute count at each 60 second overflow
		movlw	.196			; to check for 60 minute overflow (196+60=256)
		addwf	logmin,W		; check if 60 minutes (C=1 for 60 minutes)
		btfsc	STATUS,C		; skip if < 60 minutes
		clrf	logmin			; clear log minute count
		clrw					; to propagate 60 minute carry into hours
		addwfc	loghr,F			; increment hour count at each 60 minute overflow
		movfw	loghr			; get log hour count
		addlw	.232			; check if 24 hours (232+24=256)
		btfss	STATUS,C		; skip if 24 hour overflow
		goto	intrtn			; done
		clrf	loghr			; clear log hour count
		movfw	logday			; get log day count
		addlw	.249			; check if already at limit (249+7=256)
		btfss	STATUS,C		; skip if so
		incf	logday,F		; increment day

	endif

;		Return from interrupt

intrtn	clrf	BSR				; make sure BANK0 is selected

	bcf	PORTC,3

;		return					; return from interrupt routine

		retfie					; return with interrupts enabled


;		SUBROUTINES TO CONTROL 120KHZ PWM TRANSMITTER

;		Turn on 120KHz PWM transmitter

xmton
		banksel	CCP1CON			; select BANK5 for PWM control
		btfsc	CCP1CON,STR1C	; skip if PWM transmitter is not on yet
		goto	xmton1			; just return if PWM is already on

		movlw	.6				; for 3.12uS pulsewidth
		movwf	CCPR1L			; set PWM duty cycle
		movlw	b'00001100'		; set PWM active high mode with single output
		movwf	CCP1CON			; set PWM mode
		clrf	PWM1CON			; no PWM delay between phases
		clrf	CCP1AS			; no PWM auto-shutdown
		movlw	b'00010001'		; to enable PWM P1A output at next PWM period
		movwf	PSTR1CON		; enable 120KHz PWM
xmton1	clrf	BSR				; back to BANK0
		bsf		rcvflg,hirang	; make sure high range is set when transmitting (9.9V max)
		return

;		Turn off 120KHz PWM transmitter

xmtoff
		banksel	CCP1CON			; select BANK5 for PWM control registers
		clrf	CCP1CON			; turn off PWM
		clrf	BSR				; back to BANK0
		bcf		PORTC,PWMOUT	; make sure PWM output is a "0"
		return

		
;		Text Strings

	if			pro == .1		; if Pro version
		org		0x1500			; message text strings
	else
		org		0x0E00			; message text strings
	endif


strngs	brw
strint	retlw	"X"				; "X-.-- F -- " initial setup string
		retlw	"-"
		retlw	"."
		retlw	"-"
		retlw	"-"
		retlw	" "
strfno	retlw	"F"				; "F --  " for no frequency to display
		retlw	" "
		retlw	"-"
		retlw	"-"
		retlw	" "
		retlw	" "+H'80'
strxno	retlw	"X"				; "X-.-- "
		retlw	"-"
		retlw	"."
		retlw	"-"
		retlw	"-"
		retlw	" "+H'80'
strmon	retlw	"M"
		retlw	"O"
		retlw	"N"
		retlw	"I"
		retlw	"T"
		retlw	"O"
		retlw	"R"+H'80'
strpri	retlw	"*"
		retlw	"P"
		retlw	"R"
		retlw	"I"
		retlw	"O"
		retlw	"R"
		retlw	"*"+H'80'
strvld	retlw	"-"
		retlw	"V"
		retlw	"A"
		retlw	"L"
		retlw	"I"
		retlw	"D"
		retlw	"-"+H'80'
strrcv	retlw	"E"
		retlw	"R"
		retlw	"R"
		retlw	" "
		retlw	"R"
		retlw	"C"
		retlw	"V"+H'80'
strins	retlw	"I"
		retlw	"N"
		retlw	"S"
		retlw	"T"
		retlw	"E"
		retlw	"O"
		retlw	"N"+H'80'
strbsc	retlw	"E"
		retlw	"R"
		retlw	"R"
		retlw	" "
		retlw	"B"
		retlw	"S"
		retlw	"C"+H'80'
strcol	retlw	"E"
		retlw	"R"
		retlw	"R"
		retlw	" "
		retlw	"C"
		retlw	"O"
		retlw	"L"+H'80'
strnhi	retlw	"^"
		retlw	"N"
		retlw	"O"
		retlw	"I"
		retlw	"S"
		retlw	"E"
		retlw	"^"+H'80'
strxmt	retlw	" "
		retlw	"-"
		retlw	"C"
		retlw	"H"
		retlw	"E"
		retlw	"C"
		retlw	"K"
		retlw	"-"+H'80'
strrst	retlw	"!"
		retlw	"C"
		retlw	"O"
		retlw	"M"
		retlw	"P"
		retlw	"L"
		retlw	"E"
		retlw	"T"
		retlw	"E"
		retlw	" "
		retlw	"R"
		retlw	"E"
		retlw	"S"
		retlw	"E"
		retlw	"T"
		retlw	"!"+H'80'
strnal	retlw	" "			; NOISE ALERT (13)
		retlw	"N"
		retlw	"O"
		retlw	"I"
		retlw	"S"
		retlw	"E"
		retlw	" "
		retlw	"A"
		retlw	"L"
		retlw	"E"
		retlw	"R"
		retlw	"T"
		retlw	" "+H'80'
strsts	retlw	"S"			; STATUS LEVEL (13)
		retlw	"T"
		retlw	"A"
		retlw	"T"
		retlw	"U"
		retlw	"S"
		retlw	" "
		retlw	"L"
		retlw	"E"
		retlw	"V"
		retlw	"E"
		retlw	"L"
		retlw	" "+H'80'
straon	retlw	"O"
		retlw	"N"
		retlw	" "+H'80'
straof	retlw	"O"
		retlw	"F"
		retlw	"F"+H'80'
strnup	retlw	"H"
		retlw	"I"
		retlw	"G"
		retlw	"H"
		retlw	" "
		retlw	"N"
		retlw	"O"
		retlw	"I"
		retlw	"S"
		retlw	"E"
		retlw	" "
		retlw	"L"
		retlw	"E"
		retlw	"V"
		retlw	"E"
		retlw	"L"+H'80'
strndn	retlw	" "
		retlw	"N"
		retlw	"O"
		retlw	"I"
		retlw	"S"
		retlw	"E"
		retlw	" "
		retlw	"D"
		retlw	"E"
		retlw	"C"
		retlw	"R"
		retlw	"E"
		retlw	"A"
		retlw	"S"
		retlw	"E"
		retlw	" "+H'80'
strtst	retlw	" "
		retlw	"F"
		retlw	"A"
		retlw	"I"
		retlw	"L"
		retlw	" "
strslf	retlw	"S"
		retlw	"E"
		retlw	"L"
		retlw	"F"
		retlw	"-"
		retlw	"T"
		retlw	"E"
		retlw	"S"
		retlw	"T"
		retlw	" "+H'80'
strpas	retlw	"P"
		retlw	"A"
		retlw	"S"
		retlw	"S"
		retlw	"E"
		retlw	"D"+H'80'
strlow	retlw	"L"
		retlw	"O"
		retlw	"W"
		retlw	" "
		retlw	"O"
		retlw	"U"
		retlw	"T"
		retlw	"P"
		retlw	"U"
		retlw	"T"
		retlw	" "
		retlw	"L"
		retlw	"E"
		retlw	"V"
		retlw	"E"
		retlw	"L"+H'80'
strrbk	retlw	"V"
		retlw	" "
		retlw	"R"
		retlw	"E"
		retlw	"A"
		retlw	"D"
		retlw	"B"
		retlw	"A"
		retlw	"C"
		retlw	"K"
		retlw	" "+H'80'
strerr	retlw	"!"
		retlw	"E"
		retlw	"R"
		retlw	"R"
		retlw	"O"
		retlw	"R"
		retlw	"!"+H'80'
strovr	retlw	"o"
		retlw	"v"
		retlw	"e"
		retlw	"r"+H'80'
strexn	retlw	"["
		retlw	"#"
		retlw	"["
		retlw	"N"
		retlw	"O"
		retlw	"I"
		retlw	"S"
		retlw	"E"
		retlw	"]"
		retlw	"#"
		retlw	"]"
		retlw	" "
strfrq	retlw	"F"
		retlw	"R"
		retlw	"E"
		retlw	"Q"+H'80'
strnrd	retlw	"R"
		retlw	"N"
		retlw	"D"
		retlw	"M"+H'80'
strntr	retlw	"T"
		retlw	"R"
		retlw	"A"
		retlw	"N"+H'80'
strnty	retlw	"T"
		retlw	"Y"
		retlw	"P"
		retlw	"E"+H'80'
strnon	retlw	" "
		retlw	"-"
		retlw	"-"
		retlw	" "+H'80'

	if			pro == .1		; if Pro version
		org		0x1600			; command decode text strings
	else
		org		0x0F00
	endif

strdcd	brw
		retlw	"A"				; 0000 -> AOFF (ALL Units OFF)
		retlw	"O"
		retlw	"F"
		retlw	"F"+H'80'
		retlw	"A"				; 0001 -> ALON (ALL Lights ON)
		retlw	"L"
		retlw	"O"
		retlw	"N"+H'80'
		retlw	"O"				; 0010 -> ON (ON command)
		retlw	"N"
		retlw	" "
		retlw	" "+H'80'
		retlw	"O"				; 0011 -> OFF (OFF command)
		retlw	"F"
		retlw	"F"
		retlw	" "+H'80'
		retlw	"D"				; 0100 -> DIM (DIM command)
		retlw	"I"
		retlw	"M"
		retlw	" "+H'80'
		retlw	"B"				; 0101 -> BRT (BRIGHT command)
		retlw	"R"
		retlw	"T"
		retlw	" "+H'80'
		retlw	"L"				; 0110 -> LOFF (ALL Lights OFF)
		retlw	"O"
		retlw	"F"
		retlw	"F"+H'80'
		retlw	"X"				; 0111 -> Xcod (Extended code)
		retlw	"c"
		retlw	"o"
		retlw	"d"+H'80'
		retlw	"H"				; 1000 -> HReq (Hail Request)
		retlw	"R"
		retlw	"e"
		retlw	"q"+H'80'
		retlw	"H"				; 1001 -> HAck (Hail Acknowledge)
		retlw	"A"
		retlw	"c"
		retlw	"k"+H'80'
		retlw	"P"				; 1010 -> PDim (Preset Dim)
		retlw	"D"
		retlw	"i"
		retlw	"m"+H'80'
		retlw	"P"				; 1011 -> PDim (Preset Dim)
		retlw	"D"
		retlw	"i"
		retlw	"m"+H'80'
		retlw	"X"				; 1100 -> Xdat (Extended Data)
		retlw	"d"
		retlw	"a"
		retlw	"t"+H'80'
		retlw	"S"				; 1101 -> StON (Status ON)
		retlw	"t"
		retlw	"O"
		retlw	"N"+H'80'
		retlw	"S"				; 1110 -> StOF (Status OFF)
		retlw	"t"
		retlw	"O"
		retlw	"F"+H'80'
		retlw	"S"				; 1111 -> StRq (Status Request)
		retlw	"t"
		retlw	"R"
		retlw	"q"+H'80'
strrpt	retlw	"R"
		retlw	"P"
		retlw	"T"
		retlw	" "
		retlw	"C"
		retlw	"H"
		retlw	"K"
		retlw	" "+H'80'

	if			pro == .1		; if Pro version

strsw0	retlw	"<"
		retlw	"M"
		retlw	"O"
		retlw	"D"
		retlw	"E"
		retlw	" "
		retlw	" "
		retlw	" "
		retlw	"R"
		retlw	"P"
		retlw	"T"
		retlw	" "
		retlw	"C"
		retlw	"H"
		retlw	"K"
		retlw	">"+H'80'
strsw1	retlw	"<"
		retlw	"D"
		retlw	"N"
		retlw	" "
		retlw	" "
		retlw	" "
		retlw	"M"
		retlw	"O"
		retlw	"D"
		retlw	"E"
		retlw	" "
		retlw	" "
		retlw	" "
		retlw	"U"
		retlw	"P"
		retlw	">"+H'80'
strsw4	retlw	"<"
		retlw	"R"
		retlw	"E"
		retlw	"V"
		retlw	" "
		retlw	"^"
		retlw	"B"
		retlw	"O"
		retlw	"T"
		retlw	"H"
		retlw	"^"
		retlw	" "
		retlw	"F"
		retlw	"W"
		retlw	"D"
		retlw	">"+H'80'
strmd0	retlw	"X"
		retlw	"1"
		retlw	"0"
		retlw	" "
		retlw	"L"
		retlw	"I"
		retlw	"N"
		retlw	"E"
		retlw	" "
		retlw	"M"
		retlw	"O"
		retlw	"N"
		retlw	"I"
		retlw	"T"
		retlw	"O"
		retlw	"R"+H'80'
strmd1	retlw	" "
		retlw	"P"
		retlw	"R"
		retlw	"I"
		retlw	"O"
		retlw	"R"
		retlw	" "
		retlw	"S"
		retlw	"E"
		retlw	"S"
		retlw	"S"
		retlw	"I"
		retlw	"O"
		retlw	"N"
		retlw	" "
		retlw	" "+H'80'
strmd2	retlw	"P"
		retlw	"R"
		retlw	"E"
		retlw	" "
		retlw	"&"
		retlw	" "
		retlw	"P"
		retlw	"O"
		retlw	"S"
		retlw	"T"
		retlw	" "
		retlw	"N"
		retlw	"O"
		retlw	"I"
		retlw	"S"
		retlw	"E"+H'80'
strmd3	retlw	" "
		retlw	" "
		retlw	" "
		retlw	" "
		retlw	"B"
		retlw	"A"
		retlw	"R"
		retlw	"G"
		retlw	"R"
		retlw	"A"
		retlw	"P"
		retlw	"H"
		retlw	" "
		retlw	" "
		retlw	" "
		retlw	" "+H'80'
strmd4	retlw	" "
		retlw	" "
		retlw	" "
		retlw	" "
		retlw	" "
		retlw	"B"
		retlw	"I"
		retlw	"T"
		retlw	"M"
		retlw	"A"
		retlw	"P"
		retlw	" "
		retlw	" "
		retlw	" "
		retlw	" "
		retlw	" "+H'80'
strmd5	retlw	" "
		retlw	" "
		retlw	"C"
		retlw	"O"
		retlw	"M"
		retlw	"M"
		retlw	"A"
		retlw	"N"
		retlw	"D"
		retlw	" "
		retlw	"L"
		retlw	"O"
		retlw	"G"
		retlw	" "
		retlw	" "
		retlw	" "+H'80'

	endif

stralr	retlw	"A"
		retlw	"L"
		retlw	"R"
		retlw	"T"
		retlw	" "+H'80'
strstm	retlw	"S"
		retlw	"T"
		retlw	"S"
		retlw	" "+H'80'

	if			pro == .1		; if Pro version
		org		0x1700
	else
		org		0x0FA0
	endif

;		CUSTOM CHARACTERS

;		Bit patterns for custom characters

cuschr	brw
		retlw	h'00'			; "00"
		retlw	h'1F'
		retlw	h'15'
		retlw	h'15'
		retlw	h'15'
		retlw	h'1F'
		retlw	h'00'
		retlw	h'00'
		retlw	h'00'			; "01"
		retlw	h'1D'
		retlw	h'15'
		retlw	h'15'
		retlw	h'15'
		retlw	h'1D'
		retlw	h'00'
		retlw	h'00'
		retlw	h'00'			; "10"
		retlw	h'17'
		retlw	h'15'
		retlw	h'15'
		retlw	h'15'
		retlw	h'17'
		retlw	h'00'
		retlw	h'00'
		retlw	h'00'			; "11"
		retlw	h'0A'
		retlw	h'0A'
		retlw	h'0A'
		retlw	h'0A'
		retlw	h'0A'
		retlw	h'00'
		retlw	h'00'
		retlw	h'10'			; |
		retlw	h'10'
		retlw	h'10'
		retlw	h'10'
		retlw	h'10'
		retlw	h'10'
		retlw	h'10'
		retlw	h'10'
		retlw	h'18'			; ||
		retlw	h'18'
		retlw	h'18'
		retlw	h'18'
		retlw	h'18'
		retlw	h'18'
		retlw	h'18'
		retlw	h'18'
		retlw	h'1C'			; |||
		retlw	h'1C'
		retlw	h'1C'
		retlw	h'1C'
		retlw	h'1C'
		retlw	h'1C'
		retlw	h'1C'
		retlw	h'1C'
		retlw	h'1E'			; ||||
		retlw	h'1E'
		retlw	h'1E'
		retlw	h'1E'
		retlw	h'1E'
		retlw	h'1E'
		retlw	h'1E'
		retlw	h'1E'
		retlw	h'80'


;		Initialize EEPROM locations

EE		CODE	0xF000			; (location found by trial and error)

	if			pro == .1		; if Pro version
		DE		"XTBM-PRO V1.09"; program name and version
	else
		DE		"  XTBM V2.03  "
	endif

		if		ac50hz == .0	; if not for 50Hz AC powerline
		  DE	"  "			; standard 60Hz version
		else					; must be for 50Hz
		  DE	"E "			; 50Hz version
		endif

		CODE	0xF010			; EEPROM storage for configuration & archive
		DE		.0
		DE		.0
		DE		.0
		DE		.0
		DE		.0
		DE		.0
		DE		.0
		DE		.0
		DE		.0
		DE		.0
		DE		.0
		DE		.0
		DE		.0
		DE		.0
		DE		.0
		DE		.0

		END						; directive 'end of program'


