; program: up_downsweep-18b-pot.asm
; UID = 000035 - unique id to eliminate conflicts between variables
; 18b address space (3s sample time)
; stereo data
; pot (MOD1) controlled buffer size (3ms - 1.5s)

; program overview
;
; data is read in from memory and written out to the codec at the same time
; new data is written to the memory from the codec.  the write address
; increments until it reaches the top of the buffer and then starts at
; zero.  the read address increases at twice the rate until it reaches the
; write address, and then decreases until it reaches the write address.
; it continues this back and forth sample playback.  ADC0 (MOD1) is read
; and averaged over 256 samples to reduce jitter.  this value is subtracted
; from the write address to create the desired read address.  if the actual
; read address doesnt match the desired read address, it is either
; incremented or decremented by one sample each sample period until it
; matches.  this reduces noise during delay time transitions.

; register usage - may be redefined in other sections
;
; r0  desired buffer size fractional byte
; r1  
; r2  left lsb out
; r3  left msb out
; r4  right lsb out
; r5  right msb out
; r6  left lsb in
; r7  left msb in
; r8  right lsb in
; r9  right msb in
; r10 adc accumulation lsb
; r11 adc accumulation msb
; r12 read address offset lsb
; r13 read address offset msb
; r14 adc/switch sample counter
; r15 
; r16 temporary swap register
; r17 temporary swap register
; r18 null register
; r19 adc accumulation fractional byte
; r20 temporary swap regiser
; r21 read address offset high byte
; r22 write address high byte
; r23 read address high byte
; r24 write address lsb
; r25 write address msb
; r26 desired buffer size lsb
; r27 desired buffer size msb
; r28 read address lsb
; r29 read address msb
; r30 jump location for interrupt lsb
; r31 jump location for interrupt msb
; t   forward/reverse indicator

; program starts here first time
; initialze z pointer for correct jump
; this assumes a less than 256 word jump
ldi r30,$29 ; set jump location to program start
clr r24 ; clear write register
clr r25
ldi r22,$00 ; setup write address high byte
clr r18 ; setup r18 as null register for carry addition and ddr setting
ldi r17,$ff ; setup r17 for ddr setting

clear_000035: ; clear delay buffer
; eliminates static when first switching to the delay setting

adiw r25:r24,$01 ; increment write register
adc r22,r18 ; increment write third byte
cpi r22,$04 ; check if full memory space has been cleared
breq cleardone_000035 ; continue until end of buffer reached
out portd,r24 ; set address
sts porth,r25
out portg,r22 ; pull ce low,we low,and set high bits of address
out ddra,r17 ; set porta as output for data write
out ddrc,r17 ; set portc as output for data write
out porta,r18 ; set data
out portc,r18 ; r18 is cleared above
sbi portg,portg2 ; pull we high to write
out ddra,r18 ; set porta as input for data lines
out ddrc,r18 ; set portc as input for data lines
rjmp clear_000035 ; continue clearing

cleardone_000035: ; reset registers

ldi r24,$00 ; initialize write register
ldi r25,$00
ldi r22,$00 ; setup write address high byte
ldi r28,$00 ; set read address to minimum delay
ldi r29,$fe
ldi r23,$07 ; setup read address high byte
clr r0 ; set buffer size to minimum
ldi r26,$01
clr r27
clr r12 ; set read address offset to minimum buffer size
ldi r16,$01
mov r13,r16
clr r21
clr r2 ; initialize data output registers
clr r3
clr r4
clr r5
clt ; clear t register to start with forward play
reti ; finish with initialization and wait for next interrupt

; program starts here all but first time
; this is the point the z-pointer is incremented to
; initiate data transfer to codec
sbi portb,portb0 ; toggle slave select pin
out spdr,r3 ; send out left channel msb
cbi portb,portb0

wait1_000035: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait1_000035
in r7,spdr ; recieve in left channel msb
out spdr,r2 ; send out left channel lsb

;get left channel data from sram
out portg,r23 ; pull ce low, we high, and set high bits of register
out portd,r28 ; set address
sts porth,r29
adiw r29:r28,$01 ; increment read address
adc r23,r18 ; placed here for efficient use of setup time
andi r23,$03 ; mask off unsed bits
ori r23,$04 ; set we/ bit in high byte register
in r2,pina ; get data
in r3,pinc ; get data

wait2_000035: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait2_000035
in r6,spdr ; recieve in left channel lsb
out spdr,r5 ; send out right channel msb

;write left channel data to sram
out portd,r24 ; set address
sts porth,r25
out portg,r22 ; pull ce low,we low,and set high bits of address
ldi r17,$ff
out ddra,r17 ; set porta as output for data write
out ddrc,r17 ; set portc as output for data write
out porta,r6 ; set data
out portc,r7
sbi portg,portg2 ; pull we high to write
out ddra,r18 ; set porta as input for data lines
out ddrc,r18 ; set portc as input for data lines

wait3_000035: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait3_000035
in r9,spdr ; recieve in right channel msb
out spdr,r4 ; send out right channel lsb

;get right channel data from sram
out portg,r23 ; pull ce low, we high, and set high bits of register
out portd,r28 ; set address
sts porth,r29
adiw r25:r24,$01 ; increment write address
adc r22,r18 ; placed here to use setup time efficiently
andi r22,$03 ; mask off unsed bits
in r4,pina ; get data
in r5,pinc ; get data

wait4_000035: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait4_000035
in r8,spdr ; recieve in left channel lsb

;write right channel data to sram
out portd,r24 ; set address
sts porth,r25
out portg,r22 ; pull ce low,we low,and set high bits of address
ldi r17,$ff
out ddra,r17 ; set porta as output for data write
out ddrc,r17 ; set portc as output for data write
out porta,r8 ; set data
out portc,r9
sbi portg,portg2 ; pull we high to write
out ddra,r18 ; set porta as input for data lines
out ddrc,r18 ; set portc as input for data lines

;increment write address to next sample position
adiw r25:r24,$01 ; increment write address
adc r22,r18 ; increment write third byte
andi r22,$03 ; mask off unsed bits
movw r29:r28,r25:r24 ; move write address to read address
mov r23,r22

;calculate read offset
brtc increment_000035 ; move read address forward if in foward mode
ldi r16,$04 ; else move read address backwards by 2
add r12,r16 ; this requires incrementing the read offset by 4
adc r13,r18 ; r18 is cleared above
adc r21,r18
cp r12,r0 ; check if at bottom of buffer
cpc r13,r26
cpc r21,r27
brlo readadd_000035 ; load read address if not at bottom of buffer
clt ; else clear t register to indicate forward motion after next sample
; decrement offset by 2 - this halts playback for one sample
; to reduce spikes at transitions

increment_000035: ; move read address forward by 4
; moving read address forward requires reducing the read offset by 2
ldi r16,$02 ; subtract 2 from read offset
sub r12,r16
sbc r13,r18 ; r18 is cleared above
sbc r21,r18
brne readadd_000035 ; load read address if not at top of buffer
ldi r16,$04 ; set to playback last played sample
mov r12,r16 ; reduces spikes at transitions
set ; else set t register to start reversing on next sample

readadd_000035: ; add in read offset

sub r28,r12 ; subtract offset from read address
sbc r29,r13
sbc r23,r21
andi r23,$03 ; mask off unused bits
ori r23,$04 ; set we/ bit for reading

adcsample_000035: ; get buffer size from adc

lds r17,adcsra ; get adc control register
sbrs r17,adif ; check if adc conversion is complete
rjmp done_000035 ; skip adc sampling if not
lds r16,adcl ; get low byte adc value
lds r17,adch ; get high byte adc value
add r19,r16 ; accumulate adc samples
adc r10,r17
adc r11,r18 ; r18 is cleared above
ldi r17,$f7
sts adcsra,r17 ; clear interrupt flag
dec r14 ; countdown adc sample clock
brne done_000035 ; dont get buffer size till its been long enough
lsr r11 ; divide adc sample by 2
ror r10 ; as the max buffer size is only $020000
ror r19 ; as the buffer is read backwards half of the time
ldi r17,$01 ; check if adc value is less than $000100
cp r10,r17
cpc r11,r18 ; r18 cleared above
brsh deadband_000035 ; check if adc value changed enough to update delay
mov r10,r17 ; set minimum delay to $000100 = 3ms
clr r19

deadband_000035: ; check for change in adc value

movw r17:r16,r11:r10 ; move adc sample to temporary register
mov r20,r19
sub r20,r0 ; find difference between adc sample and desired buffer size
sbc r16,r26
sbc r17,r27
brsh check_000035 ; check for deadband if positive
com r20 ; invert if negative
com r16 ; using ones complement as it is faster, and only has 1 bit error
com r17

check_000035: ; check if difference is greater than deadband

cpi r20,$80 ; check if difference is less than 1 lsb
cpc r16,r18 ; r18 cleared above
cpc r17,r18
brlo empty_000035 ; do nothing if less than 1 lsb
movw r27:r26,r11:r10 ; move adc sample to delay time if large enough change
andi r19,$fc ; make sure buffer size is a multiple of 4
mov r0,r19

empty_000035: ; empty accumulation registers and finish off

clr r10 ; empty accumulation registers
clr r11
clr r19

switchsample_000035: ; check if at same function

lds r16,pinj ; get switch data
andi r16,$78 ; mask off rotary switch
lsr r16 ; adjust switch position to program memory location
lsr r16
ldi r17,$02
add r16,r17 ; move to jump register
cpse r16,r31 ; check if location has changed
clr r30 ; reset jump register to intial state
mov r31,r16

done_000035:

reti ; return to waiting

