Full Version : Using AVRs with APRS Packet Radio (ASM)
avr >>COMMUNICATIONS & WEB PROJECTS >>Using AVRs with APRS Packet Radio (ASM)


Admin5- 04-20-2006
Experiments with AVR Microprocessors and APRS Packet Radio.

--------------------------------------------------------------------------------

Here is some experimental hardware and software to transmit and receive AX.25 packets.
It is essentially a PIC-E clone designed around a Atmel AT90S2313 with a few extra bells and whistles.
I had picked up a couple of MXCOM MX-614s at the TAPR display (I also joined TAPR) at the Dayton Hamvention, and I needed a project to try them out on. I had designed many projects around Microchips PIC series micros, but had recently started experimenting with Atmel AVR micros. On the pic mail list this is referred to as 'going over to the dark side'. AVRs are PIC like risc processors, but with some additional features that made them slightly more suited for certain projects. AVRs are also very cost competitive against midrange PIC parts. Simple flash based in circuit programming and cheap development tools also contribute the the attractiveness of the AVR processors. AVRs also code very well in C, but I'm still stuck with slugging away with assembler.

Most of the AVR devices are available now from Digi-Key or other online sources. Digi-Key also stocks the STK-500 starter kit which is a great way to start working with Atmels for a very reasonable price.


--------------------------------------------------------------------------------

NOTE: This project is not intended as a PIC-E or MIC-E (I guess I could call it the AVR-E) replacement or competitor. It's just as another tool to further increase the utility of APRS. The hardware is pretty flexible, the example code implements a very basic UI-TNC, but you could easily edit out the console code, leave the AX.25 encode/decode routines to create trackers, WX stations or minimal function digipeaters. It should also be possible to implement an almost complete KISS modem, the main limiting factor is ram.


--------------------------------------------------------------------------------

Parts availability: If anyone has trouble finding some of the parts for the project, I have some spare PCF8583s (I only have smd parts for the PCF8583, sorry) . You can find them on my FOR SALE page. I'm not trying to make any profit on any parts for this project, just trying to cover my expenses.. I'm sorry that I have no source to recommend for small quantities of the FM25160.


--------------------------------------------------------------------------------

NEW: Here is a revised design. Hardware REV 1.5, software 1.7.

The newer deisign no longer uses the PCF8583 I2C RAM/RTC. After the generous donation of some sample RAMTRON FM25160 fram parts by Klaus Hirschelmann I revised the hardware. We now have 2K of storage but no RTC. I simplified the Beacon function and used some of the additional storage to buffer incoming packets. I also made some minor changes to the hardware based on input from Klaus. The schematic and source code may be downloaded as one archive.

Link: http://users.rcn.com/carlott/avr_projects.html

Code Verrsion 1.0 for 8535
CODE

;***************************************************************************
;* Title: AVR_UI_TNC PRELIMINARY CODE 1.0
;* 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@interport.net  www.interport.net/~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.
;***************************************************************************
;* 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 in minutes,  examples 'BEACON 10' or 'B 10' or 'B E 10' or 'B 00' ('B 00' turns off beaconing)
; 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_1_on
sbi PORTD,led_1
.endm
.macro led_1_off
cbi PORTD,led_1
.endm
.macro led_2_on
sbi PORTD,led_2
.endm

.macro led_2_off
cbi PORTD,led_2
.endm

;--- basic i2c macros
.macro _scl_low
sbi DDRB,SCL
.endm

.macro _scl_hi
cbi DDRB,SCL
.endm

.macro _sda_low
sbi DDRD,SDA
.endm

.macro _sda_hi
cbi DDRD,SDA
.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 i2c ram (left shift data first)  
.macro _copy_ax_string_shift
ldi temp,@0
       rcall ax25_s_string
.endm

;--- copy zero terminated eeprom string into i2c ram  
.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 deinations
;--- portb
.equ scl = 0; 0 i2c clock
.equ sw_inp = 1; I sw_inp
.equ txd_614  = 2; 614_txd
.equ det_614 = 3; O I 614_det
.equ mic_ptt  = 4; I mic ptt input
.equ rad_ptt = 5; 0 radio ptt output
.equ mode1 = 6; 0 mx614 mode 1 select
.equ mode0 = 7; 0 mx614 mode 0 select

;--- portd
.equ sda = 6; I/0 i2c data
.equ led_1 = 5; O led_1
.equ led_2  = 4; 0 led_2
.equ rxd_614  = 3; I 614_rxd /int0  
.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 i2c_ram_begin = 0x10; beginning of free ram on i2c rtc  

.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 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  = 224; 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_001  = r02; incs @ 100 hz
.def random  = r03; used for pseudorandom number generation
.def u_data   = r04; rs-232 data (rx)
.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
.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 uart_rx  = 0x00 ; has a char been received via rs232?
.equ ax25_flag = 0x01 ; sending flags? (no bit stuffing or crc)
.equ ax25_shift = 0x02 ; do we shift the data before sending? (address)
.equ echo  = 0x03 ; if set echo console chars
.equ i2c_busy = 0x06 ; i2c is currently trying to rd/wr
;-------------------------
;**************************
;- 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 good_packet = 0x03 ; a valid packet was received
.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
.equ half_flag = 0x06 ; set if last bit received was a zero (more of an 1/8 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 in
 rjmp t1int  
.org  OVF0addr  ; overflow of 8  bit timer 0 usedx for secondary 4800 baud serial port
 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

 sbrc rx_flags,good_packet; if last packet has not been processed
 rjmp rx_flush ;

 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 PIND,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: cbr rx_flags,(1 << old_bit); copy new_bit to old
 sbrc rx_flags,new_bit  
 sbr rx_flags,(1 << 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
 brcc pc +2  ; check for carry, if we are not near a page boundary, we could probaly ignore  
 inc zh  ; 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
 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 packet_len,ax25_rx_chars; and save length
 led_1_on   ; indicator
 sbr rx_flags,(1 << good_packet); set flag to indicate all done

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_001   ; bump hunds of seconds (10ms)
     
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 status, calcs crc on full bytes only
;--- store the byte if in packet
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

store_ax_byte: cbr rx_flags,(1<<flag_det)|(1<<half_flag); out of flags and into data
 sbr rx_flags,(1<<in_packet) ; within packet data (in theory)

rx_crc_byte: mov zl,ax25_rxbuff  ; grab a copy of the data

rx_crc_loop: lsr zl   ; rotate lsb into carry
 rcall ax25_calc_crc
 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: ldi zl,(i2c_ram_begin - 1)
 add zl,ax25_rx_chars
 mov temp,ax25_rxbuff
 rcall i2c_wr
store_ax_end: ret

;--- init registers and flags for next bit
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  ;
 mov u_data,temp ; save actual data
 sbr flags,(1<<uart_rx); set received data flag
 rjmp uart_isr_end

uart_err: in temp, UDR ; read and discard byte to clear UDR
 cbr flags,(1<<uart_rx); clear flag return

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

;--- 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)|(1<<led_2)|(1<<txd_232)
 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
;--- global enable ints
 sei    ; enable global ints

 ldi EEaddr,_EE_flags
 rcall EEread
 mov flags,EEdata

 rcall rtc_init  ; init the real time clock
 off_mx614   ; init mx614
 _send_232_str _hello  ; display sign on message
 rcall led_flash  ; blink
 rx_mx614   ; put into rx mode to start

 rcall get_switch  ; get switch status
 tst temp
 breq converse_mode  ; if switch is up go directly to conv mode
 
;----------------------------------------------
cmd_top: cbr rx_flags,(1 << rx_enable)|(1 << good_packet); disable recieve

 rcall crlf   ; prompt
 _send '.'

    rcall get_string

 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

 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 beacon (id)
 brne cmd_top
 rjmp test_beacon
 

; enter converse mode
; display all received packets
; send all strings
; send beacon text every beacon time out
; ctrl-c gets back to command mode

converse_mode: rcall crlf

converse_loop: sbr rx_flags,(1 << rx_enable); enable/re-enable reception
 
 sbrs flags,uart_rx  ; check for console data
     rjmp converse_1

     rcall get_string
 brcc pc + 2   ; check for ctrl-c
 rjmp cmd_top
 rcall send_ram_string  ; send the string
 rcall crlf   ; make pretty
 rcall display_packet  ; local echo of transmitted string

converse_1: sbrc rx_flags,good_packet ; check for received packet
 rcall display_packet
  rcall check_beacon  ; back to top
  rjmp converse_loop  ; keep looping

;--- send beacon text, then back to top
test_beacon: 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 get_beacon_time
 cpi temp,'E'
 breq get_beacon_time
 rjmp cmd_top

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

 ldi ii,1  ; point to first char of arg
 rcall pos_zl
 ld temp,z+
 rcall to_upper
 cpi temp,'O'
 brne echo_mode_end ; err
 ld temp,z
 rcall to_upper
 cpi temp,'F'
 breq echo_off
 cpi temp,'N'
 brne echo_mode_end

echo_on: sbr flags,(1<<echo)  
 rjmp pc + 2
echo_off: cbr flags,(1<<echo)

 mov EEdata,flags
 andi EEdata,(1 << echo)
 ldi EEaddr,_EE_flags
 rcall EEwrite

echo_mode_end: rjmp cmd_top


;--- copy beacon text to eeprom -----------------
get_b_text: rcall count_param
 cpi byte_cnt,0x02
 brne get_btxt_end ; check for correct number of arguments

 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


;--- parse beacon timeout rate from console string
get_beacon_time:
 rcall count_param
 ldi ii,01  ; point to second arg

 cpi byte_cnt,0x02
 breq get_btime_b ; second arg is time out val

 cpi byte_cnt,0x03
 brne get_btime_end ; error
 
 rcall pos_zl  ; point to second arg
 ldi ii,2  ; point to third arg

 ld temp,z
 rcall to_upper
 cpi temp,'E'
 breq get_btime_b
 cpi temp,'A'
 brne get_btime_end

get_btime_b: rcall pos_zl  ; position zl at beginning of second arg
 ld temp,z+
 rcall asc_hex

 brcc pc + 2  ; test for bad char
 rjmp get_btime_end

 push temp

 ld temp,z
 rcall asc_hex

 brcs btime_0_9

 rcall asc_hex
 pop temp2
 swap temp2
 or temp,temp2
 push temp


btime_0_9: pop EEdata
 ldi EEaddr,_b_time
 rcall EEwrite

get_btime_end:  rcall crlf
 _send_232_str _b_msg

 ldi EEaddr,_b_time
 rcall EEread  
 mov temp,EEdata
 rcall send_byte
 rjmp cmd_top

;---------------------------------------
;--- 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_1_on   ; blink led
 ldi temp,100
 rcall del_hunds
 led_1_off
 ret

;--- call before building any messages in 12c ram for sending
;--- if any packets waiting to display,display them, check carrier, disable ax25 reception
wait_display: sbrc rx_flags,good_packet ; check for received packet
 rcall display_packet
 sbic PINB,det_614  ; check for carrier
 rjmp wait_display
 cbr rx_flags,(1 << rx_enable)|(1 << good_packet); disable receive
 ret

;--- ship formatted packet data to consoole
;--- data must reside at i2c_ram_begin, packet_len is msg length (no crc)
display_packet:
;--- un-comment to only display packets with a pid of f0 (pure ax25)
;  ldi zl,(i2c_ram_begin); point to beginning of data
;pid_loop: rcall i2c_rd  ; look for end of path and start of data
;  sbrs temp,0
;  rjmp pid_loop
;  mov temp2,zl ; save pointer to data
;
;  inc zl
;  rcall i2c_rd
;  cpi temp,0xf0 ; compare pid
;  brne disp_pd_end ; no match abort
 
 ldi zl,(i2c_ram_begin + 7); pointer to txmit station
 rcall disp_call
 _send '>'  ; make pretty

 ldi zl,(i2c_ram_begin); load pointer of destination station  
 rcall disp_call

 ldi zl,(i2c_ram_begin + 13)
 rcall i2c_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

 ldi ii,i2c_ram_begin
 add ii,packet_len
 sub ii,zl  ; how many chars left

 breq disp_pd_end ; if no data just exit


disp_pd_loop: rcall i2c_rd
 rcall putc
 dec ii
 brne disp_pd_loop

disp_pd_end: rcall crlf  ; new line
 cbr rx_flags,(1 << good_packet) ; clear flag to allow reception of next packet
 led_1_off
 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 i2c_rd  ; get char
 lsr temp  ;
 cpi temp,' ' ; is space?
 breq pc + 2  ; don't ship
 rcall putc
 dec ii
 brne dp_loop

 rcall i2c_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 i2c_rd  ; get ssid again
 lsr temp  ; shift lsb into carry
 ret
;--------------------------------------------------------------------------------------
;--- send zero terminated string from ram, string must start at ram_begin
;--- trashes xl,zl,packet_len,temp
;--- sends eepom stored ui path before string
send_ram_string:rcall wait_display    ; make sure there are no packets waiting to display
 rcall copy_path_i2c    ; start buildeing packet in i2c_ram
 ldi xl,ram_begin    ; point to beginning of string
send_str_loop: ld  temp,x+     ; start copying ram msg to 12c_ram
 tst temp
 breq send_str_doit    
 rcall i2c_wr     ; save it    
 rjmp send_str_loop

send_str_doit: subi zl,(i2c_ram_begin); total msg length
 mov packet_len,zl
 rcall send_packet
send_str_end: ret


check_beacon: ldi EEaddr,_b_time
 rcall EEread   ; get b_time
 tst EEdata
 breq check_b_end  ; if b time is zero never beacon
 ldi zl,7   ; location in rtc for minutes counter
 rcall i2c_rd
 eor temp,EEdata
 brne check_b_end
 rcall send_beacon
 
check_b_end: ret


;---------------------------------------------
;--- send beacon text,  reset beacon timer
send_beacon: rcall wait_display
 rcall copy_path_i2c ;  build msg in i2c ram
 _copy_ax_string _b_text ;  copy the message text

beaconl_1: subi zl,(i2c_ram_begin); total msg length
 mov packet_len,zl

 rcall send_packet
 rcall display_packet ; display on local console
 
 ldi zl,7  ; reset minutes timer in rtc
 ldi temp,0
 rcall i2c_wr

 ret

;--- copy path from eeprom to i2c_ram
;--- add control and pid bytes
copy_path_i2c: ldi zl,i2c_ram_begin; point to beggining of i2c ram

 _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 i2c_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 i2c_wr

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

 ret

;--- send packet from i2c_ram, packet must start at i2c_ram_begin,
;--- packet_len  contains length total length
send_packet:  rcall wait_dcd ; wait for clear channel
 tx_mx614  ; change mode
 ptt_down  ; key the radio
 led_2_on  ; indicator

 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
 ldi zl,i2c_ram_begin; pointer to beggining of message
send_p_loop: rcall i2c_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
 led_2_off  
 rx_mx614  ; back to rx mode
 cbi PORTB,txd_614 ; clear data out line, if high mx614 sends backchannel tone
 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
 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

;--- original crc calculation routine
; now the isr does it on each bit sent
;calc_tx_crc: ldi temp,0xff
;  mov ax25_crc_lo,temp; init crc
;  mov ax25_crc_hi,temp
;
;  ldi zl,i2c_ram_begin  
;  mov ii,packet_len
;
;tx_crc_l1: rcall i2c_rd
;  ldi bitcnt,8
;  mov temp2,temp
;tx_crc_l2: lsr temp2  ; rotate lsb into carry
;  rcall ax25_calc_crc
;  dec bitcnt  ; bitcnt was set to 8 above
;  brne tx_crc_l2
;  dec ii
;  brne tx_crc_l1 ; get next byte
;  ret


;--- wait for carrier det to drop, then wait a random period, recheck carrier det pin.
wait_dcd: 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

;--- init rtc chip to enable minutes timer
rtc_init: cli   ;
 
 ldi zl,0  ; control status
 ldi temp,0x04 ; enable alarm register
 rcall i2c_wr

 ldi zl,0x07  ;  
 ldi temp,00
 rcall i2c_wr  ; zero minutes counter

;ldi zl,0x08  ; last wr should post inc zl
 ldi temp,0b00000011 ; enable minutes counter
 rcall i2c_wr

 sei
 ret
           
;--- get toggle switch position
;--- charge cap on input pin, switch pin to input,
;--- see how long cap takes to discharge
get_switch: 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

 ret

; get a pseudo random 8 bit value via linear congruential algorithm
; ported from pic code posted by Nikolai Golovchenko
get_random: mov temp,random
 lsl temp
 swap temp
 andi temp,0xE0

 add random,temp
 add random,temp
 add random,temp
 ldi temp,0x35
 sub random,temp
 mov temp,random
 ret

;--- get cr terminated string from console, store as zero terminated string in ram
;--- max length 96 chars, if terminated by ctrl-c, zero length, set carry
;--- try to support back space, may not work on all terminals
get_string: ldi zl,ram_begin

get_str_loop: rcall getc
 cpi temp,cr
 breq get_str_done

 cpi temp,ctrl_c
 breq get_str_abort

 cpi temp,bs
 brne save_str_char

 _send ' '
 _send bs
 dec zl
 cpi zl,ram_begin ; we don't want to be able to dec past beginning of ram
 brsh pc + 2
 ldi zl,ram_begin
 rjmp get_str_loop


save_str_char: cpi zl,ram_begin + max_str_len
 breq get_str_loop
 st z+,temp
 rjmp get_str_loop
   
get_str_abort: ldi zl,0
 sec

get_str_done: ldi temp,0
 st  z,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 of  

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_001
del_h_loop: cpse temp,sec_001
 rjmp del_h_loop
 ret
;----------------------------------------------------
;--- send a cr and lf to console
crlf:  ldi temp,cr
 rcall putc
 ldi temp,lf
 rcall putc
 ret

;--- wait for char from console, returns in u_data
getc:  sbrs flags,uart_rx ; check for data
 rjmp getc
 cbr flags,(1<<uart_rx); clear bit flag
 mov temp,u_data ; move data to temp
 sbrc flags,echo
 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

;--- minimal 12c routines for accessing the pcf8583
;--- NOTE: we are running the i2c bus faster then spec,
;--- it works fine with the pcf8583, but we may want to slow things down later
;-----------------------------------------------------------------------------
;--- random write, temp to address in zl on the external i2c_bus post incs zl
;--- trashes bit_cnt
i2c_wr:  push temp  ; save the data
 rcall i2c_get_ack  
 pop temp
 rcall i2c_tx
 rjmp i2_rw_done
 
;-----------------------------------------------  
;--- random read, data pointed to by zl into temp post inc zl
;--- trashes bit_cnt
i2c_rd:  rcall i2c_get_ack
 rcall i2c_start ; generate another start, for random read
 ldi temp,0b10100001 ; device address with rd/wr bit set
 rcall i2c_tx
 rcall i2c_rx
 
i2_rw_done: rcall i2c_stop
 inc zl
 cbr flags,(1 << i2c_busy); let isr know i2c is free
 ret
;-------------------------------------------------  
;--- see if device is present, and is not currently in write phase
i2c_get_ack: sbr flags,(1 << i2c_busy); let isr know that i2c is locked
 rcall i2c_start ; generate start condition
 ldi temp,0b10100000 ; device address with rd/wr bit cleared
 rcall i2c_tx
 brcs i2c_get_ack ; no ack try again
 mov temp,zl
 rcall i2c_tx  ; send address
 ret
;-------------------------------------------------
;--- create start condition, sda going low while scl is high
i2c_start: _sda_hi
 _scl_hi
 _sda_low ; sda goin low while scl is high
 _nop_2
 _scl_low ; start clock chain
 ret
;--------------------------------------------------
;--- stop condition, data goes high while clock high for stop bit
i2c_stop: _sda_low
i2c_st_1: _scl_hi  ; set clock high
 _sda_hi  ; data goes high while clock high for stop bit
 ret
;-----------------------------------------------
;--- clock out 8 bits on i2c bus  
i2c_tx:  ldi bitcnt,8; total number of bits to send

i2c_tx_1: rol temp ; shift msb to carry
 brcc i2c_tx_2
 _sda_hi
i2c_tx_2: brcs pc + 2 ; skpcs
 _sda_low
 rcall i2c_clock
 dec bitcnt ; any more bits?
 brne i2c_tx_1
 _sda_hi  ; bring data line hi for ack phase
 rol temp ; one more rotate to restores temp


i2c_clock: _scl_hi  ; bring clock hi
 _nop_2  ; a little delay
 clc  ; copy status on data pin to carry bit
 sbic PIND,SDA
 sec
 _scl_low ; drop clock
 ret  ; and done

;--------------------------------------------------
;--- read 8 bits from 8 bit bus to i2c_data
;--- no support for ack phase!
i2c_rx:  ldi bitcnt,8; eight bits total to get from 12c bus
 _sda_hi  ; just make sure data line is configured as an input
i2c_rx_1: _scl_hi  ; bring clock hi
 clc
 sbic PIND,SDA; copy pin status to carry bit
 sec
 rol temp ; rotate carry bit into lsb
 _scl_low ; drop clock
 dec bitcnt ; done?
 brne i2c_rx_1
 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 i2c_ram
;--- 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 i2c_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.0 Copyright Henry Carl Ott N2RVQ",cr,lf,0
_b_msg:  .db "B_TIME=",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



Admin5- 04-20-2006
Updated Version 1.7

CODE

;***************************************************************************
;* 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


Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.