; MBCN.asm
; MicroBeacon Transmitter Program
; Public version 01 : 10 August 2009
; Read protect enabled
; POSIT reporting only
; Air-tested successfully 10 Aug 2009

; See http://www.silcom.com/~pelican2/PicoDopp/XDOPP.htm#WBCN for a config utility program 
; that runs on a PC

; 18F442 chip, 44 pin TQFP version, 20 MHz xtal

; Weight = 0.24 oz.
; Output = 1.3 W @ 9.0 VDC 0.33 A = 43% eff
; Output = 2.4 W @ 12.0 VDC 0.46 A = 43% eff
; Output = 1.6 W @ 10.0 VDC 0.39 A = 41% eff
; Output = 3.2 W @ 13.8 VDC 0.52 A = 45% eff
 
; TMR0 = TONE clock ( for sine wave gen ) 
; TMR1 = Baud rate clock
; TMR3 = TX timer clock ( repeat timer ) 

; Enable host messages by jumpering ICSP plug, pins 1 and 6, and then cold re-boot
; Disable by removing jumper, then re-boot

; Host message format : 66 chars including <cr>

; 1443900020WB6EYV0WIDE1 1WIDE2 B/\COMMENT HERE DE WB6EYV           <cr>
; FFFFFFTTTTSSSSSSsVVVVVVvVVVVVVvPpCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC<cr>

; FFFFFF = TX frequency in KHz ( EX : 144390 = 144.390 KHz )
; TTTT = TX repeat time in seconds ( EX : 3600 = 1 hour )
; SSSSSS = Source call sign ( left justified, pad with spaces ) 
;	( EX 1 : WB6EYV ) ( = 6 chars )
;	( EX 2 : K6AU   ) ( = 6 chars, left justified and filled with space characters )
; s = Source SSID byte ( description below )
; VVVVVV = VIA 1 call sign ( left justified, pad with spaces )( this is optional... ) 
;	( EX 1 : WB6EYV ) ( = 6 chars )
;	( EX 2 : WIDE1  ) ( = 6 chars, left justified and filled with space characters )
; v = VIA 1 SSID byte ( description below )( only req'd if VIA 1 is used )
; VVVVVV = VIA 2 call sign ( left justified, pad with spaces ) ( this is optional... )
;	( EX 1 : WB6EYV ) ( = 6 chars )
;	( EX 2 : WIDE2  ) ( = 6 chars, left justified and filled with space characters )
; v = VIA 2 SSID byte ( description below )( only req'd if VIA 2 is used )
; P = APRS symbol table for POSIT reports
; p = APRS symbol code for POSIT reports
; CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC = comment
; <cr> = carriage return ( end of host message )

; SSID characters
; DESTINATION and SOURCE call signs are required
; VIA 1 and VIA 2 call signs are optional
; Therefore, header may contain 2, 3 or 4 call signs and SSID bytes
; ( number of call signs = user defined )
; Last SSID byte is different from earlier ones, this difference signals "end of header"

; Select SSID number from left hand column
; Use char from middle column, EXCEPT for last SSID byte
; Use character from right hand column for LAST SSID byte

;    SSID   	WITH		    W/O EXT
;     NR    	EXT	     	  (LAST SSID)

;	 0	  0	(=0x30)	  @	(=0x40)
;	 1	  1	(=0x31)	  A	(=0x41)
;	 2	  2	(=0x32)	  B	(=0x42)
;	 3	  3	(=0x33)	  C	(=0x43)
;	 4	  4	(=0x34)	  D	(=0x44)
;	 5	  5	(=0x35)	  E	(=0x45)
;	 6	  6	(=0x36)	  F	(=0x46)
;	 7	  7	(=0x37)	  G	(=0x47)
;	 8	  8	(=0x38)	  H	(=0x48)
;	 9	  9	(=0x39)	  I	(=0x49)
;	10	  :	(=0x3a)	  J	(=0x4a)
;	11	  ;	(=0x3b)	  K	(=0x4b)
;	12	  <	(=0x3c)	  L	(=0x4c)
;	13	  =	(=0x3d)	  M	(=0x4d)
;	14	  >	(=0x3e)	  N	(=0x4e)
;	15	  ?	(=0x3f)	  O	(=0x4f)


;--------------------------------------------------------------------------------------------------------------------------------------
;
;
;	DECLARATIONS
;
;
;--------------------------------------------------------------------------------------------------------------------------------------

; Pin assignments

#DEFINE		PLL_CLK	PORTC,4	;AnDev PLL clock output
#DEFINE		PLL_DATA	PORTC,5	;AnDev PLL data output
#DEFINE		PLL_LE	PORTC,1	;AnDev PLL latch enable output
#DEFINE		PLL_CE	PORTC,2	;AnDev PLL chip enable output

#DEFINE		PA_ENABLE	PORTC,0	;PA stage enable output

; Status and flag bits

#DEFINE		BIT_DONE	FLAGS,0	;previous bit has been sent if bit = 1
#DEFINE		NEXT_BIT	FLAGS,1	;next data bit to send
#DEFINE		HI_TONE	FLAGS,2	;present tone = 2200 Hz if bit = 1
#DEFINE		TX_NOW	FLAGS,3	;send a message if bit = 1
#DEFINE		TICK		FLAGS,4	;utility 100 millisec timer bit
#DEFINE		CHAR_RCVD	FLAGS,5	;bit = 1 if UART character was recieved
#DEFINE		GPS_RCVD	FLAGS,6	;bit = 1 if complete GPRMC message was recieved
#DEFINE		HOST_SAVED	FLAGS,7	;bit = 1 if HOST message was EE saved


; Constants
;-------------------------------------------------------------------

; UART baud rate

BAUD				equ		0x40		;baud rate 4.8 KB w/BRGH = 0 @ 20 MHz

; Tone frequencies ( for TMR0 )

HZ_1200 			equ	 	0xc1
HZ_2200 			equ		0xdf

; Tone baud rate ( for TMR1 )
; ( 1200 baud = complement of 4166 decimal with 20 MHz crystal ) 

TONE_BAUD_HI		equ		0xef		;1200 baud hi byte
TONE_BAUD_LO		equ		0xb9		;1200 baud low byte

; APRS message parameters

START_ZEROS		equ		0x0a		;10 starting zeros
START_FLAGS		equ		0x0a		;10 starting flags
END_FLAGS			equ		0x0a		;10 ending flags

; AnDev PLL chip, R register start-up bytes
; 00000 10 00 1000 0 0 0011 00000000000 00
; 0000 0100 0100 0000 0110 0000 0000 0000
; 04 40 60 00

PLL_BYTE_R1		equ		0x04		;for 150 MHz, VCO = DIV/4
PLL_BYTE_R2		equ		0x40
PLL_BYTE_R3		equ		0x60
PLL_BYTE_R4		equ		0x00

; AnDev PLL chip, N register start-up bytes
; 00000000 0 0 00100011 010000000110 01
; 00000000 00001000 11010000 00011001
; 00 08 D0 19 

;PLL_BYTE_N1		equ		0x00		;for 144.390 MHz
;PLL_BYTE_N2		equ		0x08
;PLL_BYTE_N3		equ		0xd0
;PLL_BYTE_N4		equ		0x19

; AnDev PLL chip, M register start-up bytes
; 0000 000 00 000 000000000 111111 0 00 10
; 0000 0000 0000 0000 0000 0111 1110 0010
; 00 00 07 E2

PLL_BYTE_M1		equ		0x00
PLL_BYTE_M2		equ		0x00
PLL_BYTE_M3		equ		0x07
PLL_BYTE_M4		equ		0xe2

; AnDev PLL chip, F register start-up bytes
; 0000 00000 111 1010 1 0100 0 00 01 0 0 1 1 11
; 0000 0000 0111 1010 1010 0000 0100 1111
; 00 7A A0 4F

PLL_BYTE_F1		equ		0x00
PLL_BYTE_F2		equ		0x7a
PLL_BYTE_F3		equ		0xa0
PLL_BYTE_F4		equ		0x4f

; EE memory addresses

	cblock	0x00				;start of EE memory

			EE_N1				;PLL N register byte 1
			EE_N2					
			EE_N3					
			EE_N4					
	
			EE_TX_TIME_HI		;TX repeat time, hi byte, seconds
			EE_TX_TIME_LO

			EE_DEST_1			;destination call sign ( "APDF00" )
			EE_DEST_2
			EE_DEST_3
			EE_DEST_4
			EE_DEST_5
			EE_DEST_6
			EE_DEST_SSID

			EE_SRC_1			;source call sign
			EE_SRC_2				
			EE_SRC_3				
			EE_SRC_4				
			EE_SRC_5				
			EE_SRC_6				
			EE_SRC_SSID		;source SSID

			EE_VIA1_1			;VIA 1 call sign
			EE_VIA1_2				
			EE_VIA1_3				
			EE_VIA1_4				
			EE_VIA1_5				
			EE_VIA1_6				
			EE_VIA1_SSID		;VIA 1 SSID

			EE_VIA2_1			;VIA 2 call sign
			EE_VIA2_2				
			EE_VIA2_3				
			EE_VIA2_4				
			EE_VIA2_5				
			EE_VIA2_6				
			EE_VIA2_SSID		;VIA 2 SSID

			EE_POS_TABLE		;APRS symbol table for POSIT reports
			EE_POS_CODE		;APRS symbol code for POSIT reports

			EE_COMMENT_01		;POSIT comment char 1
			EE_COMMENT_02				
			EE_COMMENT_03			
			EE_COMMENT_04			
			EE_COMMENT_05			
			EE_COMMENT_06			
			EE_COMMENT_07			
			EE_COMMENT_08			
			EE_COMMENT_09			
			EE_COMMENT_10			
			EE_COMMENT_11			
			EE_COMMENT_12			
			EE_COMMENT_13			
			EE_COMMENT_14			
			EE_COMMENT_15			
			EE_COMMENT_16			
			EE_COMMENT_17			
			EE_COMMENT_18			
			EE_COMMENT_19			
			EE_COMMENT_20			
			EE_COMMENT_21			
			EE_COMMENT_22			
			EE_COMMENT_23			
			EE_COMMENT_24			
			EE_COMMENT_25			
			EE_COMMENT_26			
			EE_COMMENT_27			
			EE_COMMENT_28			
			EE_COMMENT_29			
			EE_COMMENT_30			
			EE_COMMENT_31			
			EE_COMMENT_32
			EE_CR	

	endc						;end of EE memory


; Variables
;-------------------------------------------------------------------

; NOTE : Due to limited amount of ACCESS RAM, ( 00-7F ) HOST MESSAGE
; characters are located in BANK 0, but outside the ACCESS bank. 
; ( HOST message characters  = 0x80 to 0xff )  


; APRS mode variables follow :
;-------------------------------------------------------------------
 
	cblock	0x00				; start of ACCESS RAM bank
							; for regular APRS operation
 
; General-purpose 'utility' variables
; ( No specific purpose, usually a loop index )

			NDX1
			NDX2
			NDX3

; Various flag bits
; See #DEFINE statements ( above ) for more details

			FLAGS				;bit 0 = BIT_DONE
							;bit 1 = NEXT_BIT
							;bit 2 = HI_TONE
							;bit 3 = TX_NOW
							;bit 4 = TICK
							;bit 5 = CHAR_RCVD
							;bit 6 = GPS_RCVD

; PLL chip variables

			ANDEV1			;first byte to send to PLL chip
			ANDEV2			;second byte to send
			ANDEV3			;third byte
			ANDEV4			;last byte
			ANDEV_BUFF			;byte being presently sent

; Tone generator variables

			NEXT_TMR0			;next reload value for TMR0
			NOW_TMR0			;present reload value for TMR0

; GPS message variables
; These variables are used to detect and parse the GPS message as it arrives

			CHAR_BUFF			;last char rcv'd from GPS

			ALPHA_TEST			;temporary buffer for alpha test

			GPRMC_STATUS		;present status of GPRMC message parsing
							;0x00 = looking for start char ( $ )
							;0x04 = looking for G character
							;0x08 = looking for P character
							;0x0c = looking for R character
							;0x10 = looking for M character
							;0x14 = looking for C character
							;0x18 = looking for comma character	( , )
							;0x1c = looking for GPS_TIME char ( 6 chars )
							;0x20 = looking for comma char ( , )
							;0x24 = looking for GPS_VALID char ( A )
							;0x28 = looking for comma char ( , )
							;0x2c = looking for GPS_LAT char ( 7 chars, DDMM.MM )
							;0x30 = looking for comma char ( , )
							;0x34 = looking for GPS_LAT_HEMI char( N or S )
							;0x38 = looking for comma char ( , )
							;0x3c = looking for GPS_LONG char ( 8 chars, DDDMM.MM )
							;0x40 = looking for comma char ( , )
							;0x44 = looking for GPS_LONG_HEMI char ( W or E )
							;0x48 = looking for comma char ( , )
							;0x4c = looking for GPS_SPD char ( 3 chars, SSS )
							;0x50 = looking for comma char ( , )
							;0x54 = looking for GPS_COURSE char ( 3 chars, CCC ) 
							;0x58 = looking for end-of-message char ( <lf> )

			GPS_PTR			;Pointer to GPS data presently being parsed
			GPS_BYTES			;number of GPS bytes that presently have been parsed

			SPD_BYTES			;number of speed bytes recieved ( can be 1 to 3 )
			CRS_BYTES			;number of course bytes recieved ( can be 1 to 3 )

; These variables hold the parsed GPS data
; ASCII characters

			TIME_HOURS_X10		;UTC hours X10		
			TIME_HOURS_X1
			TIME_MINUTES_X10
			TIME_MINUTES_X1
			TIME_SECONDS_X10
			TIME_SECONDS_X1

			LAT_DEG_X10		;latitude degrees X10
			LAT_DEG_X1
			LAT_MIN_X10
			LAT_MIN_X1
			LAT_DP			;latitude decimal point
			LAT_MIN_x.1
			LAT_MIN_x.01

			LAT_HEMI			;latitude hemisphere ( N or S )

			LONG_DEG_X100		;longitude degrees X100	
			LONG_DEG_X10
			LONG_DEG_X1
			LONG_MIN_X10
			LONG_MIN_X1
			LONG_DP			;longitude decimal point
			LONG_MIN_x.1
			LONG_MIN_x.01

			LONG_HEMI			;longitude hemisphere ( E or W ) 

			SPD_X100			;speed X100 knots or mph
			SPD_X10
			SPD_X1

			CRS_X100			;true course degrees X100	
			CRS_X10
			CRS_X1

; BCD/binary conversion variables

			BB_NDX			;BCD-binary loop index

			BBT_HI			;BCD-binary temporary registers
			BBT_MID
			BBT_LO

			BBR_HI			;BCD-binary result registers
			BBR_MID
			BBR_LO

			BCDX1T			;binary-BCD temporary registers
			BCDX10T
			BCDX100T

			BCDX1				;binary-BCD result registers 
			BCDX10
			BCDX100
			
; APRS message bit and byte variables

			SEND_REG			;byte being presently sent
			BIT_CNT			;bits remaining to be sent in this byte

			BIT_STUFF_CNT		;total consecutive "1" bits since last 0 bit

			CRC_HI			;CRC value hi bye
			CRC_LO			;CRC value lo byte
			CRC_TEMP			;temporary reg for CRC

			JUMP_PTR			;pointer for APRS jump table
			CHAR_CNT			;char counter for APRS text fields
			CHAR_PTR			;char pointer for APRS text fields

; Transmit timer values

			TX_CNT_HI			;time to next TX x 50 millisec, hi byte
			TX_CNT_LO			;lo byte

; HOST mode variables

			HOST_BYTES

; EE memory variables

			EE_ADDR
			EE_DATA

; End of RAM addresses for regular APRS operation

	endc


; HOST mode variables follow :
;-------------------------------------------------------------------

	cblock	0x80				; BANK 0 RAM for HOST routines
							; ( outside of ACCESS bank )

; Host message characters

			HOST_FREQ_MX100		;TX frequency, MHX x100
			HOST_FREQ_MX10
			HOST_FREQ_MX1
			HOST_FREQ_KX100
			HOST_FREQ_KX10
			HOST_FREQ_KX1

			HOST_TIME_X1K		;TX repeat time, seconds x1K
			HOST_TIME_X100
			HOST_TIME_X10
			HOST_TIME_X1

			HOST_SRC_BYTE1		;source call sign, 1st char
			HOST_SRC_BYTE2
			HOST_SRC_BYTE3
			HOST_SRC_BYTE4
			HOST_SRC_BYTE5
			HOST_SRC_BYTE6

			HOST_SRC_SSID		;source SSID byte

			HOST_V1_BYTE1		;VIA 1 call sign, 1st char
			HOST_V1_BYTE2
			HOST_V1_BYTE3
			HOST_V1_BYTE4
			HOST_V1_BYTE5
			HOST_V1_BYTE6

			HOST_V1_SSID		;VIA 1 SSID byte

			HOST_V2_BYTE1		;VIA 2 call sign, 1st char
			HOST_V2_BYTE2
			HOST_V2_BYTE3
			HOST_V2_BYTE4
			HOST_V2_BYTE5
			HOST_V2_BYTE6

			HOST_V2_SSID		;VIA 2 SSID byte

			HOST_POS_TABLE		;POSIT report symbol table
			HOST_POS_CODE		;POSIT report symbol code

			HOST_COMMENT_01		;Comment
			HOST_COMMENT_02
			HOST_COMMENT_03
			HOST_COMMENT_04
			HOST_COMMENT_05
			HOST_COMMENT_06
			HOST_COMMENT_07
			HOST_COMMENT_08
			HOST_COMMENT_09
			HOST_COMMENT_10
			HOST_COMMENT_11
			HOST_COMMENT_12
			HOST_COMMENT_13
			HOST_COMMENT_14
			HOST_COMMENT_15
			HOST_COMMENT_16
			HOST_COMMENT_17
			HOST_COMMENT_18
			HOST_COMMENT_19
			HOST_COMMENT_20
			HOST_COMMENT_21
			HOST_COMMENT_22
			HOST_COMMENT_23
			HOST_COMMENT_24
			HOST_COMMENT_25
			HOST_COMMENT_26
			HOST_COMMENT_27
			HOST_COMMENT_28
			HOST_COMMENT_29
			HOST_COMMENT_30
			HOST_COMMENT_31
			HOST_COMMENT_32

			HOST_CR

; End of RAM addresses for HOST message bytes

	endc
	

;--------------------------------------------------------------------------------------------------------------------------------------
;
;
;	ASSEMBLER DIRECTIVES AND CONFIG BYTES
;
;
;--------------------------------------------------------------------------------------------------------------------------------------

		processor	18F442		; identify the device

		#include 	<P18f442.inc> 	; register + bit definitions

; Configuration bits

		CONFIG	OSCS=OFF
		CONFIG	OSC=HS
		CONFIG	BOR=OFF
		CONFIG	PWRT=ON
		CONFIG	WDT=OFF
		CONFIG	CCP2MUX=ON
		CONFIG	STVR=OFF
		CONFIG	LVP=OFF
		CONFIG	DEBUG=OFF
		CONFIG	CP0=ON
		CONFIG	CP1=ON
		CONFIG	CPB=ON
		CONFIG	CPD=OFF
		CONFIG	WRT0=ON
		CONFIG	WRT1=ON
		CONFIG	WRTC=ON
		CONFIG	WRTB=ON
		CONFIG	WRTD=OFF
		CONFIG	EBTR0=OFF
		CONFIG	EBTR1=OFF
		CONFIG	EBTRB=OFF

; Default EE memory contents

		org		0xf00000		;EE memory start

		db		0x00,0x08		;144.390 MHz
		db		0xd0,0x19

		db		0x0b,0xb8		;5 minute repeat time
							;300 sec x10 int/sec

		db		0x82,0xa0		;"APDF00-0"
		db		0x88,0x8c	
		db		0x60,0x60
		db		0x60,0xaa		;"URCALL-0"
		db		0xa4,0x86
		db		0x82,0x98
		db		0x98,0x60

		db		0xae,0x92		;"WIDE1-1"	
		db		0x88,0x8a
		db		0x62,0x40
		db		0x62,0xae		;"WIDE2-2"
		db		0x92,0x88
		db		0x8a,0x64
		db		0x40,0x65

		db		0x2f,0x3e		;car symbol ( POSIT reports )

		db		0x43,0x4f		;"COMMENT"<cr>
		db		0x4d,0x4d
		db		0x45,0x4e
		db		0x54,0x0d

;--------------------------------------------------------------------------------------------------------------------------------------
;
;
;                   EXECUTABLE CODE BEGINS HERE
;
;
;--------------------------------------------------------------------------------------------------------------------------------------

; CSTART routine ( coldstart )
; CPU comes here when DC power = on
;-------------------------------------------------------------------

		org 		0x00	 		;start of Program Memory

CSTART
		bra		MSTART		;jump around the interrupt routine


;--------------------------------------------------------------------------------------------------------------------------------------
;
;
;	INTERRUPT ROUTINES																	INTERRUPT
;
;
;--------------------------------------------------------------------------------------------------------------------------------------


; INT routine
; CPU comes here when an interrupt occurs
;-------------------------------------------------------------------

		org		0x08			;start of interrupt routine

INT

; Interrupt source = TMR0 ?

		btfss		INTCON,TMR0IF	;TMR0 interrupt ?
		goto		INT_BAUD		;jump if not
		btfss		INTCON,TMR0IE	;TMR0 interrupt enabled ?
		goto		INT_BAUD		;jump if not


; INT_TONE routine
;-------------------------------------------------------------------

; Interrupt came from TMR0 
;( clock for 1200 Hz / 2200 Hz tones ) 
; Clear the TMR0 interrupt flag

INT_TONE
		bcf		INTCON,TMR0IF

; Reload TMR0 with proper value for the present tone

		movf		NOW_TMR0,W
		addwf		TMR0L

; Read the sine table value and then increment the pointer
; Send the table value to PORT D ( = DAC )

		tblrd*+				;get table value and increment pointer
		movff		TABLAT,PORTD	;send table value to PORT D

; Check if table pointer has overflowed ( = end of table )
; If not, exit the interrupt routine 

		btfss		TBLPTRL,6		;TBLPTRL overflowed ?
		goto		INT_DONE		;exit

; Table pointer has overflowed, reset it to starting value

		movlw		LOW SIN_TAB	+1	;first entry in SIN_TAB
		movwf		TBLPTRL
		goto		INT_DONE


; INT_BAUD routine
;-------------------------------------------------------------------

INT_BAUD

; Interrupt source = TMR1 ?

		btfss		PIR1,TMR1IF
		goto		INT_TX
		btfss		PIE1,TMR1IE
		goto		INT_TX

; Interrupt came from TMR1
; ( bit timer for 1200 baud data rate )
; Clear the interrupt flag bit

		bcf		PIR1,TMR1IF

; Get the next reload value for TMR0 and store it
; ( = tone for next data bit ) 

		movf		NEXT_TMR0,W
		movwf		NOW_TMR0

; Reload TMR1 low byte
; ( TMR1 high byte reloads automatically ) 

		movlw		TONE_BAUD_LO	;1200 baud low byte
		addwf		TMR1L,F

; Flag the bit change and exit

		bsf		BIT_DONE
		goto		INT_DONE


; INT_TX routine
;-------------------------------------------------------------------

INT_TX

; Interrupt source = TMR3 ?

		btfss		PIE2,TMR3IE
		goto		INT_UART
		btfss		PIR2,TMR3IF
		goto		INT_UART

; Interrupt came from TX timer
; ( repeat timer for transmissions )
; Clear the interrupt flag
; Reload TMR3 with value for 100 milliseconds 

		bcf		PIR2,TMR3IF

		movlw		0x0b
		addwf		TMR3H,F
		movlw		0xdc
		addwf		TMR3L,F

; Set the 100 millisec flag bit

		bsf		TICK 

; Decrement the TX time counter
; Exit if no underflow

		decf		TX_CNT_LO,F
		btfsc		STATUS,C
		goto		INT_DONE

		decf		TX_CNT_HI,F
		btfsc		STATUS,C
		goto		INT_DONE

; TX time counter has underflowed
; Reload TX_CNT
 		
		movlw		EE_TX_TIME_HI	;TX_TIME_HI EE addr
		movwf		EE_ADDR
		call		EE_READ
		movf		EE_DATA,W		;get TX_TIME_HI
		movwf		TX_CNT_HI		;store it

		movlw		EE_TX_TIME_LO	;TX_TIME_LO EE addr
		movwf		EE_ADDR
		call		EE_READ
		movf		EE_DATA,W		;get TX_TIME_LO
		movwf		TX_CNT_LO		;store it

; Set flag to start a new TX and exit
 		
		bsf		TX_NOW

		goto		INT_DONE


; INT_UART routine
;-------------------------------------------------------------------

INT_UART
	
; Interrupt came from UART 
; Clear the interrupt flag ( read the RS232 byte )

		movf		RCREG,W		;get char
		andlw		0x7f			;7 bits only
		movwf		CHAR_BUFF		;save it

		bsf		CHAR_RCVD

		goto		INT_DONE		;exit


; INT_DONE routine
;-------------------------------------------------------------------

; Done with interrupt routine

INT_DONE

		retfie	FAST			;pop registers and return


;--------------------------------------------------------------------------------------------------------------------------------------
;
;
;	MAIN STARTUP ROUTINES																        STARTUP
;
;
;--------------------------------------------------------------------------------------------------------------------------------------


; MSTART routine ( main start-up routines are here )
;-------------------------------------------------------------------

MSTART

; Provide time delay for wake-up

		clrf		NDX1
		clrf		NDX2

MSTART_2
		decfsz	NDX1,F
		goto		MSTART_2
		decfsz	NDX2,F
		goto		MSTART_2

; Config the ADC ( 20 MHz )

		movlw		h'99'			;ADCON0 byte
							;clk div = 32 ( for 20 MHz )
							;ADC = on
							;selected input = AN3 ( DF )
		movwf		ADCON0

		movlw		h'82'			;ADCON1 byte
							;result = right justify
							;clk div = 32 ( for 20 MHz )
							;vref = vdd + vss
							;inputs AN0 to AN4
		movwf		ADCON1	

; Config the UART and BRG
; 4800 baud for GPS messages
; UART RX is disabled here... enabled in GPS routines

		movlw		0x80			;RCSTA config byte
							;serial port = enabled
							;RX data = 8 bits
							;recieve = off
							;address detect = off
		movwf		RCSTA

		movlw		0x24			;TXSTA byte, BRGH = 1
							;TX data = 8 bits
							;TX = enabled
							;mode = asynchronous
		movwf		TXSTA		

		movlw		BAUD			;4.8 KB
		movwf		SPBRG
		
		bcf		TXSTA,BRGH		;use low speed

; Config port pins

		bcf		TRISC,4		;PLL clock
		bcf		TRISC,5		;PLL data
		bcf		TRISC,1		;PLL latch
		bcf		TRISC,2		;PLL chip enable

		bcf		TRISC,0		;PA stage enable

		bcf		TRISC,6		;UART TX

		bcf		TRISD,0		;DAC bit 0
		bcf		TRISD,3		;DAC bit 1
		bcf		TRISD,4		;DAC bit 2
		bcf		TRISD,5		;DAC bit 3
		bcf		TRISD,6		;DAC bit 4
		bcf		TRISD,7		;DAC bit 5
		bcf		TRISD,1		;DAC bit 6
		bcf		TRISD,2		;DAC bit 7

; Initialize port pins

		bcf		PORTC,4		;PLL clock = 0
		bcf		PORTC,5		;PLL data = 0
		bcf		PORTC,1		;PLL latch = 0
		bcf		PORTC,2		;PLL chip enable = 0

		bcf		PORTC,0		;PA stage enable = 0

		clrf		PORTD			;all DAC bits = 0

; Config TMR0 for the tone generator
; ( 1200 Hz / 2200 Hz tones )

		movlw		0x48			;TMR0 = off ( no tone )
		movwf		T0CON

		movlw		HZ_1200		;1200 Hz

		movwf		NOW_TMR0
		movwf		NEXT_TMR0

; Config TMR1 for the baud rate clock
; ( 1200 baud )

		movlw		0x81	
		movwf		T1CON
		movlw		TONE_BAUD_HI	;1200 baud hi byte
		movwf		TMR1H
		movlw		TONE_BAUD_LO	;1200 baud low byte
		movwf		TMR1L

; Config TMR3 for TX repeat timer

		movlw		0x35
		movwf		T3CON
		movlw		0x0b			;100 millisec hi byte
		movwf		TMR3H
		movlw		0xdc			;100 millisec low byte
		movwf		TMR3L

; Set TX repeat time for immediate TX ( 1 second delay )

		movlw		0x00
		movwf		TX_CNT_HI
		movlw		0x0a
		movwf		TX_CNT_LO
		
; Initialize the variables

		bcf		TX_NOW

		movlw		LOW SIN_TAB	+1	;first entry in SIN_TAB
		movwf		TBLPTRL
		movlw		HIGH SIN_TAB	;high address byte of SIN_TAB
		movwf		TBLPTRH

; Check if HOST control is enabled
; ( port B bit 5 = TQFP pin 15 = grounded )
; ( allows beacon parameters to be changed )

		btfss		PORTB,5		;pin 15 = low ?
		goto		HOST_START		;jump if true

; Host control is not enabled
; Continue with start-up

; Clear all interrupt flags
; Enable interrupts

MSTART_3
		bcf		INTCON,TMR0IF	;1200/2200 tone timer
		bsf		INTCON,TMR0IE

		bcf		PIR1,TMR1IF	;1200 baud bit timer
		bsf		PIE1,TMR1IE

		bcf		PIR2,TMR3IF	;TX repeat timer
		bsf		PIE2,TMR3IE

		bsf		INTCON,PEIE
		bsf		INTCON,GIE

; Done with startup routines, proceed to EXEC
	

;--------------------------------------------------------------------------------------------------------------------------------------
;
;
;	EXECUTIVE ROUTINE																		     EXEC
;
;
;--------------------------------------------------------------------------------------------------------------------------------------


; EXEC routine ( main program loop )
; Top-level executive routines are here
;-------------------------------------------------------------------

EXEC

; Check if a timed POSIT report should be sent now

EXEC_TIMED

; Check TX_NOW flag bit
; Exit if not set

		btfss		TX_NOW
		goto		EXEC_FORCED

; Time to transmit ( timed POSIT report )
; Clear the TX_NOW flag

		bcf		TX_NOW

; Sent the timed POSIT report

		call		EXEC_SEND

; Continue forward and check for FORCED reports
; Check if a forced report is desired ( switch closure )

EXEC_FORCED

; Check PORTB RB6 ( forced TX switch input )
; Exit if switch = not closed

		btfsc		PORTB,6
		goto		EXEC			;exit if SWITCH = not closed

; Send the forced report ( POSIT )
; Loop to beginning and repeat

FORCED_SEND
		call		EXEC_SEND

		goto		EXEC

; Build and send an APRS message
; Get a fresh GPS message
; Send the APRS message

EXEC_SEND

; Get a fresh GPS message

		call		GPS_QUIT		;clear GPS buffers
		call		UART_ENABLE	;enable the UART

; Check for arrival of a GPS char

CHECK_UART
		btfss		CHAR_RCVD
		goto		CHECK_UART		;wait if no char recieved

		bcf		CHAR_RCVD
		call		RMC_PARSE		;process the new char

; Check if a complete GPS message has arrived

		btfss		GPS_RCVD		;GPS message finished ?
		goto		CHECK_UART		;loop if not

; Message has arrived and has been parsed
; Clear the GPS_RCVD flag bit

		bcf		GPS_RCVD

; Send the APRS message and return

		call		SEND_MSG		;send the message

		return


;--------------------------------------------------------------------------------------------------------------------------------------
;
;
;	GPS READ / PARSE ROUTINES																      GPS
;
;
;--------------------------------------------------------------------------------------------------------------------------------------


; GPS_QUIT routine
; Resets all GPS variables to their starting values
; Clears all GPS data buffers ( fills them with ASCII "0" characters )
;----------------------------------------------------------------------

GPS_QUIT
		clrf		GPRMC_STATUS	;begin search for $ char

		bcf		CHAR_RCVD		;discard any char waiting in UART

		bcf		GPS_RCVD		;valid GPS data = not available

		movlw 	TIME_HOURS_X10	;point to first multi-byte data field
		movwf		GPS_PTR

		clrf		GPS_BYTES		;zero message bytes have been parsed 
		clrf		SPD_BYTES		;zero speed bytes have been parsed
		clrf		CRS_BYTES		;zero course bytes have been parsed

; Fill the data buffers with ASCII "0"

		clrf		FSR0H
		movlw		TIME_HOURS_X10	;starting from HOURS_X10
		movwf		FSR0L

		movlw		0x1e			;all the way to CRS_X1 
		movwf		NDX1

		movlw		"0"			;data = ASCII "0"

; Do it

GPS_QUIT_2
		movwf		POSTINC0

		decfsz	NDX1,F
		goto		GPS_QUIT_2
		
		return


; UART_ENABLE routine
; Clear all UART errors and enable the UART 
;----------------------------------------------------------------------

UART_ENABLE
		bcf		RCSTA,4		;disable UART RX
		bcf		RCSTA,1		;overrun error clear
		bcf		RCSTA,2		;frame error clear
		movf		RCREG,0		;empty UART buffer
		movf		RCREG,0
		movf		RCREG,0
		bsf		RCSTA,4		;enable UART RX
		bsf		PIE1,RCIE		;enable UART interrupts

		return


; RMC_PARSE routine ( GPRMC jump table )
; Identify next action req'd to find / parse the GPRMC message
; Get GPRMC_STATUS and use it for jump table index
;----------------------------------------------------------------------

		org		0x200			;( table must not cross over
							;  a 256 byte boundary )  

; This is the GPRMC message "jump table"
; The value of GPRMC_STATUS is added to the PC registers 
; to do a "computed jump" to a specific table entry. Each table entry 
; consists of an executable "goto" statement which then directs the CPU
; to the particular routine that needs to process the character. 

; As each message character arrives, the CPU comes here and "jumps"
; to the specific routine that needs the character. When each routine 
; is finished, ( no more characters are needed ) the value of 
; GPRMC_STATUS is incremented, ( 4 times ) to point to the next table 
; entry. ( proceed to the next action req'd )

; The first routine that seeks a character is GPS_HDR_START, which
; seeks the "$" character. ( = GPS message start )

; The last routine that seeks a character is GPS_LF, which seeks
; a "line feed" character. ( = end of GPS message )

; Some routines seek only a single character, others seek multiple 
; characters. If an error is detected, everything is reset to the 
; starting conditions. ( this often happens when recieving other kinds 
; of GPS messages... not GPRMC messages )

; Only a GPRMC message will succeed in passing through ALL the 
; routines identified in this jump table.
   
RMC_PARSE
		movlw		HIGH RMC_PARSE
		movwf		PCLATH
		movlw		UPPER RMC_PARSE
		movwf		PCLATU
		movf		PCL,W			;this updates PCLATH and PCLATU
		movf		GPRMC_STATUS,W
		addwf		PCL,F			;jump to table entry

; Jump table starts here

		goto		GPS_HDR_START
		goto		GPS_HDR_G
		goto		GPS_HDR_P
		goto		GPS_HDR_R
		goto		GPS_HDR_M
		goto		GPS_HDR_C
		goto		GPS_COMMA
		goto		GPS_TIME
		goto		GPS_COMMA
		goto		GPS_VALID
		goto		GPS_COMMA
		goto		GPS_LAT
		goto		GPS_COMMA
		goto		GPS_LAT_HEMI
		goto		GPS_COMMA
		goto		GPS_LONG
		goto		GPS_COMMA
		goto		GPS_LONG_HEMI
		goto		GPS_COMMA
		goto		GPS_SPEED
		goto		GPS_COMMA
		goto		GPS_COURSE
		goto		GPS_LF

; End of RMC_PARSE jump table


; GPS detection / parsing routines begin here
;----------------------------------------------------------------------


; GPS_HDR_START
; Look for $ character ( = start of GPS message )
;----------------------------------------------------------------------

GPS_HDR_START
		movlw		"$"
		subwf		CHAR_BUFF,W	;UART char = $ ?
		btfss		STATUS,Z
		goto		GPS_QUIT		;quit if no match
							;( restart the whole search )

		incf		GPRMC_STATUS,F	;otherwise, next char
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	

		return


; GPS_HDR_G
; Look for G character ( 1st char in GPRMC header )
;----------------------------------------------------------------------

GPS_HDR_G
		movlw		"G"			;UART char = G or g ?
		goto		GPS_CHAR_CHECK	;find out


; GPS_HDR_P
; Look for P character ( 2nd char in GPRMC header )
;----------------------------------------------------------------------

GPS_HDR_P
		movlw		"P"			;UART char = P or p?
		goto		GPS_CHAR_CHECK	;find out


; GPS_HDR_R
; Look for R character ( 3rd char in GPRMC header )
;----------------------------------------------------------------------

GPS_HDR_R
		movlw		"R"			;UART char = R or r ?
		goto		GPS_CHAR_CHECK	;find out


; GPS_HDR_M
; Look for M character ( 4th char in GPRMC header )
;----------------------------------------------------------------------

GPS_HDR_M
		movlw		"M"			;UART	char = M or m ?
		goto		GPS_CHAR_CHECK	;find out


; GPS_HDR_C
; Look for C character ( 5th char in GPRMC header )
;----------------------------------------------------------------------

GPS_HDR_C
		movlw		"C"			;UART char = C or c ?
		goto		GPS_CHAR_CHECK	;find out


; GPS_CHAR_CHECK
; Compare UART char with char stored in W register
; Quit if no match
;----------------------------------------------------------------------

GPS_CHAR_CHECK
		bcf		CHAR_BUFF,5	;capitalize the char
		subwf		CHAR_BUFF,W	;capitalized char = W register ?
		btfss		STATUS,Z
		goto		GPS_QUIT		;quit if no match
							;( restart the whole search )
 
		incf		GPRMC_STATUS,F	;match found, so proceed to next step
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	

		return


; GPS_COMMA
; Look for comma character
; If no match, do NOT quit... 
; ( WAIT for a comma )
; If found, ignore it and proceed to the next step
;----------------------------------------------------------------------

GPS_COMMA
		call		CHECK_COMMA	;UART char = comma ?
		btfss		STATUS,Z
		return				;return if no match
							;do not quit... wait for a comma

		incf		GPRMC_STATUS,F	;comma found, so proceed to next step
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	

		return


; GPS_TIME
; Get GPS time
; 6 digits total
; HHMMSS ( GMT time )
;----------------------------------------------------------------------

GPS_TIME

; Is character numeric ?
; Quit if not numeric

		call		CHECK_NUMERIC
		btfss		STATUS,C
		goto		GPS_QUIT		;quit if char = not numeric

; Character is numeric, so save it

		clrf		FSR0H			;prepare to save the char
		movf		GPS_PTR,W
		movwf		FSR0L

		movf		CHAR_BUFF,W
		movwf		INDF0			;save the char

; Update the destination pointer and byte counter

		incf		GPS_PTR,F		;point to next char
		incf		GPS_BYTES,F	;bytes = bytes + 1

; Got 6 bytes yet ?
; Return if not

		movlw		6			;check if 6 bytes rcv'd yet
		subwf		GPS_BYTES,W
		btfss		STATUS,Z		;6 bytes yet ?

		return				;if not, return

; 6 bytes have been recieved and stored
; Add a "Z" character ( zulu time )

		movf		GPS_PTR,W
		movwf		FSR0L

		movlw		'Z'
		movwf		INDF0
		
; Prepare for next step ( wait for a comma )

		incf		GPRMC_STATUS,F	;next step
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	

		movlw 	LAT_DEG_X10	;prepare for next step
		movwf		GPS_PTR
		clrf		GPS_BYTES

		return


; GPS_VALID
; Check if GPS data = VALID
; Quit if not VALID
;----------------------------------------------------------------------

GPS_VALID
		movlw		"A"			
		goto		GPS_CHAR_CHECK


; GPS_LAT
; Get GPS latitude
; 7 bytes total ( more are ignored, if present )
; DDMM.MM
;----------------------------------------------------------------------

GPS_LAT

; Is character numeric ?

		call		CHECK_NUMERIC
		btfsc		STATUS,C		;char = numeric ?
		goto		GPS_LAT_2		;jump if true

; Char is not numeric, is it a decimal point ?

		call		CHECK_DP
		btfss		STATUS,Z		;char = decimal point ?
		goto		GPS_QUIT		;quit if not

; Character is numeric or a decimal point, so save it

GPS_LAT_2
		clrf		FSR0H			;prepare to save the char
		movf		GPS_PTR,W
		movwf		FSR0L

		movf		CHAR_BUFF,W		
		movwf		INDF0			;save the char

; Update the destination pointer and byte counter

		incf		GPS_PTR,F		;point to next char
		incf		GPS_BYTES,F	;bytes = bytes + 1

; Got 7 bytes yet ?
; Return if not

		movlw		7			;check if 7 bytes rcv'd yet
		subwf		GPS_BYTES,W
		btfss		STATUS,Z		;7 bytes yet ?
		return				;if not, return

; 7 bytes have been recieved and stored
; ( if more are sent, ignore them... wait for a comma )

		incf		GPRMC_STATUS,F	;otherwise, next step
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	

		movlw 	LAT_HEMI		;prepare for next step
		movwf		GPS_PTR
		clrf		GPS_BYTES

		return


; GPS_LAT_HEMI
; Get GPS latitude hemisphere ( N or S )
;----------------------------------------------------------------------

GPS_LAT_HEMI

; Is character alphabetical ?
; Quit if not

		call		CHECK_ALPHA
		btfss		STATUS,C		;char = alpha ?
		goto		GPS_QUIT		;quit if not

; Is character = N or n ?
; ( CHECK_ALPHA routine will capitalize the char )

		movlw		"N"
		subwf		CHAR_BUFF,W
		btfss		STATUS,Z		;north ?
		goto		LAT_HEMI_2		;no, try south

; Character = N or n, so save it

		clrf		FSR0H
		movf		GPS_PTR,W
		movwf		FSR0L

		movf		CHAR_BUFF,W
		movwf		INDF0			;store "N" char

; Prepare for next step

		incf		GPRMC_STATUS,F	;next step
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F
		incf		GPRMC_STATUS,F
	
		movlw 	LONG_DEG_X100	;prepare for next step
		movwf		GPS_PTR
		clrf		GPS_BYTES

		return

; Is character = S or s ?
; ( CHECK_ALPHA routine will capitalize the char )

LAT_HEMI_2	
		movlw		"S"
		subwf		CHAR_BUFF,W
		btfss		STATUS,Z		;south ?
		goto		GPS_QUIT		;quit if neither N or S

; Character = S or s, so save it

		clrf		FSR0H
		movf		GPS_PTR,W
		movwf		FSR0L

		movf		CHAR_BUFF,W
		movwf		INDF0			;store "S" char

; Prepare for next step

		incf		GPRMC_STATUS,F	;next step
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	

		movlw 	LONG_DEG_X100	;prepare for next step
		movwf		GPS_PTR
		clrf		GPS_BYTES

		return


; GPS_LONG
; Get GPS longitude
; 8 bytes total ( more are ignored, if present )
; DDDMM.MM
;----------------------------------------------------------------------

GPS_LONG

; Is character numeric ?

		call		CHECK_NUMERIC
		btfsc		STATUS,C		;char = numeric ?
		goto		GPS_LONG_2		;jump if true

; Char is not numeric, is it a decimal point ?

		call		CHECK_DP
		btfss		STATUS,Z		;char = decimal point ?
		goto		GPS_QUIT		;quit if not

; Character is numeric or a decimal point, so save it

GPS_LONG_2
		clrf		FSR0H			;prepare to save the char
		movf		GPS_PTR,W
		movwf		FSR0L

		movf		CHAR_BUFF,W
		movwf		INDF0			;save the char

; Update the destination pointer and byte counter

		incf		GPS_PTR,F		;point to next char
		incf		GPS_BYTES,F	;bytes = bytes + 1

; Got 8 bytes yet ?
; Return if not

		movlw		8			;check if 8 bytes rcv'd yet
		subwf		GPS_BYTES,W
		btfss		STATUS,Z		;8 bytes yet ?
		return				;if not, return

; 8 bytes have been recieved and stored
; ( if more are sent, ignore them... wait for a comma )

		incf		GPRMC_STATUS,F	;otherwise, next step
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F
	
		movlw 	LONG_HEMI		;prepare for next step
		movwf		GPS_PTR
		clrf		GPS_BYTES

		return


; GPS_LONG_HEMI
; Get GPS longitude hemisphere ( E or W )
;----------------------------------------------------------------------

GPS_LONG_HEMI

; Is character alphabetical ?
; Quit if not

		call		CHECK_ALPHA
		btfss		STATUS,C		;char = alpha ?
		goto		GPS_QUIT		;quit if not

; Is character = E or e ?
; ( CHECK_ALPHA routine will capitalize the char )

		movlw		"E"
		subwf		CHAR_BUFF,W
		btfss		STATUS,Z		;east ?
		goto		LONG_HEMI_2	;no, try west

; Character = E or e, so save it

		clrf		FSR0H
		movf		GPS_PTR,W
		movwf		FSR0L

		movf		CHAR_BUFF,W
		movwf		INDF0			;store "E" char

; Prepare for next step

		incf		GPRMC_STATUS,F	;next step
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F
	
		movlw 	SPD_X100		;prepare for next step
		movwf		GPS_PTR
		clrf		GPS_BYTES

		return

; Is character = W or w ?
; ( CHECK_ALPHA routine will capitalize the char )

LONG_HEMI_2	
		movlw		"W"
		subwf		CHAR_BUFF,W
		btfss		STATUS,Z		;west ?
		goto		GPS_QUIT		;neither E or W, so quit

; Character = W or w, so save it

		clrf		FSR0H
		movf		GPS_PTR,W
		movwf		FSR0L

		movf		CHAR_BUFF,W
		movwf		INDF0			;store "W" char

; Prepare for next step

		incf		GPRMC_STATUS,F	;next step
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F
	
		movlw 	SPD_X100		;prepare for next step
		movwf		GPS_PTR
		clrf		GPS_BYTES

		return


; GPS_SPEED
; Get GPS speed
; 3 bytes total ( more are ignored, if present )
; SSS
; If decimal point is detected, it will trigger end of search
; If less than 3 bytes are rcv'd, value is adjusted to right-justify
; the numbers... this is done after message = finished
;----------------------------------------------------------------------

GPS_SPEED

; Is character numeric ?

		call		CHECK_NUMERIC
		btfsc		STATUS,C		;char = numeric ?
		goto		SPEED_2		;jump if true

; Char is not numeric, is it a decimal point ?

		call		CHECK_DP
		btfss		STATUS,Z		;char = decimal point ?
		goto		GPS_QUIT		;quit if not

; Character is a decimal point
; Ignore it and prepare for next step
; ( end of search for speed characters )

		incf		GPRMC_STATUS,F	;next step if char = dp
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F
	
		movlw 	CRS_X100		;prepare for next step
		movwf		GPS_PTR
		clrf		GPS_BYTES

		return

; Character is numeric, so save it

SPEED_2
		clrf		FSR0H			;prepare to save char
		movf		GPS_PTR,W
		movwf		FSR0L

		movf		CHAR_BUFF,W
		movwf		INDF0			;save the char

; Update the destination pointer and byte counter

		incf		GPS_PTR,F		;point to next char
		incf		SPD_BYTES,F	;bytes = bytes + 1

; Got 3 bytes yet ?
; Return if not

		movlw		3			;check if 3 bytes rcv'd yet
		subwf		SPD_BYTES,W
		btfss		STATUS,Z		;3 bytes yet ?
		return				;if not, return

; 3 bytes have been recieved and stored
; ( if more are sent, ignore them... wait for a comma )

		incf		GPRMC_STATUS,F	;otherwise, next step
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F

		movlw 	CRS_X100		;prepare for next step
		movwf		GPS_PTR
		clrf		GPS_BYTES
	
		return


; GPS_COURSE
; Get GPS course
; 3 bytes total ( more are ignored, if present )
; CCC
; If decimal point is detected, it will trigger end of search
; If less than 3 bytes are rcv'd, value is adjusted to right-justify
; the numbers... this is done after message = finished
;----------------------------------------------------------------------

GPS_COURSE

; Is character numeric ?

		call		CHECK_NUMERIC
		btfsc		STATUS,C		;char = numeric ?
		goto		COURSE_2		;jump if true

; Char is not numeric, is it a decimal point ?

		call		CHECK_DP
		btfss		STATUS,Z		;char = decimal point ?
		goto		GPS_QUIT		;quit if not

; Character is a decimal point
; Ignore it and prepare for next step
; ( end of search for course characters )

		incf		GPRMC_STATUS,F	;next step if char = dp
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F
	
		return

; Character is numeric, so save it

COURSE_2
		clrf		FSR0H			;prepare to save char
		movf		GPS_PTR,W
		movwf		FSR0L

		movf		CHAR_BUFF,W
		movwf		INDF0			;save the char

; Update the destination pointer and byte counter

		incf		GPS_PTR,F		;point to next char
		incf		CRS_BYTES,F	;bytes = bytes + 1

; Got 3 bytes yet ?
; Return if not

		movlw		3			;check if 3 bytes rcv'd yet
		subwf		CRS_BYTES,W
		btfss		STATUS,Z		;3 bytes yet ?
		return				;if not, return

; 3 bytes have been recieved and stored
; ( if more are sent, ignore them... wait for a comma )

		incf		GPRMC_STATUS,F	;otherwise, next step
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	
		incf		GPRMC_STATUS,F	

		return


; GPS_LF
; Wait for ASCII <lf> character ( = end of GPS message )
;----------------------------------------------------------------------

GPS_LF
		movlw		0x0a			;ASCII <lf>
		subwf		CHAR_BUFF,W
		btfss		STATUS,Z		;char = <lf> ?

		return				;return if not


; If CPU gets here, a complete GPS message has been recieved and parsed
; Turn off the UART ( not needed any more )

DISABLE_GPS
		bcf		PIE1,RCIE		;disable UART interrupts
		bcf		RCSTA,4		;disable UART RX

; Adjust the speed data
; ( in case speed < 3 bytes )
 
ADJUST_SPEED
		movlw		3
		subwf		SPD_BYTES,W
		btfsc		STATUS,Z		;3 bytes yet ?
		goto		ADJUST_CRS		;jump if true

; Less than 3 speed bytes were recieved
; Adjust the data buffer and re-test
; Repeat until speed data is aligned properly
 
		movf		SPD_X10,W
		movwf		SPD_X1
		movf		SPD_X100,W
		movwf		SPD_X10
		movlw		"0"
		movwf		SPD_X100
		incf		SPD_BYTES,F
		goto		ADJUST_SPEED

; Adjust the course data
; ( in case course < 3 bytes )

ADJUST_CRS
		movlw		0x03
		subwf		CRS_BYTES,W
		btfsc		STATUS,Z		;3 bytes yet ?
		goto		ADJUST_DONE	;jump if true

; Less than 3 course bytes were recieved
; Adjust the data buffer and re-test
; Repeat until speed data is aligned properly

		movf		CRS_X10,W
		movwf		CRS_X1
		movf		CRS_X100,W
		movwf		CRS_X10
		movlw		"0"
		movwf		CRS_X100
		incf		CRS_BYTES,F
		goto		ADJUST_CRS

; If speed = 0 then make course = "000"
; ( APRS rule for DF messages ) 

; Check if speed = 0
 
ZERO_COURSE
		movlw		"0"
		subwf		SPD_X100,W
		btfss		STATUS,Z
		goto		NON_ZERO_COURSE

		movlw		"0"
		subwf		SPD_X10,W
		btfss		STATUS,Z
		goto		NON_ZERO_COURSE

		movlw		"0"
		subwf		SPD_X1,W
		btfss		STATUS,Z
		goto		NON_ZERO_COURSE

; Speed = 0
; Make course = 000

		movlw		"0"

		movwf		CRS_X100
		movwf		CRS_X10
		movwf		CRS_X1

		goto		ADJUST_DONE

; If speed > 0 then course must be > 0
; ( APRS rule for DF messages )

; Check if course = 000

NON_ZERO_COURSE
		movlw		"0"
		subwf		CRS_X100,W
		btfss		STATUS,Z
		goto		ADJUST_DONE

		movlw		"0"
		subwf		CRS_X10,W
		btfss		STATUS,Z
		goto		ADJUST_DONE

		movlw		"0"
		subwf		CRS_X1,W
		btfss		STATUS,Z
		goto		ADJUST_DONE

; Course = 000 and speed > 0
; Make course = 001
 
		incf		CRS_X1,F		;make course = "001"

; GPS mesage has been recieved and parsed successfully
; GPS course and speed values have been right-justifed ( if req'd )
; Set the GPS_RCVD flag bit to indicate valid GPS data is available
; Then return... all done here

ADJUST_DONE
		bsf		GPS_RCVD		;flag the new GPS message

		return


; Additional routines to check GPS characters for validity
; ( range checking of characters )

; CHECK_DP
; Check if GPS char is decimal point
; On return, STATUS ZERO bit is set if char = "."
;----------------------------------------------------------------------

CHECK_DP
		movlw		"."
		subwf		CHAR_BUFF,W
		return


; CHECK_COMMA
; Check if GPS char is comma
; On return, STATUS ZERO bit is set if char = ","
;----------------------------------------------------------------------

CHECK_COMMA
		movlw		","
		subwf		CHAR_BUFF,W
		return


; CHECK_NUMERIC
; Check if UART char is numeric
; On return, STATUS CARRY bit is set if char = numeric
;----------------------------------------------------------------------
 
CHECK_NUMERIC
		movlw		"0"
		subwf		CHAR_BUFF,W	;W = CHAR_BUFF - "0"
		btfss		STATUS,C		;char > "0" ?
		return				;not numeric, so return

		movf		CHAR_BUFF,W
		sublw		"9"			;W = "9" - CHAR_BUFF
		return


; CHECK_ALPHA
; Check if UART char is alpha
; On return, STATUS CARRY bit is set if char = alpha
; On return, char = capitalized if char = alpha
;----------------------------------------------------------------------

CHECK_ALPHA
		movf		CHAR_BUFF,W	;make a copy of the char
		movwf		ALPHA_TEST

		bcf		ALPHA_TEST,5	;capitalize the char
		movlw		"A"
		subwf		ALPHA_TEST,W	;W = ALPHA_TEST - "A"
		btfss		STATUS,C		;char > or = "A" ?
		return				;return if not alpha

		movf		ALPHA_TEST,W
		sublw		"Z"			;W = "Z" - ALPHA_TEST
		btfsc		STATUS,C		;char = alpha ?
		bcf		CHAR_BUFF,5	;if true, capitalize it

		return


;--------------------------------------------------------------------------------------------------------------------------------------
;
;
;	APRS MESSAGE CREATE / SEND ROUTINES														   	     APRS 
;
;
;--------------------------------------------------------------------------------------------------------------------------------------

; These routines generate and transmit the APRS packet

; SEND_MSG routine
; Send an APRS message
;-------------------------------------------------------------------

SEND_MSG	

; Check if channel is clear

; ( provisional... add code here, if desired )
 
; Channel is clear
; Prepare for next transmission
; Reset the CRC value

		movlw		h'ff'
		movwf		CRC_HI		;reset CRC calc
		movwf		CRC_LO

; Clear the bit stuff counter

		clrf		BIT_STUFF_CNT	;clear BSC

; Turn off TX timer ( TMR3 )
; ( it interferes with tone generation )
; ( reason = ? )

		bcf		T3CON,0		;turn off TMR3
	
; Start the transmission

		call		TX_START		;TX = on

; Energise the tone gen

		movlw		0xc8
		movwf		T0CON

; Send "zero bytes" 
; ( data = 0x00, accelerates clock sync at reciever end )
; ( performed as a courtesy for message reciever )

		movlw		START_ZEROS
		movwf		NDX1

SEND_START_ZEROS
		call		SEND_ZEROS		;send starting zeros
		decfsz	NDX1,F
		goto		SEND_START_ZEROS

; Send starting flags
; Required to identify start / end of bytes at the receiver

		movlw		START_FLAGS
		movwf		NDX1

SEND_START_FLAGS
		call		SEND_FLAG		;send starting flag
		decfsz	NDX1,F
		goto		SEND_START_FLAGS

; Prepare to send text
; Reset TXT_PTR to starting value

		movlw		0x00			;start of text table
		movwf		JUMP_PTR

; Send message text
; Last text char = 0x0d 
; ( ASCII <cr> signals end of text ) 

SEND_TEXT
		call		TEXT_TAB		;get next char to send
		movwf		SEND_REG
		movlw		0x0d			;last char = end of table ?
		subwf		SEND_REG,W
		btfsc		STATUS,Z
		goto		SEND_CRC		;jump if char = end of table
		call		SEND_BYTE		;otherwise, send byte and continue
		goto		SEND_TEXT

; Done with text
; Send inverted CRC bytes, LSB first

SEND_CRC
		movlw		0xff
		xorwf		CRC_HI,W
		movwf		CRC_TEMP		;save hi byte ( temp )
		movlw		0xff
		xorwf		CRC_LO,W
		movwf		SEND_REG
		call		SEND_BYTE		;send CRC low byte
		movf		CRC_TEMP,W		;retrieve temp save
		movwf		SEND_REG
		call		SEND_BYTE		;send CRC hi byte

; Send ending flags

		movlw		END_FLAGS
		movwf		NDX1

SEND_END_FLAGS
		call		SEND_FLAG		;send ending flags
		decfsz	NDX1,F
		goto		SEND_END_FLAGS

; Finish the transmission
; Turn off the tone gen
; Turn off the PLL chip
; Turn off the PA stage
; Re-start the TX timer

END_TX
		movlw		0x48			;TMR0 = off ( no tone )
		movwf		T0CON

		bcf		PLL_CE		;PLL = off
		bcf		PORTC,0		;PA = off
		bsf		T3CON,0		;TX timer = on

		return


; Message text look-up table
; This "jump table" generates the APRS message
;-------------------------------------------------------------------

		org		0x0600		;TEXT_TAB origin

; Get the next byte of text to send
; ( origin = 0x600 so table = a single 256 byte page )

TEXT_TAB
		movlw		HIGH TEXT_TAB	;table address hi byte
		movwf		PCLATH		;prepare for jump
		movf		JUMP_PTR,W		;point to next char		
		addwf		PCL,F

; Destination, Source and VIA call signs + SSID bytes ( from EE memory )
; ( ready to send : left rotated + bit 0 = cleared )

HEADER_TEXT
		goto		GET_HEADER
		goto		GET_HEADER_2

; CTRL code

CTRL_TEXT
		goto		GET_CTRL

; PROTO code

PROTO_TEXT
		goto		GET_PROTO

; Message format code

FORMAT_TEXT
		goto		GET_FORMAT
		
; Latitude bytes ( 8 bytes, alphanumeric )
; From RAM, includes hemisphere byte 

LAT_TEXT
		goto		GET_LAT
		goto		GET_LAT_2

; Symbol table identification

SYM_TAB_TEXT
		goto		GET_SYM_TAB

; Longitude bytes ( 9 bytes, alphanumeric )
; From RAM, includes hemisphere byte

LONG_TEXT
		goto		GET_LONG
		goto		GET_LONG_2

; Symbol code

SYM_CODE_TEXT
		goto		GET_SYM_CODE

; Course bytes ( 3 numeric bytes )
; From RAM

COURSE_TEXT
		goto		GET_CRS
		goto		GET_CRS_2

; Speed bytes ( 3 numeric bytes )
; From RAM

SPEED_TEXT
		goto		GET_SPD
		goto		GET_SPD_2

; Comment bytes, POSIT report ( 32 bytes max )

POSIT_COMMENT_TEXT
		goto		GET_COMMENT
		goto		GET_COMMENT_2


; Character fetch routines for individual APRS data fields begin here
;-------------------------------------------------------------------



; GET_HEADER routine
; Get and send HEADER characters ( from EE memory )
;-------------------------------------------------------------------

; Prepare for EE reads

GET_HEADER
		movlw		EE_DEST_1		;point to DEST byte 1
		movwf		EE_ADDR

; Advance the JUMP pointer

		incf		JUMP_PTR,F		;point to GET_HEADER_2
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

; Get the EE header byte
; Point to next byte

GET_HEADER_2
		call		EE_READ		;get from EE memory
		incf		EE_ADDR,F		;next EE address

; Check if this is last header byte
; ( last SSID bit 0 = 1 )

		btfss		EE_DATA,0
		goto		GET_HEADER_3

; This is last header byte
; ( SSID bit 0 = 1 )
; Advance the JUMP pointer

		incf		JUMP_PTR,F		;point to GET_CTRL
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

; Copy the header byte to W register and return

GET_HEADER_3	
		movf		EE_DATA,W

		return


; GET_CTRL
; Get and send CTRL character ( constant, always = 0x03 )
;-------------------------------------------------------------------

GET_CTRL

; Get the CTRL character

		movlw		0x03			;get the character

; Advance the JUMP pointer

		incf		JUMP_PTR,F		;point to GET_PROTO
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

		return


; GET_PROTO
; Get and send PROTO character ( constant, always = 0xf0 )
;-------------------------------------------------------------------

GET_PROTO

; Get the PROTO character

		movlw		0xf0			;get the character

; Advance the JUMP pointer

		incf		JUMP_PTR,F		;point to GET_FORMAT
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

		return


; GET_FORMAT
; Get and send FORMAT character ( from RAM memory )
;-------------------------------------------------------------------

GET_FORMAT

; Advance the JUMP pointer

		incf		JUMP_PTR,F		;point to GET_OBJ
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

; Get the FORMAT character for POSIT reports

		movlw		'!'

		return


; GET_LAT routine
; Get and send GPS latitude characters ( from RAM memory )
;-------------------------------------------------------------------

; Point to start of LAT characters in RAM memory

GET_LAT
		movlw		LAT_DEG_X10	;point to first LAT char
		movwf		CHAR_PTR

; Clear CHAR_CNT

		clrf		CHAR_CNT 

; Advance the JUMP pointer

		incf		JUMP_PTR,F		;point to GET_LAT_2
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

; Prepare to get the LAT character

GET_LAT_2
		movf		CHAR_PTR,W		
		movwf		FSR0L

		incf		CHAR_PTR,F		;next lat char
		incf		CHAR_CNT,F		

; Check if this is last LAT char

		movlw		0x08
		subwf		CHAR_CNT,W
		btfss		STATUS,Z		;last char ?
		goto		GET_LAT_2A		;jump if not

; This is last LAT character
; Advance the JUMP pointer

		incf		JUMP_PTR,F		;point to GET_SYM_TAB
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

; Get the character and return

GET_LAT_2A
		movf		INDF0,W		;get lat char

		return		

		
; GET_SYM_TAB routine
; Get and send SYM_TAB character ( from EE memory )
;-------------------------------------------------------------------

; Get the SYM_TABLE character 

GET_SYM_TAB

; Advance the JUMP pointer

		incf		JUMP_PTR,F		;point to GET_LONG
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

; Use symbol table char for POSIT reports

		movlw 	EE_POS_TABLE	;EE address for data
		movwf		EE_ADDR

		call		EE_READ		;get from EE memory
		movf		EE_DATA,W		;copy to W register

		return


; GET_LONG routine
; Get and send GPS longitude characters ( from RAM memory )
;-------------------------------------------------------------------

; Point to start of LONG characters in RAM memory

GET_LONG
		movlw		LONG_DEG_X100
		movwf		CHAR_PTR

; Clear CHAR_CNT

		clrf		CHAR_CNT 

; Advance the JUMP pointer

		incf		JUMP_PTR,F		;point to GET_LONG_2
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

; Prepare to get the LONG character

GET_LONG_2
		movf		CHAR_PTR,W		
		movwf		FSR0L

		incf		CHAR_PTR,F		;next long char
		incf		CHAR_CNT,F		

; Check if this is last LONG char

		movlw		0x09
		subwf		CHAR_CNT,W
		btfss		STATUS,Z		;last char ?
		goto		GET_LONG_2A	;jump if not

; This is last LONG character
; Advance the JUMP pointer

		incf		JUMP_PTR,F		;point to GET_SYM_CODE
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

; Get the character and return

GET_LONG_2A
		movf		INDF0,W		;get long char

		return		


; GET_SYM_CODE routine
; Get and send SYM_CODE character ( from EE memory )
;-------------------------------------------------------------------

; Get the SYM_CODE character 

GET_SYM_CODE

; Advance the JUMP pointer

		incf		JUMP_PTR,F		;point to GET_CRS
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

; Use symbol code char for POSIT reports

		movlw 	EE_POS_CODE	;EE address for data
		movwf		EE_ADDR

		call		EE_READ		;get from EE memory
		movf		EE_DATA,W		;copy to W register

		return


; GET_CRS routine
; Get and send GPS course characters ( from RAM memory )
; Include a delimiter character ( suffix )
;-------------------------------------------------------------------

; Advance the JUMP pointer

GET_CRS
		incf		JUMP_PTR,F		;point to GET_CRS_2
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

; Point to start of CRS characters in RAM memory

		movlw		CRS_X100
		movwf		CHAR_PTR

; Clear CHAR_CNT

		clrf		CHAR_CNT 

; Prepare to get the CRS character

GET_CRS_2
		movf		CHAR_PTR,W		
		movwf		FSR0L

		incf		CHAR_PTR,F		;next course char
		incf		CHAR_CNT,F		

; Check if time to send a delimiter char ( 4th char )

		movlw		0x04
		subwf		CHAR_CNT,W
		btfss		STATUS,Z		;4th char ?
		goto		GET_CRS_2A		;jump if not

; Time to send the delimiter char
; Advance the JUMP pointer

		incf		JUMP_PTR,F		;point to GET_SPD
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

; Stick a delimiter character in W register and return

		movlw		'/'

		return

; Get the course character and return

GET_CRS_2A
		movf		INDF0,W		;get time char

		return		


; GET_SPD routine
; Get and send GPS speed characters ( from RAM memory )
; Include a delimiter character ( suffix )
;-------------------------------------------------------------------

; Advance the JUMP pointer

GET_SPD
		incf		JUMP_PTR,F		;point to GET_SPD_2
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

; Point to start of SPD characters in RAM memory

		movlw		SPD_X100
		movwf		CHAR_PTR

; Clear CHAR_CNT

		clrf		CHAR_CNT 

; Prepare to get the SPD character

GET_SPD_2
		movf		CHAR_PTR,W		
		movwf		FSR0L

		incf		CHAR_PTR,F		;next speed char
		incf		CHAR_CNT,F		

; Check if time to send a delimiter char ( 4th char )

		movlw		0x04
		subwf		CHAR_CNT,W
		btfss		STATUS,Z		;4th char ?
		goto		GET_SPD_2A		;jump if not

; Time to send the delimiter char
; Advance the JUMP pointer

		incf		JUMP_PTR,F		;point to GET_BRG
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

; Stick a delimiter character in W register and return

		movlw		'/'

		return

; Get the speed character and return

GET_SPD_2A
		movf		INDF0,W		;get speed char

		return		


; GET_COMMENT routine
; Get user-defined COMMENT characters ( from EE memory )
;-------------------------------------------------------------------

; Advance the JUMP pointer

GET_COMMENT
		incf		JUMP_PTR,F		;point to GET_COMMENT_2
		incf		JUMP_PTR,F	
		incf		JUMP_PTR,F
		incf		JUMP_PTR,F

; Point to start of POSIT report COMMENT characters in EE memory

		movlw		EE_COMMENT_01
		movwf		EE_ADDR

; Send POSIT report comment characters 
; Get the next char
; ( last COMMENT char = ASCII <cr> )

GET_COMMENT_2
		call		EE_READ
		incf		EE_ADDR,F		;next comment char

		movf		EE_DATA,W

		return		


; SEND_ZEROS routine
; Sends one byte, 0x00 = b'00000000'
; No CRC input, no bit stuffing
;-------------------------------------------------------------------

SEND_ZEROS

		bcf		NEXT_BIT
		call 		SEND_BIT		;send 8 zeros
		call 		SEND_BIT
		call 		SEND_BIT
		call 		SEND_BIT
		call 		SEND_BIT
		call 		SEND_BIT
		call 		SEND_BIT
		call 		SEND_BIT
		return 				;done, exit


; SEND_FLAG routine
; Sends one byte, 0x7e, b'01111110'
; No CRC input, no bit-stuffing
;-------------------------------------------------------------------

SEND_FLAG
		bcf		NEXT_BIT
		call 		SEND_BIT		;send a zero
		bsf		NEXT_BIT
		call 		SEND_BIT		;send six ones
		call 		SEND_BIT
		call 		SEND_BIT
		call 		SEND_BIT
		call 		SEND_BIT
		call 		SEND_BIT
		bcf		NEXT_BIT
		call 		SEND_BIT 		;send another zero
		return 				;done, exit


; SEND_BYTE routine
; Sends one byte out, LSB first
; Bit stuffing is done here
; CRC calculation is called here
;-------------------------------------------------------------------

SEND_BYTE

; Prepare to send 8 bits

		movlw		0x08
		movwf		BIT_CNT		;send 8 bits

; Get next bit
; Send it to CRC routine to update CRC value

SEND_BYTE_2
		rrcf		SEND_REG,W 	;rotate data bit to CY
		call 		CRC_CALC		;calculate the new CRC value

; Identify if next bit = 0 or 1

		rrcf		SEND_REG,F		;get bit to send ( LSB first )
		btfss		STATUS,C		

; Next bit = 0

		goto 		SEND_BYTE_4	;jump if NEXT_BIT = 0

; Next bit = 1

		bsf		NEXT_BIT		;NEXT_BIT = 1
		call 		SEND_BIT 		;send a 1

; Bit stuffing is done here
; ( 5 consecutive "1" bits = send a "0" bit )

		incf 		BIT_STUFF_CNT,F ;BSC = BSC + 1
		movf		BIT_STUFF_CNT,W	;check BSC
		xorlw		0x05
		btfsc		STATUS,Z		;BSC = 5 ?
		goto 		SEND_BYTE_4	;if yes, add a zero

; Check if 8 bits have been sent yet
; Exit if true

SEND_BYTE_3
		decfsz 	BIT_CNT,F		;8 bits sent yet ?
		goto 		SEND_BYTE_2	;loop if not finished
		return				;otherwise, exit

; Send a zero bit and clear the bit stuff counter

SEND_BYTE_4	
		clrf 		BIT_STUFF_CNT 	;reset BSC
 		bcf		NEXT_BIT		;NEXT_BIT = 0
		call 		SEND_BIT 		;send a 0
		goto 		SEND_BYTE_3


; CRC_CALC routine
; Assumes data bit is in STATUS reg carry bit	
;----------------------------------------------------------------------

CRC_CALC
		movlw		0x01
		btfsc		STATUS,C
		xorwf		CRC_LO,F		; crc calculation
		bcf		STATUS,C
		rrcf		CRC_HI,F
		rrcf		CRC_LO,F
		btfss		STATUS,C
		return
		movlw		0x08
		xorwf		CRC_LO,F
		movlw		0x84
		xorwf		CRC_HI,F
		return


; SEND_BIT routine
; Waits for TMR1 to time out ( TMR1 = baud rate timer )
; If data bit = 1 then do not change TX tone
; If data bit = 0 then flip TX to opposite tone
;-------------------------------------------------------------------

SEND_BIT 
		btfss		BIT_DONE		;TMR1 timed out?
		goto 		SEND_BIT 		;no
		bcf		BIT_DONE		;clear flag

		btfss		NEXT_BIT		;data bit = 0 ?
		call		FLIP_TONE		;if true, flip the tone freq

		return				;otherwise, return ( no tone change )


; FLIP_TONE routine
; Switch from present TX tone to opposite TX tone 
; ( 1200 / 2200 Hz or vice versa )
;-------------------------------------------------------------------

FLIP_TONE

; Switch TX to a high tone or a low tone ?

		btfsc		HI_TONE
		goto 		SET_1200

; Switch to a high tone

SET_2200	
		bsf		HI_TONE

		movlw		HZ_2200
		movwf		NEXT_TMR0
		return

; Switch to a low tone

SET_1200	
		bcf		HI_TONE

		movlw		HZ_1200
		movwf		NEXT_TMR0
		return		

; TX_START routine
; Start up the PLL chip
;-------------------------------------------------------------------

TX_START

; Enable the PLL chip

		bsf		PLL_CE

; Delay for PLL chip to completely wake up

		clrf		NDX1
		clrf		NDX2

TX_START_2
		decfsz	NDX1,1
		goto		TX_START_2			
		decfsz	NDX2,1
		goto		TX_START_2	

; Start up the PLL chip
; Send the R register data

		movlw		PLL_BYTE_R1
		movwf		ANDEV1

		movlw		PLL_BYTE_R2
		movwf		ANDEV2

		movlw		PLL_BYTE_R3
		movwf		ANDEV3

		movlw		PLL_BYTE_R4
		movwf		ANDEV4

		call		PLL_SEND		;do it

; Start up the PLL chip
; Send the N register data ( get it from EE memory )

		movlw		EE_N1			;N byte 1 EE addr
		movwf		EE_ADDR
		call		EE_READ
		movf		EE_DATA,W		;get EE N byte 1
		movwf		ANDEV1

		movlw		EE_N2			;N byte 2 EE addr
		movwf		EE_ADDR
		call		EE_READ
		movf		EE_DATA,W		;get EE N byte 2
		movwf		ANDEV2

		movlw		EE_N3			;N byte 3 EE addr
		movwf		EE_ADDR
		call		EE_READ
		movf		EE_DATA,W		;get EE N byte 3
		movwf		ANDEV3

		movlw		EE_N4			;N byte 4 EE addr
		movwf		EE_ADDR
		call		EE_READ
		movf		EE_DATA,W		;get EE N byte 4
		movwf		ANDEV4

		call		PLL_SEND		;do it

; Start up the PLL chip
; Send the M register data

		movlw		PLL_BYTE_M1
		movwf		ANDEV1

		movlw		PLL_BYTE_M2
		movwf		ANDEV2

		movlw		PLL_BYTE_M3
		movwf		ANDEV3

		movlw		PLL_BYTE_M4
		movwf		ANDEV4

		call		PLL_SEND		;do it

; Start up the PLL chip
; Send the F register data

		movlw		PLL_BYTE_F1
		movwf		ANDEV1

		movlw		PLL_BYTE_F2
		movwf		ANDEV2

		movlw		PLL_BYTE_F3
		movwf		ANDEV3

		movlw		PLL_BYTE_F4
		movwf		ANDEV4

		call		PLL_SEND		;do it

; Delay for PLL chip to stabilize on TX freq

		clrf		NDX1
		clrf		NDX2

TX_START_3
		decfsz	NDX1,1
		goto		TX_START_3			
		decfsz	NDX2,1
		goto		TX_START_3	
 
; Start up the PA stage

		bsf		PORTC,0		;turn on the PA

; Done, return


; PLL_SEND routine
; Send a message to the AnDev PLL chip
; ( 1 message = one control register in PLL chip = 4 bytes )
;-------------------------------------------------------------------

PLL_SEND

; Send four bytes to AnDev chip

		movf		ANDEV1,W
		movwf		ANDEV_BUFF
		call		PLL_SEND_1

		movf		ANDEV2,W
		movwf		ANDEV_BUFF
		call		PLL_SEND_1

		movf		ANDEV3,W
		movwf		ANDEV_BUFF
		call		PLL_SEND_1

		movf		ANDEV4,W
		movwf		ANDEV_BUFF
		call		PLL_SEND_1

; Latch the data
; ( end of PLL chip message )

		bsf		PLL_LE		;latch it
		bcf		PLL_LE

; Done, return

		return

; Send one byte of PLL data

PLL_SEND_1	
		bcf		PLL_DATA		;data pin = 0
		btfsc		ANDEV_BUFF,7	;bit 7 = hi ?
		bsf		PLL_DATA		;if true, data pin = 1
		bsf		PLL_CLK		;toggle the clock pin
		bcf		PLL_CLK

		bcf		PLL_DATA
		btfsc		ANDEV_BUFF,6	;bit 6 = hi ?
		bsf		PLL_DATA		;if true, data pin = 1
		bsf		PLL_CLK
		bcf		PLL_CLK

		bcf		PLL_DATA
		btfsc		ANDEV_BUFF,5	;bit 5 = hi ?
		bsf		PLL_DATA		;if true, data pin = 1
		bsf		PLL_CLK
		bcf		PLL_CLK

		bcf		PLL_DATA
		btfsc		ANDEV_BUFF,4	;bit 4 = hi ?
		bsf		PLL_DATA		;if true, data pin = 1
		bsf		PLL_CLK
		bcf		PLL_CLK

		bcf		PLL_DATA
		btfsc		ANDEV_BUFF,3	;bit 3 = hi ?
		bsf		PLL_DATA		;if true, data pin = 1
		bsf		PLL_CLK
		bcf		PLL_CLK

		bcf		PLL_DATA
		btfsc		ANDEV_BUFF,2	;bit 2 = hi ?
		bsf		PLL_DATA		;if true, data pin = 1
		bsf		PLL_CLK
		bcf		PLL_CLK

		bcf		PLL_DATA
		btfsc		ANDEV_BUFF,1	;bit 1 = hi ?
		bsf		PLL_DATA		;if true, data pin = 1
		bsf		PLL_CLK
		bcf		PLL_CLK

		bcf		PLL_DATA
		btfsc		ANDEV_BUFF,0	;bit 0 = hi ?
		bsf		PLL_DATA		;if true, data pin = 1
		bsf		PLL_CLK
		bcf		PLL_CLK

		return

;--------------------------------------------------------------------------------------------------------------------------------------
;
;
;	HOST ROUTINES																		     HOST
;
;
;--------------------------------------------------------------------------------------------------------------------------------------

; HOST_START routine
; These routines are only called if the beacon is operating in HOST ( programming ) mode
; ( jumper wire was detected when power was turned on )
; These routines detect, parse and save the major parameters of the beacon transmitter in EE memory
; Major parameters are set via RS232, see comments at top of this file for details 

HOST_START

; HOST message characters are stored outside the ACCESS bank, in BANK 0
; Force the BANKSEL register to BANK = 0

		movlb		0x0

; Turn on the TX for 10 seconds
; Use freq presently stored in EE memory
; 3 sec dead carrier
; 3 sec 1200 Hz tone
; 3 sec 2200 Hz tone
; ( signals that the HOST mode has been invoked )

		call		HOST_TX

; Reset all HOST variables to their starting values
; Start up the UART
;----------------------------------------------------------------------

HOST_UART
		bcf		CHAR_RCVD

		bcf		HOST_SAVED

		clrf		HOST_BYTES

		call		UART_ENABLE

; Enable UART interrupts

		bsf		INTCON,PEIE
		bsf		INTCON,GIE

; Check if a character has arrived from host
; If true, process it
; Otherwise, loop and wait

HOST_WAIT
		btfss		CHAR_RCVD
		goto		HOST_WAIT		;wait if no char recieved

		bcf		CHAR_RCVD
		call		HOST_PARSE		;process the new char

		btfsc		HOST_SAVED		;message = EE saved ?
		goto		HOST_START		;restart HOST routine if true

		goto		HOST_WAIT		;otherwise, repeat
							; ( NOTE : coldboot to exit )


; HOST_PARSE
;----------------------------------------------------------------------

HOST_PARSE

; Make room in the buffer for the new char
; Initialize the destination pointer

		movlw		high	HOST_FREQ_MX100
		movwf		FSR1H
		movlw		LOW	HOST_FREQ_MX100
		movwf		FSR1L

; Initialize the source pointer

		movlw		high	HOST_FREQ_MX10
		movwf		FSR0H
		movlw		LOW	HOST_FREQ_MX10
		movwf		FSR0L

; Initialize the loop index counter

		movlw		0x41			;65 characters to shift
		movwf		NDX1

; Shift all the buffer contents by 1 byte

HOST_PARSE_2
		movf		POSTINC0,W
		movwf		POSTINC1
		decfsz	NDX1,F
		goto		HOST_PARSE_2

; Move the new char to the buffer
; ( this is char no. 66 )

		movf		CHAR_BUFF,W
		movwf		HOST_CR,1

; Increment the HOST_BYTES counter

		incf		HOST_BYTES,F

; Check if new char = ASCII <cr>
; If true, process the message

		movlw		0x0d			;ASCII <cr>
		subwf		CHAR_BUFF,W
		btfsc		STATUS,Z		;char = ASCII <cr> ?
		goto		HOST_EOM		;jump if true

; Last char was not ASCII <cr>
; Continue waiting for <cr>

		return


; HOST_EOM
;----------------------------------------------------------------------
; ASCII <cr> has been detected, process the host message

HOST_EOM

; Check if less than 66 characters have been recieved
; If true, shift the HOST buffer contents
; until HOST_BYTES = 66

		movlw		0x42			;= decimal 66
		subwf		HOST_BYTES,W
		btfsc		STATUS,Z
		goto		HOST_SAVE

; Less than 66 characters were recieved
; Shift all the HOST message bytes by 1 position
; Initialize the destination pointer

		movlw		high	HOST_FREQ_MX100
		movwf		FSR1H
		movlw		LOW	HOST_FREQ_MX100
		movwf		FSR1L

; Initialize the source pointer

		movlw		high	HOST_FREQ_MX10
		movwf		FSR0H
		movlw		LOW	HOST_FREQ_MX10
		movwf		FSR0L

; Initialize the loop index counter

		movlw		0x41			;65 characters to shift
		movwf		NDX1

; Shift all the buffer contents by 1 byte

HOST_EOM_2
		movf		POSTINC0,W
		movwf		POSTINC1
		decfsz	NDX1,F
		goto		HOST_EOM_2

; Move 0x00 to the end of the HOST buffer ( dummy data, ignored )
; ( this is char no. 66 )

		movlw		0x00
		movwf		HOST_CR,1		

; Increment the HOST_BYTES counter and re-test for 66 bytes

		incf		HOST_BYTES,F
		goto		HOST_EOM

; Bytes in the HOST buffer are now properly aligned
; Convert and save the various message fields

HOST_SAVE
  
; Store the new HOST frequency

		call		NEW_FREQ

; Store the new HOST time

		call		NEW_TIME

; Store the new call signs and SSID

		call		NEW_HEADER

; Store the new symbol table ID and code

		call		NEW_SYMBOL

; Store the new comment

		call		NEW_COMMENT

; Flag the EE save operation

		bsf		HOST_SAVED

; Done, return

		return

; New HOST message was recieved or power on = now
; Start the transmitter and run it for about 10 seconds
; ( allows test of freq, dev and power out )
; ( 3 sec dead carrier, 3 sec 1200 Hz, 3 sec 2200 Hx )

; Start the TX

HOST_TX
		call		TX_START

; Set the DAC to midrange value

		movlw		0xc2			;0x70 DAC = 100 Hz low
		movwf		PORTD

; Prepare to wait about 3 seconds
; ( dead carrier )

		clrf		NDX1
		clrf		NDX2
		movlw		0x55
		movwf		NDX3

; Wait about 3 seconds
; ( dead carrier )

HOST_TX_2
		decfsz	NDX1,F
		goto		HOST_TX_2
		decfsz	NDX2,F
		goto		HOST_TX_2
		decfsz	NDX3,F
		goto		HOST_TX_2

; Switch to 1200 Hz tone

		movlw		HZ_1200		;1200 Hz

		movwf		NOW_TMR0
		movwf		NEXT_TMR0

; Energise the tone generator

		movlw		0xc8
		movwf		T0CON

; Clear interrupt flag for tone timer
; Enable tone timer interrupts

		bcf		INTCON,TMR0IF	;1200/2200 tone timer
		bsf		INTCON,TMR0IE
		bsf		INTCON,GIE

; Prepare to wait about 3 seconds
; ( 1200 Hz tone )

		clrf		NDX1
		clrf		NDX2
		movlw		0x55
		movwf		NDX3

; Wait about 3 seconds
; ( 1200 Hz tone )

HOST_TX_3
		decfsz	NDX1,F
		goto		HOST_TX_3
		decfsz	NDX2,F
		goto		HOST_TX_3
		decfsz	NDX3,F
		goto		HOST_TX_3

; Switch to 2200 Hz tone

		movlw		HZ_2200		;2200 Hz

		movwf		NOW_TMR0
		movwf		NEXT_TMR0

; Prepare to wait about 3 seconds
; ( 2200 Hz tone )

		clrf		NDX1
		clrf		NDX2
		movlw		0x55
		movwf		NDX3

; Wait about 3 seconds
; ( 2200 Hz tone )

HOST_TX_4
		decfsz	NDX1,F
		goto		HOST_TX_4
		decfsz	NDX2,F
		goto		HOST_TX_4
		decfsz	NDX3,F
		goto		HOST_TX_4


; Turn off the tone generator 
; Turn off the TX and return

		movlw		0x48			;TMR0 = off ( no tone )
		movwf		T0CON

		bcf		PLL_CE		;PLL = off
		bcf		PORTC,0		;PA = off

		return


; Convert the HOST_FREQ message to PLL N register bytes
; Save them in EE memory 
;----------------------------------------------------------------------

NEW_FREQ

		clrf		BBR_LO		;start with result = 0
		clrf		BBR_MID
		clrf		BBR_HI

; KHZ X1
		movf		HOST_FREQ_KX1,W,1
		andlw		0x0f
		movwf		BB_NDX

		movlw		0x01			;add 1 x KHZX1
		movwf		BBT_LO
		movlw		0x00
		movwf		BBT_MID
		movlw		0x00
		movwf		BBT_HI

		call		BCDBIN

; KHZ X10
		movf		HOST_FREQ_KX10,W,1
		andlw		0x0f
		movwf		BB_NDX

		movlw		0x0a			;add 10 x KHZX10
		movwf		BBT_LO
		movlw		0x00	
		movwf		BBT_MID
		movlw		0x00
		movwf		BBT_HI

		call		BCDBIN

; KHZ X100
		movf		HOST_FREQ_KX100,W,1
		andlw		0x0f
		movwf		BB_NDX

		movlw		0x64			;add 100 x KHZX100
		movwf		BBT_LO
		movlw		0x00
		movwf		BBT_MID
		movlw		0x00
		movwf		BBT_HI

		call		BCDBIN

; MHZ X1
		movf		HOST_FREQ_MX1,W,1
		andlw		0x0f
		movwf		BB_NDX

		movlw		0xe8			;add 1000 x MHZX1
		movwf		BBT_LO
		movlw		0x03
		movwf		BBT_MID
		movlw		0x00
		movwf		BBT_HI

		call		BCDBIN

; MHZ X10
		movf		HOST_FREQ_MX10,W,1
		andlw		0x0f
		movwf		BB_NDX

		movlw		0x10			;add 10,000 x MHZX10
		movwf		BBT_LO
		movlw		0x27
		movwf		BBT_MID
		movlw		0x00
		movwf		BBT_HI

		call		BCDBIN

; MHZ X100
		movf		HOST_FREQ_MX100,W,1
		andlw		0x0f
		movwf		BB_NDX

		movlw		0xa0			;add 100,000 x MHZX100
		movwf		BBT_LO
		movlw		0x86
		movwf		BBT_MID
		movlw		0x01
		movwf		BBT_HI

		call		BCDBIN

; Left-shift the result 2 bits
; ( make room for register address bits )

		rlcf		BBR_HI,F		;left-shift BBR_HI by 2 bits
		rlcf		BBR_HI,F
		bcf		BBR_HI,0		;clear BBR_HI bottom 2 bits
		bcf		BBR_HI,1
		btfsc		BBR_MID,6		;should BBR_HI bit 0 be 1 ?
		bsf		BBR_HI,0		;yes
		btfsc		BBR_MID,7		;should BBR_HI bit 1 be 1 ?
		bsf		BBR_HI,1		;yes

		rlcf		BBR_MID,F		;left-shift BBR_MID by 2 bits
		rlcf		BBR_MID,F
		bcf		BBR_MID,0		;clear BBR_MID bottom 2 bits
		bcf		BBR_MID,1
		btfsc		BBR_LO,6		;should BBR_MID bit 0 be 1 ?
		bsf		BBR_MID,0		;yes
		btfsc		BBR_LO,7		;should BBR_MID bit 1 be 1 ?
		bsf		BBR_MID,1		;yes

		rlcf		BBR_LO,1		;left-shift BBR_LO by 2 bits
		rlcf		BBR_LO,1

; Add the N register address bits

		bsf		BBR_LO,0
		bcf		BBR_LO,1

; Save the N register data in EE memory

		clrf		EE_DATA		;N byte 1 = all zeros
		movlw		EE_N1			;N byte 1 EE addr
		movwf		EE_ADDR
		call		EE_WRITE

		movf		BBR_HI,W		;N byte 2 data
		movwf		EE_DATA
		movlw		EE_N2			;N byte 2 EE addr
		movwf		EE_ADDR
		call		EE_WRITE

		movf		BBR_MID,W		;N byte 3 data
		movwf		EE_DATA
		movlw		EE_N3			;N byte 3 EE addr
		movwf		EE_ADDR
		call		EE_WRITE

		movf		BBR_LO,W		;N byte 4 data
		movwf		EE_DATA
		movlw		EE_N4			;N byte 4 EE addr
		movwf		EE_ADDR
		call		EE_WRITE

		return


; Convert the HOST_TIME message to two binary bytes
; Save them in EE memory 
;----------------------------------------------------------------------

; NOTE : Host values are multiplied by 10 because TMR3 = 0.1 sec per tick

NEW_TIME

		clrf		BBR_LO		;start with result = 0
		clrf		BBR_MID
		clrf		BBR_HI

; TIME X1 seconds

		movf		HOST_TIME_X1,W,1
		andlw		0x0f
		movwf		BB_NDX

		movlw		0x0a			;add 10 x HOST_TIME_X1
		movwf		BBT_LO
		movlw		0x00	
		movwf		BBT_MID
		movlw		0x00
		movwf		BBT_HI

		call		BCDBIN

; TIME X10 seconds

		movf		HOST_TIME_X10,W,1
		andlw		0x0f
		movwf		BB_NDX

		movlw		0x64			;add 100 x HOST_TIME_X10
		movwf		BBT_LO
		movlw		0x00
		movwf		BBT_MID
		movlw		0x00
		movwf		BBT_HI

		call		BCDBIN

; TIME X100 seconds

		movf		HOST_TIME_X100,W,1
		andlw		0x0f
		movwf		BB_NDX

		movlw		0xe8			;add 1000 x HOST_TIME_X100
		movwf		BBT_LO
		movlw		0x03
		movwf		BBT_MID
		movlw		0x00
		movwf		BBT_HI

		call		BCDBIN

; TIME X1K seconds

		movf		HOST_TIME_X1K,W,1
		andlw		0x0f
		movwf		BB_NDX

		movlw		0x10			;add 10,000 x HOST_TIME_X1K
		movwf		BBT_LO
		movlw		0x27
		movwf		BBT_MID
		movlw		0x00
		movwf		BBT_HI

		call		BCDBIN

; Save the binary TIME value in EE memory

		movf		BBR_MID,W		;N byte 3 data
		movwf		EE_DATA
		movlw		EE_TX_TIME_HI	;TX_TIME_HI EE addr
		movwf		EE_ADDR
		call		EE_WRITE

		movf		BBR_LO,W		;N byte 4 data
		movwf		EE_DATA
		movlw		EE_TX_TIME_LO	;EE_TX_TIME_LO EE addr
		movwf		EE_ADDR
		call		EE_WRITE

		return


; Convert the SOURCE call sign to APRS format
; Convert the SOURCE SSID to APRS format
; Convert the VIA 1 call sign to APRS format ( if provided )
; Convert the VIA 1 SSID to APRS format ( if provided )
; Convert the VIA 2 call sign to APRS format ( if provided )
; Convert the VIA 2 SSID to APRS format ( if provided )
; Save them all in EE memory 
;----------------------------------------------------------------------

NEW_HEADER

; Left shift the SOURCE, VIA1 and VIA2 call sign bytes
; Also clear the bottom bit of each byte ( bit 0 = 0 )

; Point to first char of SOURCE call sign

		movlw		high HOST_SRC_BYTE1
		movwf		FSR0H
		movlw		low HOST_SRC_BYTE1
		movwf		FSR0L

; Left shift the SOURCE characters
; Clear the bottom bit

		rlcf		INDF0,F		;first SOURCE char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;second SOURCE char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;third SOURCE char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;fourth SOURCE char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

 		rlcf		INDF0,F		;fifth SOURCE char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;last SOURCE char	
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;SOURCE SSID char	
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

; Left shift the VIA 1 characters
; Clear the bottom bit

		rlcf		INDF0,F		;first VIA 1 char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;second VIA 1 char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;third VIA 1 char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;fourth VIA 1 char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

 		rlcf		INDF0,F		;fifth VIA 1 char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;last VIA 1 char	
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;VIA 1 SSID char	
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

; Left shift the VIA 2 characters
; Clear the bottom bit

		rlcf		INDF0,F		;first VIA 2 char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;second VIA 2 char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;third VIA 2 char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;fourth VIA 2 char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

 		rlcf		INDF0,F		;fifth VIA 2 char
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;last VIA 2 char	
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

		rlcf		INDF0,F		;VIA 2 SSID char	
		bcf		POSTINC0,0		;bottom bit = 0 + increment pointer

; Process the SSID bytes
; Detection of VIA 1 / VIA 2 presence or absence is done here

		btfsc		HOST_SRC_SSID,7,1	;last call sign ?
		bsf		HOST_SRC_SSID,0,1	;set bit 0 if true

		btfsc		HOST_V1_SSID,7,1	;last call sign ?
		bsf		HOST_V1_SSID,0,1	;set bit 0 if true

		btfsc		HOST_V2_SSID,7,1	;last call sign ?
		bsf		HOST_V2_SSID,0,1	;set bit 0 if true

; Discard SSID bits 5, 6 and 7 ( clear them )
; Bit 0 now indicates last SSID ( bit = 1 )
; Bits 1 thru 4 = SSID station number ( 0 to 15 )

		movlw		0x1f
 		andwf		HOST_SRC_SSID,F,1
		andwf		HOST_V1_SSID,F,1
		andwf		HOST_V2_SSID,F,1

; Set SSID bits 5 and 6

		movlw		0x60
 		iorwf		HOST_SRC_SSID,F,1
		iorwf		HOST_V1_SSID,F,1
		iorwf		HOST_V2_SSID,F,1

; Header processing is done
; Characters are ready for EE save

; Point to first char of ( processed ) SOURCE call sign
; Point EE routine to corresponding EE address

		movlw		high HOST_SRC_BYTE1
		movwf		FSR0H
		movlw		low HOST_SRC_BYTE1
		movwf		FSR0L

		movlw		EE_SRC_1
		movwf		EE_ADDR

		clrf		NDX1			;starting value

; EE save 21 consecutive bytes ( = all HEADER bytes )

NEW_HEADER_2
		movf		POSTINC0,W		;get the byte from RAM
							;and point to next RAM byte
		movwf		EE_DATA		;prepare to EE save it

		call		EE_WRITE		;EE save the byte

		incf		EE_ADDR,F		;and point to next EE address

		incf		NDX1,F
		movlw		0x15
		subwf		NDX1,W

		btfsc		STATUS,Z		;saved 21 bytes yet ?
		return				;return if true				

		goto		NEW_HEADER_2	;otherwise, repeat


; Convert the symbol table ID and code to APRS format
; Save them in EE memory 
;----------------------------------------------------------------------

NEW_SYMBOL

; No special conversion is req'd for SYMBOL_TABLE bytes
; No special conversion is req'd for SYMBOL_CODE bytes

; Point to SYMBOL_TABLE byte in RAM
; Point EE routine to corresponding EE address

		movlw		high HOST_POS_TABLE
		movwf		FSR0H
		movlw		low HOST_POS_TABLE
		movwf		FSR0L

		movlw		EE_POS_TABLE
		movwf		EE_ADDR

; Save the SYMBOL TABLE byte for POSIT reports

		movf		POSTINC0,W		;get the byte from RAM
							;and point to next RAM byte
		movwf		EE_DATA		;prepare to EE save it

		call		EE_WRITE		;EE save SYMBOL TABLE byte

; Increment the EE pointer and save the SYMBOL CODE byte for POSIT reports

		incf		EE_ADDR,F		;next EE addr

		movf		POSTINC0,W		;get the byte from RAM
							;and point to next RAM byte
		movwf		EE_DATA		;prepare to EE save it

		call		EE_WRITE		;EE save SYMBOL CODE byte

; Done, return
 
		return


; Convert the comment characters to APRS format
; Save them in EE memory 
;----------------------------------------------------------------------

NEW_COMMENT

; No special conversion is req'd for COMMENT bytes

; Point to start of COMMENT bytes in RAM
; Point EE routine to corresponding EE address

		movlw		high HOST_COMMENT_01
		movwf		FSR0H
		movlw		low HOST_COMMENT_01
		movwf		FSR0L

		movlw		EE_COMMENT_01
		movwf		EE_ADDR

; Limit COMMENT chars to 32 in case of overrun in message

		movlw		0x20
		movwf		NDX1	

NEW_COMMENT_2
		movf		POSTINC0,W		;get the byte from RAM
							;and point to next RAM byte
		movwf		EE_DATA		;prepare to EE save it

		call		EE_WRITE		;EE save COMMENT byte

		incf		EE_ADDR,F		;point to next EE address

; Check if more COMMENT character remain to be EE saved
; Exit if last char = ASCII <cr>

		movlw		0x0d			;= ASCII <cr>
		subwf		EE_DATA		;last char = <cr> ?
		btfsc		STATUS,Z

		return				;last byte has been saved

; Check if 32 COMMENT characters have been saved
; Truncate and terminate the COMMENT, if true

		decfsz	NDX1,F

; If CPU gets here, more COMMENT characters remain to be saved

		goto		NEW_COMMENT_2	;jump if more to save

; COMMENT limit has been reached
; EE save an ASCII <cr> and exit

		movlw		0x0d
		movwf		EE_DATA

		movlw		EE_CR
		movwf		EE_ADDR

		call		EE_WRITE

		return 


; BCDBIN Routine
; Multiply a 1 byte number by a 3 byte number with repeated addition
;----------------------------------------------------------------------

BCDBIN

; Check if BB_NDX = 0
; Return if true

		movlw		0x00
		subwf		BB_NDX,W
		btfsc		STATUS,Z
		return

; Index = greater than zero
; Add temporary registers to result registers
; Loop and repeat until BB_NDX = 0

		movf		BBT_LO,W
		addwf		BBR_LO,F
		btfsc		STATUS,C
		incf		BBR_MID,F

		movf		BBT_MID,W
		addwf		BBR_MID,F
		btfsc		STATUS,C
		incf		BBR_HI,F

		movf		BBT_HI,W
		addwf		BBR_HI,F

		decf		BB_NDX,F
		goto		BCDBIN


;--------------------------------------------------------------------------------------------------------------------------------------
;
;
;	EE ROUTINES																		             EE
;
;
;--------------------------------------------------------------------------------------------------------------------------------------

; These routines are used to save / fetch bytes to / from EE memory

; EE_WRITE routine
; Writes one byte to EEPROM memory
;-------------------------------------------------------------------

EE_WRITE

; Get the data and address bytes

		movf 		EE_ADDR,W		;get EE address
		movwf		EEADR

		movf		EE_DATA,0 		;get EE data
		movwf		EEDATA

; Write the data byte

		bcf 		EECON1,EEPGD	;point to DATA memory
		bcf		EECON1,CFGS	;EE memory
		bsf 		EECON1,WREN	;enable EE write

; Clear any old interrupts

		bcf		PIR2,EEIF		;clear EE write interrupt flag

; Exact sequence

		bcf		INTCON,GIE		;no interrupts allowed
		movlw		0x55			;Write 55h to EECON2
		movwf		EECON2
		movlw		0xaa			;Write aah to EECON2
		movwf		EECON2
		bsf 		EECON1,WR		;Start EE write operation
		bsf		INTCON,GIE		;enable interrupts

; Wait for completion of write operation

EE_WRITE_1
		btfss		PIR2,EEIF		;EE write interrupt ?
		goto		EE_WRITE_1		;if not, continue sleep

; EE write is done

		bcf		PIR2,EEIF		;clear EE write interrupt flag
		bcf 		EECON1,WREN	;Disable further writes

		return


; EE_READ routine
; Reads one byte from EEPROM memory
;-------------------------------------------------------------------

EE_READ
		movf 		EE_ADDR,W		;get EE address
		movwf		EEADR

		bcf 		EECON1,EEPGD	;point to DATA memory
		bcf		EECON1,CFGS

		bsf 		EECON1,RD		;start the read
		movf		EEDATA,W		;get the byte

; Save data and return

		movwf		EE_DATA		;save the byte 

		return


; Sine wave look-up table for 1200 / 2200 Hz tone generation
; ( values adjusted for PORT D pin definitions to drive 8 bit DAC )
; ( 1 dummy value + 63 table values )
;-------------------------------------------------------------------

		org		0x0e00		;SIN_TAB origin

SIN_TAB
		db		0x00,0x04
		db		0x35,0x65
		db		0x9c,0xcc
		db		0xf5,0x26

		db		0x4e,0x76
		db		0x96,0xb6
		db		0xce,0xdf
		db		0xef,0xfe

		db		0xff,0xff
		db		0xfe,0xf7
		db		0xe7,0xd7
		db		0xbf,0xa6

		db		0x86,0x5f
		db		0x37,0x0f
		db		0xe4,0xb4
		db		0x84,0x4d

		db		0x1c,0xea
		db		0xb2,0x82
		db		0x52,0x22
		db		0xf1,0xc9

		db		0xa1,0x80
		db		0x60,0x41
		db		0x29,0x19
		db		0x09,0x01

		db		0x00,0x00
		db		0x08,0x11
		db		0x21,0x38
		db		0x50,0x70

		db		0x90,0xb8
		db		0xe0,0x0b
		db		0x3a,0x6a
		db		0x9b,0xcb


;-------------------------------------------------------------------
;
;
;	END OF CODE
;
;
;-------------------------------------------------------------------

		end


