;*******************************************************************
;
;	IR2RS232.ASM
;	IR to RS232 to IR Program
;	Version 1.0 (10/17/05) (Checksum ----)
;	(C) 2005 Reymes
;	Programmer: Jim Furey
;
;*******************************************************************
;
; Purpose:
;
; This program uses a PIC16F627A for RS232 communication, IR receive and IR transmit.  
; Through a switch, it can be set for learn or operate to communicate with Infared signals 
; from various modulated and non-modulated remote controls. Once it detects these signals, 
; it stores it in EEPROM and asks the operator for a control character in ASCII to re-transmit 
; the signal.
;
; Description:
;
; There are two modes of operation. One is "Learn" and the other is "Operate". This is 
; changed by switch SW1.
;
; The description of "Learn" is as follows:
;
; Using the PORTB interrupt, the program waits for a "dark space" (low signal) out of the IR 
; detector for more than 2mS. (There are many short pulses brought about by ambient noise 
; before solid remote control IR.)
; This is the pre-amble (2 to 3 mS low). Each following "mark" high pulse (inverted condition 
; out of IR detector) is followed by either a short (700uS) or long (1300us) low. A short low 
; means "0" and long low means "1". This method of IR transmission/reception is called 
; "space width coded" IR.
; Timer 1 determines the 0 or 1 space length by its width and saves one bit per byte into 
; the RAM bytes "IRDATA". The count of how many bits is saved in "CLENGTH". The maximum 
; bits that can be saved is 32. Most IR commands are between 12 and 22.
; When the IR detector outut goes high for more than 2mS, the transmission is over. 
; The message "Ready to save, pick a character: " is then transmitted over the RS232 port.
; The unit waits for an ASCII character (see list of usable ASCII characters) and then searches the 
; EEPROM for a match. If this character is already used, it outputs "Already used. Overwrite?"
; A "Y" will overwrite the previous bitstream saved for that character. A "N" will abandon the write.
; If that character is not already used, or "Y" was selected, the program searches for an unused 
; block in the EEPROM to store the data in. If all blocks are used, it will output "No available 
; space" and then end without storing.
; If a block is found, it then writes the data along with it's associated ASCII character in EEPROM 
; and outputs the message "Ready".
;
;The description if "Operate" is as follows:
;
; When the SW1 switch is in the "Operate" mode, the unit will accept RS232 commands (at 9600 baud) 
; and output associated IR bitstreams. When first powered up in the Operate mode, and also after 
; every ASCII character in sent, the unit outputs "Ready" to the RS232 port.
; When each character is entered, (the return key is not needed) the unit goes to a look up table 
; created by the user in "Learn" mode, and finds the associated match in ASCII.
; If no match is found, then the message "Error" is output to the RS232 port.
; If a match is found, then using the timer 1, it outputs a preamble 2.5mS low and then a 500uS 
; pulse followed by each bit saved. This "pulse-bit-pulse-bit" is continued until all bits are sent. Then 
; the last pulse stays high. This completes the IR transmission, and a "Ready" is sent again.
;
; ASCII table:
;
; 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 
;  A   B  C   D  E   F  G   H   I   J   K  L   M  N  O   P  Q  R   S  T  U   V  W X   Y   Z
;
; 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 
;  a   b  c   d  e   f   g   h   i    j   k   l   m  n   o   p  q   r   s   t   u  v   w  x   y   z
;
; Notes:
;	1. For use with modulated or non-modulated, space width coded remotes with less than 
;	    31 characters per instruction. If there are more than 32 bits, then only the first 32 
;	    are captured. (Most are 12-22 bits)
;	2. RS232 operates at 9600 baud, 8 bits, 1 stop bit, no parity, ANSI emulation only
;
;*******************************************************************
;
; Revisions:
;	1.0 :	Original program
;
;*******************************************************************
; Notes:
; Things left to do:
; 1. DONE
; 2. Do bits to real time IR out
; 3. Do bitstream output to IR
; 4. Logic for store, overwrite, delete of commands:
;     a. Check for match, offer overwrite
;     b. If full, show all used characters
;     c. Entering characters while in learn mode deletes command
;     d. 
;
;
	list	p=16f627a		; list directive to define processor
	#include <p16f627a.inc>		; processor specific variable definitions
	__CONFIG   _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_OFF &_EXTCLK_OSC &_LVP_OFF

	errorlevel  -302              ; suppress message 302 from list file

	;***** VARIABLE DEFINITIONS *****
	; RAM has 3 banks of usable space: | 0x20 to 0x7F (96 bytes)| 0xA0 to 0xEF (80 bytes)| 0x120 to 0x14F (48 bytes)|

IRDATA		equ	0x20	; 32 bytes of data (from 0x20 to 0x3F) each representing a bit of IR
irsave		equ	0x40	; Saved 32 bytes (from 0x40 to 0x5F) of IRDATA for storage to E2
AMTCH		equ	0x60	; ASCII charater to match to from incoming RS232 command
asave		equ	0x61	; Saved ASCII matched character
CLENGTH	equ	0x62	; Length of bits to send on outgoing comand
lsave		equ	0x63	; Saved bit length count
E2A		equ	0x64	; Byte for EEPROM address
E2D		equ	0x65	; 6 bytes of data for E2 data to/from EEPROM (65-6A)
dcnth		equ	0x6B	; Delay counter high
dcntl		equ	0x6C	; Delay counter low
pointer		equ	0x6D	; ASCII Message pointer
counter		equ	0x6E	; ASCII byte counter
wtemp		equ	0x6F	; Temporary storage of W register during interrupt
stemp		equ	0x70	; Temporary storage of Status register during interrupt
stat1		equ	0x71	; Status byte for flags
irctrl		equ	0x72	; Counter for input capture and output strobe of IR
irctrh		equ	0x73	; Counter for input capture and output strobe of IR
ectr		equ	0x74	; 8 bit counter for E2 bit transfer
e2bctr		equ	0x75	; Byte contains the number f bytes to transfer in/out of E2
ircnt		equ	0x75	; IR output counter
xctr		equ	0x77	; Transfer counter
tempb		equ	0x78	; Temp byte for black transfer
RCV_REG	equ	0x79	; Receive register
irlength		equ	0x7A
irpoint		equ	0x7B	; Pointer for irplay's output


			; ************** stat1 explanation: ***************
			; 7	1 = Enable IR out condition				stat1,7
			; 6	1 = If 1 being sent to IR LED (for 40kHz)			stat1,6
			; 5	0 = space, 1 = pulse for IR out				stat1,5
			; 4	1 = RS232 RCV character received			stat1,4
			; 3	0/1 = history of Modulate/Non-modulate pin (PORTB,7)	stat1,3
			; 2	1 = echos what's on the IROUT pin			stat1,2
			; 1	1 = flag to output "Ready to save, pick a character:"	stat1,1
			; 0	1 = start bit found (in IR input)				stat1,0

	;PORTA:
O_L		equ	4	; Used for Operate/Learn switch			in
MODSW		equ	3	; Used for Modulate/Non-modulate SW2		in
BITSW		equ	2	; Used for ASCII/Bitstream SW3			in
REDLED		equ	1	; Used for red LED error indicator			out
GRNLED		equ	0	; Used for green LED IR accept indicator		out

	;PORTB:
NU1		equ	7	; Port pin not used				out
NU2		equ	6	; Port pin not used				out
NU3		equ	5	; Port pin not used				out
IRIN		equ	4	; IR receiver input				in
IRNOT		equ	3	; Inverse of IROUT pin				out
RS232O		equ	2	; RS232 output to PC				out(in)
RS232I		equ	1	; RS232 input from PC				in
IROUT		equ	0	; IR transmitter output				out

		org	0	; Reset Vector
 		goto	start	; Go to Main Program

		org	0004	; Interrupt Vector
		goto	ints	; Go to Interrupt service routine


	org	0005h		; Start of user program ($0005h to $03FFh) (twice as much in PIC16F628A)
;****************************************************************************
;****************************************************************************
;******************************** Main Program *********************************
;****************************************************************************
;****************************************************************************
;
;************************* INITIALIZE PIC **********************
start
	bcf	STATUS,RP0	; Select Bank 0
	bcf	STATUS,RP1
	bcf	STATUS,IRP

	movlw	0x1F		; Initialize data for PORTA
	movwf	PORTA
	movlw	0xD1		; Initialize data for PORTB
	movwf	PORTB

	bsf	STATUS,RP0	; Initialize PORTA directions - Select Bank 1
	movlw	0x10		; Set porta directions (a 0 is out, a 1 is in)
	movwf	TRISA
	bcf	STATUS,RP0	; De-select Bank 1

	bsf	STATUS,RP0	; Initialize PORTB directions - Select Bank 1
	movlw	0xD6		; Set portb directions (a 0 is out, a 1 is in) was 1B
	movwf	TRISB
	bcf	STATUS,RP0	; De-select Bank 1

	movlw	0x07		; Initialize the comparitors as off
	movwf	CMCON

	bcf	STATUS,IRP	; Clear all RAM locations from 0x20 to 0x7F
	movlw	0x20
	movwf	FSR
rcloop	clrf	INDF
	incf	FSR,f
	btfss	FSR,7
	goto	rcloop

				; ******* INITIALIZE RS232 PORT *******
	bsf	STATUS,RP0	; Initialize RS232 TSCR - Select Bank 1
	movlw	0xA0		; WAS A2 Load Transmit Status and Control Register ((24))
	movwf	TXSTA		; 7)NA, 6)1=9bit, 5)1-TX EN, 4)0=Asyn/1=Sync, 3)NA, 2)Baud 0=lo/1=hi, 1)TXFLG, 0)Parity bit set
	bcf	STATUS,RP0	; De-select Bank 1

	bsf	STATUS,RP0	; Initialize RS232 SPBRG - Select Bank 1
	movlw	0x20		; Load Baud Rate Register (Set for 9600 baud using a 20MHz Xtal)
	movwf	SPBRG		; Entire byte for baud divisor.
	bcf	STATUS,RP0	; De-select Bank 1

	movlw	0x90		; Was B0 WAS A0 (Could be B0) Load Transmit Status and Control Register (B0 is enable receiver) ((90))
	movwf	RCSTA		; 7)RS232 EN, 6)9bitRcvEN, 5)Single Rcv En, 4)Cont. Rcv En, 3)Addr En, 2-1-0)Rest are flags


				; ******** INITIALIZE OTHER PERIPHERAL INTERRUPTS *********
	bsf	STATUS,RP0	; Select Bank 1
	movlw	0xC0		; Set Opt_Reg: disable pull-ups(7), rising edge int on PORTB(6), Timer0 clock source(5)prescale
	movwf	OPTION_REG	; Timer0 source edge select(4), prescaller assignment(3), Timer0 prescaller(0-2)
	bcf	STATUS,RP0	; De-select Bank 1

	bsf	STATUS,RP0	; Select Bank 1
	movlw	0xC8		; Set Interrupt Control Reg. Global(7), Peripheral enable(6) [for RS232, E2, TMR1&2], Timer0 OVF(5),
	movwf	INTCON		; External Int.(4), RB Port Change(3), Flags(0-2)
	bcf	STATUS,RP0	; De-select Bank 1

	clrf	TMR1L		; Clear Timer1 values
	clrf	TMR1H

	bsf	STATUS,RP0	; Select Bank 1
	movlw	0x01		; 03 Was 01 WAS 21 Set Perpherial Interrupt Control Reg. EE Int.(7), Comparitor(6), RS232RCV(5), RS232TX(4)
	movwf	PIE1		; Not used(3), Compare(2), Timer2 match(1), Timer1 OVF(0)
	bcf	STATUS,RP0	; De-select Bank 1

	movlw	0x05		; 05 Initialize Timer1 Control Register
	movwf	T1CON

	bsf	STATUS,RP0	; Select Bank 1
	movlw	0x3E		; Initialize Timer2 value			(This will make a 40kHz signal)
	movwf	PR2
	bcf	STATUS,RP0	; De-select Bank 1
	movlw	0x04		; Initialize Timer2 pre and post scalers
	movwf	T2CON
	;TMR2	


; ***********************************************************************
; * Main routine. The main routine detects first the transmission * <<<<<<<<<<<<<<<<<<<<<<
; * time of the incoming calibration character. After that the *	<<<<<<<<<<<<<<<<<<<<<<
; * routine receives and transmits incoming characters. *		<<<<<<<<<<<<<<<<<<<<<<
; ***********************************************************************

Main	;bsf	STATUS,RP0	; Select bank 1						+++ Clear random interrupts happening from O/L transition +++
;main1	bcf	INTCON,GIE	; Disable interrupts
;	btfsc	INTCON,GIE	; Test if no other interrupts happened during disable
;	goto	main1
;	bsf	INTCON,GIE	; Re-enable interrupts
;	bcf	STATUS,RP0	; Deselect bank 1

	movlw	0x00		; RS232 transmit message "Ready" 00,07
	movwf	pointer
	movlw	0x07
	movwf	counter
	call	msgout

;goof	goto	goof


loop1	btfsc	PORTA,O_L	; Check if switch set for Learn(0) or Operate(1)
	goto	oper
	goto	learn

	; <<<<<<<<<< OPERATE >>>>>>>>>>>
oper	bsf	STATUS,RP0	; Select Bank 1
	bcf	INTCON,RBIE	; Disable Interrupts on PORTB change
	bcf	EECON1,WREN	; Disable E2 writes
	bcf	STATUS,RP0	; De-select Bank 1

oper1	btfss	PORTA,O_L	; Check if switch still set for Operate(1) or changed to Learn(0)
	goto	Main		; If switch changed to LEARN while in OPERATE, then restart main loop
	call	modchk		; Check if modulate/non-modulate switch has changed and set accordingly

	btfsc	PORTA,BITSW	; Test if E2 mode (1) or Bitstream mode (0) on SW3
	goto	oper2		; If E2 mode, go to that mode continue
	; Bitstream mode:
	; Wait for "b" or "B" to start
	; Accumulate charaters
	; Wait for "e" or "E" to end, and then:
	; call	irplay
	goto	oper1		; And return
	; E2 mode:
oper2	btfss	PIR1,RCIF	; Check if data in RS232 Receiver buffer (looking for input character)		+++ WAIT FOR CHARACTER +++
	goto	oper1		; Go on to next test if nothing there
	call	rcv232		; Get byte OR clear errors. If return with 1, then error. If return with 0, then good
	xorlw	0x00		; Check if good
	btfss	STATUS,Z	; Check if 0 which is good
	goto	oper1		; If it was a 1, then is was just an error, loop back again. Else:
	movf	RCV_REG,w	; Get what's in Received byte

	call	e2mtch		; Do match to compare E2 data with RCV_REG ASCII character C=0=no, C=1=yes
	btfss	STATUS,C	; Test if match was found.
	goto	opernm		; Match not found - loop back to main operate loop
	bcf	PORTA,GRNLED	; Turn on the green LED
	call	ee2r		; Transfer E2D 6 bytes to "IRDATA" bytes
	call	irplay
	movlw	0x64		; Then RS232 transmit message "OK" 64,06
	movwf	pointer
	movlw	0x06
	movwf	counter
	call	msgout
	bsf	PORTA,GRNLED	; Turn off the green LED
	goto	oper1		; Loop the main operate loop

opernm	bcf	PORTA,REDLED	; No match found so send "Abort" - Turn on red LED				+++ NO MATCH FOUND +++
	movlw	0x5D		; RS232 transmit message "Abort" 5D,09 Need to be 1 back (5C rather than 5D) because sub in a sub
	movwf	pointer
	movlw	0x09
	movwf	counter
	call	msgout
	bsf	PORTA,REDLED	; Turn off red LED
	goto	oper1		; Loop the main operate loop


	; <<<<<<<<<< LEARN >>>>>>>>>>>
learn	bsf	STATUS,RP0	; Select Bank 1
	bsf	INTCON,RBIE	; Enable Interrupts on PORTB change
	bcf	PIE1,TMR2IE	; Disable Timer2 Interrupts (40kHz timer)
	bcf	STATUS,RP0	; De-select Bank 1

learn1	btfsc	PORTA,O_L	; Check if switch set for Learn(0) or Operate(1)
	goto	Main		; If switch changed to OPERATE while in LEARN, then restart main loop

	btfsc	PORTA,BITSW	; Test if E2 mode (1) or Bitstream mode (0) on SW3
	goto	learn2		; If E2 mode, go to that mode continue

	; Bitstream mode:
	btfss	stat1,7		; If in Bitstream mode, test if "send string" bit set
	goto	learn1		; if not set, loop back
	call	cmdout		; If set, send out string of characters - THIS IS FOR BITSTREAM MODE ONLY
	bcf	stat1,0		; Clear start bit found
	clrf	CLENGTH
	clrf	irctrl		; Clear counters
	clrf	irctrh
	bcf	stat1,7		; Clear this and any new bit set
	goto	learn1

	; E2 mode:										+++CHECK FOR ~ TO ERASE ALL E2+++
learn2	btfss	PIR1,RCIF	; Check if data in RS232 Receiver buffer (looking for tilde)
	goto	learng		; Go on to next test if nothing there
	call	rcv232		; Get byte OR clear errors. If return with 1, then error. If return with 0, then good
	xorlw	0x00		; Check if good
	btfss	STATUS,Z	; Check if 0 which is good
	goto	learng		; If it was a 1, then is was just an error, loop on to next test. Else:
	movf	RCV_REG,w	; Get what's in Received byte
	xorlw	0x7E		; Compare with tilde (~)
	btfss	STATUS,Z	; Check if the same
	goto	learng		; If not the same, go on to next test

	movlw	0x48		; If it's a tilde, then RS232 transmit message "Erase all E2? Y/N? " 48,15
	movwf	pointer
	movlw	0x15
	movwf	counter
	call	msgout
				;										+++LOOKING FOR "Y" ON TILDE +++
learna	btfss	PIR1,RCIF	; Check if data in RS232 Receiver buffer (looking for tilde)
	goto	learna		; Go on to next test if nothing there
	call	rcv232		; Get byte OR clear errors. If return with 1, then error. If return with 0, then good
	xorlw	0x00		; Check if good
	btfss	STATUS,Z	; Check if 0 which is good
	goto	learna		; If it was a 1, then is was just an error, loop on to next test. Else:
	movf	RCV_REG,w	; Get what's in Received byte
	xorlw	0x79		; Compare with 'y"
	btfsc	STATUS,Z	; Check if the same
	goto	learnec		; If the same, go to clear all E2
abort	bcf	PORTA,REDLED	; If not a "y", then RS232 transmit message "Abort" 5D,09 - Turn on red LED	+++ NOT A "Y" ON TILDE +++
	movlw	0x5D		; RS232 transmit message "Abort" 5D,09 Need to be 1 back (5C rather than 5D) because sub in a sub
	movwf	pointer
	movlw	0x09
	movwf	counter
	call	msgout
	bsf	PORTA,REDLED	; Turn off red LED

lsame1	movlw	0x00		; Then RS232 transmit message "Ready" 00,07
	movwf	pointer
	movlw	0x07
	movwf	counter
	call	msgout
	bsf	PORTA,GRNLED	; Turn off the green LED
	bcf	stat1,7		; Clear all bits and restart
	bcf	stat1,1		; Clear "Send - Ready to save, pick a character:" bit
	bcf	stat1,0		; Clear start bit found
	goto	learn1

learnec	bcf	PORTA,GRNLED	; Turn on the green LED								+++ YES, A "Y" ON TILDE +++
	call	clre2		; Clear all internal E2 data (except last 2 bytes) to "00"
	movlw	0x64		; Then RS232 transmit message "OK" 64,06
	movwf	pointer
	movlw	0x06
	movwf	counter
	call	msgout
	goto	lsame1


learng	btfss	stat1,7		; If in E2 mode, check if command found						+++ CHKING FOR COMMAND END BIT +++
	goto	learn1		; If not loop back

	btfss	stat1,1		; Test for "output "Ready to save, pick a character:" bit
	goto	learn1		; If status bit not set, loop back
	call	bxfer		; Transfer data from IRDATA and CLENGTH to irsave and lsave			+++ XFER FROM IRDATA TO IRSAVE +++
	movlw	0x07		; RS232 transmit message "Ready to save, pick a character:"  07,21		+++ XMIT "PICK A CHARACTER" +++
	movwf	pointer
	movlw	0x21		; Load message count of bytes (characters) to send
	movwf	counter
	call	msgout
	bcf	stat1,1		; Clear send message bit


learnk	btfss	PIR1,RCIF	; Check if data in RS232 Receiver buffer						+++ CHKING FOR CHARACTER +++
	goto	learnk		; Go on to next test if nothing there
	call	rcv232		; Get byte OR clear errors. If return with 1, then error. If return with 0, then good
	xorlw	0x00		; Check if good
	btfss	STATUS,Z	; Check if 0 which is good
	goto	learnk		; If it was a 1, then is was just an error, loop on to next test. Else:

	movf	RCV_REG,w	; Get what's in Received byte							+++ CHK WHAT'S IN CHARACTER +++
	xorlw	0x1B		; Compare with escape key							+++ CHK IF IT'S AN ESCAPE +++
	btfss	STATUS,Z	; Check if the same
	goto	learnsc		; If not the same, go to save character
abort2	movlw	0x0D		; Do a carriage return
	movwf	TXREG		; Put the returned byte into the RS232 buffer
	call	bitchk		; Check transmit flag, that byte is sent
	bcf	stat1,7		; Clear all bits and restart							+++ IT'S AN ESCAPE, SO ABORT +++
	bcf	stat1,1
	bcf	stat1,0		; Clear start bit found
	bsf	PORTA,GRNLED	; Clear the LED
	goto	abort

				;										+++ IT'S A CHARACTER, CHK 4 MATCH +++
learnsc	call	e2mtch		; Check if there's a match in internal E2 that matches ascii in RCV_REG. C=0=no, C=1=yes
	btfss	STATUS,C	; Test if match found
	goto	learnbl		; If no match, go to "find a blank"
				;										+++ MATCH FOUND WITH E2 +++
	movlw	0x28		; If already saved/a match, then RS232 message "Already used. Overwrite? Y/N? "  28,20	+++ "OVERWRITE?" +++
	movwf	pointer
	movlw	0x20		; Load message count of bytes (characters) to send
	movwf	counter
	call	msgout

learni	btfss	PIR1,RCIF	; Check if data in RS232 Receiver buffer (looking for tilde)				+++ LOOKING 4 "Y" on OVERWRITE +++
	goto	learni		; Go on to next test if nothing there
	call	rcv232		; Get byte OR clear errors. If return with 1, then error. If return with 0, then good
	xorlw	0x00		; Check if good
	btfss	STATUS,Z	; Check if 0 which is good
	goto	learni		; If it was a 1, then is was just an error, loop on to next test. Else:
	movf	RCV_REG,w	; Get what's in Received byte
	xorlw	0x79		; Compare with 'y"
	btfsc	STATUS,Z	; Check if the same
	goto	learn5		; If the same, go to save in same location					+++ "Y" FOUND, GOTO +++
	goto	abort2		; If not a "y", then RS232 transmit message "Abort" 5D,09 - Turn on red LED and clear and start over  +++ ELSE ABORT +++

learnbl	call	findsp		; Locate empty slot for new command						+++ FIND EMPTY SLOT +++
	btfsc	STATUS,C	; Test if blank spot was found. C=0=no space found, C=1=space found
	goto	learn5		; If yes, then save it to empty space
	goto	abort2		; If no blank spaces, then RS232 transmit message "Abort" 5D,09
				;										+++ "Y" ON OVERWRITE OR WRITE +++
learn5	call	r2ee		; Convert IRDATA 32 bytes into E2D data 4 bytes, ready for writing.		+++ SO CONVERT DATA TO 6 BYTES +++
	call	e2wr		; Write 6 bytes to internal E2							+++ AND SAVE IN E2 +++
	movlw	0x64		; RS232 transmit message "OK" 64,06						+++ XMIT "OK" +++
	movwf	pointer
	movlw	0x06
	movwf	counter
	call	msgout
	goto	lsame1		; RS232 transmit message "Ready" 00,07, clear bits and return to main loop	+++ XMIT "READY", CLR & LOOP +++


learnez	bcf	stat1,7		; Clear this and any new bit set
	goto	learn1


; ***********************************************************************
; ***********************************************************************
; ************************** End Of Main Loop ******************************
; ***********************************************************************
; ***********************************************************************

;************************************************************************
;************************************************************************
;******************************* Subroutines ******************************
;************************************************************************
;************************************************************************

;************************** CHECK MODULATE SWITCH SW2 *****************
modchk	btfss	PORTA,MODSW	
	goto	turnon

turnoff	btfss	stat1,3		; Test status			MODSW is high (normal)
	return
	bcf	stat1,3
	bsf	STATUS,RP0	; Select Bank 1
	bcf	PIE1,TMR2IE	; Disable TIMER2 Interrupts
	bcf	STATUS,RP0	; De-select Bank 1
	return

turnon	btfsc	stat1,3		;				MODSW is low (Modulate)
	return
	bsf	stat1,3
	bsf	STATUS,RP0	; Select Bank 1
	bsf	PIE1,TMR2IE	; Enable TIMER2 Interrupts
	bcf	STATUS,RP0	; De-select Bank 1
	return


	btfss	stat1,3		; Check status of Modulate switch history
	goto	mody
	; History is 1
modn	btfsc	PORTA,MODSW	; Check if modulated switch is on (low)
	return			; End mod check
	; Switch changed from 1 to 0
	bcf	stat1,3		; Change occured to create modulate state
modu	bsf	STATUS,RP0	; Select Bank 1
	bsf	PIE1,TMR2IE	; Enable TIMER2 Interrupts
	bcf	STATUS,RP0	; De-select Bank 1
	;bsf	T2CON,TMR2ON	; Turn on the timer
	return			; End mod check
	; History is 0
mody	btfss	PORTA,MODSW	; Check if modulated switch is off (high)
	return			; End mod check
	; Switch changed from 0 to 1
	bsf	stat1,3		; Change occured to go to non-modulated state
nonmod	bsf	STATUS,RP0	; Select Bank 1
	bcf	PIE1,TMR2IE	; Disable TIMER2 Interrupts
	bcf	STATUS,RP0	; De-select Bank 1
	;bcf	T2CON,TMR2ON	; Turn off the timer
modend	return


;************************** GENERAL DELAY OF 40MS **********************
cdelay	movlw	0x1F		; Load small delay count
	goto	delcom
idelay	movlw	0x7F		; Load large delay count		(IDELAY IS 40 MS LONG)
delcom	movwf	dcnth
idly0	clrf	dcntl		; Load small delay count
idly1	nop			; Wait a bit
	nop
	nop
	decfsz	dcntl, F		; Decrement small delay count and check for 0
	goto	idly1		; If not 0, loop
	decfsz	dcnth, F	; Decrement large delay count and check for 0
	goto	idly0		; If not 0, loop
	return

;************************* RS232 MESSAGE OUT SUBROUTINE **************
msgout	movlw	0x03
	movwf	PCLATH
	movf	pointer,0	; Load with beginning pointer of message
	call	msg1		; Get byte to load into RS232 buffer
	movwf	TXREG		; Put the returned byte into the RS232 buffer
	call	bitchk		; Check transmit flag, that byte is sent
	incf	pointer,1	; Increment the pointer to the next character address
	decfsz	counter,1	; Decrement counter and check for last byte
	goto	msgout		; If not last byte, return for more
	return

;************************* TEST FOR RS232 TRANSMITTER BUFFER EMPTY *****
bitchk	bsf	STATUS,RP0	; Initialize RS232 TSCR - Select Bank 1
bc1	btfss	TXSTA,TRMT	; Test if ready for next character
	goto	bc1		; Loop if not
	bcf	STATUS,RP0	; De-select Bank 1
	return

; 
; Relationship of 32 "bit" data to Internal E2 data bytes
;
; AMTCH (0x40 1 byte) --> E2D (0x43 first byte)
; CLENGTH (0x21 1 byte) --> E2D+1 (0x44 second byte)
; IRDATA (0x20 32 bytes) --> E2D+2 to E2D+5 (0x45 to 0x48 4 bytes)
; 


;********************** Writes 6 bytes into internal E2 **********************
; Set up is: start address to write is already in E2A and will be transferred to EEADR.
; For saving of data, subroutine "r2ee" has already been run and IRDATA, AMTCH
; and CLENGTH is already in E2D through E2D+5. Note: 8mS write time per byte.
;
e2wr	movf	E2A,w		; Get start address to write to
	bsf	STATUS,RP0	; Select bank 1
	movwf	EEADR		; Store it in Internal EE address byte
	bcf	STATUS,RP0	; Deselect bank 1

	movf	E2D,w		; Get ascii character to save as first byte
	call	e2wsub		; Write byte
	movf	E2D+1,w	; Load command length byte
	call	e2wsub		; Write byte
	movf	E2D+2,w	; Load command data byte 1
	call	e2wsub		; Write byte
	movf	E2D+3,w	; Load command data byte 2
	call	e2wsub		; Write byte
	movf	E2D+4,w	; Load command data byte 3
	call	e2wsub		; Write byte
	movf	E2D+5,w	; Load command data byte 4
	call	e2wsub		; Write byte
	return


e2wsub	bsf	STATUS,RP0	; Write to E2 - Set to bank 1
	movwf	EEDATA		; Save byte to Internal EEDATA byte
esub1	bcf	INTCON,GIE	; Disable interrupts
	btfsc	INTCON,GIE	; Test if no other interrupts happened during disable
	goto	esub1

	bsf	EECON1,WREN	; Enable writes
	movlw	0x55		; Do start sequence (55h first)
	movwf	EECON2
	movlw	0xAA		; Do start sequence (then AAh writes to E2)
	movwf	EECON2
	bsf	EECON1,WR	; Set write bit to start write

	bcf	STATUS,RP0	; Return to bank 0
esub2	btfss	PIR1,EEIF	; Check if write is complete
	goto	esub2		; If not, loop and test
	bsf	STATUS,RP0	; If write is done - Set to bank 1
	incf	EEADR,f		; Increment the address to write to
	bsf	INTCON,GIE	; Re-enable interrupts
	bcf	STATUS,RP0	; Return to bank 0
	return


;********************** Reads 6 bytes from internal E2 **********************
; Set up is: start address to write is already in E2A and will be transferred to EEADR.
;
e2rd	movf	E2A,w		; Get start address to write to
	bsf	STATUS,RP0	; Select bank 1
	movwf	EEADR		; Store it in Internal EE address byte
	bsf	EECON1,RD	; Initiate a read
	movf	EEDATA,w	; Save data into w
	bcf	STATUS,RP0	; Deselect bank 1
	movwf	E2D		; Save it to first byte
	call	e2rsub
	movwf	E2D+1		; Save it to second byte byte
	call	e2rsub
	movwf	E2D+2		; Save it to third byte byte
	call	e2rsub
	movwf	E2D+3		; Save it to fourth byte byte
	call	e2rsub
	movwf	E2D+4		; Save it to fifth byte byte
	call	e2rsub
	movwf	E2D+5		; Save it to sixth byte byte
	return

e2rsub	bsf	STATUS,RP0	; Select bank 1
	incf	EEADR,f		; Increment to next address
	bsf	EECON1,RD	; Initiate a read
	movf	EEDATA,w	; Save data into w
	bcf	STATUS,RP0	; Deselect bank 1
	return


;*** Transfers bit data in asave(AMTCH), csave(CLENGTH) and irsave(IRDATA) bytes into 8 bit bytes in E2D bytes for Internal EEPROM storage ***
;
r2ee	movf	RCV_REG,w	; Transfer ASCII match byte
	movwf	E2D
	movf	lsave,w		; Transfer bit length byte
	movwf	E2D+1

	movlw	0x20		; Load total bit counter with 32
	movwf	e2bctr
	movlw	irsave		; Load pointer with IR data start
	movwf	FSR

r2e1	rrf	INDF,w		; Rotate b0 of pointed IR data register into Carry
	rrf	E2D+2,1
	rrf	E2D+3,1
	rrf	E2D+4,1
	rrf	E2D+5,1		; Rotate through

	incf	FSR,1		; Point to next bit
	decfsz	e2bctr,1		; Check if last (32nd) bit
	goto	r2e1		; If not, go do another rotate
	return


; Relationship of 32 "bit" data to Internal E2 data bytes
;
; AMTCH (0x20 1 byte) --> E2D (0x50 first byte)
; CLENGTH (0x21 1 byte) --> E2D+1 (0x51 second byte)
; IRDATA (0x22 32 bytes) --> E2D+2 to E2D+5 (0x52 to 0x55 4 bytes)


;************** Transfers 8 bit bytes in E2D bytes into (active output) bit data in IRDATA, CLENGTH and IRDATA bytes ***********
; This subroutine is called in the operate mode when a match has already been found and the internal EEPROM data is already in the 
; E2D bytes. It then transfers those bits/bytes to the "active" IRDATA and CLENGTH to be transmitted out. AMTCH is not necessary.
;
ee2r	movf	E2D+1,w	; Transfer bit length byte
	movwf	CLENGTH	

	movlw	0x20		; Load total bit counter with 32
	movwf	e2bctr		; Will transfer full 32 bits even if not all were used
	movlw	IRDATA		; Load pointer with IR data start address (IRDATA)
	movwf	FSR

rmore	rrf	E2D+2,1		; Rotate through
	rrf	E2D+3,1
	rrf	E2D+4,1
	rrf	E2D+5,1

	btfss	STATUS,C	; Test if 0 or 1
	goto	risa0
risa1	bsf	INDF,0
	goto	bitin
risa0	bcf	INDF,0
bitin	incf	FSR,f		; Point to next byte for bit
	decfsz	e2bctr,1		; Decrement the total bit counter
	goto	rmore		; Not done with stored bits, go get another
	return


;******************* E2 MATCH - Check each location for 00 or ASCII match *****************
; Addresses: 00, 06, 0C, 12, 18, 1E, 24, 2A, 30, 36, 3C, 42, 48, 4E, 54, 5A, 60, 66, 6C, 72, 78
e2mtch	movlw	0x00		; Set E2 address to the beginning
	movwf	E2A		; Store inE2A for EEADR
e2m1	call	e2rd		; Reads 6 bytes from that address and puts in E2D data
	movf	E2D,w		; Load what's in first read character byte from E2 (CHaracter location)
	xorwf	RCV_REG,w	; Compare it with what's in RCV_REG
	btfsc	STATUS,Z	; Test if Zero, which means a match
	goto	mtched		; If Zero, then it matched
	movf	E2A,w		; Check address byte for top (78) address already achieved
	xorlw	0x78		; 
	btfss	STATUS,Z	; If zero, it's a match with 78
	goto	e2m2		; If not zero yet, go get another block to test
	bcf	STATUS,C	; Clear the carry bit, meaning "no match"
	return			; And return
e2m2	movlw	0x06		; Else get ready to add 6 to address
	addwf	E2A,f		; Store back to E2A
	goto	e2m1
mtched	bsf	STATUS,C	; Set the carry bit meaning "match found"
	return			; And return


;************************* Look for empty spot in Internal E2 ***********************
findsp	movlw	0x00		; Set E2 address to the beginning
	movwf	E2A		; Store in EEADR
findsp1	call	e2rd		; Reads 6 bytes from that address
	movf	E2D,f		; Check if data is "00"
	btfss	STATUS,Z	; Check if byte was a 0
	goto	notsp		; If not 0, then get another location
	bsf	STATUS,C	; Return with carry bit set, meaning "here's a space"
	return			; And return (This will leave the open address intact in E2A)
notsp	movf	E2A,w		; Check address byte for top (78) address already achieved
	xorlw	0x78		; Compare with top address
	btfss	STATUS,Z	; If zero, it's a match with 78
	goto	nextsp
	bcf	STATUS,C	; Clear the carry bit, meaning "no match"
	return			; And return	
nextsp	movlw	0x06		; Else get ready to add 6 to address
	addwf	E2A,f		; Store back to E2A
	goto	findsp1


;************************* Clear all Internal E2 *******************************
clre2	movlw	0x00		; Set E2 address to the beginning
	movwf	E2A		; Store in EEADR
clre22	clrf	E2D
	clrf	E2D+1
	clrf	E2D+2
	clrf	E2D+3
	clrf	E2D+4
	clrf	E2D+5
	call	e2wr		; Write 6 blanks to byte block
	comf	E2A,w		; Check address byte for top (78) address already achieved
	addlw	0x87		; Add compliment of 78
	btfsc	STATUS,Z	; If zero, it's a match with 78
	return
	movlw	0x06		; If not, add 6 to address
	addwf	E2A,f		; Store back to E2A
	goto	clre22		; And go clear another block


;******************* IR output *****************
irplay	; 2.5ms on			50 (x50uS)  		[12.750mS max with 0xFF]
	; 0 = 550uS off / 650uS on	11 / 14 (x50uS)
	; 1 = 550uS off / 1250uS on	11 / 26 (x50uS)
	; 10ms on			200 (x50uS)

	movlw	0xFF		; Do 12.75mS dark/on time
	movwf	ircnt
	bsf	stat1,2		; Show that ir is "on"
	bcf	PORTB,IROUT	; Turn on IR LED
	bsf	PORTB,IRNOT	; Do inverse
	bsf	stat1,5		; Set enable bit
irplay1	btfsc	stat1,5		; Check if 12.75mS has gone by
	goto	irplay1		; If not, wait

	movlw	0x32		; Now do start marking by turning off LED for 2.5mS
	movwf	ircnt
	bcf	stat1,2		; Show that ir is "off"
	bsf	PORTB,IROUT	; Turn off IR LED
	bcf	PORTB,IRNOT	; Do inverse
	bsf	stat1,5		; Set enable bit
irplay2	btfsc	stat1,5		; Check if 2.5mS has gone by
	goto	irplay2		; If not, wait

	movf	CLENGTH,w	; Load with count to do
	movwf	irlength

	movlw	IRDATA		; Load pointer with starting bit
	movwf	FSR

irbits	call	iroff		; Start with a "space"
irout3	btfsc	stat1,5		; Test if enable bit reset
	goto	irout3		; If not, keep testing

	rrf	INDF,w		; Rotate bit into carry bit
	btfss	STATUS,C	; Test to see if it's a 0 or 1
	goto	irout0
irout1	call	ir1		; Send out a long 1
	goto	irout2
irout0	call	ir0		; Send out a short 0
irout2	btfsc	stat1,5		; Test if enable bit reset
	goto	irout2		; If not, keep testing
	incf	FSR,f		; Increment to the next bit to send
	decfsz	irlength,f	; Check if this is the last
	goto	irbits		; If not, go get another bit

	movlw	0xFF		; Do another 12.75mS of dark/on time before returning
	movwf	ircnt
	bsf	stat1,2		; Show that ir is "on"
	bcf	PORTB,IROUT	; Turn on IR LED
	bsf	PORTB,IRNOT	; Do inverse
	bsf	stat1,5		; Set enable bit
irplay3	btfsc	stat1,5		; Check if 12.75mS has gone by
	goto	irplay3		; If not, wait
	bcf	stat1,2		; Show that ir is "off"
	bsf	PORTB,IROUT	; Turn off IR LED before leaving
	bcf	PORTB,IRNOT	; Do inverse
	return

iroff	bsf	stat1,2		; Show that ir is "on"
	bcf	PORTB,IROUT	; Turn off IR LED
	bsf	PORTB,IRNOT	; Do inverse
	movlw	0x0B		; Load a count of 11 for 550uS off
	movwf	ircnt
	bsf	stat1,5		; Set enable bit
	return

ir0	bcf	stat1,2		; Show that ir is "off"
	bsf	PORTB,IROUT	; Turn on IR LED
	bcf	PORTB,IRNOT	; Do inverse
	movlw	0x0D		; Load a count of 13 for 650uS on
	movwf	ircnt
	bsf	stat1,5		; Set enable bit	
	return

ir1	bcf	stat1,2		; Show that ir is "off"
	bsf	PORTB,IROUT	; Turn on IR LED
	bcf	PORTB,IRNOT	; Do inverse
	movlw	0x19		; Load a count of 25 for 1250uS on
	movwf	ircnt
	bsf	stat1,5		; Set enable bit
	return


;*** Transfers 34 bytes from AMTCH/CLENGTH/IRDATA to asave/lsave/irsave for saving ***

bxfer	;movf	AMTCH,w
;	movwf	asave
	movf	CLENGTH,w
	movwf	lsave

	movlw	0x20		; Load total bit counter with 32
	movwf	e2bctr		; Will transfer full 32 bits even if not all were used

	movlw	IRDATA		; Save source location address
	movwf	ectr
	movlw	irsave		; Save destination location address
	movwf	xctr

blkloop	movf	ectr,w		; Load with source address
	movwf	FSR
	movf	INDF,w		; Get byte at source address
	movwf	tempb		; Save it temporarily
	movf	xctr,w		; Load with destination address
	movwf	FSR
	movf	tempb,w	; Get temporary byte
	movwf	INDF		; And save it in destination address
	incf	ectr,f		; Increment addresses
	incf	xctr,f
	decfsz	e2bctr,f		; Decrement byte transfer counter
	goto	blkloop		; If not last byte, loop back for another
	return			; Else return


;**************************** RS232 Receive Byte Subroutine ****************************
; Call this routine after successfully testing PIR1,RCIF flag for byte present.  Will put byte in RCV_REG
rcv232	btfss	RCSTA,OERR	; If overrun error occurred	(in bank 0)
	goto	rcv1
	bcf	RCSTA,CREN	; Reset the receiver logic
	bsf	RCSTA,CREN	; Enable reception again
	retlw	1		; Return with error
rcv1	btfss	RCSTA,FERR	; If framing error occurred
	goto	rcv2
	movf	RCREG,W	; Discard received data that has error
	retlw	1		; Return with error
rcv2	movf	RCREG,W	; Get received data		(in bank 0)
	movwf	RCV_REG	; Save it in Receive Register

ffff	addlw	0xBF		; Test if less than 41(capital A)
	btfss	STATUS,C
	retlw	0		; If less than 41, then bail
	movf	RCV_REG,w	; Get byte again
	addlw	0xA5		; Test if more than 5A (capital Z)
	btfsc	STATUS,C
	retlw	0		; If more than 5A, then bail
	movlw	0x20		; If it's a capital letter, add 0x20 to it
	addwf	RCV_REG,f	; And make it a small case letter and save
	retlw	0		; Return good


;************************************************************************
;************************************************************************
;************************** ALTERNATE SUBROUTINES ***********************
;************************************************************************
;************************************************************************

;******************* Alternate send command as 1's and 0's subroutine *********
; Addresses: 00, 06, 0C, 12, 18, 1E, 24, 2A, 30, 36, 3C, 42, 48, 4E, 54, 5A, 60, 66, 6C, 72, 78

cmdout	movlw	0x42		; Send a "B"
	movwf	TXREG		; Put the returned byte into the RS232 buffer
	call	bitchk		; Check transmit flag, that byte is sent

	movf	CLENGTH,w	; Save length to counter
	movwf	ectr

	movlw	IRDATA		; Set FSR with beginning of IR data to serve as pointer
	movwf	FSR

cmdl1	rrf	INDF,0
	btfss	STATUS,C
	goto	itsa0
	goto	itsa1

itsa0	movlw	0x30		; Send a "0"
	movwf	TXREG		; Put the returned byte into the RS232 buffer
	call	bitchk		; Check transmit flag, that byte is sent
	goto	cmd2

itsa1	movlw	0x31		; Send a "1"
	movwf	TXREG		; Put the returned byte into the RS232 buffer
	call	bitchk		; Check transmit flag, that byte is sent

cmd2	incf	FSR,1		; Increment the pointer
	decfsz	ectr,1		; Decrement the counter and test if that was the last byte to be sent
	goto	cmdl1		; If not the last byte, send another byte. Else if last byte, then:

	movlw	0x45		; Send an "E"
	movwf	TXREG		; Put the returned byte into the RS232 buffer
	call	bitchk		; Check transmit flag, that byte is sent
	call	idelay		; Delay 40mS

	movlw	0x20		; Send an " "
	movwf	TXREG		; Put the returned byte into the RS232 buffer
	call	bitchk		; Check transmit flag, that byte is sent
	call	idelay		; Delay 40mS

	movlw	0x20		; Send an " "
	movwf	TXREG		; Put the returned byte into the RS232 buffer
	call	bitchk		; Check transmit flag, that byte is sent
	call	idelay		; Delay 40mS

	return



;****************************************************************************
;****************************************************************************
;*********************************** Interrupts ********************************
;****************************************************************************
;****************************************************************************
;	; *** For all interrupts, save registers ***
ints	movwf	wtemp		; save off current W register contents
	movf	STATUS,w	; move status register into W register
	movwf	stemp		; save off contents of STATUS register
	bcf	STATUS,RP0	; STATUS saved, so now corrupt and select bank 0 no matter what

	; ***** Timer 1 interrupt service routine *****
tmr1	btfss	PIR1,TMR1IF	; Test if Timer 1 has Overflowed	[50uS] (bit 0)
	goto	rsrcv		; If not, go to next interrupt check
	movlw	0x18		; Reload low end register for 50uS
	movwf	TMR1L
	movlw	0xFF		; Reload high end register for 50uS
	movwf	TMR1H

	btfss	PORTA,O_L	; Check if switch set for Learn(0) or Operate(1)
	goto	tmr1l		; If learn, go to learn section
tmr1o	btfss	stat1,5		; Check if bit set meaning enable count
	goto	tmr1b		; If not set, clear and go on
	decfsz	ircnt,f		; Decrement counter
	goto	tmr1b		; If not 0 yet, bail
	bcf	stat1,5		; It's zero so clear request and bail
	goto	tmr1b

tmr1l	incf	irctrl,1		; Increment the IR interval counter lower byte
	btfss	STATUS,Z	; Test if low IR counter didn't roll over to zero
	goto	tmr1b		; If not, just clear interrupt and return
	incf	irctrh,1		; If rolled over, increment the IR counter high byte
	btfss	STATUS,Z	; Test if high byte is rolling over
	goto	tmr1b		; If not, just clear interrupt and return
	movlw	0xFF		; But if rolling over, don't want to do that
	movwf	irctrl		; So load both bytes with FF to show as high
	movwf	irctrh		; Gets stuck high until RB interrupt clears it, so never rolls over to low (00) number

tmr1b	bcf	PIR1,TMR1IF	; Clear Timer1 Interrupt Flag


	; ***** USART Receive Interrupt service routine *****
rsrcv	btfss	PIR1,RCIF	; Test if USART Receive Interrupt Flag set (bit 5)
	goto	pbint		; If not, go to next interrupt check
	;movf	RCREG,w	; Get byte from internal RS232 Receiver byte
	;movwf	RCV_REG	; And store it in ascii receiver byte
	;bsf	stat1,4		; Set ascii character received bit
	;bcf	PORTA,REDLED	; Turn on red LED
	;bcf	PIR1,RCIF	; Clear USART Receive Interrupt Flag
	; Not using Interrupt so skip over clearing or worrying about

	; ***** PORT B Interrupt service routine *****
pbint	btfss	INTCON,RBIF	; Test if PORTB Transition Interrupt Flag set (bit )
	goto	rstx		; If not, go to next interrupt check

	btfsc	PORTA,O_L	; Check if switch set for Learn(0) or Operate(1)
	goto	rbO		; Was rstx		; If switch is in OPERATE, don't do a thing

	btfss	PORTB,IRIN	; Test if transition was on falling edge
	goto	rbO		; If falling edge, then ignore, go to save value of PORTB and clear interrupt

	btfsc	stat1,7		; for lock bit - Check if "LOCK" bit is set
	goto	rbO		; If yes, ignore input and just clear and exit

rbA	btfsc	stat1,0		; Test if start bit set
	goto	rbF		; If yes, go to test for 1/0/end

rbB	movf	irctrh,f		; Test of irctrh is 00 - TEST HIGH BYTE
	btfss	STATUS,Z	; Test if result is 0
	goto	rbC		; Not 0, so continue to check if 01, 02, 03 or more
	movlw	0x9B		; Else, high byte zero, so see what's in low byte - Load w with 9B h (inverse of 64h = 5mS)
	addwf	irctrl,w		; Add w with value in irctrl. If higher, i.e. more than 5mS, then negative on STATUS,C = 0
	btfss	STATUS,C	; If number is too small, C will be 0
	goto	rbN		; Number was less than 5mS, Clear and bail
rbC	movf	irctrh,f		; Check if anything in counter high byte
	btfss	STATUS,Z	; 
	goto	rbE		; Something in high byte, so definately more than 8mS
	movlw	0x5F		; Now we know that high counter is 0, so let's see if more than 8mS in low byte
	addwf	irctrl,w		; Start with 5F (inverse of A0 = 8mS) and add value
	btfss	STATUS,C
	goto	rbN		; If less than 8mS, then bail		
rbE	bsf	stat1,0		; Set start bit found
	clrf	CLENGTH	; Clear the counter byte
	movlw	IRDATA		; 0x20 - Start with beginning of data storage area
	movwf	FSR		; Load it into the register pointer
	goto	rbN

rbF	movf	irctrh,f		; Test if irctrh is 00
	btfss	STATUS,Z	; If loaded and not zero, to continue to check
	goto	rbL		; But, if anything in high byte, it's more than 12.5mS so do final check
	movlw	0xEE		; Load w with EE (inverse of 11h) (850us)
	addwf	irctrl,w		; Add with lower (17x50us = 850us) from current counter value in W
	btfss	STATUS,C	; If number is too small, C will be 0
	goto	rbD		; If less than 850uS, too small for anything, clear start bit and restart
rbG	movlw	0xE3		; Load w with E3 (inverse of 1Ch (1400us)
	addwf	irctrl,w		; Subtract 1Ch (28d) (28x50us = 1400us) from current counter value in W
	btfss	STATUS,C	; If number is too small, C will be 0
	goto	rbH		; If less than 1400uS, then it's a 0, go to store it
rbI	movlw	0xD7		; Load w with D7 (inverse of 28h (2000us)
	addwf	irctrl,w		; Subtract 28h (40d) (40x50us = 2000us = 2mS) from current counter value in W
	btfss	STATUS,C	; If number is too small, C will be 0
	goto	rbJ		; If less than 2000uS, then it's a 1, go to store it
rbK	movf	irctrh,f		; Check if more than 8 mS. Load high counter and if anything there, it's greater
	btfss	STATUS,Z	; If loaded and NOT zero...
	goto	rbL		; ...it's more than 10mS, so go check if it's less than 30mS
	movlw	0x5F		; High byte zero, so see what's in low byte - Load w with 5F h (inverse of A0h = 8mS)
	addwf	irctrl,w		; Add w with value in irctrl. If higher, i.e. more than 8mS then negative on STATUS,C = 0
	btfss	STATUS,C	; If number is too small, C will be 0
	goto	rbD		;WAS rbM	; Number was smaller than 8mS so might be an error. Go to clear all.
	goto	rbL		; Number was at least 8mS so consider it the end of scan.
rbH	; Store a 0
	movlw	0xE0		; Check if already 32 bytes stored
	addwf	CLENGTH,w	; Add inverse of 20h
	btfsc	STATUS,Z	; Check if zero, meaning it's 20 already
	goto	rbN		; Already 20 (32 bytes stored), don't store any more
	clrf	INDF		; Store "0" in the current pointed register
	incf	FSR,1		; Point to next location
	incf	CLENGTH,1	; Increment the character pointer
	goto	rbN
rbJ	; Store a 1
	movlw	0xE0		; Check if already 32 bytes stored
	addwf	CLENGTH,w	; Add inverse of 20h
	btfsc	STATUS,Z	; Check if zero, meaning it's 20 already
	goto	rbN		; Already 20 (32 bytes stored), don't store any more
	movlw	0x01		; Store "1" in the current pointed location
	movwf	INDF
	incf	FSR,1		; Point to next location
	incf	CLENGTH,1	; Increment the character pointer
	goto	rbN

rbL	; Done, store command
	movlw	0xFA		; Check to see if at least 5 bits were stored
	addwf	CLENGTH,w
	btfss	STATUS,C
	goto	rbD		; If less than 5 bits stored, it's noise, so clear all and bail
	bcf	stat1,0		; ELSE GOOD/DONE! Then: Clear the start bit
	bsf	stat1,1		; Set send message bit
	bcf	PORTA,GRNLED	; Turn on green LED
	bsf	stat1,7		; Set "output string" bit

	goto	rbN
rbD	bcf	stat1,0		; Clear start bit found
	bsf	PORTA,GRNLED	; Turn off the green LED
	clrf	CLENGTH
rbM	; Error condition here. Final measure was less than 8mS. Clear all previously stored bits, pointer, the start bit, and then:
rbN	clrf	irctrl		; Clear counters
	clrf	irctrh
rbO	movf	PORTB,f		; Acts to save changed transitions on PORTB and stop double interrupts
	bcf	INTCON,RBIF	; Clear PORTB Transition Interrupt Flag set

	; ***** USART Transmit Interrupt service routine *****
rstx	btfss	PIR1,TXIF	; Test if USART Transmit Interrupt Flag set (bit 4)
	goto	tmr0		; If not, go to next interrupt check
	;bcf	PIR1,TXIF	; Clear the interrupt flag
	; Not using Interrupt so skip over clearing or worrying about

	; ***** Timer 0 interrupt service routine *****
tmr0	btfss	INTCON,T0IF	; Test if Timer0 has Overflowed (bit )
	goto	othint		; If not, go to next interrupt check
	bcf	INTCON,T0IF	; Clear the interrupt flag


	; ***** All other Interrupts *****
othint	btfsc	PIR1,7		; Test for EEPROM written Interrupt Flag
	bcf	PIR1,7
	btfsc	PIR1,6		; Test for Comparitor Interrupt Flag
	bcf	PIR1,6
	btfsc	PIR1,2		; Test for Capture 1 Interrupt FLag
	bcf	PIR1,2

	; ***** Timer 2 interrupt service routine *****
	btfss	PIR1,1		; Test for Timer 2 Match Interrupt FLag
	goto	othint2
	btfss	PORTA,O_L	; Test if set for operate(1) of learn(0)
	goto	othint2		; If learn (0) then skip
	btfss	stat1,3		; PORTA,MODSW	; Test if set for modulation(0)
	goto	tmr2a		; If not set for modulation, just clear and return
	btfss	stat1,2		; Check if current bit being transferred is a 1
	goto	tmr2a		; If not, skip and just output 0. Otherwise, is 1, then:
	movlw	0x09		; Toggle IR on state @ 40kHz (39.01kHz) (on PORTB,0 and PORTB,3)
	xorwf	PORTB,1
tmr2a	bcf	PIR1,TMR2IF	; Clear Timer2 Interrupt flag


othint2	btfsc	INTCON,T0IF	; Test for Timer0 Interrupt Flag
	bcf	INTCON,T0IF
	btfsc	INTCON,INTF	; Test for External Interrup Flag
	bcf	INTCON,INTF


iend	movf	stemp,w	; Retrieve copy of STATUS register
	movwf	STATUS		; Restore pre-isr STATUS register contents
	swapf	wtemp,f
	swapf	wtemp,w	; Restore pre-isr W register contents

	retfie




;****************************************************************************
;****************************************************************************
;************************************ Tables *********************************
;****************************************************************************
;****************************************************************************
	org	0390h
msg1	addwf	PCL,F	;,1
	retlw	'R'		; "Ready" 00,07
	retlw	'e'
	retlw	'a'
	retlw	'd'
	retlw	'y'
msgx	retlw	0x0D		; Carriage return
	retlw	0x0A		; Line feed
msg2	retlw	A'R'		; "Ready to save, pick a character: " 07,22
	retlw	A'e'
	retlw	A'a'
	retlw	A'd'
	retlw	A'y'
	retlw	A' '
	retlw	A't'
	retlw	A'o'
	retlw	A' '
	retlw	A's'
	retlw	A'a'
	retlw	A'v'
	retlw	A'e'
	retlw	A','
	retlw	A' '
	retlw	A'p'
	retlw	A'i'
	retlw	A'c'
	retlw	A'k'
	retlw	A' '
	retlw	A'a'
	retlw	A' '
	retlw	A'c'
	retlw	A'h'
	retlw	A'a'
	retlw	A'r'
	retlw	A'a'
	retlw	A'c'
	retlw	A't'
	retlw	A'e'
	retlw	A'r'
	retlw	A':'
	retlw	A' '
msg3	retlw	0x0D		; Carriage return   	; "Already used. Overwrite? Y/N?" 29,1C
	retlw	0x0A		; Line feed
	retlw	A'A'
	retlw	A'l'
	retlw	A'r'
	retlw	A'e'
	retlw	A'a'
	retlw	A'd'
	retlw	A'y'
	retlw	A' '
	retlw	A'u'
	retlw	A's'
	retlw	A'e'
	retlw	A'd'
	retlw	A'.'
	retlw	A' '
	retlw	A'O'
	retlw	A'v'
	retlw	A'e'
	retlw	A'r'
	retlw	A'w'
	retlw	A'r'
	retlw	A'i'
	retlw	A't'
	retlw	A'e'
	retlw	A'?'
	retlw	A' '
	retlw	A'Y'
	retlw	A'/'
	retlw	A'N'
	retlw	A'?'
	retlw	A' '
msg4	retlw	0x0D		; "Erase all E2? Y/N?" 43,12 (Carriage return first)
	retlw	0x0A		; Line feed
	retlw	A'E'
	retlw	A'r'
	retlw	A'a'
	retlw	A's'
	retlw	A'e'
	retlw	A' '
	retlw	A'a'
	retlw	A'l'
	retlw	A'l'
	retlw	A' '
	retlw	A'E'
	retlw	A'2'
	retlw	A'?'
	retlw	A' '
	retlw	A'Y'
	retlw	A'/'
	retlw	A'N'
	retlw	A'?'
	retlw	A' '
msg5	retlw	0x0D		; "Abort" 5B,09 - Carriage return
	retlw	0x0A		; Line feed
	retlw	A'A'
	retlw	A'b'
	retlw	A'o'
	retlw	A'r'
	retlw	A't'
msg6	retlw	0x0D		; "OK" 62,06  Carriage return
	retlw	0x0A		; Line feed
	retlw	A'O'
	retlw	A'K'
	retlw	0x0D		; Carriage return
	retlw	0x0A		; Line feed


;---------------------------------------------------------------------------------------------------
	END
