;****************************************
;
;                Unet_KBD
;                ======== 
;
; Software for Keyboard Interface Module
; (Toensmann Micronet Project)
;
; Copyright (c) 2009 
; by Carsten Toensmann
;
; History
; -------
; 23.05.2009  V0.1 
;  - Start of development
; 05.06.2009  V0.1 
;  - Display interface completed
; 15.06.2009  V0.1
;  - Keyboard interface and MIDI
;    completed
; 27.06.2009  V0.1
;  - Interfaces for switch, joystick and
;    rotary encoders completed
; 12.07.2009  V0.1
;  - EEPROM storage implemented
; 03.11.2009  V1.0
;  - Joystick MIDI sequences made slower
; 30.10.2010  V2.0
;  - Code migrated to ATmega8535
;****************************************
	
.include "m8535def.inc";microcontroller

;***** definitions
.equ PROGSTART = 0x000
.equ STACKP = 0x200
.equ VARIABLES = 0x201
.equ DATA_OUT = PORTC
.equ DATA_IN = PINC
.equ SWITCH = PINA
.equ ENCODER = PINA
.equ DATADIR = DDRC
.equ ROWLEN = 0x40; 2 row lcd display row offset
.equ MAXNOTES = 10; size of note buffer
.equ BLOCK_0 = 0; keyboard block number 0
.equ BLOCK_1 = 1; keyboard block number 1
.equ BLOCK_2 = 2; keyboard block number 2
.equ BLOCK_3 = 3; keyboard block number 3
.equ BLOCK_4 = 4; keyboard block number 4
.equ BLOCK_5 = 5; keyboard block number 5
.equ BLOCK_6 = 6; keyboard block number 6
.equ BLOCK_7 = 7; keyboard block number 7
.equ ZERO = 0
.equ BLOCKLEN = 8; length of key block
.equ NOTE_OFFSET = $24
.equ JOY_X = $03; ADC channel X axis of joystick 
.equ JOY_Y = $02; ADC channel Y axis of joystick
.equ PITCHBEND = $e0; Pichbend controller byte
.equ CNTRLCHANGE = $b0; Modulation controller change
.equ MODULATIONHI = $01; Modulation controller MSB
.equ MODULATIONLO = $21; Modulation controller LSB 

;***** variables
.DSEG
.ORG VARIABLES
	notebuf: 		 .byte MAXNOTES; memory area for note values
	velobuf: 		 .byte 1; memory for velocity of key strokes
	noffsent:		 .byte 1; reminder flag for note off was sent
	adc_high:		 .byte 1; AD conversion result high byte
	adc_low:		 .byte 1; AD conversion result low byte
	joyx_high:		 .byte 1; X position high byte
	joyx_low:		 .byte 1; X position low byte
	joyy_high:		 .byte 1; Y position high byte
	joyy_low:		 .byte 1; Y position low byte
	encodval_1:		 .byte 1; scan of rotary encoder 1
	encodval_2:		 .byte 1; scan of rotary encoder 2
	switchpos:		 .byte 1; switch position (on / off)
	wpt: 			 .byte 1; write pointer ring buffer
	rpt: 			 .byte 1; read pointer ringbuffer
	kvs:			 .byte 1; current key velocity sensitivity
	jss:			 .byte 1; current joystick sensitivity
	ringbuf: 		 .byte 32; ring buffer for incoming MIDI messages

;***** interrupt vectors
.CSEG
.ORG PROGSTART

	rjmp RESET			; Reset Handler
	rjmp EXT_INT0		; IRQ0 Handler
	rjmp EXT_INT1		; IRQ1 Handler
	rjmp TIM2_COMP		; Timer 2 Compare Handler
	rjmp TIM2_OVF		; Timer 2 Overflow Handler
	rjmp TIM1_CAPT		; Timer 1 Capture Handler
	rjmp TIM1_COMPA		; Timer 1 Compare A Handler
	rjmp TIM1_COMPB		; Timer 1 Compare B Handler
	rjmp TIM1_OVF		; Timer 1 Overflow Handler
	rjmp TIM0_OVF		; Timer 0 Overflow Handler
	rjmp SPI_STC		; SPI Transfer Complete Handler
	rjmp UART_RXC		; UART RX Complete Handler
	rjmp UART_DRE		; UDR Empty Handler
	rjmp UART_TXC		; UART TX Complete Handler
	rjmp CADC			; AD Conversion Complete Handler
	rjmp EE_RDY			; EEPROM Ready Handler
	rjmp ANA_COMP		; Analog Comparator Handler
	; ATmega8535
	rjmp TWSI           ; Two-wire Serial Interface Handler
    rjmp EXT_INT2       ; IRQ2 Handler
    rjmp TIM0_COMP      ; Timer0 Compare Handler
    rjmp SPM_RDY        ; Store Program Memory Ready Handler

;***** constants
	start_txt:		.db "Toens uNet V2.0#"
	joyx_txt:		.db "X:# "
	joyy_txt:		.db "Y:# "
	kvs_txt:		.db " KVS:#"; Key Velocity Sensitivity
	jss_txt:		.db " JSS:#"; JoyStick Sensitivity
	swe_txt:		.db "E#"; switch position on 'external'
	swi_txt:		.db "I#"; switch position on 'internal'
	key_values:		.db $01,$09,$11,$19,$21,$29,$31,$39
					.db $02,$0a,$12,$1a,$22,$2a,$32,$3a
					.db $03,$0b,$13,$1b,$23,$2b,$33,$3b
					.db $04,$0c,$14,$1c,$24,$2c,$34,$3c
					.db $05,$0d,$15,$1d,$25,$2d,$35,$3d
					.db $06,$0e,$16,$1e,$26,$2e,$36,$ff
					.db $07,$0f,$17,$1f,$27,$2f,$37,$ff
					.db $08,$10,$18,$20,$28,$30,$38,$ff

;****************************************
;
;***** Main Program 
;
;****************************************
START: 
;-- set stackpointer
	ldi r16, high(STACKP)
	out SPH, r16
	ldi r16, low(STACKP)
	out SPL, r16

;-- configure chip operation			
	ldi r16, $00 ; disable external interrupts
	out GIMSK, r16
	ldi r16, $05 ; enable timer/counter 1 overflow interrupt and timer/counter 0 overflow interrupt
	out TIMSK, r16
	ldi r16, $40 ; enable sleep function
	out MCUCR, r16
	ldi r16, $00 ; timer/counter 1 no compare no pwm
	out TCCR1A, r16
	out TCCR1B, r16; stop timer/counter 1

;	Port direction configuration
;	----------------------------
;-- PORT A
	ldi r16,$20; Pin 5 input, rest output
	out DDRA,r16
;-- PORT B
	ser r16; all pins output
	out DDRB, r16
;-- PORT C: data bus, controlled by function
	rcall set_databus_read
;-- PORT D
	ldi r16,$FE; PORTD Pin 0(RXD) input, rest output
	out DDRD, r16

	bset 7 ; enable interrupts

;-- Initialization routines
	rcall init_keyboard
	rcall init_display
	rcall init_midi
	rcall init_encoder
	rcall init_switch
	rcall init_joystick

MAIN:
	rcall check_switch
;-- load switch position from memory
	ldi xl,low(switchpos)
	ldi xh,high(switchpos)
	ld r16,x
	tst r16; switch on?
	breq external_operation

;-- internal operation
	rcall check_joystick
	rcall check_encoder
	rcall clear_flags; clear 'key is still pressed' flag in note buffer
	rcall scan_kbd_block_0
	rcall scan_kbd_block_1
	rcall scan_kbd_block_2
	rcall scan_kbd_block_3
	rcall scan_kbd_block_4
	rcall scan_kbd_block_5
	rcall scan_kbd_block_6
	rcall scan_kbd_block_7
	rcall note_off; send 'note off' for all released keys
	rjmp MAIN

;-- external operation
external_operation:
	rcall midi_mon
	rjmp MAIN

STOP:
	rjmp STOP ; program end - stop - in case of leaving main scheduler

;****************************************
;
;***** Common functions
;
;****************************************
;----------------------------------------
; Function int_division (A / B)
;
; Well, not a real division, more a
; weightening
;
; Parameter: r17: value high byte
;            r16: value low byte
;            r18: 'divisor'
;----------------------------------------
int_division:
	push r19
	push r20

	cpi r18, $10; full sensitivity
	breq int_division_exit

	lsr r16; r17:r16 :/ 64 => r16
	lsr r16
	lsr r16
	lsr r16
	lsr r16
	lsr r16
	lsl r17
	lsl r17
	add r16,r17
	mov r20,r16

	clr r17
	clr r19
	clr r16
int_division_01:
	cp r19,r18
	breq int_division_exit
	add r16,r20
	inc r19	
	rjmp int_division_01

int_division_exit:
	pop r20
	pop r19
	ret

;----------------------------------------
; Function init_joystick
;
; Write init value to joystick variables
; to force joystick handling
;----------------------------------------
init_joystick:
	push xl
	push xh
	push r16

	ser r16; write $ff to joystick variables
	ldi xl,low(joyx_low); joy X low value
	ldi xh,high(joyx_low)
	st x,r16
	ldi xl,low(joyx_high); joy X high value
	ldi xh,high(joyx_high)
	st x,r16
	ldi xl,low(joyy_low); joy Y low value
	ldi xh,high(joyy_low)
	st x,r16
	ldi xl,low(joyy_high); joy Y high value
	ldi xh,high(joyy_high)
	st x,r16

	pop r16
	pop xh
	pop xl
	ret

;----------------------------------------
; Function init_switch
;
; Write init value to switch status variable
; to force switch handling
;----------------------------------------
init_switch:
	push xl
	push xh
	push r16

	ser r16; write $ff to switchpos
	ldi xl,low(switchpos)
	ldi xh,high(switchpos)
	st x,r16

	pop r16
	pop xh
	pop xl
	ret

;----------------------------------------
; Function init_encoder
;
; Init of 'poti position' of each encoder
; with max value / position after ISP
; eeprom initialisation
;----------------------------------------
init_encoder:
	push r20
	push r21
	push xl
	push xh

;-- First encoder
	clr r20; read first encoder byte
	rcall read_eeprom
	cpi r21,$ff; initial value?
	brne init_encoder_01; if not continue
	ldi r21,$10; save max value
	rcall write_eeprom
init_encoder_01:
	ldi xl,low(kvs); provide it in variable
	ldi xh,high(kvs)
	st x,r21

;-- Second encoder
	ldi r20,$01; read second encoder byte
	rcall read_eeprom
	cpi r21,$ff; initial value?
	brne init_encoder_02; if not continue
	ldi r21,$10; save max value
	rcall write_eeprom
init_encoder_02:
	ldi xl,low(jss); provide it in variable
	ldi xh,high(jss)
	st x,r21

	pop xh
	pop xl
	pop r21
	pop r20
	ret

;----------------------------------------
; Function read_eeprom
;
; Reads data from eeprom. Only byte 0 to
; 255 can be used. 
;
; Input:  r20: No. of eeprom byte (0 - $ff)
; 
; Output: r21: result of read operation
;----------------------------------------
read_eeprom:
	push r16

	clr r16; only byte 0 to 255 can be used
	out EEARH, r16; encoder address high byte
	out EEARL, r20; encoder address low byte
	ldi r16,$09; enable EEPROM ready interrupt and EEPROM read function
	out EECR,r16; read EEPROM into data register
	sleep; wait for EEPROM process to terminate
	in r21,EEDR; copy EEPROM data into working register
	clr r16
	out EECR,r16; disable EEPROM access

	pop r16
	ret

;----------------------------------------
; Function write_eeprom
;
; Writes data to eeprom. Only byte 0 to
; 255 can be used. 
;
; Input:  r20: No. of eeprom byte (0 - $ff)
;         r21: Data to be written
;----------------------------------------
write_eeprom:
	push r16

	clr r16; only byte 0 to 255 can be used
	out EEARH, r16; encoder address high byte
	out EEARL, r20; encoder address low byte
	out EEDR,r21; copy data from working register to EEPROM data register
	bclr 7; disable interrupt
	ldi r16,$0c; enable EEPROM ready interrupt, EEPROM master write (prepare write)
	out EECR,r16
	ldi r16,$0a; enable EEPROM ready interrupt, EEPROM write (do write)
	out EECR,r16
	bset 7 ; enable interrupts
	sleep; wait for EEPROM process to terminate
	clr r16
	out EECR,r16; disable EEPROM access	

	pop r16
	ret

;----------------------------------------
; Function mod_encod_1
;
; modifies current position of pot1 
; according to encoder movement (up down)
;
; input: r16: 01: move up
;			  02: move down
;----------------------------------------
mod_encod_1:
	push r20
	push r21
	push xl
	push xh

;-- load last position
	clr r20; read EEPROM byte 0
	rcall read_eeprom

;-- check encoder direction
	cpi r16,$01; up?
	brne mod_encod_1_01; if not decrement position
	cpi r21,$10; high value?
	breq mod_encod_1_02; if yes leave function
	inc r21; next position
	rjmp mod_encod_1_03

mod_encod_1_01:
	tst r21; low value?
	breq mod_encod_1_02; if yes leave function
	dec r21

mod_encod_1_03:
	rcall write_eeprom
	ldi xl,low(kvs); provide it in variable
	ldi xh,high(kvs)
	st x,r21
	rcall display_pos1

mod_encod_1_02:
	pop xh
	pop xl
	pop r21
	pop r20
	ret

;----------------------------------------
; Function mod_encod_2
;
; modifies current position of pot2 
; according to encoder movement (up down)
;
; input: r16: 01: move up
;			  02: move down
;----------------------------------------
mod_encod_2:
	push r20
	push r21
	push xl
	push xh

;-- load last position
	ldi r20,$01; read EEPROM byte 1
	rcall read_eeprom

;-- check encoder direction
	cpi r16,$01; up?
	brne mod_encod_2_01; if not decrement position
	cpi r21,$10; high value?
	breq mod_encod_2_02; if yes leave function
	inc r21; next position
	rjmp mod_encod_2_03

mod_encod_2_01:
	tst r21; low value?
	breq mod_encod_2_02; if yes leave function
	dec r21

mod_encod_2_03:
	rcall write_eeprom
	ldi xl,low(jss); provide it in variable
	ldi xh,high(jss)
	st x,r21
	rcall display_pos2

mod_encod_2_02:
	pop xh
	pop xl
	pop r21
	pop r20
	ret

;----------------------------------------
; Function check_encoder
;
; Reads the pin values of rotary encoders
;----------------------------------------
check_encoder:
	push xl
	push xh
	push r16
	push r17
	push r20
	push r21

;-- read encoder data
	in r16,ENCODER; read port with rotary encoders
	push r16; save for analysis of second encoder
;-- analysis of first encoder
	andi r16,$03; select pin 1 and 0 for first encoder signals
	ldi xl,low(encodval_1); last encoder scan result
	ldi xh,high(encodval_1)
	ld r17,x
	cp r16,r17; new value detected?
	breq check_encoder_01; if no leave function
;-- new value detected
	mov r21,r16; save
	tst r16; both pins cleared?
	breq check_encoder_04; if yes store and exit
	cpi r16,$03; both pins set?
	breq check_encoder_04; if yes store and exit
;-- pins are 1 or 2
	tst r17; last scan result was zero?
	breq check_encoder_03; if yes process rising edge
;-- process falling edge
	ldi r20,$03; invert bit 0 and 1 from r16
	eor r16,r20
;-- process new scan result
check_encoder_03:
	rcall mod_encod_1
check_encoder_04:
	mov r16,r21
	st x,r16
;-- check second encoder
check_encoder_01:
	pop r16; restore encoder scan for second encoder
;-- analysis of second encoder
	andi r16,$c0; select pin 7 and 6 for second encoder signals
	lsr r16; shift to bit 1 and 0
	lsr r16
	lsr r16
	lsr r16
	lsr r16
	lsr r16
	ldi xl,low(encodval_2); last encoder scan result
	ldi xh,high(encodval_2)
	ld r17,x
	cp r16,r17; new value detected?
	breq check_encoder_05; if no leave function
;-- new value detected
	mov r21,r16; save
	tst r16; both pins cleared?
	breq check_encoder_06; if yes store and exit
	cpi r16,$03; both pins set?
	breq check_encoder_06; if yes store and exit
;-- pins are 1 or 2
	tst r17; last scan result was zero?
	breq check_encoder_07; if yes process rising edge
;-- process falling edge
	ldi r20,$03; invert bit 0 and 1 from r16
	eor r16,r20
;-- process new scan result
check_encoder_07:
	rcall mod_encod_2
check_encoder_06:
	mov r16,r21
	st x,r16
;-- exit function
check_encoder_05:

	pop r21
	pop r20
	pop r17
	pop r16
	pop xh
	pop xl
	ret

;----------------------------------------
; Function check_joystick
;
; Reads the X and Y position of joystick
;----------------------------------------
check_joystick:
	push xl
	push xh
	push r16
	push r17
	push r18
	push r19
	push r20
	push r21

	ldi r19,$05; col
	clr r20; flag: did any change happen?

;-- read joystick X axis
	ldi r16,JOY_X; select adc channel X axis
	out ADMUX,r16
	ldi r16,$88; set ADC operation mode
	out ADCSR,r16; start conversion
	sleep; wait for conversion end
	ldi xl,low(adc_low); ADC result low value
	ldi xh,high(adc_low)
	ld r16,x
	ldi xl,low(joyx_low); joy X low value
	ldi xh,high(joyx_low)
	ld r17,x
	lsr r17; lower chane sensibility
	lsr r16
	lsr r17; lower chane sensibility
	lsr r16
	lsr r17; lower chane sensibility
	lsr r16
	lsr r17; lower chane sensibility
	lsr r16
	cp r16,r17; value changed?
	breq check_joystick_01; if not continue with X high value
	ser r20; change happened
	lsl r16
	lsl r16
	lsl r16
	lsl r16
	st x,r16

check_joystick_01:
	mov r21,r16; save for further processing

	ldi xl,low(adc_high); ADC result high value
	ldi xh,high(adc_high)
	ld r16,x
	ldi xl,low(joyx_high); joy X high value
	ldi xh,high(joyx_high)
	ld r17,x
	cp r16,r17; value changed?
	breq check_joystick_02; if not check for display
	ser r20; change happened
	st x,r16

check_joystick_02:
;-- display X value
	tst r20; display a new X value?
	breq check_joystick_03; if not continue with Y axis
	rcall cursor_home
	ldi r18,$01; row
	rcall display_hex
	mov r16,r21; restore r16
	rcall display_hex
	rcall send_midi_x
	clr r20

	rcall display_mask; display jostick prefixes

;-- read joystick Y axis
check_joystick_03:
	ldi r16,JOY_Y; select adc channel Y axis
	out ADMUX,r16
	ldi r16,$88; set ADC operation mode
	out ADCSR,r16; start conversion
	sleep; wait for conversion end
	ldi xl,low(adc_low); ADC result low value
	ldi xh,high(adc_low)
	ld r16,x
	ldi xl,low(joyy_low); joy Y low value
	ldi xh,high(joyy_low)
	ld r17,x
	lsr r17; lower change sensibility
	lsr r16
	lsr r17; lower change sensibility
	lsr r16
	lsr r17; lower change sensibility
	lsr r16
	lsr r17; lower change sensibility
	lsr r16
	cp r16,r17; value changed?
	breq check_joystick_04; if not continue with Y high value
	ser r20; change happened
	lsl r16
	lsl r16
	lsl r16
	lsl r16
	st x,r16

check_joystick_04:
	mov r21,r16; save for further processing

	ldi xl,low(adc_high); ADC result high value
	ldi xh,high(adc_high)
	ld r16,x
	ldi xl,low(joyy_high); joy Y high value
	ldi xh,high(joyy_high)
	ld r17,x
	cp r16,r17; value changed?
	breq check_joystick_05; if not check for display
	ser r20; change happened
	st x,r16

check_joystick_05:
;-- display Y value
	tst r20; display a new Y value?
	breq check_joystick_06; if not leave function
	rcall cursor_home
	clr r18; row
	rcall display_hex
	mov r16,r21; restore r16
	rcall display_hex
	rcall send_midi_y

	rcall display_mask; display jostick prefixes

check_joystick_06:
	clr r16
	out ADCSR,r16; disable ADC

	pop r21
	pop r20
	pop r19
	pop r18
	pop r17
	pop r16
	pop xh
	pop xl
	ret

;----------------------------------------
; Function send_midi_x
;
; Creates and sends MIDI message which is
; configured to joystick X axis movement
;----------------------------------------
send_midi_x:

	push xl
	push xh
	push r16
	push r17
	push r18

	ldi xl,low(joyx_low); get joy X low value
	ldi xh,high(joyx_low)
	ld r17,x
	ldi xl,low(joyx_high); get joy X high value
	ldi xh,high(joyx_high)
	ld r18,x

	rcall cr_midi_databytes; change data format to midi

;-- send midi message
	ldi r16,CNTRLCHANGE; send controller info
	out UDR,r16
	sleep
	rcall wait
	ldi r16,MODULATIONHI; send controller info
	out UDR,r16
	sleep
	rcall wait
	out UDR,r18; send controller high byte
	sleep
	rcall wait
	ldi r16,MODULATIONLO; send controller info
	out UDR,r16
	sleep
	rcall wait
	out UDR,r17; send controller low byte
	sleep
	rcall wait

	pop r18
	pop r17
	pop r16
	pop xh
	pop xl
	ret

;----------------------------------------
; Function send_midi_y
;
; Creates and sends MIDI message which is
; configured to joystick Y axis movement
;----------------------------------------
send_midi_y:

	push xl
	push xh
	push r16
	push r17
	push r18

	ldi xl,low(joyy_low); get joy Y low value
	ldi xh,high(joyy_low)
	ld r17,x
	ldi xl,low(joyy_high); get joy Y high value
	ldi xh,high(joyy_high)
	ld r18,x

	rcall cr_midi_databytes; change data format to midi

;-- send midi message
	ldi r16,PITCHBEND; send controller info
	out UDR,r16
	sleep
	rcall wait
	out UDR,r17; send pitchbend low byte
	sleep
	rcall wait
	out UDR,r18; send pitchbend high byte
	sleep
	rcall wait

	pop r18
	pop r17
	pop r16
	pop xh
	pop xl

	ret

;----------------------------------------
; Function cr_midi_databytes
;
; Creates midi data bytes of the format
;   MSB      LSB
; 0xxxxxxx 0xxx0000
; from 10 bit ADC result of the format
;   MSB      LSB
; 000000xx xxxxxxxx
;
; I/O: r17: LSB
;	   r18: MSB
;----------------------------------------
cr_midi_databytes:
	push r19

;-- create output LSB
	mov r19,r17; save LSB for further processing
	andi r17,$07; keep only bit 0 - 2
	lsl r17; move them to position 4 - 6
	lsl r17
	lsl r17
	lsl r17

;-- create output MSB
	lsr r19; move LSB 3 times to the right
	lsr r19
	lsr r19

	andi r18,$03; isolate bit 0 and 1 of MSB
	lsl r18; move bit 0 and 1 to position 5 and 6
	lsl r18
	lsl r18
	lsl r18
	lsl r18
	or r18,r19; add LSB high digits to MSB
	
	pop r19
	ret	

;----------------------------------------
; Function check_switch
;
; Checks the condition of the external
; switch (on / off)
;----------------------------------------
check_switch:
	push zh
	push zl
	push xh
	push xl
	push r16
	push r17
	push r18
	push r19

	ldi r18,$01; row
	clr r19; column

;-- load old status from memory
	ldi xl,low(switchpos)
	ldi xh,high(switchpos)
	ld r17,x

;-- check switch position (on / off)
	in r16,SWITCH; read port A
	andi r16,$10; isolate bit 4
	cp r16,r17; anything changed?
	breq check_switch_02; if not leave function
	st x,r16; save new status
	rcall clear_display; prepare display for new output
	rcall cursor_home
	tst r16; switch on?
	breq check_switch_01; if not modus "external"

;-- modus "internal"
	rcall display_mask
	rcall init_joystick; force joystick scan
	rcall check_joystick
	ldi zh,high(swi_txt<<1); text address
	ldi zl,low(swi_txt<<1)
	rcall text_out; writes text on display
	rjmp check_switch_02

;-- modus "external"
check_switch_01:
	ldi zh,high(swe_txt<<1); text address
	ldi zl,low(swe_txt<<1)
	rcall text_out; writes text on display

check_switch_02:
	pop r19
	pop r18
	pop r17
	pop r16
	pop xl
	pop xh
	pop zl
	pop zh
	ret

;----------------------------------------
; Function DEBUG
;
; Writes a bit mask 10101010 on data bus
; and stops run
;----------------------------------------
debug: 

	rcall set_databus_write; set direction of databus to output
	ldi r16,$aa ; test bit mask on data bus
	out DATA_OUT,r16

	rjmp stop

;----------------------------------------
; Function clear_databus
;
; Writes a bit mask 00000000 on data bus
;----------------------------------------
clear_databus: 
	push r16

	rcall set_databus_write; set direction of databus to output
	clr r16; zero bit mask on data bus
	out DATA_OUT,r16

	pop r16
	ret

;----------------------------------------
; Function SET_DATABUS_WRITE
;
; Sets direction of data bus to output
;----------------------------------------
set_databus_write:
	push r16

	ser r16; set data bus as output
	out DATADIR, r16 

	pop r16
	ret

;----------------------------------------
; Function SET_DATABUS_READ
;
; Sets direction of data bus to input
;----------------------------------------
set_databus_read:
	push r16

	clr r16; set data bus as input
	out DATADIR, r16 

	pop r16
	ret

;----------------------------------------
; Function INIT_WAIT
;
; Waits for some time, depending on crystal
; With an 8Mhz crystal this function waits
; for 0.065536 secs
;----------------------------------------
init_wait:
	push r16

	clr r16; start value timer/counter 1
	out TCNT1L, r16
	out TCNT1H, r16
	; test
	ldi r16, $02; prescale timer/counter 1 = clock / 8
	out TCCR1B, r16
	sleep

	pop r16
	ret

;----------------------------------------
; Function debounce_elem
;
; Waits for some time, depending on crystal
; With an 8Mhz crystal this function waits
; for 0.000032 secs
;----------------------------------------
debounce_elem:
	push r16

	clr r16; start value timer/counter 1
	out TCNT0, r16
	ldi r16, $01; prescale timer/counter = clock
	out TCCR0, r16
	sleep

	pop r16
	ret

;----------------------------------------
; Function debounce
;
; Waits for Waits for 0.000128 sec
;----------------------------------------
debounce:
	push r16; counter

	clr r16; init counter
debounce_01:
	rcall debounce_elem; wait some time
	inc r16; next counter
	cpi r16,$04; number of iterations
	brlt debounce_01; if not reached once again

	pop r16
	ret

;----------------------------------------
; Function wait
;
; Waits for 0.000384 sec
;----------------------------------------
wait:
	push r17

	ldi r17,$fc; $ff - $fd = $03
wait_next:
	rcall debounce
	inc r17
	tst r17; 256 iterations?
	brne wait_next

	pop r17
	ret

;----------------------------------------
; Function set_buffer
;
;
; Initialises a memory area (buffer) of a
; given size with a given value
;
; Input: r16: 	 value to be stored
;		 xl, xh: buffer start address
;		 r17: 	 length of buffer
;----------------------------------------
set_buffer:
	push r18

	clr r18; initial counter value
set_buffer_01:
	st x,r16; write byte
	adiw xl,1; next buffer position
	inc r18; next counter
	cp r18,r17; check end of buffer reached
	brlt set_buffer_01; not yet reached

	pop r18
	ret

;****************************************
;
;***** MIDI functions
;
; µC pin functions:
;
; PD2(16): enable MIDI in
; PD3(17): enable iMIDI out
; PD4(18): enable iMIDI in
; PD5(19): enable MIDI out
;
;****************************************
;----------------------------------------
; Function init_midi
;
; o Initializes UART with 31250 baud 
;   (MIDI, Clock devider value corresponds to 
;   8 Mhz crystal and 31250 baud)
; o Enables MIDI I/O
;----------------------------------------
init_midi:
	push xl
	push xh
	push r16
	push r17

;-- clear read / write pointer of MIDI input buffer
	clr r16
	ldi xl,low(wpt)
	ldi xh,high(wpt)
	st x,r16
	ldi xl,low(rpt)
	ldi xh,high(rpt)
	st x,r16

;-- set clock divider for baud rate
	clr r17
	ldi r16,$0f; clock devider
	out UBRRL,r16
	out UBRRH,r17

;-- prepare hardware connections
	sbi PORTD,2; enable MIDI in
	sbi PORTD,3; enable iMIDI out
	cbi PORTD,4; disable iMIDI in
	sbi PORTD,5; enable MIDI out

	rcall set_uart_write; prepare UART for sending
	rcall note_off_all; send note off for all notes

	pop r17
	pop r16
	pop xh
	pop xl
	ret

;----------------------------------------
; Function set_uart_write
;
; Prepares UART for sending (midi out)
;----------------------------------------
set_uart_write:
	push r16

	ldi r16,$48; prepare UART for sending
	out UCSRB,r16
	ser r16; load ACTIVE SENSING
	out UDR,r16; cleanup initial uart content
	sleep
	out UDR,r16
	sleep

	pop r16
	ret

;----------------------------------------
; Function disable_uart
;
; disables UART
;----------------------------------------
disable_uart:
	push r16

	clr r16; disable UART
	out UCSRB,r16

	pop r16
	ret

;----------------------------------------
; Function midi_mon
;
; Shows incoming serial data on display
;----------------------------------------
midi_mon:
	push xl
	push xh
	push r16
	push r17
	push r18
	push r19
	push r20
	push r21

	rcall set_uart_read; prepare UART for reading
	clr r18; row
	clr r19; col
	ldi r21,ROWLEN
	rcall cursor_home
	ldi r16, $0f; blinking cursor
	rcall dis_com

;-- check for received data
midi_mon_01:
	ldi xl,low(wpt); get buffer write pointer
	ldi xh,high(wpt)
	ld r17,x
	ldi xl,low(rpt); get buffer read pointer
	ldi xh,high(rpt)
	ld r20,x
	cp r17,r20; anything to do?
	brne midi_mon_03; if yes process data

;-- check switch
	rcall check_switch
	ldi xl,low(switchpos)
	ldi xh,high(switchpos)
	ld r16,x
	tst r16; switch on external?
	breq midi_mon_01; if yes stay in midi_mon function

;-- actions before leaving midi_mon function
	ldi r16,$48; prepare UART for sending
	out UCSRB,r16
	ldi r16, $0c; cursor off
	rcall dis_com
	rjmp midi_mon_04; leave function

;-- read byte from buffer
midi_mon_03:
	ldi xl,low(ringbuf); get buffer start address
	ldi xh,high(ringbuf)
	add xl,r20; compute current buffer address
	ld r16,x; read midi byte

;-- display midi byte
	rcall display_hex
	inc r19; next cursor position
	cpi r19,$10; end of row reached?
	brlt midi_mon_05; if not continue
	clr r19; first col
    tst r18; actual row first row?
	breq midi_mon_06; if yes set next row
	clr r18; else start from display beginning
	rcall cursor_home
	rjmp midi_mon_05
midi_mon_06:
	ldi r18,$01;move cursor to begin of next line
	rcall cursor_home

;-- compute next read pointer address
midi_mon_05:
	inc r20
	cpi r20,$1f; max buffer reached?
	brlt midi_mon_02; if not continue
	clr r20; reset read buffer
midi_mon_02:
	ldi xl,low(rpt); get buffer read pointer
	ldi xh,high(rpt)
	st x,r20; save new read pointer
	rjmp midi_mon_01; check for next byte	

midi_mon_04:
	pop r21
	pop r20
	pop r19
	pop r18
	pop r17
	pop r16
	pop xh
	pop xl
	ret

;----------------------------------------
; Function set_uart_read
;
; Prepares UART for reading (midi in)
;----------------------------------------
set_uart_read:
	push r16

	ldi r16,$90; prepare UART for reading
	out UCSRB,r16

	pop r16
	ret

;----------------------------------------
; Function note_off_all
;
; Send note off for all notes
;----------------------------------------
note_off_all:
	
	push xl
	push xh
	push r16; counter
	push r17; note value
	push r18; data byte to send

	clr r16; init counter
	ldi r17,NOTE_OFFSET; start note value
note_off_all_01:
	inc r17; next note value
	ldi r18,$90; send midi channel
	out UDR,r18
	sleep
	mov r18,r17; send note value
	out UDR,r18
	sleep
	clr r18; send zero volume (note off)
	out UDR,r18
	sleep
	inc r16; next counter
	cpi r16,$3d; note 61 reached?
	brlt note_off_all_01

note_off_all_02:
;-- clear note buffer
	clr r16; initial buffer value
	ldi r17, MAXNOTES
	ldi xl,low(notebuf); initial buffer address
	ldi xh,high(notebuf)
	rcall set_buffer; write buffer
;-- clear velocity buffer
	ldi xl,low(velobuf); initial buffer address
	ldi xh,high(velobuf)
	ldi r17,$01; buffer length
	rcall set_buffer; write buffer

;-- register that note_off_all was executed to avoid multiple calls
	ldi xl,low(noffsent); set variable start adress
	ldi xh,high(noffsent)
	ser r16
	st x,r16

	pop r18
	pop r17
	pop r16
	pop xh
	pop xl
	ret

;****************************************
;
;***** Keyboard functions
;
; µC pin functions:
;
; PB0(01): Bit 2 of keyboard block adress
; PB1(02): Bit 1 of keyboard block adress
; PB2(03): Bit 0 of keyboard block adress
;
; PA7(33): read keyboard
; PA6(34): freeze keboard data
; PA5(35): provide data of keys 1-4
;		   of one block scan on data bus 
; PD7(21): provide data of keys 5-8
;		   of one block scan on data bus
;
;****************************************
;----------------------------------------
; Function init_keyboard
;
;
; initialises keyboard scan
;----------------------------------------
init_keyboard:

;-- disable keyboard interface
	cbi PORTA,5; GAB1: don't provide key data 1-4
	cbi PORTD,7; GAB2: don't provide key data 5-8

	ret

;----------------------------------------
; Function scan_kbd_block_0
;
;
; scans first keyboard block with the keys 
;   01: c0
;   09: g#0
;   17: e1
;   25: c2
;   33: g#2
;	41: e3
;	49: c4
;   57: g#4
; by setting block adress 4 (100)
;
; uses r20 (lower keyboard block)
;      r17 (higher keyboard block)
;	   r16 Keyboard block number
;----------------------------------------
scan_kbd_block_0:
	push r16
	push r17
	push r20

	sbi PORTB,0; Bit 2 of keyboard block adress
	cbi PORTB,1; Bit 1 of keyboard block adress
	cbi PORTB,2; Bit 0 of keyboard block adress
	rcall debounce

	rcall scan_kbd_block; enables keyboard scan and reads data
	ldi r16,BLOCK_0
	rcall process_keyblock

	pop r20
	pop r17
	pop r16
	ret

;----------------------------------------
; Function scan_kbd_block_1
;
;
; scans second keyboard block with the keys 
;   02: c#0
;   10: a0
;   18: f1
;   26: c#2
;   34: a2
;	42: f3
;	50: c#4
;   58: a4
; by setting block adress 2 (010)
;
; uses r20 (lower keyboard block)
;      r17 (higher keyboard block)
;	   r16 Keyboard block number
;----------------------------------------
scan_kbd_block_1:
	push r16
	push r17
	push r20

	cbi PORTB,0; Bit 2 of keyboard block adress
	sbi PORTB,1; Bit 1 of keyboard block adress
	cbi PORTB,2; Bit 0 of keyboard block adress
	rcall debounce

	rcall scan_kbd_block; enables keyboard scan and reads data
	ldi r16,BLOCK_1
	rcall process_keyblock

	pop r20
	pop r17
	pop r16
	ret

;----------------------------------------
; Function scan_kbd_block_2
;
;
; scans third keyboard block with the keys 
;   03: d0
;   11: b0
;   19: f#1
;   27: d2
;   35: b2
;	43: f#3
;	51: d4
;   59: b4
; by setting block adress 6 (110)
;
; uses r20 (lower keyboard block)
;      r17 (higher keyboard block)
;	   r16 Keyboard block number
;----------------------------------------
scan_kbd_block_2:
	push r16
	push r17
	push r20

	sbi PORTB,0; Bit 2 of keyboard block adress
	sbi PORTB,1; Bit 1 of keyboard block adress
	cbi PORTB,2; Bit 0 of keyboard block adress
	rcall debounce

	rcall scan_kbd_block; enables keyboard scan and reads data
	ldi r16,BLOCK_2
	rcall process_keyblock

	pop r20
	pop r17
	pop r16
	ret

;----------------------------------------
; Function scan_kbd_block_3
;
;
; scans fourth keyboard block with the keys 
;   04: d#0
;   12: h0
;   20: g1
;   28: d#2
;   36: h2
;	44: g3
;	52: d#4
;   60: h4
; by setting block adress 1 (001)
;
; uses r20 (lower keyboard block)
;      r17 (higher keyboard block)
;	   r16 Keyboard block number
;----------------------------------------
scan_kbd_block_3:
	push r16
	push r17
	push r20

	cbi PORTB,0; Bit 2 of keyboard block adress
	cbi PORTB,1; Bit 1 of keyboard block adress
	sbi PORTB,2; Bit 0 of keyboard block adress
	rcall debounce

	rcall scan_kbd_block; enables keyboard scan and reads data
	ldi r16,BLOCK_3
	rcall process_keyblock

	pop r20
	pop r17
	pop r16
	ret

;----------------------------------------
; Function scan_kbd_block_4
;
;
; scans fifth keyboard block with the keys 
;   05: e0
;   13: c1
;   21: g#1
;   29: e2
;   37: c3
;	45: g#3
;	53: e4
;   61: c5
; by setting block adress 0 (000)
;
; uses r20 (lower keyboard block)
;      r17 (higher keyboard block)
;	   r16 Keyboard block number
;----------------------------------------
scan_kbd_block_4:
	push r16
	push r17
	push r20

	cbi PORTB,0; Bit 2 of keyboard block adress
	cbi PORTB,1; Bit 1 of keyboard block adress
	cbi PORTB,2; Bit 0 of keyboard block adress
	rcall debounce

	rcall scan_kbd_block; enables keyboard scan and reads data
	ldi r16,BLOCK_4
	rcall process_keyblock

	pop r20
	pop r17
	pop r16
	ret

;----------------------------------------
; Function scan_kbd_block_5
;
;
; scans sixth keyboard block with the keys 
;   06: f0
;   14: c#1
;   22: a1
;   30: f2
;   38: c#3
;	46: a3
;	54: f4
;   62: n. a.
; by setting block adress 7 (111)
;
; uses r20 (lower keyboard block)
;      r17 (higher keyboard block)
;	   r16 Keyboard block number
;----------------------------------------
scan_kbd_block_5:
	push r16
	push r17
	push r20

	sbi PORTB,0; Bit 2 of keyboard block adress
	sbi PORTB,1; Bit 1 of keyboard block adress
	sbi PORTB,2; Bit 0 of keyboard block adress
	rcall debounce

	rcall scan_kbd_block; enables keyboard scan and reads data
	ldi r16,BLOCK_5
	rcall process_keyblock

	pop r20
	pop r17
	pop r16
	ret

;----------------------------------------
; Function scan_kbd_block_6
;
;
; scans seventh keyboard block with the keys 
;   07: f#0
;   15: d1
;   23: b1
;   31: f#2
;   39: d3
;	47: b3
;	55: f#4
;   63: n. a.
; by setting block adress 3 (011)
;
; uses r20 (lower keyboard block)
;      r17 (higher keyboard block)
;	   r16 Keyboard block number
;----------------------------------------
scan_kbd_block_6:
	push r16
	push r17
	push r20

	cbi PORTB,0; Bit 2 of keyboard block adress
	sbi PORTB,1; Bit 1 of keyboard block adress
	sbi PORTB,2; Bit 0 of keyboard block adress
	rcall debounce

	rcall scan_kbd_block; enables keyboard scan and reads data
	ldi r16,BLOCK_6
	rcall process_keyblock

	pop r20
	pop r17
	pop r16
	ret

;----------------------------------------
; Function scan_kbd_block_7
;
;
; scans eighth keyboard block with the keys 
;   08: g0
;   16: d#1
;   24: h1
;   32: g2
;   40: d#3
;	48: h3
;	56: g4
;   64: n. a.
; by setting block adress 5 (101)
;
; uses r20 (lower keyboard block)
;      r17 (higher keyboard block)
;	   r16 Keyboard block number
;----------------------------------------
scan_kbd_block_7:
	push r16
	push r17
	push r20

	sbi PORTB,0; Bit 2 of keyboard block adress
	cbi PORTB,1; Bit 1 of keyboard block adress
	sbi PORTB,2; Bit 0 of keyboard block adress
	rcall debounce

	rcall scan_kbd_block; enables keyboard scan and reads data
	ldi r16,BLOCK_7
	rcall process_keyblock

	pop r20
	pop r17
	pop r16
	ret

;----------------------------------------
; Function scan_kbd_block
;
; enables keyboard scan and reads data 
;
; returns lower keybord block in r20
;		  higher keyboard block in r17
;----------------------------------------
scan_kbd_block:
	push r16
	push r18
	push r19

	sbi PORTA,5; GAB1: provide key data 1-4
	rcall set_databus_read; set databus input
	in r20,DATA_IN; read keyboard data
	cbi PORTA,5; GAB1: don't provide key data 1-4

	sbi PORTD,7; GAB2: provide key data 5-8
	rcall set_databus_read; set databus input
	in r17,DATA_IN; read keyboard data
	cbi PORTD,7; GAB2: don't provide key data 5-8

	pop r19
	pop r18
	pop r16
	ret

;----------------------------------------
; Function process_keyblock
;
; analyses a keyblock scan (r20 and r17),
; determins the keys pressed, gets the
; note values of the actual block (r16)
; and stores them into the note buffer,
; determins key velocity and sends MIDI
; message
;
; input: r16: keyboard block number
; 		 r20: lower block half
;		 r17: higher block half
;----------------------------------------
process_keyblock:
	push xl; pointer to velocity buffer
	push xh
	push zl; pointer to list of note values
	push zh
	push r19; counter

	ldi xl,low(velobuf); velocity buffer address
	ldi xh,high(velobuf)
;-- compute address of note value constant
	clr r19; counter := 0
	ldi zh,high(key_values<<1); start address list of key values
	ldi zl,low(key_values<<1)
process_keyblock_03:
	cp r19,r16; destination block number reached?
	breq process_keyblock_02
	adiw zl,BLOCKLEN; add offset
	inc r19; next counter
	rjmp process_keyblock_03

;-- check lower key block
process_keyblock_02:
	tst r20; check if zero
	breq process_keyblock_01
	sbrc r20,0; second contact of first key?
	rcall key_1
	sbrc r20,1; first contact of first key?
	rcall velo_1
	sbrc r20,2; second contact of second key?
	rcall key_2
	sbrc r20,3; first contact of second key?
	rcall velo_2
	sbrc r20,4; second contact of third key?
	rcall key_3
	sbrc r20,5; first contact of third key?
	rcall velo_3
	sbrc r20,6; second contact of fourth key?
	rcall key_4
	sbrc r20,7; first contact of fourth key?
	rcall velo_4

;-- check upper key block
process_keyblock_01:
	tst r17; check if zero
	breq process_keyblock_exit
	sbrc r17,0; second contact of fifth key?
	rcall key_5
	sbrc r17,1; first contact of fifth key?
	rcall velo_5
	sbrc r17,2; second contact of sixth key?
	rcall key_6
	sbrc r17,3; first contact of sixth key?
	rcall velo_6
	sbrc r17,4; second contact of seventh key?
	rcall key_7
	sbrc r17,5; first contact of seventh key?
	rcall velo_7
	sbrc r17,6; second contact of eighth key?
	rcall key_8
	sbrc r17,7; first contact of eighth key?
	rcall velo_8

process_keyblock_exit:
	pop r19
	pop zh
	pop zl
	pop xh
	pop xl
	ret

;----------------------------------------
; Function velo_1
;
; Processes velocity of key 1 of scanned 
; key block
;----------------------------------------
velo_1:
	push r18
	push r19

	ldi r19,$01; bit 0
	mov r18,r20; provide current half of key block
	rcall process_velo

	pop r19
	pop r18
	ret

;----------------------------------------
; Function velo_2
;
; Processes velocity of key 2 of scanned 
; key block
;----------------------------------------
velo_2:
	push r18
	push r19

	ldi r19,$04; bit 2
	mov r18,r20; provide current half of key block
	rcall process_velo

	pop r19
	pop r18
	ret

;----------------------------------------
; Function velo_3
;
; Processes velocity of key 3 of scanned 
; key block
;----------------------------------------
velo_3:
	push r18
	push r19

	ldi r19,$10; bit 4
	mov r18,r20; provide current half of key block
	rcall process_velo

	pop r19
	pop r18
	ret

;----------------------------------------
; Function velo_4
;
; Processes velocity of key 4 of scanned 
; key block
;----------------------------------------
velo_4:
	push r18
	push r19

	ldi r19,$40; bit 6
	mov r18,r20; provide current half of key block
	rcall process_velo

	pop r19
	pop r18
	ret

;----------------------------------------
; Function velo_5
;
; Processes velocity of key 5 of scanned 
; key block
;----------------------------------------
velo_5:
	push r18
	push r19

	ldi r19,$01; bit 0
	mov r18,r17; provide current half of key block
	rcall process_velo

	pop r19
	pop r18
	ret

;----------------------------------------
; Function velo_6
;
; Processes velocity of key 6 of scanned 
; key block
;----------------------------------------
velo_6:
	push r18
	push r19

	ldi r19,$04; bit 2
	mov r18,r17; provide current half of key block
	rcall process_velo

	pop r19
	pop r18
	ret

;----------------------------------------
; Function velo_7
;
; Processes velocity of key 7 of scanned 
; key block
;----------------------------------------
velo_7:
	push r18
	push r19

	ldi r19,$10; bit 4
	mov r18,r17; provide current half of key block
	rcall process_velo

	pop r19
	pop r18
	ret

;----------------------------------------
; Function velo_8
;
; Processes velocity of key 8 of scanned 
; key block
;----------------------------------------
velo_8:
	push r18
	push r19

	ldi r19,$40; bit 6
	mov r18,r17; provide current half of key block
	rcall process_velo

	pop r19
	pop r18
	ret

;----------------------------------------
; Function process_velo
;
; Processes velocity of key of scanned 
; key block.
; Each recognized contact decreases a
; byte value until the second contact
; of a key stroke happens. The remaining
; value in the velocity byte is an
; equivalent to the key velocity
;
; Input: xl, xh: Address of velocity byte
;                of key stroke
;		 r18:	 current half of key block
;		 r19:	 bit mask which key
;----------------------------------------
process_velo:
	push r16
	push r19
	push r18
	push r21
	push r22
	push yl
	push yh

;-- check if second contact alraedy happened
	and r18,r19; isolate bit of current key stroke

	tst r18; keystroke complete?
	brne process_velo_exit; if not continue

;-- decrease velocity byte
process_velo_01:
	ldi r22,$f0; for complement construction
	ldi yl,low(kvs); load keyboard sensitivity value
	ldi yh,high(kvs)
	ld r21,y
;-- compute minimum velocity value
	tst r21; is zero?
	breq process_velo_02
	dec r21
process_velo_02:
	lsl r21; r21 *= 16
	lsl r21
	lsl r21
	lsl r21
	sub r22,r21; build complement
	ld r16,x; load old velocity value
	dec r16; decrease velocity byte
	cp r16,r22; minimum value reached?
	breq process_velo_exit; then exit

	st x,r16; store new value

process_velo_exit:
	pop yh
	pop yl
	pop r22
	pop r21
	pop r18
	pop r19
	pop r16
	ret

;----------------------------------------
; Function key_1
;
; Processes key 1 of scanned key block
;----------------------------------------
key_1:

	rcall process_key; no offset in list of note values

	ret

;----------------------------------------
; Function key_2
;
; Processes key 2 of scanned key block
;----------------------------------------
key_2:
	push zl
	push zh

	adiw zl,$01; offset in list of note values
	rcall process_key

	pop zh
	pop zl
	ret

;----------------------------------------
; Function key_3
;
; Processes key 3 of scanned key block
;----------------------------------------
key_3:
	push zl
	push zh

	adiw zl,$02; offset in list of note values
	rcall process_key

	pop zh
	pop zl
	ret

;----------------------------------------
; Function key_4
;
; Processes key 4 of scanned key block
;----------------------------------------
key_4:
	push zl
	push zh

	adiw zl,$03; offset in list of note values
	rcall process_key

	pop zh
	pop zl
	ret

;----------------------------------------
; Function key_5
;
; Processes key 5 of scanned key block
;----------------------------------------
key_5:
	push zl
	push zh

	adiw zl,$04; offset in list of note values
	rcall process_key

	pop zh
	pop zl
	ret

;----------------------------------------
; Function key_6
;
; Processes key 6 of scanned key block
;----------------------------------------
key_6:
	push zl
	push zh

	adiw zl,$05; offset in list of note values
	rcall process_key

	pop zh
	pop zl
	ret

;----------------------------------------
; Function key_7
;
; Processes key 7 of scanned key block
;----------------------------------------
key_7:
	push zl
	push zh

	adiw zl,$06; offset in list of note values
	rcall process_key

	pop zh
	pop zl
	ret

;----------------------------------------
; Function key_8
;
; Processes key 1 of scanned key block
;----------------------------------------
key_8:
	push zl
	push zh

	adiw zl,$07; offset in list of note values
	rcall process_key

	pop zh
	pop zl
	ret

;----------------------------------------
; Function process_key
;
; Processes key of scanned key block
;
; Input: zl,zh: Address of note value 
;----------------------------------------
process_key:
	push r0
	push xl
	push xh
	push r16
	push r17
	push r18
	push r19

	ldi r19,NOTE_OFFSET

	lpm; load (Z) into r0 => load note value from list

;-- Check if midi sequence is already sent
	clr r17; clear counter
	ldi xl,low(notebuf); set note variables / buffer start adress
	ldi xh,high(notebuf)
new_note_01:
	ld r16,x; compare note value to memory
	andi r16,$7f; clear confirm bit
	push r0
	cp r0,r16
	breq confirm_note; key is still pressed
	pop r0
	adiw xl,1
	inc r17
	cpi r17,MAXNOTES ; 10 voice polyphonic
	brlt new_note_01

;-- find free note variable
	clr r17; clear counter
	ldi xl,low(notebuf); set note variables start adress
	ldi xh,high(notebuf)
new_note_02:
	push r0; save note value on stack
	ld r16,x; load register from data memory
	tst r16; compare with initial value
	breq send_midi; send note, save note value + confirm bit in data segment 
	pop r0
	adiw xl,1
	inc r17
	cpi r17,MAXNOTES 
	brlt new_note_02
	rjmp process_key_exit

send_midi:
;-- it is a new note, so send midi
	pop r0
	ldi r18,$90; send midi channel
	out UDR,r18
	sleep
	mov r18,r0
	add r18,r19
	OUT UDR,r18; send note value
	sleep
	push xl
	push xh
	ldi xl,low(velobuf); send note volume
	ldi xh,high(velobuf)
	ld r18,x
	pop xh
	pop xl
	lsr r18; r18:=r18/2, otherwise volume too strong
	inc r18
	OUT UDR,r18
	sleep
;-- register that note_off_all can be called
	push xl
	push xh
	ldi xl,low(noffsent); set variable start adress
	ldi xh,high(noffsent)
	clr r18
	st x,r16
	pop xh
	pop xl
	push r0

confirm_note:
;-- save note value + confirm bit in data segment
	pop r0
	mov r16,r0
	ori r16,$80; set confirm bit
	st x,r16

process_key_exit:
	pop r19
	pop r18
	pop r17
	pop r16
	pop xh
	pop xl
	pop r0
	ret

;----------------------------------------
; Function clear_flags
;
; Clears key_is_still_pressed - flag in
; note buffer 
;----------------------------------------
clear_flags:
;-- clear all confirm bits of pressed keys
	push xl
	push xh
	push r16
	push r17

	clr r17; clear counter
	ldi xl,low(notebuf); set note variables start adress
	ldi xh,high(notebuf)
clear_flags_02:
	ld r16,x
	tst r16; currenly not used?
	breq clear_flags_01; then skip
	andi r16,$7f; else clear Bit 7
	st x,r16
clear_flags_01:
	adiw xl,1
	inc r17
	cpi r17,MAXNOTES
	brne clear_flags_02

	pop r17
	pop r16
	pop xh
	pop xl
	ret

;----------------------------------------
; Function note_off
;
; Send note off for all released keys
;----------------------------------------
note_off:
	push xl
	push xh
	push r16
	push r17
	push r18
	push r19
	push r20
	push r21
	push r22

	ldi r19,$90; midi channel
	ldi r20,NOTE_OFFSET
	clr r22; check if a key is pressed

	clr r17; clear counter
	ldi xl,low(notebuf); set note variables start adress
	ldi xh,high(notebuf)
note_off_02:
	ld r16,x
	tst r16; initial value?
	brne note_off_01; if not continue
note_off_03:
	adiw xl,1; next buffer address
	inc r17; next counter
	cpi r17,MAXNOTES; max. reached?
	breq note_off_exit; if yes exit function
	rjmp note_off_02; else continue operation
note_off_01:
	mov r18,r16
	andi r16,$80; isolate bit 7
	sbrc r16,7; register in r22 if a key is still pressed
	inc r22
	cpi r16,$80; bit still set?
	breq note_off_03; then next stored value

;-- send note off
	mov r16,r18
	andi r16,$7f; clear bit 7
	clr r21; clear volume
	out UDR,r19; send midi channel
	sleep
	mov r19,r16
	add r19,r20
	OUT UDR,r19; send note value
	sleep
	OUT UDR,r21; send volume
	sleep
	clr r16; mark note variable as free
	st x,r16
	push xl; save actual key note buffer address
	push xh
	adiw xl,MAXNOTES; jump to velocity buffer
	st x,r16; clear velocity byte
	pop xh; restore key note buffer address
	pop xl
	rjmp note_off_03; next stored value

note_off_exit:
	tst r22; is any key pressed at all?
	brne note_off_exit_01
	ldi xl,low(noffsent); set variable start adress
	ldi xh,high(noffsent)
	ld r16,x; load variable content
	tst r16
	brne note_off_exit_01
	rcall note_off_all
note_off_exit_01:
	pop r22
	pop r21
	pop r20
	pop r19
	pop r18
	pop r17
	pop r16
	pop xh
	pop xl
	ret

;****************************************
;
;***** Display functions
;
;	Hardware connections:
;
;	Pin display = Pin µC
;	 RS(4) 		= PD6(20) (Data / Command)
;	 RW(5) 		= PB4(05) (Read / Write)
;	 EN(6) 		= PB3(04) (Enable / Disable)
;****************************************
;----------------------------------------
; Function display_pos1
;
; display position of encoder 1
;----------------------------------------
display_pos1:
	push r16
	push r18
	push r19
	push r20
	push r21

;-- read encoder positions from EEPROM
	clr r20; read EEPROM byte 0
	rcall read_eeprom

;-- display value
	mov r16,r21; copy eeprom byte to display register
	rcall cursor_home
	clr r18; row
	ldi r19,$0e; col
	rcall display_hex

	pop r21
	pop r20	
	pop r19
	pop r18
	pop r16
	ret

;----------------------------------------
; Function display_pos2
;
; display position of encoder 2
;----------------------------------------
display_pos2:
	push r16
	push r18
	push r19
	push r20
	push r21

;-- read encoder positions from EEPROM
	ldi r20,$01; read EEPROM byte 1
	rcall read_eeprom

;-- display value
	mov r16,r21; copy eeprom byte to display register
	rcall cursor_home
	ldi r18,$01; row
	ldi r19,$0e; col
	rcall display_hex

	pop r21
	pop r20	
	pop r19
	pop r18
	pop r16
	ret

;----------------------------------------
; Function display_mask
;
; creates display mask
;----------------------------------------
display_mask:
	push zh
	push zl
	push r18
	push r19

;-- Joystick X position
	ldi zh,high(joyx_txt<<1); text address
	ldi zl,low(joyx_txt<<1)
	ldi r18,$00; row
	ldi r19,$04; column
	rcall text_out; writes text on display

;-- Joystick Y position
	ldi zh,high(joyy_txt<<1); text address
	ldi zl,low(joyy_txt<<1)
	ldi r18,$01; row
	ldi r19,$04; column
	rcall text_out; writes text on display

;-- Key Velocity Sensitivity
	ldi zh,high(kvs_txt<<1); text address
	ldi zl,low(kvs_txt<<1)
	ldi r18,$00; row
	ldi r19,$09; column
	rcall text_out; writes text on display

;-- JoyStick Sensitivity
	ldi zh,high(jss_txt<<1); text address
	ldi zl,low(jss_txt<<1)
	ldi r18,$01; row
	ldi r19,$09; column
	rcall text_out; writes text on display

;-- KVS and JSS values
	rcall display_pos1
	rcall display_pos2

	pop r19
	pop r18
	pop zl
	pop zh
	ret

;----------------------------------------
; Function start_message
;
; displays start messages
;----------------------------------------
start_message:
	push zh
	push zl
	push r18
	push r19

;-- write reset message on display
	ldi zh,high(start_txt<<1); text address
	ldi zl,low(start_txt<<1)
	ldi r18,$00; row
	ldi r19,$01; column
	rcall text_out; writes text on display
;--wait some time
	clr r18; init counter
start_message_01:
	rcall init_wait; waits for ca. 0.065536 secs
	inc r18; next counter
	cpi r18,$14; number of iterations
	brlt start_message_01; if not reached once again
	rcall clear_display

	pop r19
	pop r18
	pop zl
	pop zh
	ret

;----------------------------------------
; Function DISPLAY_HEX
;
; displays a byte (register content) at
; (row,col) and (row,(col+1)) in hex
; format
;
; parameter: r16: byte to display
;			 r18: row
;			 r19: column
;----------------------------------------
display_hex:

	push r16; save data byte for reuse
	swap r16; swap upper and lower half nibble
	rcall display_nibble; display upper half nibble
	pop r16; reload data byte
	inc r19; next cursor position
	rcall display_nibble; display lower half nibble

	ret

;----------------------------------------
; Function DISPLAY_NIBBLE
;
; displays ascii value of nibble
;
; parameter: r16: nibble to display
;			 r18: row
;			 r19: column
;----------------------------------------
display_nibble:
	push r0
	push r17

	andi r16,$0f; clear bits 4-7
	cpi r16,$0a; conversion necessary?
	brge dn01; yes, move to ascii 'a'
	ldi r17,$30; no, move to ascii '0'
	rjmp dn02
dn01:
	ldi r17,$37; move to ascii 'a'
dn02:
	add r16,r17; compute ascii value of nibble
	mov r0,r16

	mov r16,r19; set cursor column
	rcall set_cursor; move cursor to destination row
	rcall dis_text; send character to display

	pop r17
	pop r0
	ret

;----------------------------------------
; Function cursor_home
;
; Moves cursor to first display position
;----------------------------------------
cursor_home:
	push r16

	ldi r16, $02; cursor home
	rcall dis_com; send command to display

	pop r16
	ret

;----------------------------------------
; Function set_cursor
;
; Moves cursor to destination position
;
; Parameter: r16    destination column
;            r18    destination row
;----------------------------------------
set_cursor:
	push r19

	clr r19; default row offset 0
	cpi r18,$00; check row selection
	breq sc_continue; no row addition necessary
	ldi r19,ROWLEN; offset 1st digit of 2nd row
sc_continue:
	add r16,r19; add column to compute target digit in display
	ori r16,$80; create cursor command
	rcall dis_com; send command to display

	pop r19
	ret

;----------------------------------------
; Function TEXT_OUT
;
; Displays a text on lcd module
;
; Parameter: zh, zl - Text adress
;            r18    - row ($00 - $01)
;            r19    - column($00-$13)
;----------------------------------------
text_out:
	push r16
	push r20

	rcall cursor_home

	ldi r20,$23; end character in text constant
	mov r16,r19; set cursor command register / column counter
	rcall set_cursor; move cursor to destination row
text_01:
	lpm; load (Z) into r0 => load character
	cp r0,r20; end of text reached?
	brne text_02; if not send character to display

	pop r20
	pop r16
	ret

text_02:
	rcall dis_text; send character to display
	inc r16; next cursor position
	rcall dis_com; send command to display
	adiw zl,1; next text byte position
	rjmp text_01; same procedure for next character

;----------------------------------------
; Function dis_text
;
; Sends a character to display
; Input Parameter: r0: Character
;----------------------------------------
dis_text: 
	rcall set_databus_write; set direction of databus to output

	sbi PORTD,6; RS high (character operation)
	cbi PORTB,4; R/W low (write operation)
	cbi PORTB,3; Enable low

	out DATA_OUT,r0; write command byte 

	rcall lcd_submit; submit command to display
	rcall stat_wait; wait for display to complete operation

	ret

;----------------------------------------
; Function INIT_DISPLAY
;
; Initializes  LCD-Module and writes
; start message
;----------------------------------------
init_display:

	push r16
 
	ldi r16, $38; set function: 2 rows 8 bit interface 5x7 dots
	rcall dis_wait; call display without status check (necessary first time)
	ldi r16, $38; once again set function: 2 rows 8 bit interface 5x7 dots
	rcall dis_com; call display with status check
	rcall clear_display
	rcall cursor_home
	ldi r16, $06; entry mode: cursor move direction increase, display is not shifted
	rcall dis_com; send command to display
	ldi r16, $0c; display on, cursor off, blinking off
	rcall dis_com; send command to display

	rcall start_message; displays reset message

	pop r16
	ret

;----------------------------------------
; Function CLEAR_DISPLAY
;
; clears lcd display
;----------------------------------------
clear_display:
	push r16

    ldi r16, $01; clear display
	rcall dis_com; send command to display

	pop r16
	ret

;----------------------------------------
; Function display_off
;
; switch off lcd module
;----------------------------------------
display_off:
	push r16

    ldi r16,$08; switch off display
	rcall dis_com; send command to display

	pop r16
	ret

;----------------------------------------
; function stat_wait
;
; waits for status flag of lcd display
; module to clear meaning lcd has completed
; operation
;----------------------------------------
stat_wait:
	push r16

	rcall set_databus_read; set direction of databus to input
	sbi PORTB,4; R/W high (LCD read operation)
stat_in:
	sbi PORTB,3; enable LCD
	nop; wait some time
	nop; 
	nop;
	nop; 
	in r16,DATA_IN; read data_bus
	cbi PORTB,3; disable LCD
	andi r16,$80; isolate bit 7 (LCD status bit)
	cpi r16,$80
	breq stat_in

	cbi PORTB,4; R/W low (LCD write operation)

	pop r16
	ret

;----------------------------------------
; Function DIS_WAIT
;
; Sends a command to display module and
; waits a constant time

; Input Parameter: r16: Command Byte
;----------------------------------------
dis_wait: 
	rcall set_databus_write; set direction of databus to output

	cbi PORTD,6; RS low (it's a command operation) 
	cbi PORTB,4; R/W low (LCD write operation)
	cbi PORTB,3; Enable low

	out DATA_OUT,r16; write command byte 
	rcall lcd_submit; submit data to LCD     
	rcall init_wait; wait for some time
 
	ret

;----------------------------------------
; Function dis_com
;
; Sends a command to display module
; Input Parameter: r16: Command Byte
;----------------------------------------
dis_com: 
	rcall set_databus_write; set direction of databus to output

	cbi PORTD,6; RS low (write a command)
	cbi PORTB,4; R/W low (write operation)
	cbi PORTB,3; Enable low

	out DATA_OUT,r16; write command byte 
	rcall lcd_submit; submit command to LCD
	rcall stat_wait; check display status

	ret

;----------------------------------------
; Function lcd_submit
;
; Enables display module by setting pin 6,
; wait for some time and disable lcd again
;----------------------------------------
lcd_submit: 
	sbi PORTB,3; enable LCD
	nop; wait some time
	nop; 
	nop;
	nop; 
	cbi PORTB,3; disable LCD

	ret

;****************************************
;
;***** Interrupts 
;
;****************************************

;----------------------------------------
;***** Reset Handler
;----------------------------------------
RESET: 
	rjmp START

;***** IRQ0 Handler
EXT_INT0: reti		

;***** IRQ1 Handler
EXT_INT1: reti		

;***** Timer 2 Compare Handler
TIM2_COMP: reti		

;***** Timer 2 Overflow Handler
TIM2_OVF: reti		

;***** Timer 1 Capture Handler
TIM1_CAPT: reti		

;***** Timer 1 Compare A Handler
TIM1_COMPA: reti		

;***** Timer 1 Compare B Handler
TIM1_COMPB: reti		

;----------------------------------------
;***** Timer 1 Overflow Handler
;----------------------------------------
TIM1_OVF:
	push r16

	clr r16 ; stop timer/counter 1
	out TCCR1B, r16

	pop r16
	reti		

;----------------------------------------
;***** Timer 0 Overflow Handler
;----------------------------------------
TIM0_OVF:
 	push r16

	clr r16 ; stop timer/counter 0
	out TCCR0, r16

	pop r16
	reti		


;***** SPI Transfer Complete Handler
SPI_STC: reti		

;----------------------------------------
;***** UART RX Complete Handler
;	reads MIDI byte and stores it in 
;   ringbuffer
;----------------------------------------
UART_RXC:
	push zl
	push zh
	push r0
	push r16
	push r18

	in r18,UDR; read MIDI Byte from UART

	ldi zl,low(wpt); get write position
	ldi zh,high(wpt)
	ld r0,z
	ldi zl,low(ringbuf)
	ldi zh,high(ringbuf)
	add zl,r0
	st z,r18; save MIDI byte in ringbuf

	ldi r16,$1f
	cp r0,r16; reached high end of ringbuf?
	brge uart_rxc_01
	inc r0
	rjmp uart_rxc_02

uart_rxc_01:
	clr r0

uart_rxc_02:
	ldi zl,low(wpt); set write position
	ldi zh,high(wpt)
	st z,r0
	
uart_rxc_exit:
	pop r18
	pop r16
	pop r0
	pop zh
	pop zl

	reti		

;***** UDR Empty Handler
UART_DRE: reti		

;***** UART TX Complete Handler
UART_TXC: reti		

;----------------------------------------
;***** AD Conversion Complete Handler
;	max. value: $3ff <=> 5V
;	important: both result registers have
;	to be read and ADCL has to be read first
;----------------------------------------
CADC:
	push zl
	push zh
	push r16
	push r17
	push r18

;-- process ADC-result
	in r16,ADCL
	in r17,ADCH
;-- get divisor from joystick sensitivity variable
	ldi zl,low(jss); joystick sensitivity
	ldi zh,high(jss)
	ld r18,z
	rcall int_division; divide (r17 r16 / r18)
;-- store values in variables
	ldi zl,low(adc_low); ADC result low value
	ldi zh,high(adc_low)
	st z,r16
	ldi zl,low(adc_high); ADC result high value
	ldi zh,high(adc_high)
	st z,r17

	pop r18
	pop r17
	pop r16
	pop zh
	pop zl
	reti

;ATmega8535

;***** EEPROM Ready Handler
EE_RDY: reti
			
;***** Analog Comparator Handler
ANA_COMP: reti		

;***** Two-wire Serial Interface Handler
TWSI: reti

;***** IRQ2 Handler   
EXT_INT2: reti

;*****  Timer0 Compare Handler 
TIM0_COMP: reti

;*****  Store Program Memory Ready Handler
SPM_RDY: reti 
