; A MIDI drum machine & sampler
; needs 8Mhz clock
; 8515 chip
; 32K SRAM
; UART set to 19200
; Jeremy Selan
; loads 4 samples. 
; comm works perfectly
; plays 4 voices at a time, 1 voice per sample
; no dynamic allocation

;Polled -- no interrrupts
;Strings can come from flash or RAM.
;input a string, then output it again

.include "8515def.inc"

.def	char	=r0	;character to send/receive
.def	zero	=r0
.def	sam0start_l=r1	; START OF SAMPLE 0
.def	sam0start_h=r2
.def	sam1start_l=r3	; START OF SAMPLE 1 & END OF SAMPLE 0
.def	sam1start_h=r4
.def	sam2start_l=r5  ; START OF SAMPLE 2 & END OF SAMPLE 1
.def	sam2start_h=r6
.def	sam3start_l=r7	; START OF SAMPLE 3 & END OF SAMPLE 2
.def	sam3start_h=r8
.def	sam3end_l=r9	; END OF SAMPLE 3
.def	sam3end_h=r10


.def	v0addr_l=r11
.def	v0addr_h=r12
.def	v1addr_l=r13
.def	v1addr_h=r14
.def	v2addr_l=r15
.def	v2addr_h=r16
.def	v3addr_l=r17
.def	v3addr_h=r18


.def	audio_l=r19
.def	audio_h=r20

.def	parm 	=r21	;2nd temp register
.def	temp	=r22	;temporary register
.def	TXflash	=r23	;text to be sent is in flash if <>0
.def	flag=r24
.def	robin=r25
.def	enable=r26


;.equ	baud96	=25	;9600 baud constant for 4Mhz crystal

 .equ	baud96	=25	;19200 baud constant for 8Mhz crystal

.equ	azero	='0'	;0x30 ascii '0'
.equ	ramstart = 0x260

;**************************************
.dseg

;define variable string to be stored/transmitted in RAM
StrBuffer:	.byte	32	;a string buffer

;**************************************
;print and read string macros

.macro PrintFlashStr	;use: PrintFlashStr flashaddress
	ldi	TXflash, 1
	ldi	ZL, low(@0<<1)
	ldi	ZH, high(@0<<1)
	rcall	putString
.endmacro

.macro PrintRamStr	;use: PrintRamStr ramaddress
	ldi	TXflash, 0
	ldi	ZL, low(ramstart)
	ldi	ZH, high(ramstart)
	rcall	putString
.endmacro

.macro ReadStr		;use: ReadStr ramaddress
	ldi	ZL, low(ramstart)
	ldi	ZH, high(ramstart)
	rcall	getSample
.endmacro

;**************************************
.cseg

.org $0000
	rjmp 	RESET	;reset entry vector
	reti		
	reti
	reti
	reti
	reti
	reti
	rjmp TIM0_OVF	; Timer 0 overflow
	reti		
	reti
	reti
	reti
	reti

;define fixed strings to be tranmitted from flash
;zero terminated
prompt0:.db	"Enter sample 0> ",0x00 
prompt1:.db	"Enter sample 1> ",0x00 
prompt2:.db	"Enter sample 2> ",0x00
prompt3:.db	"Enter sample 3> ",0x00
prompt10:.db	"Live Mode Enabled..",0x00
prompt11:.db	"Error in Sample Conversion ",0x00
prompt12:.db	"Sample Received.",0x00

;carrage return/line feed
crlf:	.db	0x0d, 0x0a, 0x00


RESET:	ldi	temp, LOW(RAMEND) ;setup stack pointer
	out 	SPL, temp
	ldi	temp, HIGH(RAMEND)
	out	SPH, temp

	;setup UART -- enable RX, TX pins
	ldi 	temp, 0b00011000
	out 	UCR, temp

	;set baud rate to 9600 or 19200baud192
	ldi	temp, baud96
	out	UBRR, temp

	; setup external SRAM
	ldi 	temp, 0b10000000
	out 	MCUCR, temp

;now print a prompt and wait for incoming string with 

	;initialize pointer to 1st ram location

	ldi	YL, low(ramstart)
	ldi	YH, high(ramstart)

	; get sample 0
	mov	sam0start_l,YL
	mov	sam0start_h,YH

	PrintFlashStr crlf
	PrintFlashStr prompt0
	PrintFlashStr crlf

	mov	YL,sam0start_l
	mov	YH,sam0start_h
	rcall	getSample
	mov	sam1start_l,YL
	mov	sam1start_h,YH

	PrintFlashStr crlf
	PrintFlashStr prompt12
	PrintFlashStr crlf

	; get sample 1

	PrintFlashStr crlf
	PrintFlashStr prompt1
	PrintFlashStr crlf
	mov	YL,sam1start_l
	mov	YH,sam1start_h
	rcall	getSample
	mov	sam2start_l,YL
	mov	sam2start_h,YH
	PrintFlashStr crlf
	PrintFlashStr prompt12
	PrintFlashStr crlf

	; get sample 2

	PrintFlashStr crlf
	PrintFlashStr prompt2
	PrintFlashStr crlf
	mov	YL,sam2start_l
	mov	YH,sam2start_h
	rcall	getSample
	mov	sam3start_l,YL
	mov	sam3start_h,YH
	PrintFlashStr crlf
	PrintFlashStr prompt12
	PrintFlashStr crlf

	; get sample 3

	PrintFlashStr crlf
	PrintFlashStr prompt3
	PrintFlashStr crlf
	mov	YL,sam3start_l
	mov	YH,sam3start_h
	rcall	getSample
	mov	sam3end_l,YL
	mov	sam3end_h,YH
	PrintFlashStr crlf
	PrintFlashStr prompt12
	PrintFlashStr crlf


mainloop_init:

	PrintFlashStr crlf
	PrintFlashStr prompt10
	PrintFlashStr crlf

	;   **** set up the PORTs ****
	; Port B
	ser	temp		; Set PortC all Outputs
	out	DDRB,temp
	ldi	temp,128	; Clear the audio out register
	out	PortB,temp

	; set up timer 1:
	; it will count up to OCR1A, then reset and set a flag
	; we will need to set it at maximum 
	; (clock) speed for good resolution

	ldi	temp, 0b00000010; set clear on compare A match
	out 	TIMSK, temp	; 
	ldi	temp, 0b00000010 
	out 	TCCR0, temp	; and set prescale to 8
	ldi	temp,0b11000000 ; set count
	out	TCNT0,temp


	;setup UART -- disable RX, TX pins
	ldi 	temp, 0b00000000
	out 	UCR, temp

	;setup port d for com link inputs
	in	temp, DDRD
	andi	temp, 0b11110000
	out	DDRD,temp

	; setup r0=zero

	clr	temp
	mov	zero,temp
	sei	;turn on interrupts

	clr	enable		; set voice to not playing

mainloop:

	; check to see if a button is pressed, which
	; correleates to resetting vxaddr to new correct value

chk0:	in	parm,PIND
	mov	temp,parm
	andi	temp,0b00000001
	breq	chk1
	; set voice 0 to start
	mov	v0addr_l,sam0start_l
	mov	v0addr_h,sam0start_h
	ori 	enable,0b00000001

chk1:
	mov	temp,parm
	andi	temp,0b00000010
	breq	chk2
	; set voice 1 to start
	mov	v1addr_l,sam1start_l
	mov	v1addr_h,sam1start_h
	ori	enable,0b00000010

chk2:
	mov	temp,parm
	andi	temp,0b00000100
	breq	chk3
	; set voice 2 to start
	mov	v2addr_l,sam2start_l
	mov	v2addr_h,sam2start_h
	ori	enable,0b00000100

chk3:
	mov	temp,parm
	andi	temp,0b00001000
	breq	waitloop
	; set voice 3 to start
	mov	v3addr_l,sam3start_l
	mov	v3addr_h,sam3start_h
	ori	enable,0b00001000


waitloop:
	tst	flag		; has timer 1 reset?
	breq	waitloop


	;clear audio data
	clr	audio_h
	clr	audio_l

;********************************
; check if voice 0 is playing
v0tst:	mov	temp,enable
	andi	temp,0b00000001
	breq	v0off
	mov	YL,v0addr_l
	mov	YH,v0addr_h
	ld	temp,Y+

	; check for end of sample
	cp	YH,sam1start_h
	brne	cont0
	cp	YL,sam1start_l
	brne	cont0
	andi	enable,0b11111110

cont0:	mov	v0addr_l,YL
	mov	v0addr_h,YH
	add	audio_l,temp
	adc	audio_h,zero
	rjmp	v1tst

v0off:  ldi	temp,127
	add	audio_l,temp
	adc	audio_h,zero

;********************************
; check if voice 1 is playing
v1tst:	mov	temp,enable
	andi	temp,0b00000010
	breq	v1off
	mov	YL,v1addr_l
	mov	YH,v1addr_h
	ld	temp,Y+

	; check for end of sample
	cp	YH,sam2start_h
	brne	cont1
	cp	YL,sam2start_l
	brne	cont1
	andi	enable,0b11111101

cont1:	
	mov	v1addr_l,YL
	mov	v1addr_h,YH
	add	audio_l,temp
	adc	audio_h,zero
	rjmp	v2tst

v1off:  ldi	temp,127
	add	audio_l,temp
	adc	audio_h,zero


;********************************
; check if voice 2 is playing
v2tst:	mov	temp,enable
	andi	temp,0b00000100
	breq	v2off
	mov	YL,v2addr_l
	mov	YH,v2addr_h
	ld	temp,Y+

	; check for end of sample
	cp	YH,sam3start_h
	brne	cont2
	cp	YL,sam3start_l
	brne	cont2
	andi	enable,0b11111011
	
cont2:	
	mov	v2addr_l,YL
	mov	v2addr_h,YH
	add	audio_l,temp
	adc	audio_h,zero
	rjmp	v3tst

v2off:  ldi	temp,127
	add	audio_l,temp
	adc	audio_h,zero


;********************************
; check if voice 3 is playing
v3tst:	mov	temp,enable
	andi	temp,0b00001000
	breq	v3off
	mov	YL,v3addr_l
	mov	YH,v3addr_h

	; check for end of sample
	cp	YH,sam3end_h
	brne	cont3
	cp	YL,sam3end_l
	brne	cont3
	andi	enable,0b11110111

cont3:	ld	temp,Y+
	mov	v3addr_l,YL
	mov	v3addr_h,YH
	add	audio_l,temp
	adc	audio_h,zero
	rjmp	play

v3off:  ldi	temp,127
	add	audio_l,temp
	adc	audio_h,zero


play:	ror	audio_h
	ror	audio_l

	;mov	parm,audio_l
	;mov	temp,audio_h

	ror	audio_h
	ror	audio_l

	;add	audio_l,parm
	;adc	audio_h,temp

	;ror	audio_h
	;ror	audio_l

	out 	PortB,audio_l
	clr	flag
	rjmp 	mainloop


TIM0_OVF:
	ser	flag	; does not modify the status register
	ldi	temp,0b11000000
	out	TCNT0,temp
	reti

;*********************************************
;comm soubroutines
;*********************************************
;routine to output one char
;enter with char in char
putc:	;wait until clear then send one char
	sbis	USR, UDRE	
	rjmp	putC
	out 	UDR, char
	ret

;routine to input one char
;exit with char in char
getc: 	;wait until ready then get one char
	sbis	USR, RXC
	rjmp	getc
	in	char, UDR	
	ret

;*****************************************************
;routine to read a sample

getSample:

	rcall	getc		;get the highorder reg # nibble

	ldi	temp, 'X'	;is digit exit?
	cp	char, temp
	breq	_RXend

	ldi	temp, 0x30	;is digit not valid? (CR,LF,...)
	cp	char, temp	; if so, ignore
	brlt	getSample

	;rcall	putc		;echo it

	mov	parm,char
	rcall	charTohex	;convert to hex

	swap	parm		;make it the highorder bits
	mov	temp, parm	;and save it

	rcall	getc		;get the loworder reg # nibble
	;rcall	putc		;echo it
	mov	parm,char
	rcall	charTohex

	or	parm, temp	;combine high and low nibbles
	st	Y+, parm	;store the char
	rjmp	getSample

_RXend: 

	ret

;*****************************************************

;routine to send a string
putString:
	tst	TXflash		;is it flash?
	breq	_txram
	lpm	;char		;if so get a char
	rjmp	_tstend
_txram:	ld	char, Z		;if mem get a char
_tstend:tst	char		;is it string end marker?
	breq	_TXend
	rcall	putc		;emit a char
	adiw	ZL, 1		;set up next pointer
	rjmp	putString
_TXend:	ret

; ****************************************
;utility routine to make a ascii char into a hex value
;enter with char in RXparm

charTohex:
	cpi	parm, 'A'
	brge	_aboveA
	cpi	parm, '0'	;error check-- is digit <'0'?
	brge	_n1
	rjmp	error
_n1:	cpi	parm, ':'	;error check -- is digit <':'
	brlt	_n2
	rjmp	error
	;if here then digit is 0 to 9
_n2:	subi	parm, '0'
	ret
_aboveA: 
	cpi	parm, 'G'	;error check -- is digit>'f'
	brlt	_ab1
	rjmp	error
_ab1:	subi	parm, 'A'-10
	ret

error: 
	PrintFlashStr crlf
	PrintFlashStr prompt11
	PrintFlashStr crlf
	ret