;    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 3 of the License, or
;    (at your option) any later version.
;
;    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.
;
;    You should have received a copy of the GNU General Public License
;    along with this program.  If not, see <http://www.gnu.org/licenses/>.

.nolist
.include "tn13def.inc"
.list

.cseg
.org 0

;define memory adresses
.equ	RAMSTART= 0x60
.equ	cnt0	= 0x60
.equ	cnt1	= 0x62
.equ	cnt2	= 0x64
.equ	wvl0	= 0x66
.equ	wvl1	= 0x68
.equ	wvl2	= 0x6A
.equ	octave	= 0x6C
.equ	bass	= 0x78
.equ	chords	= 0x80
.equ	ptx		= 0x8C

;define registers
.def	tmp		= r0
.def	zero	= r1
.def	itmp	= r2
.def	rnd		= r3
.def	rnd2	= r4
.def	duty	= r5
.def	dtych0	= r6
.def	dtych1	= r7
.def	dtych2	= r8
.def	wbcnt	= r9
.def	chcnt	= r10
.def	impr	= r11
.def	ptxp	= r12
.def	ptxo	= r13

.def 	tmp_i	= r16
.def 	itmp_i	= r17
.def	snd		= r18
.def	icnt	= r19
.def	cnt		= r20

.def 	tmp16l	= r24
.def 	tmp16h	= r25
.def	wvlenl	= r26
.def	wvlenh	= r27
.def	wvlp	= r28
.def	cntp	= r30

;register map:
;w/o imidiate access:
; r0	= tmp		(temporary register)
; r1	= zero		(zero register)
; r2	= itmp		(temporary register for interrupt routines)
; r3	= rnd		(random)
; r4	= rnd2		(also needed for random)
; r5	= duty		(used do set loudness for sound function)
; r6	= dtych0	(loudness for channel 0)
; r7	= dtych1	(loudness for channel 1)
; r8	= dtych2	(loudness for channel 2)
; r9	= wbcnt		(walking bass counter)
; r10	= chcnt		(chord counter)
; r11	= impr		(contains next step for improvisation)
; r12	= ptxp		(pentatonic offset)
; r13	= ptxo		(current octave of the pentatonic)
; r14	= 
; r15	= 
;with imidiate access:
; r16	= tmp_i		(temporary register with imidiate access)
; r17	= itmp_i	(temporary register with imidiate access for interrupts)
; r18	= snd		(sound level)
; r19	= icnt		(interrupt counter)
; r20	= cnt		(counter)
; r21	= 
; r22	= 
; r23	=
; r24	= tmp16_l	(temporary 16 bit register pair; lower part)
; r25	= tmp16_h	(temporary 16 bit register pair; upper part)
; r26	= wvlenl	(stores lower part of wavelength)
; r27	= wvlenh	(stores upper part of wavelength)
; r28	= wvlp		(wavelengthpointer)
; r29	= reserved	(for wavelengthpointer)
; r30	= cntp		(pointer to the current counter)
; r31	= reserved	(for cntp)

;##############################################################################

;***************
;interrupt table
;***************
interrupt_table:
	rjmp 	init		;Reset
	reti				;INT0
	reti				;pcint
	rjmp 	tim0_ovf	;Timer0 overflow
	reti				;EEPROM ready
	reti				;analog comparison
	reti				;Timer0 compare A
	reti				;Timer0 compare B
	reti				;WDT
	reti				;ADC

;##############################################################################

;*************
;init function
;*************

;init stack and status register
init:
	;force zero to 0
	eor 	zero,	zero
	;init the status register
	out 	SREG,	zero
	;init the stack (only SPL needed, because the RAM has less then 256 bytes)
	ldi 	tmp_i,	RAMEND
	out		SPL, 	tmp_i

;initialize to be used ram to zero
clear_bss:
	;load first ram adress
	ldi		XL,		RAMSTART
	eor		XH,		XH

;loop through all adresses
clear_bss_loop:
	cpi		XL,		RAMSTART+12
	brge	clear_bss_end
	;store zero at all theses adresses
	st		X+,		zero
	rjmp	clear_bss_loop

;bss is initialized now
clear_bss_end:

;##############################################################################

;*****************
;ADC init function
;*****************
init_adc:
	;temporary set all pins to input
	mov		tmp,	zero
	out		DDRB,	tmp
	;init adc for random number generator
	;REFS0 = 0 		--> use Vcc as reference
	;ADLAR = 0 		--> don't audjust to the left
	;MUX[1:0] = 10 	--> use PB4 as input
	ldi		tmp_i,	(1<<MUX1)
	out		ADMUX,	tmp_i
	;ADEN = 1 		--> enable ADC
	;ADSC = 1		--> start conversion
	;ADATE = 0		--> don't use auto trigger
	;ADIF = 0		--> irrelevant
	;ADIE = 0		--> disable ADC interrupts
	;ADPS[2:0] = 110--> prescaler = 64
	ldi		tmp_i,	(1<<ADEN) | (1<<ADSC) | (1<<ADPS2) | (1<<ADPS1)
	out		ADCSRA,	tmp_i
	;loop, until adc is ready
wait_for_adc:
	in		tmp_i,	ADCSRA
	andi	tmp_i,	(1<<ADSC)
	brne	wait_for_adc
adc_done:
	in		tmp_i,	ADCL
	mov		rnd,	tmp_i

;##############################################################################

;****************************
;music data loading functions
;****************************

;>>>>>>>>>> load all notes <<<<<<<<<<

load_all:
	;RAMSTART+13 is still in X
	;store all notes of 4th and 5th octave into ram
	;load C4
	ldi		tmp_i,	72
	st		X+,		tmp_i
	;load C#4
	ldi		tmp_i,	68
	st		X+,		tmp_i
	;load D4
	ldi		tmp_i,	64
	st		X+,		tmp_i
	;load D#4
	ldi		tmp_i,	60
	st		X+,		tmp_i
	;load E4
	ldi		tmp_i,	57
	st		X+,		tmp_i
	;load F4
	ldi		tmp_i,	54
	st		X+,		tmp_i
	;load F#4
	ldi		tmp_i,	51
	st		X+,		tmp_i
	;load G4
	ldi		tmp_i,	48
	st		X+,		tmp_i
	;load G#4
	ldi		tmp_i,	45
	st		X+,		tmp_i
	;load A4
	ldi		tmp_i,	43
	st		X+,		tmp_i
	;load A#4
	ldi		tmp_i,	40
	st		X+,		tmp_i
	;load B4
	ldi		tmp_i,	38
	st		X+,		tmp_i
	;load C5
	ldi		tmp_i,	36
	st		X+,		tmp_i
	;load C#5
	ldi		tmp_i,	34
	st		X+,		tmp_i
	;load D5
	ldi		tmp_i,	32
	st		X+,		tmp_i
	;load D#5
	ldi		tmp_i,	30
	st		X+,		tmp_i
	;load E5
	ldi		tmp_i,	28
	st		X+,		tmp_i
	;load F5
	ldi		tmp_i,	27
	st		X+,		tmp_i
	;load F#5
	ldi		tmp_i,	25
	st		X+,		tmp_i
	;load G5
	ldi		tmp_i,	24
	st		X+,		tmp_i
	;load G#5
	ldi		tmp_i,	23
	st		X+,		tmp_i
	;load A5
	ldi		tmp_i,	21
	st		X+,		tmp_i
	;load A#5
	ldi		tmp_i,	20
	st		X+,		tmp_i

;>>>>>>>>>> extract needed notes for main octave <<<<<<<<<<

load_octave:
	;load adress of the first note into X and Y register
	ldi		XL,		octave
	eor		XH,		XH
	ldi		YL,		octave
	eor		YH,		YH
	;generate random number between 0 and 11
	;a is element of {0, 1}
	mov		tmp_i,	rnd
	andi	tmp_i,	0b00000001
	mov		tmp,	tmp_i
	;b is element of {0, 1, 2, 3}
	rcall	random
	mov		tmp_i,	rnd
	andi	tmp_i,	0b00000011
	add		tmp,	tmp_i
	;c is element of {0, 1, 2, 3, 4, 5, 6, 7}
	rcall	random
	mov		tmp_i,	rnd
	andi	tmp_i,	0b00000111
	add		tmp,	tmp_i
	;a + b + c  is element of {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}

	add		XL,		tmp
	;copy relevant part down
copy_octave_start:
	cpi		YL,		octave+12
	breq	copy_octave_end
	ld		tmp_i,	X+
	st		Y+,		tmp_i
	rjmp	copy_octave_start

copy_octave_end:

;>>>>>>>>>> load walking bass sequence <<<<<<<<<<

;load notes for walking bass
load_walking_bass:
	;Y still contains first free adress
	ldi		tmp_i,	0
	st		Y+,		tmp_i
	ldi		tmp_i,	4
	st		Y+,		tmp_i
	ldi		tmp_i,	7
	st		Y+,		tmp_i
	ldi		tmp_i,	9
	st		Y+,		tmp_i
	ldi		tmp_i,	10
	st		Y+,		tmp_i
	ldi		tmp_i,	9
	st		Y+,		tmp_i
	ldi		tmp_i,	7
	st		Y+,		tmp_i
	ldi		tmp_i,	4
	st		Y+,		tmp_i

;>>>>>>>>>> load chord sequence <<<<<<<<<<

;load chords for 12 bar blues
load_chords:
	;Y still contains first free adress
	ldi		tmp_i,	0
	st		Y+,		tmp_i
	ldi		tmp_i,	0
	st		Y+,		tmp_i
	ldi		tmp_i,	0
	st		Y+,		tmp_i
	ldi		tmp_i,	0
	st		Y+,		tmp_i
	ldi		tmp_i,	5
	st		Y+,		tmp_i
	ldi		tmp_i,	5
	st		Y+,		tmp_i
	ldi		tmp_i,	0
	st		Y+,		tmp_i
	ldi		tmp_i,	0
	st		Y+,		tmp_i
	ldi		tmp_i,	7
	st		Y+,		tmp_i
	ldi		tmp_i,	7
	st		Y+,		tmp_i
	ldi		tmp_i,	0
	st		Y+,		tmp_i
	ldi		tmp_i,	0
	st		Y+,		tmp_i

;>>>>>>>>>> load pentatonic <<<<<<<<<<

;load pentatonic
load_ptx:
	ldi		tmp_i,	0
	st		Y+,		tmp_i
	ldi		tmp_i,	3
	st		Y+,		tmp_i
	ldi		tmp_i,	5
	st		Y+,		tmp_i
	ldi		tmp_i,	7
	st		Y+,		tmp_i
	ldi		tmp_i,	10
	st		Y+,		tmp_i

;##############################################################################
	
;**************
;setup function
;**************
setup:
	;set PB0, PB1, PB2 and PB3 to output
	ldi 	tmp_i,	(1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3)
	out		DDRB,	tmp_i

	;(1<<COM0A1) --> set on compare match, clear on top
	;(1<<WGM01) | (1<<WGM00) --> TOP=0xFF
	;(1<<CS00) --> no prescaler (-->9,600,000/256(-->MAX)=37.5kHz)
	ldi		tmp_i,	(1<<COM0A1) | (1<<WGM01) | (1<<WGM00)
	out		TCCR0A,	tmp_i
	ldi		tmp_i,	(1<<CS00)
	out		TCCR0B,	tmp_i

	;reset the timer counter
	out 	TCNT0,	zero
	
	;enable timer interrupts
	ldi		tmp_i,	(1<<TOIE0)
	out		TIMSK0, tmp_i

	;reset both counters
	eor		icnt,	icnt
	eor		cnt,	cnt

	;enable global interrupts
	sei

;##############################################################################

;*************
;main function
;*************
main:
	;init chord, walking bass and pentatonic position pointers
	mov		wbcnt,	zero
	mov		chcnt,	zero
	mov		ptxp,	zero
	;set pentatonic octave to 4 (main octave)
	ldi		tmp_i,	4
	mov		ptxo,	tmp_i
	;set loudness of the impro to 85
	ldi		tmp_i,	85
	mov		dtych0,	tmp_i
	;make chords loudest
	ldi		tmp_i,	100
	mov		dtych1,	tmp_i
	;make bass a little bit quiter then the improvisation
	ldi		tmp_i,	70
	mov		dtych2,	tmp_i
	;set first improvisation step to hold the root note (2)
	ldi		tmp_i,	2
	mov		impr,	tmp_i

;enter the mailoop
mainloop:
	;loop through notes
	cpi		cnt,	63
	brge	binary_pos
	;handle tremolo if necessary
	mov		tmp_i,	cnt
	andi	tmp_i,	3
	brne	mainloop
	rjmp	write_chord

;>>>>>>>>>> binary position indicator <<<<<<<<<<

binary_pos:
	;get counter and shift it left by one (first bit in PORTB is used for PWM)
	mov		tmp,	wbcnt
	lsl		tmp
	;load PORTB and clear the to be set bits
	in		tmp_i,	PORTB
	andi	tmp_i,	0b11110001
	;insert binary position and write it back
	or		tmp_i,	tmp
	out		PORTB,	tmp_i

;>>>>>>>>>> walking bass <<<<<<<<<<

render_notes:
	;disable interrupt here to prevent bad things
	cli
	;load adress of note
	ldi		XL,		bass
	eor		XH,		XH
	;add current note offset
	add		XL,		wbcnt
	;increment the walking bass counter
	inc		wbcnt
	;load offset
	ld		tmp_i,	X
	;load base adress
	ldi		YL,		octave
	eor		YH,		YH
	;add offset
	add		YL,		tmp_i
	;load chord offset
	ldi		ZL,		chords
	eor		ZH,		ZH
	add		ZL,		chcnt
	ld		tmp_i,	Z
	;add chord offset
	add		YL,		tmp_i
	;move it to the next octave if necessary
	cpi		YL,		octave+12
	brlt	after_mod_twelfe
	subi	YL,		12
	;load value at adress
	ld		tmp_i,	Y
	;move it one octave up (multiply by 2 to achieve that)
	lsr		tmp_i
	rjmp	write_walking_bass
after_mod_twelfe:
	;load value at adress
	ld		tmp_i,	Y
write_walking_bass:
	;write it into the memory for the interrupt
	sts		wvl2,	tmp_i
	;reset the counter
	eor		cnt,	cnt
	;check X for adress overflow
	cpi		XL,		bass+7
	brne	walking_bass_end
	;reset the walking bass counter and increment the chord counter
	eor		wbcnt,	wbcnt
	inc		chcnt
	;reset chord counter if end is reached
	ldi		tmp_i,	12
	cp		chcnt,	tmp_i
	brne	walking_bass_end
	eor		chcnt,	chcnt

walking_bass_end:

;>>>>>>>>>> improvisation <<<<<<<<<<

impro:
	mov		tmp_i,	impr
;perform a break (1/8 chance)
do_nothing:
	cpi		tmp_i,	0
	brne	note_up
	sts		wvl0,	zero
	rjmp	create_random_impro
;go one step higher in pentatonic (3/8 chance)
note_up:
	cpi		tmp_i,	4
	brge	hold_note
	inc		ptxp
	;calculate mod 5
	ldi		tmp_i,	5
	cp		ptxp,	tmp_i
	brlt	write_impro
	sub		ptxp,	tmp_i
	;and move one octave up
	inc		ptxo
	mov		tmp_i,	ptxo
	;use seventh octave as upper limit
	cpi		tmp_i,	7
	brlt	write_impro
	;go down if barrier is reached
	dec		ptxo
	ldi		tmp_i,	4
	add		ptxp,	tmp_i
	rjmp	write_impro
;hold current note	(1/8 chance)
hold_note:
	cpi		tmp_i,	5
	brge	note_down
	rjmp	write_impro
;go one step down in pentatonic (3/8 chance)
note_down:
	dec		ptxp
	;calculate mod 5
	cp		ptxp,	zero
	brge	write_impro
	ldi		tmp_i,	5
	add		ptxp,	tmp_i
	;and go down one octave
	dec		ptxo
	;use first octave as lower limit
	mov		tmp_i,	ptxo
	cpi		tmp_i,	1
	brge	write_impro
	;go up if barrier is reached
	inc		ptxo
	ldi		tmp_i,	4
	sub		ptxp,	tmp_i

write_impro:
	;load base adress
	ldi		YL,		octave
	eor		YH,		YH
	;load pentatonic offset
	ldi		ZL,		ptx
	eor		ZH,		ZH
	;add current pentatonic offset
	add		ZL,		ptxp
	ld		tmp_i,	Z
	;add offset
	add		YL,		tmp_i
	;load data
	ld		tmp_i,	Y

	;check if octave is equal to, less then or greater than 4
	mov		tmp,	zero
	inc		tmp
	inc		tmp
	inc		tmp
	inc		tmp
;check if the note is at the correct octave
check_octave:
	cp		ptxo,	tmp
	breq	octave_ready
	brlt	octave_down
;go octave up, if it is greater than 4
octave_up:
	inc		tmp
	lsr		tmp_i
	rjmp	check_octave
;go octave down, if it is les than 4
octave_down:
	dec		tmp
	lsl		tmp_i
	rjmp	check_octave

octave_ready:
	;store wavelength for interrupt
	sts		wvl0,	tmp_i

create_random_impro:
	;create random number between 0 and 7 for impro
	rcall	random
	mov		tmp_i,	rnd
	;use the upper 3 random bits, because they seem to be "more random"
	andi	tmp_i,	0b11100000
	lsr		tmp_i
	lsr		tmp_i
	lsr		tmp_i
	lsr		tmp_i
	lsr		tmp_i
	mov		impr,	tmp_i

;>>>>>>>>>> chords <<<<<<<<<<

write_chord:
	;leave the second, fifth and eighth note blank for rythm
	ldi		tmp_i,	1
	mov		tmp,	wbcnt
	cp		tmp,	tmp_i
	breq	clear_chord
	ldi		tmp_i,	4
	cp		wbcnt,	tmp_i
	breq	clear_chord
	ldi		tmp_i,	7
	cp		wbcnt,	tmp_i
	breq	clear_chord
	rjmp	set_chord
clear_chord:
	sts		wvl1,	zero
	rjmp	mainloop_end
set_chord:
	;load base adress
	ldi		YL,		octave
	eor		YH,		YH
	;load chord offset
	ldi		ZL,		chords
	eor		ZH,		ZH
	;add current chord offset
	add		ZL,		chcnt
	ld		tmp_i,	Z
	;add chord offset
	add		YL,		tmp_i
	;check whether the dominant or the tonic schould be played
	mov		tmp_i,	cnt
	andi	tmp_i,	4
	brne	tonic
	;add dominant offset and calculate mod 12 if necessary
	adiw	YL,		5
	cpi		YL,		octave+12
	brlt	tonic
	subi	YL,		12
tonic:
	ld		tmp_i,	Y
	;write it into the memory for the interrupt
	sts		wvl1,	tmp_i

mainloop_end:
	sei
	rjmp	mainloop

;##############################################################################

;***************
;Timer0 overflow
;***************
tim0_ovf:
	;save status register (only SREG & X needed, because the rest is disjunct)
	in		itmp,	SREG
	push	itmp
	;also save X register
	push	XL
	push	XH

	;clear current sound level
	eor		snd, 	snd

	;load wavelengthpointer to Y register
	;(only one ldi needed, because of small RAM size)
	ldi		wvlp,	wvl0
	eor		YH,		YH

	;and cnt to Z register
	;(only one ldi needed, because of small RAM size)
	ldi		cntp,	cnt0
	eor		ZH,		ZH

	;first channel
	;load loudness
	mov		duty,	dtych0
	;load wavelength
	ld		wvlenl,	Y+
	ld		wvlenh,	Y+
	;generate sound
	rcall	sound
	;increment the counter pointer by 2 for the next cnt location
	adiw	cntp,	2

;>>>>>>>>>> render channels <<<<<<<<<<

	;second channel
	;load loudness
	mov		duty,	dtych1
	;load wavelength
	ld		wvlenl,	Y+
	ld		wvlenh,	Y+
	;generate sound
	rcall	sound
	;increment the counter pointer by 2 for the next cnt location
	adiw	cntp,	2

	;third channel
	;load loudness
	mov		duty,	dtych2
	;load wavelength
	ld		wvlenl,	Y+
	ld		wvlenh,	Y+
	;generate sound
	rcall	sound
	;increment the counter pointer by 2 for the next cnt location
	adiw	cntp,	2

	;set the duty cycle
	out		OCR0A,	snd

	;increment counter
	inc		icnt
	cpi		icnt,	255
	brne	interrupt_end

;trigger the mainloop counter and reset the interrupt counter
trigger_counter:
	eor		icnt,	icnt
	inc		cnt

;end of the interrupt routine
interrupt_end:
	;restore status register
	pop		XH
	pop		XL
	pop		itmp
	out		SREG, itmp
	reti

;##############################################################################

;***************
;sound generator
;***************
sound:
	;load counter
	ld		tmp16l,	Z+
	ld		tmp16h,	Z

	;increment counter
	inc		tmp16l

	;store modified counter value
	st		Z,		tmp16h
	st		-Z,		tmp16l

	;check if wave is at the end in first part
	cp		tmp16l,	wvlenl
	cpc		tmp16h,	wvlenh
	brlt	sound_end

;high part of rectangular wave
sound_high:
	;set duty cycle
	mov		itmp_i, duty
	add		snd, itmp_i

	;check if wave is at the end in second part
	ld		tmp16l,	Z
	ldd		tmp16h,	Z+1
	;multiply the wvlen times two for this (-->2*pi)
	lsl		wvlenl
	adc		wvlenh, zero
	cp		tmp16l,	wvlenl
	cpc		tmp16h,	wvlenh
	brlt	sound_end

	;reset the counter
	std		Z+1,	zero
	st		Z,		zero

;end of the sound routine
sound_end:
	ret

;##############################################################################

;*****************************
;linear congruential generator
;*****************************

;X(n+1) = (a*X(n) + c) mod m
;a = 5
;c = 1
;m = 256
;--> rnd = 5 * rnd + 1
random:
	;backup random
	mov		rnd2,	rnd
	;multiply by 4
	lsl		rnd
	lsl		rnd
	;add both (rnd *= 5)
	add		rnd,	rnd2
	;increment rnd
	inc		rnd
	ret
