; program: reverser-18b-gated-delay.asm
; UID = 000050 - unique id to eliminate conflicts between variables
; 18b address space (3s sample time)
; stereo data, in and out
; pot (MOD1) controlled delay

; 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 left channel input
; data is compared to a predetermined threshold level, and if it crosses
; this level, the reverser function is initiated after a delay.  the delay
; is set via the adc reading from the pot (MOD1).  this value is sampled
; 256 times and deadbanded to reduce jitter.  the codec passes data
; through while in forward mode, and continues to do so for the delay
; period after the input has crossed the threshold.  after this time, the
; input data is no longer written to memory, in order to increase the
; available buffer size.  the reverse data is played out back to the
; beginning, plus a predetermined backtrack amount to catch the rising
; edge of notes.  at this point, the forward data saving is resumed, and
; the reverse data is faded while the current data is faded in.  this
; crossfade size is determined via a constant at the beginning of the code.
; there is also a holdoff time after the reverser function has finished,
; before it can be retriggered, in order to reduce constant triggering and
; glitches from not having enough forward data stored.

; constant definitions
;
.equ backtrack_000050 = $0800 ; past memory used as starting point
; (must be even)
.equ holdoff_000050 = $0f00 ; holdoff before retriggering
; must be larger than backtrack (must be even)
.equ stepsize_000050 = $0080 ; 65536/(stepsize * 44.1) = crossfade time (ms)
.equ threshold_000050 = $3900 ; threshold for turn on of reverser ($7fff max)
.equ minbuff_000050 = $02 ; minimum buffer time in multiples of 5.8ms
.equ maxbuff_000050 = ($03ff00 - (2 * ($010000 / stepsize_000050)) - holdoff_000050)
; maximum buffer time to keep from overlapping
.equ mem_000050 = $0200 ; memory location for storage in internal sram

; register usage - may be redefined in other sections
;
; r0  multiply result lsb
; r1  multiply result msb
; 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 buffer bottom / crossfade lsb
; r13 buffer bottom / crossfade msb
; r14 adc accumulation fractional byte
; r15 switch/adc sample counter
; r16 temporary swap register
; r17 temporary swap register
; r18 null register
; r19 reverse mode indicator
; r20 temporary swap regiser
; r21 buffer bottom third byte
; r22 write address third byte
; r23 read address third 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   foward/reverse indicator

; program starts here first time
; initialze z pointer for correct jump
; this assumes a less than 256 word jump
ldi r30,$23 ; 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_000050: ; 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_000050 ; 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_000050 ; continue clearing

cleardone_000050: ; reset registers

ldi r24,$01 ; initialize write register
ldi r25,$00
ldi r22,$00 ; setup write address high byte
ldi r28,$01 ; set read address to minimum delay
ldi r29,$ff
ldi r23,$07 ; setup read address high byte
clr r19 ; initialize reverse mode register
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

; initiate data transfer to codec
; this is the point the z-pointer is incremented to
; program starts here all but first time
sbi portb,portb0 ; toggle slave select pin
out spdr,r3 ; send out left channel msb
cbi portb,portb0

;increment write addresses
brts wait1_000050 ; dont write data to memory if reversing
adiw r25:r24,$01 ; increment write address
adc r22,r18 ; increment write third byte
andi r22,$03 ; mask off unsed bits

wait1_000050: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait1_000050
in r7,spdr ; recieve in left channel msb
out spdr,r2 ; send out left channel lsb

wait2_000050: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait2_000050
in r6,spdr ; recieve in left channel lsb
out spdr,r5 ; send out right channel msb

;write left channel data to sram
brts wait3_000050 ; dont write data to memory if reversing
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_000050: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait3_000050
in r9,spdr ; recieve in right channel msb
out spdr,r4 ; send out right channel lsb

;increment write address
brts wait4_000050 ; dont write data to memory if reversing
adiw r25:r24,$01 ; increment write address
adc r22,r18 ; increment write third byte
andi r22,$03 ; mask off unsed bits

wait4_000050: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait4_000050
in r8,spdr ; recieve in left channel lsb

;write right channel data to sram
brts dataload_000050 ; dont write data to memory if reversing
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

;check if input signal is above threshold
movw r3:r2,r7:r6 ; backup input values as they get modified
movw r5:r4,r9:r8 ; move input data to output
sbrc r19,$04 ; check if holding off after last trigger
rjmp holdtime_000050 ; countdown holdoff timer if waiting
sbrc r19,$07 ; check if already in reverse mode
rjmp reverse_000050 ; go to reverse function - else check threshold
ldi r16,low(threshold_000050) ; get threshold value
ldi r17,high(threshold_000050)
tst r7 ; check if negative
brpl compare_000050 ;  compare if positive
com r6 ; invert if negative
com r7 ; ones complement used for simplicity

compare_000050: ; compare input value to threshold

cp r6,r16 ; compare to left channel input
cpc r7,r17
brsh start_000050 ; start reversing if above threshold
rjmp adcsample_000050 ; else finish off

holdtime_000050: ; decrement holdoff timer

ldi r17,$02 ; subtract 2 from holdoff timer
sub r12,r17 ; 2 is used to have holdoff and backup match scaling
sbc r13,r18 ; r18 is cleared above
brne finishoff_000050 ; finish off if it not done holding off
clr r19 ; set mode to forward and clear hold off bit

finishoff_000050: ; finish off if not done holding off

rjmp adcsample_000050 ; finish off - too long for a branch instruction

start_000050: ; intialize reversing

ldi r19,$80 ; indicate that reverser is active, waiting for delay
movw r17:r16,r25:r24 ; move write address to temporary register
mov r21,r22
subi r16,low(backtrack_000050) ; remove initial buffer size
sbci r17,high(backtrack_000050)
sbc r21,r18 ; r18 cleared above
andi r21,$03 ; mask off low bits
movw r13:r12,r17:r16 ; move buffer bottom to its register
rjmp adcsample_000050 ; finish off

reverse_000050: ; check for reverse function

sbrc r19,$06 ; check if delay time has been met
rjmp dataload_000050 ; get data from memory if delay time has been met
; else check if delay time is up yet
movw r17:r16,r13:r12 ; move bottom of buffer to temporary register
mov r20,r21
add r17,r26 ; add in buffer size
adc r20,r27
andi r20,$03 ; mask off unused bits
cp r24,r16 ; check if write address is at top of buffer
cpc r25,r17
cpc r22,r20
breq bufferset_000050 ; set the read address to the top of the buffer
rjmp adcsample_000050 ; else finish off

bufferset_000050: ; set the read address to the top of the buffer

movw r29:r28,r25:r24 ; else set read address equal to write address
mov r23,r22
ori r19,$40 ; set delay time bit in function register
set ; set the t register to indicate reversing
rjmp dataload1_000050 ; get data from sram

dataload_000050: ; get data from sram

sbrc r19,$05 ; check if crossfading
rjmp dataload1_000050 ; skip buffer check if crossfading
andi r23,$03 ; mask off unused bits
cp r28,r12 ; check if at bottom of buffer
cpc r29,r13
cpc r23,r21
brne dataload1_000050 ; continue if not at bottom
ori r19,$20 ; set crossfade bit
ldi r16,$ff ; set crossfade distance to max value
mov r12,r16 ; buffer bottom value no longer needed
mov r13,r16
clt ; clear the t register to start recording forward again

dataload1_000050: ; continue getting data from sram

sbiw r29:r28,$03 ; decrement read address
sbc r23,r18 ;  r18 is cleared above
andi r23,$03 ; mask off unused bits
ori r23,$04 ; set we/ bit in high byte register

;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 ; increment write third byte
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

;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
nop ; wait 2 cycle setup time
nop
in r4,pina ; get data
in r5,pinc ; get data

;check if crossfading
sbrs r19,$05 ; check if crossfading
rjmp adcsample_000050 ; finish off if not crossfading
ldi r16,low(stepsize_000050) ; get crossfade stepsize
ldi r17,high(stepsize_000050)
sub r12,r16 ; decrement crossfade distance counter
sbc r13,r17
breq crossfade_done_000050 ; stop crossfading if done
brcs crossfade_done_000050 ; stop crossfading if done
rjmp crossfade_000050

crossfade_done_000050: ; stop crossfading

movw r3:r2,r7:r6 ; move forward data to output
movw r5:r4,r9:r8
ldi r16,low(holdoff_000050) ; setup holdoff counter
ldi r17,high(holdoff_000050)
movw r13:r12,r17:r16 ; r13:r12 no longer needed for crossfading
ori r19,$10 ; set reverse mode to holdoff
rjmp adcsample_000050 ; finish off

crossfade_000050: ; crossfade between forward and reverse signals

sts mem_000050,r6 ; backup data to free a register
sts (mem_000050 + 1),r7

;multiply left reverse sample by crossfade distance
movw r17:r16,r3:r2 ; move left reverse data to multiply register
movw r21:r20,r13:r12 ; move crossfade distance to multiply register
mulsu r17,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
movw r3:r2,r1:r0 ; store high bytes result for later
mul	r16,r20	; (unsigned)Al * (unsigned)Bl ; multiply low bytes
movw r7:r6,r1:r0 ; store low byets for later
mulsu r17,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
sbc	r3,r18 ; r18 is cleared above - subtract sign bit
add	r7,r0 ; accumulate result
adc	r2,r1
adc	r3,r18 ; r18 is cleared above
mul r21,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
add	r7,r0 ; accumulate result
adc	r2,r1
adc	r3,r18 ; r18 is cleared above

;multiply and accumulate left forward channel by crossfade distance
lds r16,mem_000050 ; fetch left forward channel from memory
lds r17,(mem_000050 + 1)
com r20 ; invert crossfade distance for forward channel
com r21
mulsu r17,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
add r2,r0 ; accumulate result
adc r3,r1
mul	r16,r20	; (unsigned)Al * (unsigned)Bl ; multiply low bytes
add r6,r0 ; accumulate result
adc r7,r1
adc r2,r18 ; r18 cleared above
adc r3,r18
mulsu r17,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
sbc	r3,r18 ; r18 is cleared above - subtract sign bit
add	r7,r0 ; accumulate result
adc	r2,r1
adc	r3,r18 ; r18 is cleared above
mul r21,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
add	r7,r0 ; accumulate result
adc	r2,r1
adc	r3,r18 ; r18 is cleared above

;multiply right reverse sample by crossfade distance
movw r17:r16,r5:r4 ; move right reverse data to multiply register
movw r21:r20,r13:r12 ; move crossfade distance to multiply register
mulsu r17,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
movw r5:r4,r1:r0 ; store high bytes result for later
mul	r16,r20	; (unsigned)Al * (unsigned)Bl ; multiply low bytes
movw r7:r6,r1:r0 ; store low byets for later
mulsu r17,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
sbc	r5,r18 ; r18 is cleared above - subtract sign bit
add	r7,r0 ; accumulate result
adc	r4,r1
adc	r5,r18 ; r18 is cleared above
mul r21,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
add	r7,r0 ; accumulate result
adc	r4,r1
adc	r5,r18 ; r18 is cleared above

;multiply and accumulate right forward channel by crossfade distance
movw r17:r16,r9:r8 ; move left forward data to multiply register
com r20 ; invert crossfade distance for forward channel
com r21
mulsu r17,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
add r4,r0 ; accumulate result
adc r5,r1
mul	r16,r20	; (unsigned)Al * (unsigned)Bl ; multiply low bytes
add r6,r0 ; accumulate result
adc r7,r1
adc r4,r18 ; r18 cleared above
adc r5,r18
mulsu r17,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
sbc	r5,r18 ; r18 is cleared above - subtract sign bit
add	r7,r0 ; accumulate result
adc	r4,r1
adc	r5,r18 ; r18 is cleared above
mul r21,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
add	r7,r0 ; accumulate result
adc	r4,r1
adc	r5,r18 ; r18 is cleared above

adcsample_000050: ; get buffer size from adc

lds r17,adcsra ; get adc control register
sbrs r17,adif ; check if adc conversion is complete
rjmp done_000050 ; skip adc sampling
lds r16,adcl ; get low byte adc value
lds r17,adch ; get high byte adc value
add r14,r16 ; accumulate adc samples
adc r10,r17
adc r11,r18 ; r18 is cleared above
ldi r17,$f7
sts adcsra,r17 ; clear interrupt flag
dec r15 ; countdown adc sample clock
brne done_000050 ; dont get buffer size till its been long enough
ldi r17,minbuff_000050 ; fetch minimum buffer size
cp r10,r17 ; check if adc value is less than minimum buffer size
cpc r11,r18 ; r18 is cleared above
brsh high_000050 ; check if above max buffer size if not below min
mov r10,r17 ; set to minimum buffer size
rjmp deadband_000050 ; check if adc value changed enough

high_000050: ; check max value of adc

ldi r16,low(maxbuff_000050) ; fetch max buffer size
ldi r17,high(maxbuff_000050)
cp r10,r16 ; check if adc value larger than max buffer size
cpc r11,r17
brlo deadband_000050 ; check for value chnage if lower than max size
mov r10,r16 ; set buffer size to max value
mov r11,r17

deadband_000050: ; check if adc value changed enough to warrant update

movw r17:r16,r11:r10 ; move adc sample to temporary register
sub r16,r26 ; find difference between adc sample and desired delay time
sbc r17,r27
brsh check_000050 ; check for deadband if positive
neg r16 ; invert if negative
adc r17,r18 ; r18 is cleared above
neg r17

check_000050: ; check if difference is greater than deadband

cpi r16,$02 ; check if difference is less than 2 lsb
cpc r17,r18 ; r18 cleared above
brlo empty_000050 ; do nothing if less than $02
movw r27:r26,r11:r10 ; move adc sample to delay time if large enough change

empty_000050: ; empty accumulation registers and finish off

clr r10 ; empty accumulation registers
clr r11
clr r14

switchsample_000050: ; 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
cpse r16,r31 ; check if location has changed
clr r30 ; reset jump register to new value
mov r31,r16

done_000050:

reti ; return to waiting



