;-------------------------------------------------------------------------------------------------------------------
;	Keyboard.asm
;	Written for the ATmega8
.nolist
.include "m8def.inc"
.list
.listmac
;
;	Written by Dave Brown
;   Created: July 4, 2005
;	Last Modified: July 7, 2005
;
;------------------------
; pins
;------------------------
;
;	pc0		    |midi channel select
;	pc1		    | inverted (pullups)
;	pc1			| f=channel 0
;	pc2			| 0=channel f
;
;	pd0		    midi in
;	pd1		    midi out
;	pd2			pedal-1 input (pullups)
;	pd3			pedal-2 input (pullups)
;	pd4			inverted pedal-1 output
;	pd5			inverted pedal-2 output
;	pd6			LED output (blinks on note down)
;
;	timer0		25 mS interrupts
;	timer1		not used
;	timer2		not used
;
;------------------------
; registers
;------------------------
;
;		r0			
;		r1	
;		r2	
;		r3	
;		r4	
;		r5	
;		r6	
;		r7	
;		r8	
;		r9	
;		r10			
;		r11			
;		r12	
;		r13	
;		r14	
;		r15			LED blink count
;		r16			temp
;		r17			tx character count (0=empty)
;		r18			tx put pointer (first empty)
;		r19			tx get buffer pointer (first valid)
;		r20			rx character count (0=empty)
;		r21			rx put pointer (first empty)
;		r22			rx get buffer pointer (first valid)
;		r23			running status command
;		r24			note offset
;		r25			midi channel
;		r26	xl		rx temp address pointer
;		r27	xh		rx temp address pointer
;		r28	yl		tx temp address pointer
;		r29	yh		tx temp address pointer
;		r30	zl		
;		r31	zh		
;
;------------------------
; ram
;------------------------
;
.dseg
.org	0x0060
;
.equ	rx_len	=60					;midi in buffer length (< 256)
.equ	tx_len	=100				;midi out buffer length (<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	timer2 comp
	rjmp irq_none					;0x004	timer2 ovf
	rjmp irq_none					;0x005	timer1 capt
	rjmp irq_none					;0x006	timer1 comp-a
	rjmp irq_none					;0x007	timer1 comp-b
    rjmp irq_none					;0x008	timer1 ovf
	rjmp tmr0_isr					;0x009	timer0 ovf
	rjmp irq_none					;0x00A	spi stc
	rjmp rx_isr						;0x00B	uart rx
	rjmp tx_isr						;0x00C	uart udre
	rjmp irq_none					;0x00D	uart tx
	rjmp irq_none					;0x00E	adc
	rjmp irq_none					;0x00F	ee ready
	rjmp irq_none					;0x010	ana comp
;
;------------------------
; unused interrupt isr
;------------------------
irq_none:
	reti
;
;------------------------
; serial in isr
;------------------------
;
;serial in isr
;puts data into buffer
;decrements character count if not empty
;registers used
; r16=temp
; r20=character count
; r21=put pointer
; x=buffer address
rx_isr:
	push xh
	push xl
    push r16
	in r16,sreg
	push r16
    in r16,udr	        			;read received byte
	cpi r16,0xfe					;check for running status
	breq rx_3						;filter out
	ldi xh,high(rx_bfr)				;get input buffer address
	ldi xl,low(rx_bfr)
	add xl,r21						;add put pointer
	brcc rx_1						;check for carry
	inc xh							;correct high byte
rx_1:
	st x,r16						;store character
	inc r21							;increment put pointer
	cpi r21,rx_len					;check if at end of buffer
	brne rx_2
	ldi r21,0						;wrap pointer
rx_2:
	cpi r20,rx_len					;check if buffer full
	breq rx_3						;overwrites first data if full
	inc	r20							;increment character count if not full
rx_3:
	pop r16
	out sreg,r16
	pop r16
	pop xl
	pop xh
	reti
;
;------------------------
; serial out isr
;------------------------
;
;serial out isr
;gets data from buffer
;decrements character count
;registers used
; r16=temp
; r17=character count
; r19=get pointer
; y=buffer address
tx_isr:
	push yh
	push yl
    push r16
	in r16,sreg
	push r16
	ldi yh,high(tx_bfr)				;get input buffer address
	ldi yl,low(tx_bfr)
	add yl,r19						;add put pointer
	brcc tx_1						;check for carry
	inc yh							;correct high byte
tx_1:
	ld r16,y						;get character
	out udr,r16						;send to uart
	inc r19							;increment put pointer
	cpi r19,tx_len					;check if at end of buffer
	brne tx_2
	ldi r19,0						;wrap pointer
tx_2:
	dec	r17							;decrement character count
	brne tx_3						;check for empty
	push r16						;save data
	in	r16,ucsrb    				;get control register
	andi r16,0xdf					;set udrie=0
	out	ucsrb,r16
	pop r16
tx_3:
	pop r16
	out sreg,r16
	pop r16
	pop yl
	pop yh
	reti
;
;------------------------
; timer0 isr
;------------------------
;
;25 mS timer
;blinks LED
;registers used
; r16=temp
tmr0_isr:
	push r16
	in r16,sreg						;save sreg
	push r16
	ldi r16,60
	out tcnt0,r16					;set tcnt0 for 25 mS interrupts
	and r15,r15						;check for 0
	breq tm_1
	dec r15
	brne tm_1
	cbi portd,6						;turn LED back on
tm_1:
	pop r16
	out sreg,r16					;restore sreg
	pop r16
	reti
;
;------------------------
; program start
;------------------------
;
start:
;initialize direction
	ldi r16,0b11110000				;0=input bits 0-3
	out ddrc,r16					;set port c directions
	ldi r16,0b00001111				;1=pullup bits 0-3
	out portc,r16					;set port c pullups
	ldi r16,0b11110010				;0=input bits 0 and 2-3
	out ddrd,r16					;set port d directions
	ldi r16,0b00001110				;1=pullup bits 2-3
	out portd,r16					;set port d pullups, clear pedal outputs, turn on LED
;initialize stack:
	ldi r16,0x5f					;set stack pointer to 0x045f
	ldi r17,0x04
	out spl,r16
	out sph,r17
;stop watchdog
	ldi	r16,0x1f					;set wd to longest delay & set wdtoe high
	out	wdtcr, r16
	ldi	r16,0x17					;turn off wde
	out	wdtcr,r16
;initialize timer0
; 8MHz / 1024 = 128 uS clock
; 32 uS x 256 = 32.768 mS
; set tcnt0 for 25 mS interrupts
	in	r16,tccr0					;read control register
	sbr	r16,(1<<cs02)|(1<<cs00)		;1024 prescale
	cbr	r16,(1<<cs01)
	out	tccr0,r16
	ldi r16,60
	out tcnt0,r16					;set tcnt0 for 25 mS interrupts
	in	r16,timsk					;read interrupt mask
	ori	r16,(1<<toie0)				;enable tc0 interrupt toie0
	out	timsk,r16
	in	r16,tifr					;read interrupt register
	ori	r16,(1<<tov0)				;reset any pending interrupts TOV0
	out	tifr,r16
;initialize uart
	ldi r16,15						;31250 baud rate
	out	ubrrl,r16
	ldi	r16,0x98    				;set rxcie=1, rxen=1, & txen=1
	out	ucsrb,r16
;	ldi	r16,0xfe    				;send running status to initialize pin
;	out	udr,r16
;initialize registers and ram
	clr r16
	mov r15,r16						;LED timer
	clr r17							;tx buffer character count
	clr r18							;tx empty character pointer
	clr r19							;tx first valid character pointer
	clr r20							;rx buffer character count
	clr r21							;rx empty character pointer
	clr r22							;rx first valid character pointer
	ldi r23,0x07					;initialize running status offset to invalid
	ldi r24,0x05					;note offset for Kurzweil keyboard
	in r25,pinc						;get midi channel
	com r25							;invert so open=channel 0
	andi r25,0x0f					;lower 4 bits valid
;initial midi messages
	sei								;enable interrupts
;send all notes off
	ldi r16,0xb0					;control change
	or r16,r25						;set midi channel
	rcall put_tx
	ldi r16,0x7b					;all notes off
	rcall put_tx
	ldi r16,0x00
	rcall put_tx
;pass initial pitch bend message (e0 00 40)
	rcall wait_rx
	or r16,r25						;set midi channel
	rcall put_tx					;first byte
	rcall wait_rx
	rcall put_tx					;second byte
	rcall wait_rx
	rcall put_tx					;third byte
;inhibit initial slider1 control change (b0 10 00)
	rcall wait_rx
	or r16,r25						;set midi channel
	rcall wait_rx
	rcall wait_rx
;pass initial neg mod wheel message (b0 02 00)
	rcall wait_rx
	or r16,r25						;set midi channel
	rcall put_tx					;first byte
	rcall wait_rx
	rcall put_tx					;second byte
	rcall wait_rx
	rcall put_tx					;third byte
;pass initial mod wheel message (b0 01 00)
	rcall wait_rx
	or r16,r25						;set midi channel
	rcall put_tx					;first byte
	rcall wait_rx
	rcall put_tx					;second byte
	rcall wait_rx
	rcall put_tx					;third byte
;------------------------
; main program
;------------------------
midi:
	rcall wait_rx					;get data from rx buffer
	sbrs r16,7	 					;check bit 8 for command
	rjmp running					;0=running status
	mov r23,r16						;create offset for command & running status
	swap r23						;move command to lower 4 bits
	andi r23,0x07					;remove midi channel and command bit
	ldi zh,high(command)
	ldi zl,low(command)
	add zl,r23						;add offset
	brcc mn_1						;check for carry
	inc zh							;correct high byte
mn_1:
	ijmp							;jump to routine
;
running:
	ldi zh,high(command1)
	ldi zl,low(command1)
	add zl,r23						;add offset
	brcc rn_1						;check for carry
	inc zh							;correct high byte
rn_1:
	ijmp							;jump to routine
;
command:
	rjmp note_up					;note up
	rjmp note_dn					;note down
	rjmp key_prs					;key pressure
	rjmp ctrl_chg					;control change
	rjmp prgm_chg					;program change
	rjmp chan_prs					;channel pressure
	rjmp ptch_bnd					;pitch bend
	rjmp sys_cmd					;system command
command1:
	rjmp note_up1					;note up running
	rjmp note_dn1					;note down running
	rjmp key_prs1					;key pressure running
	rjmp ctrl_chg1					;control change running
	rjmp prgm_chg1					;program change running
	rjmp chan_prs1					;channel pressure running
	rjmp ptch_bnd1					;pitch bend running
	rjmp midi						;invalid running status
;
;note up
note_up:
	or r16,r25						;set midi channel
	rcall put_tx					;send first byte
	rcall wait_rx					;get second byte
note_up1:
	add r16,r24						;offset note
	rcall put_tx					;send second byte
	rcall wait_rx					;get third byte
	rcall put_tx					;send third byte
	rjmp midi
;
;note down
note_dn:
	or r16,r25						;set midi channel
	rcall put_tx					;send first byte
	rcall wait_rx					;get second byte
note_dn1:
	add r16,r24						;offset note
	rcall put_tx					;send second byte
	rcall wait_rx					;get third byte
	rcall put_tx					;send third byte
	ldi r16,2						;blink LED for two time counts
	mov r15,r16
	sbi portd,6						;turn off LED
	rjmp midi
;
;key pressure
key_prs:
	or r16,r25						;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:
	or r16,r25						;set midi channel
	rcall put_tx					;send first byte
	rcall wait_rx					;get second byte
ctrl_chg1:
	rcall put_tx					;send second byte
	rcall wait_rx					;get third byte
	rcall put_tx					;send third byte
	rjmp midi
;
;program change
prgm_chg:
	or r16,r25						;set midi channel
	rcall put_tx					;send first byte
	rcall wait_rx					;get second byte
prgm_chg1:
	rcall put_tx					;send second byte
	rjmp midi
;
;channel pressure
chan_prs:
	or r16,r25						;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:
	or r16,r25						;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
	rjmp midi
;
;system command
sys_cmd:
	rcall put_tx					;single byte command
	rjmp midi
;
;------------------------
; buffer subroutines
;------------------------
;
;wait for data from rx buffer
;check and invert pedal inputs 
;registers used
; r16=returns with data from rx buffer
; r20=character count
; r22=get pointer
; x=buffer address
wait_rx:
	sbis pind,2						;pedal-1
	sbi portd,4						;was 0 so make 1
	sbic pind,2
	cbi portd,4						;was 1 so make 0
	sbis pind,3						;pedal-2
	sbi portd,5						;was 0 so make 1
	sbic pind,3
	cbi portd,5						;was 1 so make 0
	cpi r20,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,r22						;add starting position
	brcc gr_1						;no carry
	inc xh							;carry
gr_1:
	ld r16,x						;get character
	inc r22
	cpi r22,rx_len					;check if at end of buffer
	brne gr_2
	ldi r22,0						;reset starting position
gr_2:
	dec r20							;decrement character count
	sei								;enable interrupts
	pop xl
	pop xh
	ret
;
;puts data into tx buffer
;waits if buffer full
;registers used
; r16=data for tx buffer
; r17=character count
; r18=put pointer
; y=buffer address
put_tx:
	push yh
	push yl
pt_1:
	cpi r17,tx_len					;check if buffer full
	breq pt_1						;loop until room
	inc r17							;increment character count
	cli								;disable interrupts
	ldi yh,high(tx_bfr)				;get output buffer address
	ldi yl,low(tx_bfr)
	add yl,r18						;add starting position
	brcc pt_2						;no carry
	inc yh							;carry
pt_2:
	st y,r16						;put character
	inc r18
	cpi r18,tx_len					;check if at end of buffer
	brne pt_3
	ldi r18,0						;reset starting position
pt_3:
	push r16						;save data
	in	r16,ucsrb    				;get control register
	ori r16,0x20					;set udrie=1
	out	ucsrb,r16
	pop r16
	sei								;enable interrupts
	pop yl
	pop yh
	ret
;
;------------------------
; end of program
;------------------------

