
;***************************************************************************
;* Title: AVR_UI_TNC EXAMPLE CODE Version 1.7
;* Made available for educational, and non-commercial use.
;* Commercial use forbidden without license from author.
;* Code may be modified as long as it is released publicly and credit
;* is given to the original author(s).  
;* (c) copyright 2000 Henry Carl Ott N2RVQ, all Rights reserved.
;* Questions, comments or offers of cash? carlott@si.rr.com  http://users.rcn.com/carlott/
;***************************************************************************
;* Description: Example code to test basic UI packet functions on an AT90s2313 / MXCOMM MX-614 combo. 
;* Please reference separate documents for schematics / hardware description.
;* Provides minimal tnc functionality for ui-frames.
;* Outputs received ax25 packets to console (total packet length 224 chars including path) 
;* Transmits strings from console, max length, 96 chars not including path
;* Beacons at user determined intervals, beacon text up to 64 chars of eeprom stored message
;* Console configuration of mycall, ui destination and digi path,
;* beacon text (max 64 chars),beacon time, and console echo
;**************************************************************************
;* Credits: The main inspiration for the design was the TAPR PIC-E project.
;* I had used Microchip MCUs in a number of other projects, but I was currently
;* experimenting with Atmel AVR MCUs and found that the additional RAM/EPROM and 
;* additional indexing modes made code development easier (at least for me). Besides I
;* was getting tired of all the bank switching on the PICs :).
;* For the software I looked very closely at the PIC-E code posted by
;* Byon Garrabrant, John Hansen, Mike Berg and others (sorry if I missed anyone).
;* The jump table decoding was inspired by Byons code, and the CRC calculation 
;* is an almost direct port of his crc routine. 
;***************************************************************************
;* REVISIONS:
;*1.0 first release
;*1.1 cut a few cycles in rx isr to try to try to avoid overloading
;*1.2 put rx crc routine inline within rx-isr to speed up isr
;*1.5 changed code to support SPI FM25160 FRAM part instead of the I2C PCF8583
;*    with the lack of the rtc, we simplified the beacon timer (just on or off)
;*    NOTE: Be sure to reference the rev 1.5 schematic
;*1.6 bug fixes, changed string reception routine to be int driven. misc tweaks
;*1.7 fixed beacon text bug, added non volitile converse mode flag bit
;***************************************************************************
;* Disclaimer: I'm a lousy programmer. Please don't use this code/design for any
;* application where human life is at risk  or property damage may occur.
;***************************************************************************

; Console command descriptions:
; Commands are case insensitive. 
; In most cases only the first letter of command is significant
; 
; Mycall  	examples 'Mycall NOCALL-1' or 'M NOCALL' 
; Unproto  	examples 'UNPROTO APRS VIA RELAY,WIDE5-5' or 'U APRS V WIDE' or 'u apz001' 
; Echo		examples 'ECHO OFF' or 'ECHO ON' or 'e off'
; Beacon	examples 'BEACON ON' 'BEACON OFF' 'B OFF' 'B ON'
; BText		examples 'BTEXT Test Message' or 'BT THIS IS A TEST,'
; Converse	examples 'C' or 'converse' or 'conv' 	Enters converse mode		
; Id		examples 'ID' or 'I'  			Sends a sample beacon (will key radio!)

;--- start of the actual code ---
.nolist
.include "2313def.inc"	
.list
.listmac
;***************************************************************************
;*
;* MACROS
;*
;***************************************************************************

;--- simple led macros
.macro	led_grn
	sbi	DDRD,led	
	sbi	PORTD,led
.endm	
.macro	led_red
	sbi	DDRD,led
	cbi	PORTD,led
.endm	
.macro	led_off
	cbi	DDRD,led
	cbi	PORTD,led
.endm	

;---- SPI  macros
.macro	spi_data_inp
	cbi	DDRD,SPI_data
.endm

.macro	spi_data_out
	sbi	DDRD,spi_data
.endm

;--- mx-614 specific macros 
.macro	tx_mx614
	sbi	PORTB,mode0
	cbi	PORTB,mode1	
.endm
	
.macro	rx_mx614
	cbi	PORTB,mode0
	sbi	PORTB,mode1	

.endm
	
.macro	off_mx614		
	sbi	PORTB,mode0	; power down mode
	sbi	PORTB,mode1
.endm
	
;--- radio control
.macro	ptt_down
	sbi	PORTB,rad_ptt
.endm

.macro	ptt_up
	cbi	PORTB,rad_ptt
.endm	

;--- send single char to console
.macro	_send
	ldi	temp,@0 
	rcall	putc
.endm	

;--- send string to console
.macro _send_232_str
	ldi	zh,high(@0 * 2)
       	ldi	zl,low(@0 * 2) 	;Init Z-pointer
	rcall	ser_ram_str
.endm

;--- skip next instruction
.macro	skip
	rjmp	(pc + 2)	; skip, next instruction	
	
.endm	
;--- copy zero terminated eeprom string into  fram (left shift data first)  
.macro	_copy_ax_string_shift
	ldi	temp,@0
       	rcall	ax25_s_string
.endm	
;--- copy zero terminated eeprom string into  fram  
.macro	_copy_ax_string
	ldi	temp,@0
       	rcall	ax25_string
.endm	

;--- send zero terminated eeprom string to console  
.macro	_232_str
	ldi	temp,@0
	rcall	serial_string
.endm

;------delay two cycles in one instruction
.macro	_nop_2
	rjmp	(pc + 1)
.endm	

;-----------------------------------------------------------------
;--- port and pin defines
;--- portb 
.equ rxd_614 	= 0	; I 614_rxd 
.equ sw_inp	= 1	; I sw_inp
.equ txd_614 	= 2	; O 614_txd
.equ det_614	= 3	; O I 614_det 
.equ mic_ptt 	= 4	; I mic ptt input	
.equ rad_ptt	= 5	; O radio ptt output
.equ mode1	= 6	; O mx614 mode 1 select
.equ mode0	= 7	; O mx614 mode 0 select

;--- portd
.equ spi_port   = PORTD
.equ spi_sck	= 6	; O SPI clock 
.equ led	= 5	; O led_1
.equ spi_cs 	= 4	; O SPI chip select
.equ spi_data 	= 3	; I/O  SPI data 
.equ spare_232	= 2	; I external rs232 input 
.equ txd_232	= 1	; O 232_txd 
.equ rxd_232	= 0	; I 232_rxd

;---- some program equates and constants
.equ	ram_begin  = 0x60	; start of ram on 2313	
.equ	random	   = 0xc1   	; storage for random number generator	
.equ	pack_buff_0 = 0xc2	; 
.equ	pack_buff_1 = 0xc3
.equ	pack_buff_2 = 0xc4
.equ	pack_buff_3 = 0xc5	

.equ	ctrl_c 		= 0x03
.equ	bs  		= 0x08		; back space
.equ	bel 		= 0x07 		; bell
.equ	cr  		= 0x0d		; carriage return 
.equ	lf  		= 0x0a		; line feed
.equ	clock 		= 8000000 	; cpu clock
.equ	console_baud 	= 9600
.equ	isr_reload_val= (0xffff - (clock/9600)) + 19
.equ	beacon_time	= 235		; fixed beacon reload value in 2.56 second multiples 


.equ	fram_op_wren = 0b00000110
.equ	fram_op_rd   = 0b00000011
.equ	fram_op_wr   = 0b00000010

.equ	tx_bank		= 7	; what fram bank to send packets from
.equ	tx_delay 	= 40	; number of flag bytes before data
.equ	tail_flags 	= 2	; number of flags to send after data
.equ	tx_hang		= 1	; delay (.001) after last flag before un-keying
.equ	min_packet_len 	= 18	; minimum length, + con+pid and crc of rx packet 
.equ	max_packet_len 	= 255	; maximim length, determined by available ram, isr discards extra chars
.equ	max_str_len	= 96	; how much of avr ram to use for string storage	
.equ	reload_count	= 59	; ax25 rxd transition time interval counter  reload value, used by rx isr
.equ	control_byte	= 0x03	; control and pid for ax25 ui packets
.equ	pid_byte	= 0xf0	;	


;*****************************
;* Global Register Variables *
;*****************************
.def	r0		= r00	; used with lpm instruction
.def	tic		= r01	; incs @ 9600 hz resets at 96 0-95
.def	sec_01		= r02	; incs @ 100 hz	
.def	b_timer	     	= r03	; decs at .256 hz, will not go past zero, used for beacon timer 

.def	ax25_txbuff 	= r05	; ax25 data to send	
.def	ax25_ones	= r06	; no of continous ones sent, used for bit stuffing
.def	ax25_crc_lo	= r07	; crc calculation low order byte rx/tx
.def	ax25_crc_hi	= r08 	; crc calculation hi order byte	 rx/tx
.def	packet_len	= r09	; length of received packet not including crc
.def	ax25_rxbuff	= r10	; used to assemble rx bytes
.def	ax25_rx_count	= r11	; time interval between transitions
.def	ax25_tx_bits 	= r12	; number of bits to send

.def	isr_save	= r15	; saves mcu status reg during isr 
.def	temp		= r16	; gp working reg 	
.def	temp2		= r17   ; yet another working register 
.def	ii		= r18	; gp loop counter
.def	flags		= r19	; various bit flags and ax25_tx flags
.def	rx_flags	= r20	; bit flags used to communicate with isr
.def	ax25_rx_chars	= r21	; received chars ptr/counter (used only within isr) 
.def	ax25_rx_bitcnt  = r22	; used to assemble individual bits into  bytes
.def	EEaddr		= r23	; eeprom address
.def	EEdata		= r24	; eeprom data for rd/wr 	
.def	byte_cnt	= r25	; general purpose byte counter

.def	xl		= r26
.def	xh		= r27	

.def	yl		= r28	; string pointer, used to assemble rs-232 string in uart isr

.def	fram_bank	= r29	; bank select on FM25160 (low three bits), hi four bits are used by isr!
.def	yh		= r29

.def	zl		= r30	; used for indirect indexing and a gp register otherwise  
.def	zh		= r31	; used for indirect indexing and a gp register otherwise
.def	bitcnt		= r31	; gp bit counter, 
;**************************
;- bit flags defined
;**************************
.equ	string_rx	= 0x00		; cr terminated string was received 
.equ	ax25_flag	= 0x01		; sending flags? (no bit stuffing or crc)
.equ	ax25_shift	= 0x02		; do we shift the data before sending? (address)
.equ	fram_no_sei	= 0x03		; used to disable sei on fram return routines when called from an isr 

.equ	conv_mode	= 0x05		; non volitile flag bit, if we were in converse mode on power down
				        ;  re-enter converse mode on power up							
.equ	echo		= 0x06		; if set echo console chars
.equ	beacon_enable	= 0x07		; enable beacon 

;-------------------------
;**************************
;- ax25 recieve bits defined
;**************************
.equ	old_bit		= 0x00		; previous bit sample
.equ	rx_enable	= 0x01		; enable reception of packet data
.equ	in_packet 	= 0x02		; currently receiving packet data
.equ	half_flag	= 0x03		; set if last bit received was a zero (more of an 1/8 flag)
.equ	new_bit		= 0x04		; new bit sample, new and old bit must be in these positions
.equ	flag_det	= 0x05		; last byte was a valid flag	

;-------------------------

;*********************
;* Interrupt Vectors *
;*********************

.CSEG
.org		0x00
		rjmp	reset			; Reset Handle	
.org		INT0addr			; external int 0 ; bit bang secondary serial port	
		reti
.org		INT1addr			; external int 1
		reti
.org		OVF1addr			; overflow of 16 bit timer 1 basic 9600hz tic
		rjmp	t1int			
.org		OVF0addr			; overflow of 8  bit timer 0 
		reti
.org 		URXCaddr			; reception of data by uart
		rjmp	uart_rx_isr		

;--- timer 1 overflow interupt
;--- we should hit the isr @ 9600 hz		
;--- the isr handles the low level transmission of ax25 byte and assembles received packets
t1int:		in	isr_save,SREG		; save SREG
		push	temp	
		push	zl			; we have to use zl and zh for thejump table anyway
		push	zh			; so we might as well use the for gp 
	
		ldi	zh,high(isr_reload_val)	; reload timer1
		ldi	zl,low(isr_reload_val)
		out	tcnt1h,zh
		out	tcnt1l,zl

		inc	tic			; bump counter everytime through
		
;--- ax25 transmit portion of isr
ax25_tx:	tst	ax25_tx_bits		; any bits to send?
		breq	ax25_tx_end		; no, branch to end

		mov	temp,tic		; 9600 divided by 8, see if we are in a tx bit period (1200 baud)
		andi	temp,0x07		; mask off hi bits	
		brne	ax25_tx_end		; nope, fall through	

		sbrc	flags,ax25_flag		; 
		clr	ax25_ones		; if a flag byte, no bit stuffing
		
		mov	temp,ax25_ones		
		cpi	temp,5			; check for bit stuffing
		brne	send_bit		; no match keep going

		rcall	ax25_tx_0		; stuff a zero and exit		
		rjmp	ax25_tx_end		; 

send_bit:	sbrc	ax25_txbuff,0		; test lsb 
		inc	ax25_ones		; if a one, don't change output, just bump the ones counter
		sbrs	ax25_txbuff,0		; test lsb,  data is sent lsb first
		rcall	ax25_tx_0

		dec	ax25_tx_bits		; number of bits left to send 
		lsr	ax25_txbuff		; shift next bit into tx position, bit just sent goes into carry
		
		sbrs	flags,ax25_flag		; if a flag byte, don't calculate crc
		rcall	ax25_calc_crc		; generate crc on carry bit
ax25_tx_end:

;--- ax25 rx portion of isr
ax25_rx:	sbrs	rx_flags,rx_enable	; is reception enabled?
		rjmp	rx_reset_cnt		; no, jump to end	

		sbis	PINB,det_614		; any dcd?
		rjmp	rx_flush		; nope, reset registers 

		tst	ax25_rx_count		; test transition counter
		breq	pc + 2			; if @ zero, stay at zero
		dec	ax25_rx_count
			
		cbr	rx_flags,(1 << new_bit)
		sbic	PINB,rxd_614		; copy pin status  
		sbr	rx_flags,(1 << new_bit)

		mov	temp,rx_flags
		swap	temp			; moves old_bit and new_bit into same bit position	
		eor	temp,rx_flags
		
		sbrs	temp,0			; check for change
		rjmp	ax25_rx_end		; nope

rx_change:	bst	rx_flags,new_bit	; copy new_bit to old
		bld	rx_flags,old_bit

		tst	ax25_rx_count		; test for timout
		brne	pc + 2			; > 0 ?
		rjmp	rx_flush		; timeout

		lsr	ax25_rx_count
		lsr	ax25_rx_count
		lsr	ax25_rx_count		; divide count by 8

		ldi	zh,high(rx_jmp)		; 
		ldi	zl,low(rx_jmp) 		; Init Z-pointer
		add	zl,ax25_rx_count
		clr	temp			; 			
		adc	zh,temp			; if a carry from low order add it in
		ijmp
		
;--- jump table
rx_jmp:		rjmp	rx_flag			;0 flag
		rjmp	rx_add_5		;1 zero stuff, add five ones toss zero
		rcall	rx_add_1		;2 11110
		rcall	rx_add_1		;3 1110
		rcall	rx_add_1		;4 110
		rcall	rx_add_1		;5 10
		rjmp	rx_add_0		;6 0
		rjmp	rx_flush		;7 error, too soon

;--- check that at least one bit has been received and that it was a zero
;--- if so a flag has been received, otherwise error
rx_flag:	sbrs	rx_flags,half_flag	; check for first zero of flag
		rjmp	rx_flush		
		sbr	rx_flags,(1 << flag_det); set flag detected flag
		cbr	rx_flags,(1 << half_flag)
		ldi	ax25_rx_bitcnt,8	; reload bit counter for next byte

		sbrc	rx_flags,in_packet	; are we currently within a packet?
		rjmp	ax25_rx_close		; yes, try to close
		rjmp	rx_reset_cnt		; just keep going

;--- add 5 ones to buffer, and discard extra stuffed zero
rx_add_5:	cbr	rx_flags,(1<<half_flag)	; clear half flag bit because last bit written is a one
		
		rcall	rx_add_1		; only one more instruction then a loop
		rcall	rx_add_1		; is faster, and we needed the register elsewhere 
		rcall	rx_add_1
		rcall	rx_add_1
		rcall	rx_add_1
		rjmp	rx_reset_cnt			; done		

;--- add a single one to buffer, carry contains bit
rx_add_1:	sec					; set carry
		rcall	rx_in_bit
		ret
;--- add a zero to received byte
rx_add_0:	sbr	rx_flags,(1<<half_flag)		; set half flag bit because we are writing a zero
		clc					; clear carry	
		rcall	rx_in_bit
		rjmp	rx_reset_cnt			; done	

ax25_rx_close:	cpi	ax25_rx_chars,min_packet_len	; minimum length packet
		brlo	rx_flush

		ldi	temp,0xF0			; compare with calculated crc 
		eor	temp,ax25_crc_hi
		brne	rx_flush			; bad crc

		ldi	temp,0xB8
		eor	temp,ax25_crc_lo
		brne	rx_flush			; bad crc

		subi	ax25_rx_chars,2			; reduce to discard crc

		mov	zl,fram_bank
		swap	zl
		andi	zl,0b00000011			; mask off all but low two bits
		subi	zl,(- pack_buff_0)		; add in address
		st	z,ax25_rx_chars			; store number of chars received
		led_red				        ; indicator

rx_flush:	rcall	ax25_rx_init

rx_reset_cnt:	ldi	temp,reload_count		; get ready for next bit
		mov	ax25_rx_count,temp		; reload counter

ax25_rx_end:
		ldi	temp,96				; divide by 96 to get our 10ms tic	
		eor	temp,tic			; 
		brne	no_rollover			; no rollover		

		clr	tic
		inc	sec_01				; bump hunds of seconds (10ms)
		brne	no_rollover							
		tst	b_timer				; see if b_timer is at zero
		breq	no_rollover			; leave at zero
		dec	b_timer				; b_timer decs at .256 hz, but won't dec past zero	

no_rollover:	pop	zh				; restore
		pop	zl				; 
		pop	temp				; 
		out	SREG,isr_save			; restore
		reti

;--- flip mx614 txd line line, zero the ones counter
;--- trashes temp and zl
ax25_tx_0:	in	temp,PORTB			; get current pin status
		ldi	zl,(1 << txd_614)		; can't xor directly to io port	
		eor	temp,zl				; invert bit
		out	PORTB,temp			; and back out to pin
		clr	ax25_ones			; clear ones counter
		ret	

;--- rotates carry into rx buffer, determines packet status 
;--- store the byte if in packet, calcs crc on full bytes only
rx_in_bit:	ror	ax25_rxbuff 			; save bit rotate carry into position (lsb first)	
		dec	ax25_rx_bitcnt			
		brne	store_ax_end			; not zero, still within byte, just exit

		ldi	ax25_rx_bitcnt,8		; reset bit counter counter

		sbrc	rx_flags,in_packet
		rjmp	store_ax_byte			; within a packet, handle byte

		sbrs	rx_flags,flag_det		; not in packet, was the last byte a flag?	
		ret					; nope, just discard 
		
start_packet:	sbr	rx_flags,(1<<in_packet) 	; start of packet data (that's the  theory)
		ldi	zl,pack_buff_0			; find an empty bank to store new packet
		andi	fram_bank,0x0f			; start at bank zero

find_bank_lp:	ld	temp,z+
		tst	temp
		breq	store_ax_byte			; got an empty buffer

		subi	fram_bank,(- 0x10)		; add 0x10 
		cpi	zl,pack_buff_0 + 4		; done?
		brne	find_bank_lp

		rjmp	ax25_rx_init			; no place to store data, dump packet

store_ax_byte:	cbr	rx_flags,(1<<flag_det)|(1<<half_flag)	; clear flags 
		mov	zl,ax25_rxbuff			; grab a copy of the data

rx_crc_loop:	mov	temp,zl				; get data
		andi	temp,0x01			; mask off all but lsb	
        	eor   	ax25_crc_lo,temp        	; xor lsb
    		lsr     ax25_crc_hi			; zero hi bit, rotate 
        	ror     ax25_crc_lo			; rotate all, lsb goes into carry
        	brcc	rx_crc_tst			; if lsb was zero, get out

        	ldi	temp,0x08			; if lsb is one, xor in poly
        	eor   	ax25_crc_lo,temp
        	ldi	temp,0x84
        	eor   	ax25_crc_hi,temp
   	
rx_crc_tst:	lsr	zl				; move next bit into position
		dec	ax25_rx_bitcnt			; bitcnt was set to 8 above
		brne	rx_crc_loop	

		ldi	ax25_rx_bitcnt,8		; reset counter for next byte
 
		inc	ax25_rx_chars			;  
		cpi	ax25_rx_chars,max_packet_len	;
		brsh	store_ax_end			; too many chars, discard the byte 	

		sbrs	ax25_rxbuff,0			; check for end of path
		rjmp	rx_save_it

		cpi	ax25_rx_chars,(min_packet_len - 4) ; length - control,pid, and crc 
		brsh	rx_save_it			; long enough	
		rjmp	ax25_rx_init			; too short, note jmp		

rx_save_it:	mov	zl,ax25_rx_chars		; pointer to storage
		dec	zl				; adjust
		mov	temp,ax25_rxbuff

		swap	fram_bank			; get correct bank to write to
		rcall	fram_wr_no_sei				; write data
		swap	fram_bank			; restore fram_bank

store_ax_end:	ret

;--- init registers and flags for next packet
ax25_rx_init:	cbr	rx_flags,(1 << half_flag)|(1 << flag_det)|(1 << in_packet)	
		ldi	ax25_rx_bitcnt,8	; reset bit counter	
		clr	ax25_rxbuff
		clr	ax25_rx_chars		; start over	

		ldi	temp,0xff		; reset crc
		mov	ax25_crc_lo,temp
		mov	ax25_crc_hi,temp
		ret

;------------------------------------------------------------
; ported from Byon Garrabrant's pic based code
; assume carry contains data bit, init crc to 0xffff before starting	
ax25_calc_crc:	clr	temp			; rotate carry into lsb
		rol	temp
        	eor   	ax25_crc_lo,temp        ; xor lsb
    		lsr     ax25_crc_hi		; zero hi bit, rotate 
        	ror     ax25_crc_lo		; rotate all, lsb goes into carry
        	brcs	pc+2			; skip on carry set
        	ret
        	ldi	temp,0x08		; if carry is one, xor in poly
        	eor   	ax25_crc_lo,temp
        	ldi	temp,0x84
        	eor   	ax25_crc_hi,temp
        	ret        	

;--- rs-232 char reception isr, checks for errors and discards bad chars  
uart_rx_isr:	in	isr_save,SREG		; store SREG
		push	temp			
		sbic	USR, FE			; skip if framing error
		rjmp	uart_err		; 
		in	temp, UDR		; get rx byte
		sbic	USR, OR			; skip if overrun error
		rjmp	uart_err		; 
		
		sbrc	flags,echo		; if echo is enabled
		rcall	putc			; echo char

		sbrc	flags,string_rx		; has last string been processed
		rjmp	uart_isr_end		; no, don't string process	

;---------------------------------------------------------------------
		cpi	temp,cr
		breq	uart_str_done

;		cpi	temp,ctrl_c		; check for any char = ctrl c
;		breq	uart_rx_abort		; write ctrl_c char and terminat

		sbrs	flags,echo		; if echo is enabled support bs char
		rjmp	uart_rx_store		; nope

		cpi	temp,bs
		brne	uart_rx_store
		
		cpi	yl,ram_begin		; we don't want to dec past 
		breq	pc + 2			
		dec	yl		

		sbrs	flags,echo
		rjmp	uart_isr_end		; and exit without saving char
		_send	' '
		_send	bs
		rjmp	uart_isr_end


uart_rx_store:	cpi	yl,ram_begin + max_str_len 
		breq	uart_isr_end		; no room, discard

		st	y+,temp			; store the char in ram
		rjmp	uart_isr_end		; all done
		

;uart_rx_abort:	ldi	yl,ram_begin		; reset to beginning of ram storage
;		ldi	temp,ctrl_c		; save the char
;		st	y+,temp

uart_str_done:	ldi	temp,0
		st 	y,temp			; store zero terminate
		sbr	flags,(1 << string_rx)	; set flag to indicate complete
		ldi	yl,ram_begin		; reset pointer to beginning of ram
		rjmp	uart_isr_end

uart_err:	in	temp, UDR		; read and discard byte to clear UDR

uart_isr_end:	pop	temp			; restore
		out	SREG,isr_save		; restore	
		reti


;------------------------------------------------------------------------
;--- start here on power up or external reset
;--- init ports, ints, and  zero all registers 		
reset:		ldi	r16,0
		ldi	zl,0				; beginning of registers		
clr_reg:	st	z+,r16
		cpi	zl,0x1e				; don't zero z reg
		brne	clr_reg

		ldi	zl,ram_begin	
clr_ram:	st	z+,r16
		cpi	zl,0xe0
		brne	clr_ram		

;--- init stack pointer (somewhat important)
		ldi    temp,low(RAMEND)			; init stack 
       		out    spl,temp 

;--- portb setup
		ldi	temp,(1<<rad_ptt)|(1<<mode1)|(1<<mode0)|(1<<txd_614)|(1 << sw_inp)
		out	DDRB,temp
		sbi	PORTB,sw_inp			; charge cap on switch input pin	
;--- portd setup
		ldi	temp,(1<<led)|(1<<txd_232)|(1<<spi_sck)|(1<<spi_cs)|(1<<spi_data)
		out	DDRD,temp
;--- setup uart, 9600 baud, int on rx, no tx ints
		ldi	temp,((clock / console_baud) / 16) -1
		out	UBRR,temp			; Set baud rate generator
		ldi	temp, (1<<RXCIE)|(1<<RXEN)|(1<<TXEN) ; enable rxc interrupts
		out	UCR,temp			; enable UART tx & rx  w/o interrupts
;--- set up timer 1  
		ldi	temp,(1 << CS10)		; timer1 no prescale  
		out	TCCR1B,temp
		ldi	temp,(1<<TOIE1) 		; enable timer 1 overflow int
		out	TIMSK,temp

;--- set up tmr0
;		ldi	temp,2				; timer 0 prescale/8
;		out	TCCR0,temp			;
;		ldi	temp,0b00000010			; enable Timer 0 interrupt
;		out	TIMSK,temp



;--- global enable ints
		sei					; global enable ints

		ldi	EEaddr,_EE_flags		; copy eeprom stored flags to flag reg
		rcall	EEread
		mov	flags,EEdata

		ldi	yl,ram_begin			; location of string storage for isr
		rcall	fram_init
		clr	fram_bank

		off_mx614				; init mx614

		_send_232_str	_hello			; display sign on message

		rcall	led_flash			; blink

		rx_mx614				; put into rx mode to start

		sbr	rx_flags,(1 << rx_enable)	; enable/re-enable reception

		sbrc	flags,conv_mode
		rjmp	converse_mode

		rcall	get_switch			; get switch status
		tst	temp
		breq	converse_mode			; if switch is up goto converse mode
	
;----------------------------------------------
cmd_top:	cbr	flags,(1 << string_rx)|(1<<conv_mode)		; allow for new string
		rcall	save_ee_flags

		sbrs	flags,echo
		rjmp	cmd_no_echo

		rcall	crlf				; prompt
		_send	'.'

cmd_no_echo:	sbrs	flags,string_rx
		rjmp	cmd_no_echo	   	

		ldi	zl,ram_begin			; convert first char to upper case
		ld	temp,z
		rcall	to_upper
		
do_cmd:		cpi	temp,'B'			 
		brne	pc+2
		rjmp	b_commands			; check for btest or beacon command

		cpi	temp,'C'			; enter converse mode
		brne	pc+2
		rjmp	converse_mode

		cpi	temp,'M'			; get my callsign
		brne	pc+2
		rjmp	save_mycall 

		cpi	temp,'U'			; ui station and path
		brne	pc+2
		rjmp	save_ui 
		
		cpi	temp,'E'			; get echo mode
		brne	pc+2
		rjmp	echo_mode

		cpi	temp,'I'			; send a beacon (id) (will key radio)
		brne	cmd_top
		rjmp	test_beacon 


; enter converse mode
; display all received packets
; send all cr terminated rs-232 strings
; send beacon text every beacon time out (if beacon enabled)
; ctrl-c as first char followed by cr  will return to command mode
converse_mode:	cbr	flags,(1<<string_rx)
		sbr	flags,(1<<conv_mode)
		rcall	save_ee_flags			; save coverse mode flag bit
		rcall	crlf
		sbr	rx_flags,(1 << rx_enable)	; enable/re-enable reception

converse_loop:	sbrs	flags,string_rx			; check for console data
	    	rjmp	converse_1	

		lds	temp,ram_begin			; get first char of string
  		cpi	temp,ctrl_c
 		breq	cmd_top
 
 		rcall	send_ram_string			; send the string
		cbr	flags,(1 << string_rx)
		rcall	crlf				; make pretty 
		rcall	display_packet			; local echo of transmitted string

converse_1:	sbrs	flags,echo
		rjmp	converse_2
		cpi	yl,ram_begin			; are we currently receiving a string?
		brne	converse_loop			; yes back to top

converse_2:	rcall	disp_any_packets		; see if any packets have been received
 		rcall	check_beacon			; check for beacon
 		rjmp	converse_loop			; keep looping


;--- send beacon text, then back to top
test_beacon:	rcall	crlf
		rcall	send_beacon
		rjmp	cmd_top


;-- check to see if this is a BText or BEacon command
b_commands:	lds	temp,(ram_begin + 1)
		rcall	to_upper
		cpi	temp,'T'
		breq	get_b_text
		cpi	temp,' '
		breq	beacon_mode
		cpi	temp,'E'
		breq	beacon_mode
		rjmp	cmd_top


;--- copy beacon text to eeprom -----------------
get_b_text:	ldi	EEaddr,_b_text		; point to b_text storage
		ldi	ii,1			; point to beginning of string
		rcall	pos_zl
		
get_b_loop:	ldi	EEdata,0	
 		cpi	EEaddr,0x7f		; test for end of storage
 		breq	pc + 2	
		ld	EEdata,z+		; get char
		rcall	EEwrite
		tst	EEdata			; tst for zero termination
		brne	get_b_loop		; get next char	
			
get_btxt_end:	rjmp	cmd_top



;--- enable or disable local char echo 
echo_mode:	rcall	count_param
		cpi	byte_cnt,0x02
		brne	echo_mode_end

		rcall	get_on_off
		brcs	echo_mode_end

		cbr	flags,1<<echo		; assume off
		sbrc	ii,0
		sbr	flags,1<<echo
		rcall	save_ee_flags		; save flags	
echo_mode_end:	rjmp	cmd_top

;---- enable or disable beacon at fixed interval
beacon_mode:	rcall	count_param
		cpi	byte_cnt,0x02
		brne	beacon_m_end

		rcall	get_on_off
		brcs	beacon_m_end

		cbr	flags,1<<beacon_enable		; assume off
		sbrc	ii,0
		sbr	flags,1<<beacon_enable
		rcall	save_ee_flags			; save flags	

		sbrs	flags,echo			; if echo is enabled echo beacon status	
		rjmp	cmd_top
		
		_send_232_str _beacon
		sbrs	flags,beacon_enable
		rjmp	beacon_off

		_send	'N'
		rjmp	cmd_top

beacon_off:	_send	'F'
		_send	'F'
beacon_m_end:	rjmp	cmd_top	
	

;--- get second command argument, test for on or off (actually on or of) 
;--- return value in ii, 0 = off. 1 = on, carry set if no match
get_on_off:	
		ldi	ii,1			; point to first char of 2nd arg
		rcall	pos_zl
		ld	temp,z+
		rcall	to_upper
		cpi	temp,'O'		

		
		brne	get_of_end		; error

		ld	temp,z
		rcall	to_upper

		clr	ii
		clc

		cpi	temp,'F'		; is second char of string an 'f'?	
		breq	get_of_end
		
		inc	ii	
		cpi	temp,'N'		; is second char of string an 'n'?	
		breq	get_of_end

get_of_err:	sec				; set carry to indicate no match 
get_of_end:	ret
		

;--- write non volitile flag bits to eeprom
save_ee_flags:	mov	EEdata,flags
		andi	EEdata,0b11100000	; we only save the top 2 non-volatile flag bits
		ldi	EEaddr,_EE_flags
		rcall	EEwrite
		ret

;---------------------------------------
;--- save my call sign
save_mycall:	rcall	count_param
		cpi	byte_cnt,0x02			; check for correct num of args
		breq	pc + 2
		rjmp	cmd_top

		ldi	ii,1				; point to second arg
		rcall	pos_zl
		ldi	EEaddr,_mycall
		rcall	save_call
		rjmp	save_ui_end			; write a trailing zero to eeprom, and exit

;------------------------------------------------------------------------------
;--- save ui destination and optional digi path
save_ui:	ldi	ii,1				; point to begining of call in ram
		rcall	pos_zl
		ldi	EEaddr,_dest
		rcall	save_call

		ldi	EEdata,0
		rcall	EEwrite				; terminate station with a zero 

		rcall	count_param
		cpi	byte_cnt,4
		brlo	save_ui_end			; no digis

		cpi	byte_cnt,10
		brsh	save_ui_end			; too many digis

		ldi	ii,2				; point to first char of third arg (shoud be v)
		rcall	pos_zl
		ld	temp,z
		rcall	to_upper
		cpi	temp,'V'			
		brne	save_ui_end
		
		ldi	ii,3				; point to first digi in string
		ldi	EEaddr,_digi			; point to eeprom digi storage

save_ui_loop:	push	ii				; ii gets trashed in save_call
		rcall	pos_zl				; position zl at digi
		rcall	save_call			; parse to eeprom
		pop	ii				
		inc	ii
		cp	ii,byte_cnt			; compare current arg with total
		brne	save_ui_loop			; not done
		

save_ui_end:	ldi	EEdata,0
		rcall	EEwrite

		rjmp	cmd_top				; write a trailing zero to eeprom, and exit
		
;--- parse out call sign and ssid, save in eeprom	
; EEaddr points to storage, zl points to first char of station
;---
save_call:	push	EEaddr				; save pointer to beginning of call storage
		ldi	ii,6
		ldi	EEdata,' '
fill_sp_lp:	rcall	EEwrite				; fill destination with six space chars
		dec	ii
		brne	fill_sp_lp	
		subi	EEaddr,6			; reset ee pointer to start of storage

		ldi	ii,6				; max 6 char		

get_call_lp:	ld	temp,z+

		tst	temp				; check for end of string
		breq	ssid_0

		cpi	temp,','	
		breq	ssid_0

		cpi	temp,'-'			; test for ssid delim char
		breq	get_ssid

		cpi	temp,' '
		breq	ssid_0
		
		
save_call_char:	rcall	to_upper
		mov	EEdata,temp
		rcall 	EEwrite				; store it	
		dec	ii
		brne	get_call_lp

		ld	temp,z+
		cpi	temp,'-'			; if the char following the sixth char is a - get ssid
		brne	ssid_0				; otherwise ssid = 0

get_ssid:	ld	temp,z+				; get first char after '-'
		rcall	asc_hex				; convert
		brcc	pc + 2
		rjmp	ssid_0				; was garbage char
		
		mov 	temp2,temp			; save it		

		ld	temp,z				; get second char after '-'
		rcall	asc_hex
		brcc	pc + 2
		rjmp	ssid_0_9	

		tst	temp2
		breq	pc + 2
		subi	temp,-10
		rjmp	save_ssid
			
		
ssid_0_9:	mov	temp,temp2			; get saved first digit
		rjmp	save_ssid

ssid_0:		ldi	temp,0

save_ssid:	ori	temp,0x30			; call was less then 6 chars and no delim

		mov	EEdata,temp
		
		pop	EEaddr				; get beginning of call storage
		subi	EEaddr,-6
		rcall 	EEwrite				; and save 	
save_call_end:	ret		


;--- blink led 1
led_flash:	led_red				; blink led
		ldi	temp,100
		rcall	del_hunds	
		led_off
		ret

;---- check buffers and send any queued packets to console
disp_any_packets:
		
		ldi	zl,pack_buff_0		; pointer to first buffer
		andi	fram_bank,0xf0		; mask off low bits, start at bank zero

disp_buff_lp:	ld	temp,z+
		tst	temp			; test for packet length in buffer
		brne	disp_doit
		inc	fram_bank
		cpi	zl,pack_buff_0 + 4
		brne	disp_buff_lp			

disp_buff_done:	led_off
		ret

disp_doit:	mov	packet_len,temp
		rcall	display_packet

		clr	temp
		mov	zl,fram_bank
		andi	zl,0x03			; mask off hi bits
		subi	zl,(- pack_buff_0)
		st	z,temp			; mark buffer as empty
		rjmp	disp_any_packets	; start over	


;--- ship formatted packet data to console
;--- data must reside in fram starting at zero, packet_len is msg length without crc
;--- fram_bank must already be set 
display_packet:	
;--- un-comment to only display packets with a pid of f0 (pure ax25)
;		clr	zl			; point to beginning of data
;pid_loop:	rcall	fram_rd			; look for end of path and start of data
;		sbrs	temp,0
;		rjmp	pid_loop
;		inc	zl
;		rcall	fram_rd
;		cpi	temp,0xf0		; compare pid
;		brne	disp_pd_end		; no match abort	
		
		ldi	zl,7			; pointer to txmit station
		rcall	disp_call
		_send	'>'			; make pretty
	
		clr	zl			; load pointer of destination station		
		rcall	disp_call

		ldi	zl,13
		rcall	fram_rd
		sbrc	temp,0			; any digis?
		rjmp	disp_pack_data		; no, just display data

digi_loop:	ldi	temp,','		
		rcall	putc
		rcall	disp_call		; display formatted call, ssid, will return with lsb of ssid location in carry
		brcc	digi_loop		; keep looping	

disp_pack_data: _send	':'			; delim for data
		subi	zl,-2			; point past control and pid

disp_pd_loop:	cp	packet_len,zl		; display the msg data
		breq	disp_pd_end	
		rcall	fram_rd
		rcall	putc
		rjmp	disp_pd_loop
	
		
disp_pd_end:	rcall	crlf			; new line
		ret	

;--- display call and ssid on console
;--- z points to data, display 6 shifted chars, then ssid,
;--- returns with carry indicating lsb of ssid location
disp_call:	ldi	ii,6			; num of chars
dp_loop:	rcall	fram_rd			; get char
		lsr	temp			; 
		cpi	temp,' '		; is space?
		breq	pc + 2			; don't ship	
		rcall	putc
		dec	ii
		brne	dp_loop

		rcall	fram_rd
		mov	ii,temp			; get ssid				
		lsr	ii			; shift off lsb	
		andi	ii,0x0f			; mask off top nibble
		breq	ssid_end		; if  ssid = zero display nothing	

		_send	'-'

		cpi	ii,10 
		brlo	ssid_1_9			

		_send	'1'
		subi	ii,10

ssid_1_9:	mov	temp,ii
		rcall	send_nibble
	
ssid_end:	dec	zl
		rcall	fram_rd			; get ssid again
		lsr	temp			; shift lsb into carry
		ret	
;--------------------------------------------------------------------------------------
;--- send zero terminated string from ram, string must start at fram_begin 
;--- trashes xl,zl,packet_len,temp
;--- sends eepom stored ui path before string
send_ram_string:
		andi	fram_bank,0xf0		; mask off bottonm bits
		ori	fram_bank,tx_bank	; set to tx bank in fram 
		rcall	copy_path_fram		; start buildeing packet in fram	
		ldi	xl,ram_begin		; point to beginning of string
send_str_loop:	ld 	temp,x+			; start copying ram msg to fram
		tst	temp	
		breq	send_str_doit 			
		rcall	fram_wr			; save it					
		rjmp	send_str_loop

send_str_doit:	mov	packet_len,zl
		rcall	send_packet

send_str_end:	ret


;--- check beacon for time out(if enabled) ,
check_beacon:	sbrs	flags,beacon_enable	
		rjmp	check_b_end			; if beacon is not enabled
		tst	b_timer				; is beacon timer at zero?
		breq	send_beacon	
check_b_end:	ret

;---------------------------------------------
;--- send beacon text,  reset beacon timer
send_beacon:	andi	fram_bank,0xf0		; mask off bottonm bits
		ori	fram_bank,tx_bank	; set to high bank in fram
		rcall	copy_path_fram		; start building build msg in fram	
		_copy_ax_string _b_text		; copy the message text	

beaconl_1:	mov	packet_len,zl

		rcall	send_packet
		rcall	display_packet		; display on local console
		
		ldi	temp,beacon_time
		mov	b_timer,temp		; 
		ret

;--- copy path from eeprom to already selected fram_bank
;--- add control and pid bytes
copy_path_fram:	clr	zl			; point to beggining of  fram

		_copy_ax_string_shift	_dest	; copy destination shifted
		_copy_ax_string_shift	_mycall	; copy mycall from eeprom
		_copy_ax_string_shift 	_digi	; copy digi path (if any)
		
		dec	zl			; point last char of path
		rcall	fram_rd			; get last char
		dec	zl			; undo the inc zl at the end of the last read
		ori	temp,0x01		; set lsb to indicate end of path
		rcall	fram_wr

		ldi	temp,control_byte	; write control
		rcall	fram_wr
		ldi	temp,pid_byte		; write pid
		rcall	fram_wr

		ret

;--- send packet from fram, packet must start fram_begin, fram_bank must be set properly
;--- packet_len  contains length total length
send_packet: 	rcall	wait_dcd		; wait for clear channel

		cbr	rx_flags,(1 << rx_enable) ; disable receive (rx routine will change crc registers)
		tx_mx614			; change mode on modem chip	
		ptt_down			; key the radio

		ldi	ii,tx_delay		
		rcall	send_ax_flags		; initial delay

		ldi	temp,0xff
		mov	ax25_crc_lo,temp	; init crc	
		mov	ax25_crc_hi,temp

		mov	ii,packet_len		; get message length
		clr	zl	        	; pointer to start of message	
send_p_loop:	rcall	fram_rd			; get char
		rcall	put_ax			; send char

		dec	ii
		brne	send_p_loop

		rcall	send_ax_crc

		ldi     ii,tail_flags		; send  final flag bytes
		rcall	send_ax_flags

		ldi	temp,tx_hang
		rcall	del_hunds

		ptt_up				; release ptt
		rx_mx614			; back to rx mode
		cbi	PORTB,txd_614		; clear data out line, if high mx614 sends backchannel tone
		sbr	rx_flags,(1 << rx_enable)
		ret

;--- send calculated crc, lo byte first
send_ax_crc:	rcall	ax25_tx_wait		; wait for last char to be sent, otherwise crc is in-correct
		com	ax25_crc_lo		; invert calculated crc
		com	ax25_crc_hi
		push	ax25_crc_hi		; save hi because it will get trashed when we send lo
		mov	temp,ax25_crc_lo
		rcall	put_ax			; ship lo
		pop	temp			; get saved hi
		rcall	put_ax			; and send hi
		ret
		
;--- send ii number of flag bytes
send_ax_flags:	rcall	ax25_tx_wait		; wait till all all data is sent	
		sbr	flags,(1 << ax25_flag)	; set flag bit to disble bit stuffing and crc
		ldi	temp,0x7e		
		rcall	put_ax
		dec	ii
		brne	send_ax_flags
		rcall	ax25_tx_wait		
		cbr	flags,(1 << ax25_flag)	; wait until last bit ships to clear flag bit	
		ret

;--- wait for carrier det to drop, then wait a random period, re-check carrier det pin. 
wait_dcd:	;rcall	disp_any_packets
		sbic	PINB,det_614		; check pin
		rjmp	wait_dcd
		rcall	get_random
		andi	temp,0b00111000		; just some random delay	
		rcall	del_hunds 
		sbic	PINB,det_614		; check pin again
		rjmp	wait_dcd
		ret
            
;--- get toggle switch position 
;--- charge cap on input pin, switch pin to input, 
;--- see how long cap takes to discharge
get_switch:	tst	tic			; wait for tic = 0
		brne	pc - 1	

		cbi	DDRB,sw_inp		; make pin an input
		cbi	PORTB,sw_inp		; turn off pull up current
	
		sbic	PINB,sw_inp		; wait for cap to discharge
		rjmp 	pc -1

		ldi	temp,2			; switch down
		sbrc	tic,3			; < 8?
		ldi	temp,0			; switch up
		sbrc	tic,4			; < 0x10?
		ldi	temp,1			; switch middle

		sbi	PORTB,sw_inp		
		sbi	DDRB,sw_inp		; charge cap for next time

		sei
		ret

; get a pseudo random 8 bit value via linear congruential algorithm 
; ported from pic code posted by Nikolai Golovchenko
; trashes ii, returns random value in temp 
get_random:	lds	temp,random		; get last value from ram
		mov	ii,temp			; save in both ii and temp

		lsl	ii
		swap	ii	
		andi	ii,0xE0

		add	temp,ii
		add	temp,ii
		add	temp,ii
		ldi	ii,0x35
		sub	temp,ii
		sts	random,temp
		ret	

;--- set zl to first char of arg, ii
pos_zl:		ldi	zl,ram_begin		; point to beginning
pos_zl_loop:	rcall	next_arg	
		dec	ii
		brne	pos_zl_loop
		ret

;--- count total number of parameters in string, parameters are seperated by ' ' or ','
count_param:	clr	byte_cnt
		ldi	zl,ram_begin				; point to beginning 	

count_p_loop:	rcall	next_arg
		ld	temp,z
		brne	pc + 2
		ret
		inc	byte_cnt
		rjmp	count_p_loop
	
;--- inc zl till a non-white char or ','  is encountered
discard_white:	ld	temp,z
		cpi	temp,' '
		breq	discard_loop
		cpi	temp,','
		breq	discard_loop
		ret
discard_loop:	inc	zl					; point to to next location
		rjmp	discard_white

		
; inc zl till a ' ',',', or end of string
discard_nw:	ld	temp,z
		tst	temp					; test for zero termination
		breq	discard_end
		cpi	temp,','
		breq	discard_end	
		cpi	temp,' '
		breq	discard_end
		inc	zl
		rjmp	discard_nw
discard_end:	ret


; position zl at first char of next argument
next_arg:	ld	temp,z
		tst	temp
		brne	pc + 2					; test for end of string
		ret	
		cpi	temp,' '
		breq	pc + 2
		rcall	discard_nw
		rcall	discard_white
next_arg_end:	ret
			

;----------------------------------------------------
;---- delay temp * 10 ms (approx) 
del_hunds:	add	temp,sec_01
del_h_loop:	cpse	temp,sec_01 
		rjmp	del_h_loop
		ret
;----------------------------------------------------
;--- send a cr and lf to console
crlf:		ldi	temp,cr
		rcall	putc
		ldi	temp,lf
		rcall	putc
		ret	

;---- put char to console
putc:		sbis	USR,UDRE		;Is UART transmitter ready?
		rjmp	putc			;If not, wait
		out	UDR,temp		;Put character to UART
		ret

;--- wait for ax25_txbits = 0 then load buffer and set bit
put_ax:		rcall	ax25_tx_wait
		mov	ax25_txbuff,temp
		ldi	temp,0x08
		mov	ax25_tx_bits,temp	; 8 bits to send, start
		ret
;--- wait for buffer to empty/last bit was sent
ax25_tx_wait:	tst	ax25_tx_bits 
		brne	ax25_tx_wait
		ret	


;--- convert temp to upper case
to_upper:	cpi 	temp,'a'
		brlo	toupper_end
		cpi 	temp,'z'+1
		brsh	toupper_end
		subi	temp,' '
toupper_end:	ret

;--------------------------------------
;--- send byte to console as two asc chars
send_byte:	push	temp		; save temp
		rcall	send_hi_nibble	; send hi nibble
		pop	temp		; reget data	
		push	temp		; and save it again
		rcall	send_nibble
		pop	temp		; just restores temp
		ret
						; fall into send nibble
;----------------------------------------
;--- send low nibble as ascii		
send_hi_nibble:	swap	temp
send_nibble:	rcall	hex_asc
		rcall	putc
		ret

;--------------------------
;--- routine converts 0-f hex in temp to 0 to F ascii
hex_asc:	andi   	temp,0b00001111     	;lower nybble only
   		cpi   	temp,10  		;0-9 or A-F?
   		brlo	hex_asc_1           	;
   		subi	temp,-7    		;adjust A-F
hex_asc_1:	subi   	temp,-48  		;add offset to convert to ascii
		ret

;------------------------------------------------
;--- convert asci char in temp to 0-15 nibble
;--- set carry if char is invalid (not 0-9 or a-f)
asc_hex:	rcall	to_upper		; convert to upper case 
		cpi	temp,'F'+1		; test for 'E' and above
		brsh	asc_error
		cpi	temp,'A'
		brsh	asc_letters
		cpi	temp,'9'+1
		brsh	asc_error
		cpi	temp,'0'
		brsh	asc_numbers

asc_error:	sec				; set carry to indicate error
		ret				; and out

asc_letters:	subi	temp,0x07		;A-F ('0'+7) is the same as ('A' - 10)
asc_numbers:	subi	temp,'0'		;0-9	
		ret				; and out

;----- random write temp to address pointed to by zl
;----- post inc zl
fram_wr_no_sei: sbr	flags,(1<<fram_no_sei)
fram_wr:	cli
		push	temp			; save data
		cbi	spi_port,spi_cs		; select chip
		ldi	temp,fram_op_wren	; get op code
		rcall	fram_tx			; send it
		sbi	spi_port,spi_cs		; de-select chip

		cbi	spi_port,spi_cs		; re-enable chip
		mov	temp,fram_bank		; get hi order bits
		swap	temp
		lsr	temp			; move into position
		andi	temp,0b00111000		; mask off other bits
		ori	temp,fram_op_wr		; add in write op code
		rcall	fram_tx			; send hi bits and wr op code
		mov	temp,zl			
		rcall	fram_tx			; send low address
		pop	temp			; get data
		rcall	fram_tx			; and send data	

		inc 	zl			; bump low address
		rjmp	fram_stop


;--- clock out 8 bits from temp to spi bus
fram_tx:	spi_data_out			; set spi data line as output
		ldi	bitcnt,0x08		; 8 bits to send	
fram_tx_loop:	rol	temp			; rotate msb to carry
;		brcs	pc + 2
		cbi	spi_port,spi_data	; assume a zero
		brcc	pc + 2
		sbi	spi_port,spi_data

		sbi	spi_port,spi_sck	; blip clock
		nop				; a little delay for fram timing spec
		cbi	spi_port,spi_sck

		dec	bitcnt
		brne	fram_tx_loop
		ret

;----- read address pointed to by zl, returns in temp
;----- post inc zl
fram_rd:	cli
		cbi	spi_port,spi_cs		; enable chip
		mov	temp,fram_bank		; get hi order bits
		swap	temp
		lsr	temp			; move into position
		andi	temp,0b00111000		; mask off other bits
		ori	temp,fram_op_rd		; add in write op code
		rcall	fram_tx
		mov	temp,zl
		rcall	fram_tx
		spi_data_inp			; set spi data line as an input
		ldi	bitcnt,0x08		; 8 bits to send	
fram_rx_loop:	sbi	spi_port,spi_sck	; set clock hi
		clc				; clear carry 
		sbic	PIND,spi_data		; test input pin
		sec
		rol 	temp
		cbi	spi_port,spi_sck	; clear clock

		dec	bitcnt
		brne	fram_rx_loop
		inc	zl
		
fram_init:	cbi	spi_port,spi_sck	; put spi bus into known state
fram_stop:	sbi	spi_port,spi_cs
		spi_data_out
		cbi	spi_port,spi_data

		sbrs	flags,fram_no_sei	; if we were called by isr don't re-enable ints
		sei
		cbr	flags,(1<<fram_no_sei)
		ret

;----------------------------------------------------
;--- internal eeprom routines
;--- read eeprom (post incs EEAR!)
EERead:		cli
		sbic	EECR,EEWE	; if EEWE not clear
		rjmp	EERead		;    keep waiting

		out	EEAR,EEaddr
		sbi	EECR,EERE	; set EEPROM Read strobe
		in	EEdata,EEDR	; get data
		rjmp	inc_eear	; bump address and exit

;--- write EEPROM (post incs EEAR!)
EEWrite:	cli
		sbic	EECR,EEWE	; if EEWE not clear
		rjmp	EEWrite		;    keep waiting
 
 		out	EEAR,EEaddr
		out	EEDR,EEdata	; output the data
		sbi 	EECR,EEMWE	; set master write enable	
		sbi	EECR,EEWE	; set EEPROM Write strobe

inc_eear:	inc	EEaddr
		sei
		ret
;--------------------------------------------------------------
;--- copy eeprom strings to fram
;--- if shifting is enabled, left shift before copying 
ax25_s_string:	sbr	flags,(1 << ax25_shift) ; enable data shift before sending ax25 address data
ax25_string:	mov	EEaddr,temp		; move temp to addr
next_ax25_char:	rcall	EERead			; get char from eeprom
		tst	EEdata			; test for eom
		brne	next_ax			;
		cbr	flags,(1 << ax25_shift) ; clear shift enable flag
		ret

next_ax:	mov	temp,EEdata		;
		sbrc	flags,ax25_shift	;
		lsl	temp			;
		rcall	fram_wr
		rjmp	next_ax25_char		; loop it


;--- send zero terminated strings from eeprom to console
;serial_string:	out	EEAR,temp		; move temp to addr
;next_ser_char:	rcall	EERead			; get char from eeprom
;		tst	EEdata			; test for eom
;		brne	pc + 2			; skip nz
;		ret
;		mov	temp,EEdata		; 
;		rcall	putc			; ship it 
;		rjmp	next_ser_char		; loop it


;--- send zero terminated strings from flash to console
; use z for pointer
ser_ram_str:	lpm				;
		tst	r0			;
		brne	pc + 2			;
		ret
		mov	temp,R0			;
		rcall	putc
		adiw	zl,1
		rjmp	ser_ram_str	


;*********************************************************************
; codespace stored  string storage 
;*********************************************************************
;--- message strings----------------
_hello:		.db	cr,lf,"AVR_UI_TNC 1.7 Copyright Henry Carl Ott N2RVQ",cr,lf,0
_beacon:	.db	"BEACON=O",0

;*********************************************************************
; EEPROM 
;*********************************************************************
         	.eseg
.org		0x00

empty:		.db 	0x00			; leave empty, may get corrupted do to possible bug  
 
;--- mycall and dest should be 6 chars followed by ssid (add 30 hex) and then zero terminated
;--- total digi path should be 0 terminated
;--- the program will set the low bit of the final digi 
.org		0x01
_mycall:	.db	"NOCALL",0x30,0		; call 
.ORG		0X09
_dest: 		.db     "APZAVR",0x30,0        	; dest 

_digi:        	.db     "RELAY ",0x30           ; digi path
        	.db     "WIDE  ",0x30,0  	; final digi, zero terminate

;.org		0x3e
;_b_time:	.db	0x00			; beacon time in minutes
_ee_flags:	.db	(1 << echo)		; just various status bits to remember during power down	
.org		0x40
_b_text:	.db	"AVR_UI_TNC TEST, Hello from an AVR",0	
