;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; (c) Fredrik Olofsson 2002  version 1.4 020825
; rev. 040127, 040220, 071126
; released under gnu gpl license
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
.nolist
.include "2313def.inc"
.list
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.equ clock=4000000
.equ baudrate=31250
.equ baudkonstant=(clock/(16*baudrate))-1
	
.equ base=100						;midi note for toggles = base+switchNo (100-103)
.equ base2=110						;midi note for bangs = base2+switchNo (110-103)
.equ velocity=80					;for midi note on msg
.equ chout=0						;main channel for sending
.equ chin=0							;main channel for receiving
.equ ctrlindexout=0					;ctrl for sending index (0-15)
.equ ctrladcout=1					;ctrl for sending adc result (0-127)
.equ ctrlbypassout=2				;ctrl for sending bypass (0/1)
.equ ctrlin0=0						;ctrl for receiving maxindex
.equ ctrlin1=1						;ctrl for receiving index
.equ ctrlin2=2						;ctrl directly to led display (0-127)
.equ ctrlin3=3						;ctrl directly to led display (128-255)
.equ adcpreset=192					;adc timing
	
.def statusreg=R1
.def temp=R16
.def maxindex=R17
.def note=R18
.def index=R19
.def delay=R20
.def delay2=R21
.def laststatus=R22
.def lastbyte=R23
.def bytecounter=R24
.def counter=R25
.def adcresult=R26
.def lastadcresult=R27
.def startupbutton=R28				;startup button flags
.def currentbutton=R29				;current button status flags
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.cseg
.org 0
	rjmp reset
	rjmp ext_int0					;irq0
	rjmp ext_int1					;irq1
	rjmp tim_capt1					;timer1 capture
	rjmp tim_comp1					;timer1 compare
	rjmp tim_ovf1					;timer1 overflow
	rjmp ana_comp					;timer0 overflow
	rjmp uart_rxc					;uart receive
	rjmp uart_dre					;uart empty
	rjmp uart_txc					;uart transmit
	rjmp ana_comp					;analog comparator
	
;---------------
ext_int0:
ext_int1:
tim_capt1:
tim_comp1:
tim_ovf1:
tim_ovf0:
	rjmp ana_comp
uart_rxc:
	in statusreg,SREG
	rcall midiin
	out SREG,statusreg
	reti
uart_dre:
uart_txc:
ana_comp:
	push temp
	in adcresult,TCNT0				;load timer value
	clr temp
	out TCCR0,temp					;stop timer0
	subi adcresult,adcpreset+1		;rescale a/d output
	cbi PORTD,PD6					;start discharge
	set								;set conversion complete flag(t)
	pop temp
	reti
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
midiin:
	push temp
	in temp,UDR						;read byte
	sbrs temp,7						;skip if statusbyte
	rjmp runningstatus				;jump to runningstatus
	cpi temp,240
	brge return						;skip other midi cmd (clock etc.)
	mov laststatus,temp				;save new statusbyte
	ldi bytecounter,1
	rjmp return
	
runningstatus:
	cpi laststatus,176+(chin*16)	;check status byte
	brne return
midibyte1:
	cpi bytecounter,1
	brne midibyte2
	inc bytecounter
	mov lastbyte,temp
	rjmp return
midibyte2:
	cpi bytecounter,2
	brne return
	dec bytecounter
	cpi lastbyte,ctrlin0			;check if receiving maxindex ctrl
	brne midibyte2next1
	mov maxindex,temp
	rjmp return
midibyte2next1:
	cpi lastbyte,ctrlin1			;check if receiving index ctrl
	brne midibyte2next2
	mov index,temp
	rcall translateindex
	rcall toleds
	rjmp return
midibyte2next2:
	cpi lastbyte,ctrlin2			;check if directly to led 0-127
	brne midibyte2next3
	rcall toleds
	rjmp return
midibyte2next3:
	cpi lastbyte,ctrlin3			;check if directly to led 128-255
	brne return
	ori temp,0b10000000				;add 128 to incoming ctrl value
	rcall toleds
return:
	pop temp
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
reset:
	cli
	ldi temp,low(RAMEND)			;stack
	out spl,temp
	
	ldi temp,baudkonstant
	out UBRR,temp
	ldi temp,0b10011000				;set tx and rx output
	out UCR,temp

	ldi temp,0b00001011
	out ACSR,temp					;init & enable comparator interrupt
	ldi temp,0b00000010
	out TIMSK,temp					;enable timer interrupt
	ldi temp,0b01000000				;set portD pin 6 as charge/discharge output
	out DDRD,temp
	sbi PORTD,PD6					;pullup on charge/discharge pin
	
	ldi temp,0b01110100
	out DDRB,temp					;set portB bits 6,5,4,2 as output
	
	in startupbutton,pinD			;save startup button status
	mov currentbutton,startupbutton	;copy to current
	
	cbi portB,2						;bypass off
	
	rcall stupidflashintro
	
	ldi laststatus,176+(chin*16)
	ldi bytecounter,1
	ldi maxindex,3					;default maxindex
	clr index
	rcall translateindex
	rcall toleds
	
	sei
	
;---------------
main:
	in temp,pinD
	eor temp,currentbutton			;check if any button changed
	sbrc temp,2						;test bit2
	rcall button0
	in temp,pinD
	eor temp,currentbutton			;check if any button changed
	sbrc temp,3						;test bit3
	rcall button1
	in temp,pinD
	eor temp,currentbutton			;check if any button changed
	sbrc temp,4						;test bit4
	rcall button2
	in temp,pinD
	eor temp,currentbutton			;check if any button changed
	sbrc temp,5						;test bit5
	rcall button3
	
	rcall adccheck
	rjmp main
	
;---------------
button0:
	rcall increaseindex				;increase index
	rcall translateindex
	rcall toleds
	rcall sendctrlindex				;send control change index
	ldi note,base2+0				;send bang noteOn
	rcall sendnoteon
	in currentbutton,pinD			;check+send toggle noteOn or noteOff
	mov temp,currentbutton
	eor temp,startupbutton
	sbrs temp,2						;		!
	rjmp play0noteoff				;		!
play0noteon:
	ldi note,base+0					;		!
	rcall sendnoteon
	ret
play0noteoff:
	ldi note,base+0					;		!
	rcall sendnoteoff
	ret
	
button1:
	ldi note,base2+1				;send bang noteOn
	rcall sendnoteon
	in currentbutton,pinD			;check+send toggle noteOn or noteOff
	mov temp,currentbutton
	eor temp,startupbutton
	sbrs temp,3						;		!
	rjmp play1noteoff				;		!
play1noteon:
	ldi note,base+1					;		!
	rcall sendnoteon
	ret
play1noteoff:
	ldi note,base+1					;		!
	rcall sendnoteoff
	ret
	
button2:
	ldi note,base2+2				;send bang noteOn
	rcall sendnoteon
	in currentbutton,pinD			;check+send toggle noteOn or noteOff
	mov temp,currentbutton
	eor temp,startupbutton
	sbrs temp,4						;		!
	rjmp play2noteoff				;		!
play2noteon:
	ldi note,base+2					;		!
	rcall sendnoteon
	ret
play2noteoff:
	ldi note,base+2					;		!
	rcall sendnoteoff
	ret
	
button3:
	rcall flipbypass				;flip bypass
	ldi note,base2+3				;send bang noteOn
	rcall sendnoteon
	in currentbutton,pinD			;check+send toggle noteOn or noteOff
	mov temp,currentbutton
	eor temp,startupbutton
	sbrs temp,5						;		!
	rjmp play3noteoff				;		!
play3noteon:
	ldi note,base+3					;		!
	rcall sendnoteon
	ret
play3noteoff:
	ldi note,base+3					;		!
	rcall sendnoteoff
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
adccheck:
	clr adcresult					;clear temp counter1
	ldi temp,240					;reset temp counter2
adcloop:
	inc adcresult					;increase counter1
	brne adcloop
	inc temp						;increase counter2
	brne adcloop					;check if delay finished
	
	ldi adcresult,adcpreset
	out TCNT0,adcresult				;clear counter and load offset
	clt								;clear conversion complete flag(t)
	ldi adcresult,2
	out TCCR0,adcresult				;start timer0 with prescaling f/8
	sbi PORTD,PD6					;start charging of capacitor
	
adcwait:
	brtc adcwait					;wait until conversion finished
	cp adcresult,lastadcresult		;check if any change
	breq dontsendadc
	rcall sendctrladc				;send midi controller
	mov lastadcresult,adcresult		;store result
dontsendadc:
	ret
	
;---------------
sendctrladc:
	ldi temp,176+(chout*16)
sendctrladc1:
	sbis USR,UDRE
	rjmp sendctrladc1
	out UDR,temp
	ldi temp,ctrladcout
sendctrladc2:
	sbis USR,UDRE
	rjmp sendctrladc2
	out UDR,temp
sendctrladc3:
	sbis USR,UDRE
	rjmp sendctrladc3
	out UDR,adcresult
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
flipbypass:
	sbis pinB,2						;skip if bypass is on
	rjmp bypasson
	rjmp bypassoff
bypasson:
	rcall sendctrlbypasson			;send control change bypass
	sbi portB,2
	rcall toleds
	ret
bypassoff:
	rcall sendctrlbypassoff			;send control change bypass
	cbi portB,2
	rcall translateindex
	rcall toleds
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;PB4 = ser   = DS   = shift data
;PB6 = clock = SHCP = shift register clock
;PB5 = latch = STCP = output latch  store
	
toleds:
	sbic pinB,2						;skip if bypass is off
	ldi temp,1						;override with special bypass char
	nop
	clr counter
shiftloop:
	sbi portB,4
	rol temp
	brcc carrycleared
	cbi portB,4
carrycleared:
	cbi portB,6
	nop
	nop
	sbi portB,6	
	inc counter
	cpi counter,8
	brne shiftloop
	cbi portB,5
	nop
	nop
	sbi portB,5
	ret
	
;---------------
translateindex:
	ldi ZH,high(table*2)
	ldi ZL,low(table*2)
	mov temp,index
findaddress:
	dec temp
	cpi temp,$ff
	breq foundaddress
	adiw ZL,1
	rjmp findaddress
foundaddress:
	lpm
	mov temp,R0
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
sendnoteon:
	ldi temp,144+(chout*16)
sendnoteon1:
	sbis USR,UDRE
	rjmp sendnoteon1
	out UDR,temp
	mov temp,note
sendnoteon2:
	sbis USR,UDRE
	rjmp sendnoteon2
	out UDR,temp
	ldi temp,velocity
	;add temp,index					;(add index to velocity)
sendnoteon3:
	sbis USR,UDRE
	rjmp sendnoteon3
	out UDR,temp
	rcall longdelay
	ret
	
sendnoteoff:
	ldi temp,144+(chout*16)
sendnoteoff1:
	sbis USR,UDRE
	rjmp sendnoteoff1
	out UDR,temp
	mov temp,note
sendnoteoff2:
	sbis USR,UDRE
	rjmp sendnoteoff2
	out UDR,temp
	ldi temp,0
sendnoteoff3:
	sbis USR,UDRE
	rjmp sendnoteoff3
	out UDR,temp
	rcall longdelay
	ret
	
;---------------
increaseindex:
	cp index,maxindex				;check limit
	brlo increaseindex2				;branch if lower
	clr index						;wrap back
	ret
increaseindex2:
	inc index						;increase index
	ret
	
;---------------
sendctrlindex:
	ldi temp,176+(chout*16)
sendctrl1:
	sbis USR,UDRE
	rjmp sendctrl1
	out UDR,temp
	ldi temp,ctrlindexout
sendctrl2:
	sbis USR,UDRE
	rjmp sendctrl2
	out UDR,temp
sendctrl3:
	sbis USR,UDRE
	rjmp sendctrl3
	out UDR,index
	ret
	
;---------------
sendctrlbypasson:
	ldi temp,176+(chout*16)
sendctrl1bypasson:
	sbis USR,UDRE
	rjmp sendctrl1bypasson
	out UDR,temp
	ldi temp,ctrlbypassout
sendctrl2bypasson:
	sbis USR,UDRE
	rjmp sendctrl2bypasson
	out UDR,temp
	ldi temp, 1						;the ctrl value
sendctrl3bypasson:
	sbis USR,UDRE
	rjmp sendctrl3bypasson
	out UDR,temp
	ret
	
sendctrlbypassoff:
	ldi temp,176+(chout*16)
sendctrl1bypassoff:
	sbis USR,UDRE
	rjmp sendctrl1bypassoff
	out UDR,temp
	ldi temp,ctrlbypassout
sendctrl2bypassoff:
	sbis USR,UDRE
	rjmp sendctrl2bypassoff
	out UDR,temp
	ldi temp, 0						;the ctrl value
sendctrl3bypassoff:
	sbis USR,UDRE
	rjmp sendctrl3bypassoff
	out UDR,temp
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
stupidflashintro:
	ser index
stupidloop:
	mov temp,index
	rcall shortdelay
	rcall shortdelay
	rcall shortdelay
	rcall shortdelay
	rcall toleds
	dec index
	brne stupidloop
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
shortdelay:
	dec delay
	brne shortdelay
	ret
	
longdelay:
	dec delay
	brne longdelay
	dec delay2
	brne longdelay
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
table:
.db 254, 56, 221, 125, 59, 119, 247, 60, 255, 127, 175, 239, 198, 238, 199, 135
	
;254	;0.
; 56	;1.
;221	;2.
;125	;3.
; 59	;4.
;119	;5.
;247	;6.
; 60	;7.
;255	;8.
;127	;9.
;175	;A
;239	;B
;198	;C
;238	;D
;199	;E
;135	;F
	
;;;;;;;;;;;;; ctrlin1
;   =   0
;  .=  16
; l =  40
; 1.=  56
; 4.=  59
; 7.=  60
; 9.=  63
; S = 103
; Y = 107
; 5.= 119
; 3.= 125
; 9.= 127
;;;;;;;;;;;;; ctrlin2
; F =   7
; P =  15
; h =  35
; H =  43
; A =  47
; L =  66
; C =  70
; E =  71
; 2.=  93
; o =  97
; d = 105
; a = 109
; O = 110
; D = 110
; B = 111
; 6.= 119
; 0.= 126
; 8.= 127
	
