;-----------------------------------------------------------------------------------------------;
; LICENSE AGREEMENT:                                                                            ;
; This program is free software. You can redistribute it and/or modify it under the terms of    ;
; the GNU General Public License as published by the Free Software Foundation, either version   ;
; 2 of the License, or (at your option) any later version. No part of this code may be used     ;
; in any commercial application without prior written permission.                               ;
;                                                                                               ;
; This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY,     ;
; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     ;
; See the GNU General Public License for more details.                                          ;
;                                                                                               ;
; Copyright (c) July 11, 2005  David J. Brown                                                   ;
;                                                                                               ;
;-----------------------------------------------------------------------------------------------;
;
; MIDI processor.asm
; developed for the ATTINY2313
;
; written by David J. Brown
; email: davebr@earthlink.net
; web: http://modularsynthesis.com
;
; created: July 11, 2005
; last modified: September 17, 2005
;
; revision 0.6
; rev 0.6 added conditional directives for timing clock,
;         active status, and expression
; rev 0.5 removed rc calibration, updated watchdog timer,
;         added map expression cc to volume cc
; rev 0.4 added realtime message processing in wait_rx,
;         process and send undefined system messages
; rev 0.3 added .def statements for registers
; rev 0.2 changed transpose switch codes 13 - 15 to octaves,
;         added specifications, changed subi to cpi command
; rev 0.1 changed multi from polyphony to keyboard split
; rev 0.0 initial code release
;
;------------------------
; conditional directives
;------------------------
;
;.equ map_expr_to_vol		=1		;uncomment to map expression cc to volume cc
;
;.equ filter_timing_clock	=1		;uncomment to filter timing clock (0xf8)
;
;.equ filter_active_status	=1		;uncomment to filter active status (0xfe)
;
;------------------------
; specifications
;------------------------
;
; Switch settings:
;
; SD5  note transpose on/off
; SD4  channel transpose on/off
; SD3  note reverse on/off
; SD2  multi keyboard on/off
;
; SB7  note transpose bit3  0/1       0-11 transpose up 0 to 11 semitones
; SB6  note transpose bit2  0/1       12 transpose up 1 octave
; SB5  note transpose bit1  0/1       13 transpose up 2 octaves
; SB4  note transpose bit0  0/1       14 transpose down 1 octave
;                                     15 transpose down 2 octaves
;
; SB3  channel transpose bit3  0/1    0-15 selects MIDI channel
; SB2  channel transpose bit2  0/1
; SB1  channel transpose bit1  0/1
; SB0  channel transpose bit0  0/1
;
; Note transpose adjusts all MIDI note on and off commands by 0 to 11 semitones
; or +1, +2,  -1 or -2 octaves as selected by SB7 - SB4.
;
; Channel transpose replaces the channel in all MIDI commands with the channel
; selected by SB3 - SB0.
;
; Note reverse inverts the keyboard around key C4.  D4 is transposed to A3#, etc.  Note
; transpose operates after the reverse function.
;
; Multi keyboard splits and replicates the keyboard at key C4. The notes below key C4
; are scaled up two octaves and the notes at key C4 and above are scaled down 2 octaves
; resulting in two keyboards with key C2 and key C6 playing C4 (middle C). Positioning
; your hands appropriately can 'reverse' the registers with your right hand playing the low
; notes and your left hand playing the high notes. Add reverse for an even crazier mode
; as each multi keyboard reverses. Note transpose operates after the multi and reverse
; functions.
;
; An external 8 MHz crystal is required since power for the AVR is unregulated.
;
;------------------------
; pins
;------------------------
;
;	pa0			xtal
;	pa1			xtal
;	pa2			reset
;
;	pb0			channel transpose bit0
;	pb1			channel transpose bit1
;	pb2			channel transpose bit2
;	pb3			channel transpose bit3
;	pb4			note transpose bit0
;	pb5			note transpose bit1 / mosi 
;	pb6			note transpose bit2 / miso
;	pb7			note transpose bit3 / sck
;
;	pd0		    MIDI in
;	pd1		    MIDI out
;	pd2			multi (on=1 / off)
;	pd3			reverse (on=1 / off)
;	pd4			channel transpose (on=1 / off)
;	pd5			note transpose (on=1 / off)
;	pd6			led (on=1 / off)
;
;	timer0		25 mS interrupts
;	timer1		not used
;
;------------------------
; registers
;------------------------
;
;		r0			
;		r1	
;		r2	
;		r3	
;		r4	
;		r5	
;		r6	
;		r7	
;		r8	
;		r9	
;		r10		
;		r11	
;		r12	
;		r13	
;		r14	
.def led_blnk	=r15				;r15 led blink count
.def tempr		=r16				;r16 temp
.def tx_cnt		=r17				;r17 tx character count (0=empty)
.def tx_putptr	=r18				;r18 tx put pointer (first empty)
.def tx_getptr	=r19				;r19 tx get buffer pointer (first valid)
.def rx_cnt		=r20				;r20 rx character count (0=empty)
.def rx_putptr	=r21				;r21 rx put pointer (first empty)
.def rx_getptr	=r22				;r22 rx get buffer pointer (first valid)
.def run_cmd	=r23				;r23 running status command
.def xlat_note	=r24				;r24 translate note temp
.def xlat_chan	=r25				;r25 translate channel temp
;		r26/xl						;r26/xl rx temp address pointer
;		r27/xh						;r27/xh rx temp address pointer
;		r28/yl						;r28/yl tx temp address pointer
;		r29/yh						;r29/yh tx temp address pointer
;		r30/zl		
;		r31/zh		
;
;
;------------------------
; assembler directives
;------------------------
;
.nolist
.include "tn2313def.inc"
.list
.listmac
;
;------------------------
; ram
;------------------------
;
.dseg
.org	sram_start
;
.equ rx_len	=32						;MIDI in buffer length (must be <256)
.equ tx_len	=32						;MIDI out buffer length (must be <256)
;
rx_bfr:
	.byte rx_len					;MIDI in buffer
tx_bfr:
	.byte tx_len					;MIDI out buffer
;
;------------------------
; program
;------------------------
;
.cseg
.org	0x0000
;
	rjmp start						;0x000	reset
	rjmp irq_none					;0x001	int0
	rjmp irq_none					;0x002	int1
	rjmp irq_none					;0x003	timer1 capt
	rjmp irq_none					;0x004	timer1 comp-a
	rjmp irq_none					;0x005	timer1 ovf
	rjmp tmr0_isr					;0x006	timer0 ovf
	rjmp rx_isr						;0x007	uart rx
    rjmp tx_isr						;0x008	uart udre
	rjmp irq_none					;0x009	uart tx
	rjmp irq_none					;0x00A	analog comp
	rjmp irq_none					;0x00B	pcint
	rjmp irq_none					;0x00C	timer1 comp-b
	rjmp irq_none					;0x00D	timer0 comp-a
	rjmp irq_none					;0x00E	timer0 comp-b
	rjmp irq_none					;0x00F	usi start
	rjmp irq_none					;0x010	usi ovf
	rjmp irq_none					;0x011	ee rdy
	rjmp irq_none					;0x012	wdt ovf
;
;------------------------
; unused interrupt isr
;------------------------
;
irq_none:
	reti
;
;------------------------
; serial in isr
;------------------------
;
;serial in isr
;puts data into buffer
;decrements character count if not empty
;registers used
; tempr=temp
; rx_cnt=character count
; rx_putptr=put pointer
; x=buffer address
rx_isr:
	push xh
	push xl
    push tempr
	in tempr,sreg
	push tempr
    in tempr,udr	        		;read received byte
	;
	.ifdef filter_timing_clock		;conditional filter timing clock
	cpi tempr,0xf8					;check for timing clock
	breq rx_3						;filter out active status
	.endif
	;
	.ifdef filter_active_status		;conditional filter active status
	cpi tempr,0xfe					;check for active status
	breq rx_3						;filter out active status
	.endif
	;
	ldi xh,high(rx_bfr)				;get input buffer address
	ldi xl,low(rx_bfr)
	add xl,rx_putptr				;add put pointer
	brcc rx_1						;check for carry
	inc xh							;correct high byte
rx_1:
	st x,tempr						;store character
	inc rx_putptr					;increment put pointer
	cpi rx_putptr,rx_len			;check if at end of buffer
	brne rx_2
	ldi rx_putptr,0					;wrap pointer
rx_2:
	cpi rx_cnt,rx_len				;check if buffer full
	breq rx_3						;overwrites first data if full
	inc	rx_cnt						;increment character count if not full
rx_3:
	pop tempr
	out sreg,tempr
	pop tempr
	pop xl
	pop xh
	reti
;
;------------------------
; serial out isr
;------------------------
;
;serial out isr
;gets data from buffer
;decrements character count
;registers used
; tempr=temp
; tx_cnt=character count
; tx_getptr=get pointer
; y=buffer address
tx_isr:
	push yh
	push yl
    push tempr
	in tempr,sreg
	push tempr
	ldi yh,high(tx_bfr)				;get input buffer address
	ldi yl,low(tx_bfr)
	add yl,tx_getptr				;add put pointer
	brcc tx_1						;check for carry
	inc yh							;correct high byte
tx_1:
	ld tempr,y						;get character
	out udr,tempr					;send to uart
	inc tx_getptr					;increment put pointer
	cpi tx_getptr,tx_len			;check if at end of buffer
	brne tx_2
	ldi tx_getptr,0					;wrap pointer
tx_2:
	dec	tx_cnt						;decrement character count
	brne tx_3						;check for empty
	push tempr						;save data
	in	tempr,ucsrb    				;get control register
	andi tempr,~(1<<udrie)			;set udrie=0
	out	ucsrb,tempr
	pop tempr
tx_3:
	pop tempr
	out sreg,tempr
	pop tempr
	pop yl
	pop yh
	reti
;
;------------------------
; timer0 isr
;------------------------
;
;25 mS timer
;blinks LED
;registers used
; tempr=temp
tmr0_isr:
	push tempr
	in tempr,sreg					;save sreg
	push tempr
	ldi tempr,60
	out tcnt0,tempr					;set tcnt0 for 25 mS interrupts
	tst led_blnk					;check for 0
	breq tm_1
	dec led_blnk
	brne tm_1
	cbi portd,6						;turn led off
tm_1:
	pop tempr
	out sreg,tempr					;restore sreg
	pop tempr
	reti
;
;------------------------
; program start
;------------------------
;
start:
;initialize direction
	ldi tempr,0b00000000			;0=input bits
	out ddrb,tempr					;set port b directions
	ldi tempr,0b11111111			;1=pullup bits
	out portb,tempr					;set port b pullups
	ldi tempr,0b11000010			;0=input bits
	out ddrd,tempr					;set port d directions
	ldi tempr,0b01111110			;1=pullup
	out portd,tempr					;set port d pullups, turn on led
;initialize stack:
	ldi tempr,low(ramend)			;set stack pointer to 0x00df
	out spl,tempr
;stop watchdog
	wdr								;reset watchdog timer
	in tempr,mcusr
	andi tempr,(0xff & (0<<wdrf))	;clear wdrf
	out	mcusr,tempr
	in tempr,wdtcsr					;get prescalar settings
	ori	tempr,(1<<wdce)|(1<<wde)	;keep settings to prevent unintentional time out
	out	wdtcsr,tempr
	ldi tempr,(0<<wde)				;turn off watch dog timer
	out wdtcsr,tempr
;initialize timer0
; 8MHz / 1024 = 128 uS clock
; 32 uS x 256 = 32.768 mS
; set tcnt0 for 25 mS interrupts
	in	tempr,tccr0b				;read control register
	sbr	tempr,(1<<cs02)|(1<<cs00)	;1024 prescale
	cbr	tempr,(1<<cs01)
	out	tccr0b,tempr
	ldi tempr,60
	out tcnt0,tempr					;set tcnt0 for 25 mS interrupts
	in	tempr,timsk					;read interrupt mask
	ori	tempr,(1<<toie0)			;enable tc0 interrupt toie0
	out	timsk,tempr
	in	tempr,tifr					;read interrupt register
	ori	tempr,(1<<tov0)				;reset any pending interrupts tov0
	out	tifr,tempr
;initialize uart
	ldi tempr,15					;31250 baud rate
	out	ubrrl,tempr
	ldi	tempr,(1<<rxcie)|(1<<rxen)|(1<<txen)
	out	ucsrb,tempr
;initialize registers
	ldi tempr,20					;leave led on for 500 mS
	mov led_blnk,tempr				;led timer
	clr tx_cnt						;tx buffer character count
	clr tx_putptr					;tx empty character pointer
	clr tx_getptr					;tx first valid character pointer
	clr rx_cnt						;rx buffer character count
	clr rx_putptr					;rx empty character pointer
	clr rx_getptr					;rx first valid character pointer
	ldi run_cmd,0x07				;initialize running status offset to invalid
	ldi xlat_note,0x00				;set note offset
	sei								;enable interrupts
;
;------------------------
; main program
;------------------------
;
;process MIDI command
midi:
	rcall wait_rx					;get data from rx buffer
	sbrs tempr,7	 				;check bit 8 for command
	rjmp running					;0=running status
	mov run_cmd,tempr				;create offset for command & running status
	swap run_cmd					;move command to lower 4 bits
	andi run_cmd,0x07				;remove MIDI channel and command bit
	ldi zh,high(command)
	ldi zl,low(command)
	add zl,run_cmd					;add offset
	brcc mn_1						;check for carry
	inc zh							;correct high byte
mn_1:
	ijmp							;jump to routine
;process running status
running:
	ldi zh,high(rcommand)
	ldi zl,low(rcommand)
	add zl,run_cmd					;add offset
	brcc rn_1						;check for carry
	inc zh							;correct high byte
rn_1:
	ijmp							;jump to routine
;MIDI command table
command:
	rjmp note_up					;8x=note up
	rjmp note_dn					;9x=note down
	rjmp key_prs					;ax=key pressure
	rjmp ctrl_chg					;bx=control change
	rjmp prgm_chg					;cx=program change
	rjmp chan_prs					;dx=channel pressure
	rjmp ptch_bnd					;ex=pitch bend
	rjmp sys_cmd					;fx=system command
;running status command table
rcommand:
	rjmp note_up1					;8x=note up running
	rjmp note_dn1					;9x=note down running
	rjmp key_prs1					;ax=key pressure running
	rjmp ctrl_chg1					;bx=control change running
	rjmp prgm_chg1					;cx=program change running
	rjmp chan_prs1					;dx=channel pressure running
	rjmp ptch_bnd1					;ex=pitch bend running
	rjmp midi						;fx=running status invalid
;system command table
scommand:
	rjmp sys_ex						;f0=system exclusive
	rjmp sys_undefined				;f1=undefined
	rjmp song_ptr					;f2=song pointer
	rjmp song_sel					;f3=song select
	rjmp sys_undefined				;f4=undefined
	rjmp sys_undefined				;f5=undefined
	rjmp tune_req					;f6=tune request
	rjmp sys_undefined				;f7=end of system exclusive
	;these realtime commands are executed in wait_rx
	;and are included in the table for completeness
	rjmp time_clk					;f8=timing clock
	rjmp sys_undefined				;f9=undefined
	rjmp sys_start					;fa=start
	rjmp sys_cont					;fb=continue
	rjmp sys_stop					;fc=stop
	rjmp sys_undefined				;fd=undefined
	rjmp sys_sense					;fe=active sensing
	rjmp sys_reset					;ff=reset
;
;note up
note_up:
	rcall trans_chan				;set MIDI channel
	rcall put_tx					;send first byte
	rcall wait_rx					;get second byte
note_up1:
	rcall trans_note				;translate note
	rcall put_tx					;send second byte
	rcall wait_rx					;get third byte
	rcall put_tx					;send third byte
	rcall blink_led					;blink led
	rjmp midi
;
;note down
note_dn:
	rcall trans_chan				;set MIDI channel
	rcall put_tx					;send first byte
	rcall wait_rx					;get second byte
note_dn1:
	rcall trans_note				;translate note
	rcall put_tx					;send second byte
	rcall wait_rx					;get third byte
	rcall put_tx					;send third byte
	rcall blink_led					;blink led
	rjmp midi
;
;key pressure
key_prs:
	rcall trans_chan				;set MIDI channel
	rcall put_tx					;send first byte
	rcall wait_rx					;get second byte
key_prs1:
	rcall put_tx					;send second byte
	rcall wait_rx					;get third byte
	rcall put_tx					;send third byte
	rjmp midi
;
;control change
ctrl_chg:
	rcall trans_chan				;set MIDI channel
	rcall put_tx					;send first byte
	rcall wait_rx					;get second byte
ctrl_chg1:
	;
	.ifdef map_expr_to_vol			;conditional map expression
	cpi tempr,0x0b					;check for expression cc
	brne cc_1
	ldi tempr,0x07					;replace with volume cc
cc_1:
	.endif
	;
	rcall put_tx					;send second byte
	rcall wait_rx					;get third byte
	rcall put_tx					;send third byte
	rcall blink_led					;blink led
	rjmp midi
;
;program change
prgm_chg:
	rcall trans_chan				;set MIDI channel
	rcall put_tx					;send first byte
	rcall wait_rx					;get second byte
prgm_chg1:
	rcall put_tx					;send second byte
	rcall blink_led					;blink led
	rjmp midi
;
;channel pressure
chan_prs:
	rcall trans_chan				;set MIDI channel
	rcall put_tx					;send first byte
	rcall wait_rx					;get second byte
chan_prs1:
	rcall put_tx					;send second byte
	rjmp midi
;
;pitch bend
ptch_bnd:
	rcall trans_chan				;set MIDI channel
	rcall put_tx					;send first byte
	rcall wait_rx					;get second byte
ptch_bnd1:
	rcall put_tx					;send second byte
	rcall wait_rx					;get third byte
	rcall put_tx					;send third byte
	rcall blink_led					;blink led
	rjmp midi
;
;process system command
sys_cmd:
	push tempr						;save command
	ldi zh,high(scommand)
	ldi zl,low(scommand)
	andi tempr,0x0f					;strip top nibble
	add zl,tempr					;add offset
	brcc sc_1						;check for carry
	inc zh							;correct high byte
sc_1:
	pop tempr						;restore command
	ijmp							;jump to routine
;
;system exclusive
sys_ex:
	rcall put_tx					;send command
se_1:
	rcall wait_rx					;get byte
	rcall put_tx					;send byte
	cpi tempr,0xf7					;check for end of message
	brne se_1
	rjmp midi
;
;song pointer
song_ptr:
	rcall put_tx					;send command
	rcall wait_rx					;get second byte
	rcall put_tx					;send second byte
	rcall wait_rx					;get third byte
	rcall put_tx					;send third byte
	rjmp midi
;
;song select
song_sel:
	rcall put_tx					;send command
	rcall wait_rx					;get second byte
	rcall put_tx					;send second byte
	rjmp midi
;
;tune request
tune_req:
	rcall put_tx					;send command
	rjmp midi
;
;real time messages
;timing clock
time_clk:
	rcall rt_time_clk
	rjmp midi
rt_time_clk:
	rcall put_tx					;send message
	ret
;
;undefined
sys_undefined:
	rcall rt_undefined
	rjmp midi
rt_undefined:
	rcall put_tx					;send message
	ret
;
;start
sys_start:
	rcall rt_start
	rjmp midi
rt_start:
	rcall put_tx					;send message
	ret
;
;continue
sys_cont:
	rcall rt_continue
	rjmp midi
rt_continue:
	rcall put_tx					;send message
	ret
;
;stop
sys_stop:
	rcall rt_stop
	rjmp midi
rt_stop:
	rcall put_tx					;send message
	ret
;
;active sense
sys_sense:
	rcall rt_sense
	rjmp midi
rt_sense:
	rcall put_tx					;send message
	ret
;
;reset
sys_reset:
	rcall rt_reset
	rjmp midi
rt_reset:
	rcall put_tx					;send message
	ret
;
;------------------------
; misc subroutines
;------------------------
;
;transpose MIDI note in tempr
;registers used
; tempr=MIDI note
; xlat_note=note temp
trans_note:
	sbis pind,2						;check for multi enable
	rjmp tn_2						;multi off
;multi
	cpi tempr,60					;check for middle C
	brlo tn_1						;above
	subi tempr,24					;below - subract two octaves
	rjmp tn_2
tn_1:
	ldi xlat_note,24				;above - add two octaves
	add tempr,xlat_note
;reverse
tn_2:
	sbis pind,3						;check for note reverse enable
	rjmp tn_3						;reverse off
	com tempr						;invert note
	subi tempr,7					;offset up 5 and down 12 to center at C
	andi tempr,0x7f					;keep to 7 bits & assume no underflow
;transpose
tn_3:
	sbis pind,5						;check for note transpose enable
	rjmp tn_7						;normal mode
	in xlat_note,pinb				;get note offset
	swap xlat_note					;shift to low nibble
	andi xlat_note,0x0f				;truncate to low nibble
	cpi xlat_note,13				;check if over 12
	brlo tn_6
	brne tn_4						;check if up 2 octaves
	ldi xlat_note,24				;set two octave offset					
tn_4:
	cpi xlat_note,14				;check if down one octave
	brne tn_5
	ldi xlat_note,-12				;set one octave offset
tn_5:
	cpi xlat_note,15				;check if down two octaves
	brne tn_6
	ldi xlat_note,-24				;set two octave offset
tn_6:
	add tempr,xlat_note				;add offset & assume no overflow
tn_7:
	ret
;
;transpose MIDI channel in tempr
;registers used
; tempr=MIDI command
; xlat_chan=channel temp
trans_chan:
	sbis pind,4						;check for MIDI channel transpose enable
	rjmp tc_1						;normal mode
	in xlat_chan,pinb				;get MIDI channel
	andi xlat_chan,0x0f				;truncate to low nibble
	andi tempr,0xf0					;remove MIDI bits
	or tempr,xlat_chan				;set MIDI channel
tc_1:
	ret
;
;blink led
;registers used
; led_blnk=blink count
blink_led:
	push tempr
	ldi tempr,2						;blink led for two time counts
	mov led_blnk,tempr
	sbi portd,6						;turn on led, timer will turn off
	pop tempr
	ret
;
;------------------------
; buffer subroutines
;------------------------
;
;wait or get data from rx buffer
;processes real time messages
;registers used
; tempr=returns with data from rx buffer
; rx_cnt=character count
; rx_getptr=get pointer
; x=buffer address
wait_rx:
	cpi rx_cnt,0					;check character count
	breq wait_rx
get_rx:
	push xh
	push xl
	cli								;get character and process
	ldi xh,high(rx_bfr)				;get input buffer address
	ldi xl,low(rx_bfr)
	add xl,rx_getptr				;add starting position
	brcc gr_1						;no carry
	inc xh							;carry
gr_1:
	ld tempr,x						;get character
	inc rx_getptr
	cpi rx_getptr,rx_len			;check if at end of buffer
	brne gr_2
	ldi rx_getptr,0					;reset starting position
gr_2:
	dec rx_cnt						;decrement character count
	pop xl
	pop xh
	sei								;enable interrupts
;check for realtime messages
	push tempr						;save message
	andi tempr,0xf8					;strip off lower bits
	cpi tempr,0xf8					;check for realtime message
	pop tempr						;get message
	breq realtime					;process realtime message
	ret								;return with data
;process realtime messages
realtime:
	cpi tempr,0xfe					;check first for active sense
	brne rt_1
	rcall rt_sense					;process active sense message
	rjmp wait_rx					;and get next byte
rt_1:
	cpi tempr,0xf8					;check for timing clock
	brne rt_2
	rcall rt_time_clk				;process timing clock message
	rjmp wait_rx					;and get next byte
rt_2:
	cpi tempr,0xfa					;check for start
	brne rt_3
	rcall rt_start					;process start message
	rjmp wait_rx					;and get next byte
rt_3:
	cpi tempr,0xfb					;check for continue
	brne rt_4
	rcall rt_continue				;process continue message
	rjmp wait_rx					;and get next byte
rt_4:
	cpi tempr,0xfc					;check for stop
	brne rt_5
	rcall rt_stop					;process stop message
	rjmp wait_rx					;and get next byte
rt_5:
	cpi tempr,0xff					;check for reset
	brne rt_6
	rcall rt_reset					;process reset message
	rjmp wait_rx					;and get next byte
rt_6:
	rcall rt_undefined				;must be undefined message
	rjmp wait_rx					;and get next byte
;
;puts data into tx buffer
;waits if buffer full
;registers used
; tempr=data for tx buffer
; tx_cnt=character count
; tx_putptr=put pointer
; y=buffer address
put_tx:
	push yh
	push yl
pt_1:
	cpi tx_cnt,tx_len				;check if buffer full
	breq pt_1						;loop until room
	inc tx_cnt						;increment character count
	cli								;disable interrupts
	ldi yh,high(tx_bfr)				;get output buffer address
	ldi yl,low(tx_bfr)
	add yl,tx_putptr				;add starting position
	brcc pt_2						;no carry
	inc yh							;carry
pt_2:
	st y,tempr						;put character
	inc tx_putptr
	cpi tx_putptr,tx_len			;check if at end of buffer
	brne pt_3
	ldi tx_putptr,0					;reset starting position
pt_3:
	push tempr						;save data
	in	tempr,ucsrb    				;get control register
	ori tempr,(1<<udrie)			;set udrie=1
	out	ucsrb,tempr
	pop tempr
	sei								;enable interrupts
	pop yl
	pop yh
	ret
;
;------------------------
; end of program
;------------------------

