; ATtiny2313 project
.INCLUDE "tn2313def.inc"

; --------------------
; Register definitions
; --------------------

.DEF	SrSave	= R9

.DEF	Tone1H	= R10
.DEF	Tone1L	= R11
.DEF	Tone2H	= R12
.DEF	Tone2L	= R13

.DEF	ToneTH	= R14
.DEF	ToneTL	= R15

.DEF	Temp	= R17

.DEF	Temp1 	= R18
.DEF	Temp2	= R19
.DEF	Temp3	= R20
.DEF 	Temp4	= R21

.DEF	ADDRH	= R22
.DEF 	ADDRL	= R23

; -----------------
; MACRO definitions
; -----------------

; OUTI port, value :: send value to port
.MACRO OUTI
	ldi R16,@1 			
	out @0,R16
.ENDM

; MEMR register, (location) :: read memory to register
.MACRO MEMR
	lds @0, @1
.ENDM

; MEMW (location), register :: write register to memory
.MACRO MEMW
	sts @0, @1
.ENDM

; MEMWI (location), value :: write value to memory
.MACRO MEMWI
	ldi R16, @1
	sts @0, R16
.ENDM	

; ------------
; SRAM mapping
; ------------

.DSEG

; First melody location
melody1:
	.BYTE 2

; Second melody location
melody2:
	.BYTE 2

; Note duration
duration1:
	.BYTE 2

duration2:
	.BYTE 2

; ------------------
; Program code start
; ------------------

.CSEG
.ORG 0x0000

; -----------------
; Interrupt vectors
; -----------------

rjmp Init ; Reset Handler
reti ;RJMP INT0 ; External Interrupt0 Handler
reti ;RJMP INT1 ; External Interrupt1 Handler
reti ;RJMP TIM1_CAPT ; Timer1 Capture Handler
rjmp TIMER1_COMPA ; Timer1 CompareA Handler
reti ;RJMP TIM1_OVF ; Timer1 Overflow Handler
reti ;RJMP TIM0_OVF ; Timer0 Overflow Handler
reti ;RJMP USART0_RXC ; USART0 RX Complete Handler
reti ;RJMP USART0_DRE ; USART0,UDR Empty Handler
reti ;RJMP USART0_TXC ; USART0 TX Complete Handler
reti ;RJMP ANA_COMP ; Analog Comparator Handler
reti ;RJMP PCINT ; Pin Change Interrupt
rjmp TIMER1_COMPB ; Timer1 Compare B Handler
rjmp TIMER0_COMPA ; Timer0 Compare A Handler
reti ;RJMP TIMER0_COMPB ; Timer0 Compare B Handler
reti ;RJMP USI_START ; USI Start Handler
reti ;RJMP USI_OVERFLOW ; USI Overflow Handler
reti ;RJMP EE_READY ; EEPROM Ready Handler
reti ;RJMP WDT_OVERFLOW ; Watchdog Overflow Handler


; ------------------
; Main program start
; ------------------

Init:
	
	; Wait until key is pressed
	cbi DDRD, 4
	sbi PORTD,4

waitkey:
	
	sbic PIND, 4
	rjmp waitkey
	
	; Set outputs
	; (initially disable square wave output)
	outi DDRB, (1<<PB0) | (0<<PB3) | (0<<PB4)

	; Set stack
	outi SPL, low(RAMEND)
	
	; LED = ON
	sbi PORTB, 0
	
	; Initialize hardware features
	rcall Timer1_Init
	rcall TIMER_Init

	; Set interrupts
	outi TIMSK, (1<<OCIE1A) | (1<<OCIE1B) | (1<<OCIE0A)

	; Write initial melody addresses to memory
	
	; 1 channel
	memwi melody1, high(tblMelody1*2)
	memwi melody1+1, low(tblMelody1*2)
	
	; 2 channel
	memwi melody2, high(tblMelody2*2)
	memwi melody2+1, low(tblMelody2*2)

	; Clear compare registers
	outi OCR1AH, 0
	outi OCR1AL, 0

	outi OCR1BH, 0
	outi OCR1BL, 0

	; Set notes
	rcall setCh1
	rcall setCh2
	
	; Clear timer/counter0
	outi TCNT0, 0

	; Clear timer/counter1
	outi TCNT1H, 0
	outi TCNT1L, 0

	; Clear prescaler
	outi GTCCR, (1<<PSR10)

	; Enable interrupts - begin playing
	sei

back2:
	
	; If key2 pressed, reset
	sbi PORTD,5
	
	sbis PIND, 5
	rjmp endplay

	rjmp back2

; --------------------------
; Peripherals initialization
; --------------------------

; Initialize timer1
Timer1_Init:

	; Set toggle on compare
	outi TCCR1A, (1<<COM1A0) | (1<<COM1B0) | (0<<WGM10)

	; Enable timer with /8 prescaler
	outi TCCR1B, (2<<CS10)
	
	ret

; Initialize system timer
TIMER_Init:

	; Set mode of operation
	outi TCCR0A, (1<<WGM01) | (0<<WGM00)

	; Set overflow value
	outi OCR0A, 49

	; Set prescaler /64
	outi TCCR0B, 3

	ret

; ----------------------
; Music finished playing
; ----------------------

ENDPLAY:
	
	; Playing finished or interrupted
	cli
	
	; Clear ports
	clr R16
	out PORTB, R16

	; Halt playing on both channels
	cbi DDRB, 3
	cbi DDRB, 4
	
	; Clear interrupt flags if any
	outi TIFR, (1<<OCF1A) | (1<<OCF1B) | (1<<OCF0A)
 
	rjmp Init

; -------------------------------------
; Functions for tone and melody control
; -------------------------------------

; -------------
; Set channel 1
; -------------
setCh1:
	
	; Load note, duration
	memr ZH, melody1
	memr ZL, melody1+1
	
	; Get note byte
	lpm
	mov Temp1, R0

	; Get duration byte
	adiw zl, 1
	lpm
	mov Temp2, R0

	; Increment address and store it
	adiw ZL, 1
	memw melody1, ZH
	memw melody1+1, ZL
	
	; If end 
	cpi Temp1, 255
	breq endplay

	; If pause don't load tone
	cpi Temp1, 0
	breq isPause1

	dec Temp1
	
	ldi ADDRH, high(tblNotes*2)
	ldi ADDRL, low(tblNotes*2)
	rcall getVals

	; Get tone value
	mov Tone1H, Temp3
	mov Tone1L, Temp4

	in ToneTH, OCR1AH
	in ToneTL, OCR1AL
	
	add ToneTL, Tone1L
	adc ToneTH, Tone1H

	out OCR1AH, ToneTH
	out OCR1AL, ToneTL

	; Set output for channel 1
	sbi DDRB, 3

	rjmp noPause1
	
isPause1:
	
	; Disable output for channel 1
	cbi DDRB, 3

noPause1:

	; Load duration
	ldi ADDRH, high(tblDuration*2)
	ldi ADDRL, low(tblDuration*2)
	mov Temp1, Temp2
	rcall getVals

	memw duration1, Temp3
	memw duration1+1, Temp4

	; Return to caller
	ret

endplay1:
	rjmp endplay

; -------------
; Set channel 2
; -------------
setCh2:
	
	; Load note, duration
	memr ZH, melody2
	memr ZL, melody2+1
	
	; Get note byte
	lpm
	mov Temp1, R0
	
	; Get duration byte
	adiw zl, 1
	lpm
	mov Temp2, R0

	; Increment address and store it
	adiw ZL, 1
	memw melody2, ZH
	memw melody2+1, ZL
	
	; If end 
	cpi Temp1, 255
	breq endplay1

	; If pause don't load tone
	cpi Temp1, 0
	breq isPause2

	dec Temp1
	
	ldi ADDRH, high(tblNotes*2)
	ldi ADDRL, low(tblNotes*2)
	rcall getVals

	; Get tone value	
	mov Tone2H, Temp3
	mov Tone2L, Temp4
	
	in ToneTH, OCR1BH
	in ToneTL, OCR1BL
	
	add ToneTL, Tone2L
	adc ToneTH, Tone2H

	out OCR1BH, ToneTH
	out OCR1BL, ToneTL

	; Set output for channel 2
	sbi DDRB, 4

	rjmp noPause2
	
isPause2:
	
	; Disable output for channel 2
	cbi DDRB, 4

noPause2:

	; Load duration
	ldi ADDRH, high(tblDuration*2)
	ldi ADDRL, low(tblDuration*2)
	mov Temp1, Temp2
	rcall getVals

	memw duration2, Temp3
	memw duration2+1, Temp4

	; Return to caller
	ret

; -------------------------------
; Load values from program memory
; -------------------------------

getVals:
	
	mov ZH, ADDRH
	mov ZL, ADDRL

	lsl Temp1

	clr R16
	add ZL, Temp1
	adc ZH, R16

	lpm
	mov Temp4, R0

	adiw ZL, 1
	lpm
	mov Temp3, R0

	ret

; ----------------------
; Tone control channel 1
; ----------------------

TIMER1_COMPA:
	
	; Disable interrupts
	cli
	
	; Save SREG
	in SrSave, SREG

	; Set next 1/2 * period
	in ToneTH, OCR1AH
	in ToneTL, OCR1AL
	add ToneTL, Tone1L
	adc ToneTH, Tone1H
	out OCR1AH, ToneTH
	out OCR1AL, ToneTL
	
	; Restore SREG
	out SREG, SrSave
	
	; Enable interrupts
	sei

	reti

; ----------------------
; Tone control channel 2
; ----------------------

TIMER1_COMPB:
	
	cli

	in SrSave, SREG

	in ToneTH, OCR1BH
	in ToneTL, OCR1BL
	add ToneTL, Tone2L
	adc ToneTH, Tone2H
	out OCR1BH, ToneTH
	out OCR1BL, ToneTL

	out SREG, SrSave

	sei

	reti

; ------------------
; Main event handler
; ------------------

TIMER0_COMPA:

	; Check duration for channel 1
	memr Temp3, duration1
	memr Temp4, duration1+1
	
	clr R16
	subi Temp4, 1
	sbc Temp3, R16
	brcc saveDur1
	
	; Load next note channel 1
	cli
	rcall setCh1
	sei

	rjmp nextDur

saveDur1:
	
	memw duration1, Temp3
	memw duration1+1, Temp4

nextDur:

	memr Temp3, duration2
	memr Temp4, duration2+1

	clr R16
	subi Temp4, 1
	sbc Temp3, R16
	brcc saveDur2

	; Load next note channel 2
	cli
	rcall setCh2
	sei

	rjmp next1

saveDur2:

	memw duration2, Temp3
	memw duration2+1, Temp4

next1:
	
	reti 

; -----------
; Data tables
; -----------

; Note table :: tone = 1/(8e-7*N)
tblNotes:
	.dw 11364	; A2
	.dw 10124	; B2
	.dw 9556	; C3
	.dw 9019	; C#3
	.dw 8513	; D3
	.dw 7584	; E3
	.dw 7159	; F3
	.dw 6757	; F#3
	.dw 6020	; G#3
	.dw 5682	; A3
	.dw 5363	; A#3
	.dw 5062	; B3
	.dw 4510	; C#4
	.dw 4257	; D4
	.dw 4018	; D#4
	.dw 3792	; E4
	.dw 3579	; F4
	.dw 3378	; F#4
	.dw 3010	; G#4
	.dw 2841	; A4
	.dw	2681	; A#4
	.dw 2531	; B4
	.dw 2389	; C5
	.dw 2255	; C#5
	.dw 2128	; D5
	.dw 1896	; E5
	.dw 1689	; F#5

; Define note symbolic names

; These mark the position of note
; definitions in tblNotes

; Special values: PAUSE = 0, END = 255

	.equ 	PAUSE	=	0
	.equ 	A2		=	1
	.equ 	B2		=	2
	.equ 	C3		=	3
	.equ 	Cs3		=	4
	.equ 	D3		=	5
	.equ 	E3		=	6
	.equ 	F3		=	7
	.equ 	Fs3		=	8
	.equ 	Gs3		=	9
	.equ 	A3		=	10
	.equ 	As3		=	11
	.equ 	B3		=	12
	.equ 	Cs4		=	13
	.equ 	D4		=	14
	.equ 	Ds4		=	15
	.equ 	E4		=	16
	.equ 	F4		=	17
	.equ 	Fs4		=	18
	.equ 	Gs4		=	19
	.equ 	A4		=	20
	.equ 	As4		=	21
	.equ 	B4		=	22
	.equ 	C5		=	23
	.equ 	Cs5		=	24
	.equ 	D5		=	25
	.equ 	E5		=	26
	.equ 	Fs5		=	27
	.equ	END		=	255

; Duration table :: bpm=60
; Tim0 /64, 50ticks -> 1.6e-4 sec per CMP event
tblDuration:
	.dw 25000	; full
	.dw 18750	; 1/2 dot
	.dw 12500	; 1/2
	.dw 6250	; 1/4
	.dw 3125	; 1/8

; Define duration symbolic names

	.equ d1		= 0
	.equ d12dot	= 1
	.equ d12	= 2
	.equ d14	= 3
	.equ d18	= 4

; Melodies are built using
; symbolic note and duration names

; First melody channel
tblMelody1:
	.db Fs4, d18	; Part 1
	.db Cs5, d18
	.db Gs4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db A4, d18
	.db A4, d18
	.db A3, d18
	.db Fs4, d18
	.db Cs5, d18
	.db Gs4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db A4, d18
	.db B4, d18
	.db A4, d18
	.db Fs4, d18
	.db A3, d18
	.db D4, d18
	.db A3, d18
	.db B3, d18
	.db Gs4, d18
	.db E4, d18
	.db D4, d18
	.db E4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db Gs3, d18
	.db As3, d18
	.db Fs4, d18
	.db D4, d18
	.db Cs4, d18
	.db D4, d18
	.db E4, d18
	.db Fs4, d14
	.db PAUSE, d18
	.db Gs4, d18
	.db A4, d18
	.db B4, d18
	.db A4, d18
	.db Cs4, d18
	.db Fs4, d18
	.db A4, d18
	.db Gs4, d14
	.db Fs4, d18 ; Part 2
	.db Cs5, d18
	.db Gs4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db A4, d18
	.db A4, d18
	.db A3, d18
	.db Fs4, d18
	.db Cs5, d18
	.db Gs4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db A4, d18
	.db B4, d18
	.db A4, d18
	.db Fs4, d18
	.db A3, d18
	.db D4, d18
	.db A3, d18
	.db B3, d18
	.db Gs4, d18
	.db E4, d18
	.db D4, d18
	.db E4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db Gs3, d18
	.db As3, d18
	.db Fs4, d18
	.db D4, d18
	.db Cs4, d18
	.db D4, d18
	.db B4, d18
	.db A4, d18
	.db Gs4, d18
	.db Fs4, d18
	.db F4, d18
	.db Ds4, d18
	.db F4, d18
	.db Fs4, d18
	.db As4, d18	; Part 3
	.db B4, d18
	.db Cs5, d18
	.db D5, d18
	.db A4, d18
	.db Gs4, d18
	.db D5, d18
	.db Cs5, d18
	.db E5, d18
	.db A4, d18
	.db Cs5, d18
	.db C5, d18
	.db A4, d18
	.db Gs4, d18
	.db Fs4, d18
	.db A4, d18
	.db Cs4, d18
	.db Fs4, d18
	.db B4, d18
	.db Gs4, d12dot
	.db Fs4, d18	; Part 4
	.db Cs5, d18
	.db Gs4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db A4, d18
	.db A4, d18
	.db A3, d18
	.db Fs4, d18
	.db Cs5, d18
	.db Gs4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db A4, d18
	.db B4, d18
	.db A4, d18
	.db Fs4, d18
	.db A3, d18
	.db D4, d18
	.db A3, d18
	.db B3, d18
	.db Gs4, d18
	.db E4, d18
	.db D4, d18
	.db E4, d18
	.db Gs3, d18
	.db Cs4, d18
	.db Gs3, d18
	.db As3, d18
	.db Fs4, d18
	.db D4, d18
	.db Cs4, d18
	.db D4, d18
	.db B4, d18
	.db A4, d18
	.db Gs4, d18
	.db Fs4, d18
	.db F4, d18
	.db Ds4, d18
	.db F4, d18
	.db Fs4, d18
	.db Cs4, d18
	.db Fs4, d18
	.db B4, d18
	.db Fs5, d12

	.db END, 0

; Second melody channel
tblMelody2:
	.db PAUSE, d14
	.db F3, d12
	.db Fs3, d14
	.db A2, d18
	.db PAUSE, d18
	.db F3, d12
	.db Fs3, d14
	.db D3, d1
	.db Cs3, d12
	.db Fs3, d12
	.db B2, d12
	.db C3, d12
	.db Cs3, d12
	.db Gs3, d12
	.db F3, d12
	.db Fs3, d14
	.db A2, d18
	.db PAUSE, d18
	.db F3, d12
	.db Fs3, d14
	.db D3, d1
	.db Cs3, d12
	.db Fs3, d12
	.db B2, d14
	.db C3, d14
	.db Cs3, d14
	.db F3, d14
	.db Fs3, d12
	.db B3, d14
	.db E3, d14
	.db A3, d14
	.db Fs3, d14
	.db A3, d14
	.db C3, d14
	.db Cs3, d12
	.db Cs4, d12dot
	.db PAUSE, d14
	.db F3, d12
	.db Fs3, d14
	.db A2, d18
	.db PAUSE, d18
	.db F3, d12
	.db Fs3, d14
	.db D3, d1
	.db Cs3, d12
	.db Fs3, d12
	.db B2, d14
	.db C3, d14
	.db Cs4, d14
	.db F3, d14
	.db Fs3, d12
	.db A4, d12

	.db END, 0

