; program: reverser-16b-pot-fading.asm
; UID = 000036 - this is a unique id so variables dont conflict
; 16b address space (1.5s sample time)
; mono data in on left channel, mono data out on left and right
; pot (MOD1) controlled buffer size

; program overview
;
; data is sent out and taken in from the codec.  data is taken in on the
; left channel, and played out on both left and right.  a buffer of the
; past n seconds is kept and the output is the result of sampling this
; buffer in reverse.  this buffer size is determined by the pot (MOD1) value,
; which is multiplied up to a 16b value.  the buffer size is adjusted at a
; rate of 2 lsb per sample period, until it matches to what the pot says
; it should be. two samples are taken and averaged together by the
; ratio of their distances to the buffer boundary.  they are 180 degrees
; out of phase, so as one sample is crossing the buffer boundary, it is
; silent, and the other plays full volume.  this helps reduce the clicks
; at buffer boundary transitions.  it also creates a reverb sound.

; constant definitions
;
.equ buffer_min_000036 = $0200 ; minimum sample buffer size

; register usage - may be redefined in other sections
;
; r0  multiply result lsb
; r1  multiply result msb
; r2  sample 2 lsb
; r3  sample 2 msb
; r4  left/right lsb out / sample 1 lsb
; r5  left/right msb out / sample 1 msb
; r6  left lsb in
; r7  left msb in
; r8  
; r9  adc msb accumulator
; r10 adc fractional byte accumulator
; r11 adc lsb accumulator
; r12 desired buffer size lsb
; r13 desired buffer size msb
; r14 
; r15 switch/adc counter
; r16 temporary swap register
; r17 temporary swap register
; r18 multiplicand lsb
; r19 multiplicand msb
; r20 multiplicand lsb
; r21 multiplicand msb
; r22 null register
; r23 
; r24 write address lsb
; r25 write address msb
; r26 buffer length lsb
; r27 buffer length msb
; r28 read address lsb
; r29 read address msb
; r30 jump location for interrupt lsb
; r31 jump location for interrupt msb
; t

; program starts here first time
; initialze z pointer for correct jump
; this assumes a less than 256 word jump
ldi r30,$21 ; 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_000036: ; 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,$01 ; check if 16b memory space has been cleared
breq cleardone_000036 ; 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_000036 ; continue clearing

cleardone_000036: ; 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,$fd
clr r4 ; initialize data output registers
clr r5
ldi r26,$00 ; initialize buffer size
ldi r27,$06
movw r13:r12,r27:r26
reti ; finish with initialization and wait for next interrupt

; program begins here
; initiate data transfer to codec
sbi portb,portb0 ; toggle slave select pin
out spdr,r5 ; send out left channel msb
cbi portb,portb0

;increment write address
adiw r25:r24,$01 ; increment write address
cp r24,r26 ; check if at end of buffer
cpc r25,r27
brlo wait1_000036 ; do nothing if not at end of buffer
clr r24 ; reset buffer to bottom
clr r25

wait1_000036: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait1_000036
in r7,spdr ; recieve in left channel msb
out spdr,r4 ; send out left channel lsb

;decrement read address (for going in reverse)
sbiw r29:r28,$01
brcc wait2_000036 ; do nothing if not at end of buffer
movw r29:r28,r27:r26 ; reset to top of buffer
sbiw r29:r28,$01 ; reset to top of buffer

wait2_000036: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait2_000036
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,r22 ; set porta as input for data lines
out ddrc,r22 ; set portc as input for data lines

wait3_000036: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait3_000036
in r17,spdr ; recieve in right channel msb
out spdr,r4 ; send out right channel lsb

;get sample 1 from sram
out portd,r28 ; set address
sts porth,r29
nop ; wait required 2 cycle setup time
nop
in r4,pina ; get data
in r5,pinc ; get data

wait4_000036: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait4_000036
in r17,spdr ; recieve in right channel lsb

;get sample 2 from other side of buffer
movw r17:r16,r29:r28 ; move current position to temporary register
movw r7:r6,r27:r26 ; move buffer size to temporary register
lsr r7 ; divide buffer size by 2
ror r6
cp r16,r6 ; check if in lower or upper half of buffer
cpc r17,r7
brsh buffer_flip_000036 ; subtract half buffer if in upper half
add r16,r6 ; add half buffer size if in lower half
adc r17,r7
rjmp getsample2_000036 ; continue

buffer_flip_000036: ; adjust to opposite side of memory

sub r16,r6 ; subtract half buffer size if in upper half
sbc r17,r7

getsample2_000036: ;get left channel sample 3 data from sram

out portd,r16 ; set address
sts porth,r17
nop ; wait 2 cycle setup time
nop
in r2,pina ; get data
in r3,pinc ; get data

;get distance to boundary
movw r17:r16,r29:r28 ; move read address to temporary register
sub r16,r24 ; find distance to loop boundary
sbc r17,r25
brcc half_000036 ; check if result is negative
neg r16 ; invert distance if negative
adc r17,r22 ; r22 is cleared above
neg r17

half_000036: ; check if result is greater than half the buffer size

movw r7:r6,r27:r26 ; move buffer size to temporary register
lsr r7 ; divide buffer size by 2
ror r6
cp r16,r6 ; check if result is greater than half the buffer size
cpc r17,r7
brlo scale_000036 ; skip flip if not
sub r16,r26 ; flip result around boundary
sbc r17,r27
neg r16 ; invert distance
adc r17,r22 ; r22 is cleared above
neg r17

scale_000036: ; scale distance to match buffer size - 50% accurate

movw r7:r6,r27:r26 ; move buffer size to temporary register
sbrc r7,$07 ; check if msb of buffer size is set
rjmp attenuate_000036 ; attenuate signal if 16b value

shift_000036: ; shift buffer size till it occupies full 16b

lsl r6 ; multiply buffer size by 2
rol r7
lsl r16 ; multiply distance by 2
rol r17
sbrs r7,$07 ; check if msb of buffer size is set
rjmp shift_000036 ; keep checking if not set

attenuate_000036: ; multiply sample 1 by distance

lsl r16 ; multiply distance by 2 since max value is 1/2 buffer size
rol r17
sub r6,r16 ; find complementary distance of sample 2
sbc r7,r17
movw r21:r20,r7:r6 ; move distance to signed multiply register
movw r19:r18,r5:r4 ; move value to signed multiply register
mulsu r19,r17 ; (signed)ah * bh
movw r5:r4,r1:r0
mul	r18,r16	; al * bl
movw r7:r6,r1:r0
mulsu r19,r16 ; (signed)ah * bl
sbc	r5,r22 ; r22 is cleared above
add	r7,r0
adc	r4,r1
adc	r5,r22
mul r17,r18 ; bh * al
add	r7,r0
adc	r4,r1
adc	r5,r22

;multiply and accumulate opposing sample with result from above
movw r19:r18,r3:r2 ; move value to signed multiply register
mulsu r19,r21 ; (signed)ah * bh
add	r4,r0
adc	r5,r1
mul	r18,r20 ; al * bl
add	r6,r0
adc	r7,r1
adc	r4,r22
adc	r5,r22
mulsu r19,r20 ; (signed)ah * bl
sbc	r5,r22
add	r7,r0
adc	r4,r1
adc	r5,r22
mul r21,r18 ; bh * al
add	r7,r0
adc	r4,r1
adc	r5,r22

;check if buffer size is correct
cp r26,r12 ; compare current delay to desired delay
cpc r27,r13
brlo upcount_000036 ; increment if smaller than
breq adcsample_000036 ; do nothing if they are same size
sbiw r27:r26,$02 ; decrement buffer size
rjmp adcsample_000036 ; finish off

upcount_000036: ; increment buffer size register

adiw r27:r26,$02 ; increment buffer size

adcsample_000036: ; get loop setting

lds r17,adcsra ; get adc control register
sbrs r17,adif ; check if adc conversion is complete
rjmp done_000036 ; skip adc sampling
lds r16,adcl ; get low byte adc value
lds r17,adch ; get high byte adc value
add r10,r16
adc r11,r17 ; accumulate adc samples
adc r9,r22 ; accumulate adc samples - r22 is cleared above
ldi r17,$f7
sts adcsra,r17 ; clear interrupt flag
dec r15 ; countdown adc sample clock
brne done_000036 ; move adc value to loop setting after 256 samples
lsr r9 ; divide accumulated value by 4
ror r11
ror r10
lsr r9
ror r11
ror r10
ldi r16,low(buffer_min_000036) ; load minimum buffer size
ldi r17,high(buffer_min_000036)
cp r10,r16 ; check if less than minimum
cpc r11,r17
brsh compare_000036 ; compare to previous value if above min
movw r11:r10,r17:r16 ; set buffer size to minimum

compare_000036: ; compare to previous value

movw r17:r16,r13:r12 ; make a copy of current loop time for comparison
sub r16,r10 ; find difference between current loop time and last loop time
sbc r17,r11
brcc deadband_000036 ; see if difference is large enough to indicate a change
neg r16 ; invert difference if negative
adc r17,r22 ; r22 is cleared above
neg r17

deadband_000036: ; see if pot has moved or if its just noise

cpi r16,$40 ; see if difference is greater than 1 lsb
cpc r17,r22 ; r22 is cleared above
brlo nochange_000036 ; dont update loop time if difference is not large enough
ldi r16,$fe ; make sure buffer size is even
and r10,r16
movw r13:r12,r11:r10 ; move adc value to loop time register

nochange_000036: ; clear accumulation registers

clr r10 ; empty accumulation registers
clr r11
clr r9

;check rotary switch state
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
cpse r16,r31 ; check if location has changed
clr r30 ; reset jump register to intial state
mov r31,r16

done_000036:

reti ; return to waiting

