; program: fullwave-delay-lowpass.asm
; UID = 000055 - unique id to eliminate conflicts between variables
; mono data (left in only, delayed output on left, direct out on right)
; pot (MOD1) controls the delay time (0s - 1.5s)
; rotary encoder (MOD2) controls the cutoff frequency

; program overview
;
; data is read in from the codec, and negative values are inverted to
; positive values.  all values are then shifted down to mid-rail and
; multiplied by 2 to normalize the output.  these values are then written
; to memory, and read back out and accumulated.  this creates a simple
; moving average low pass filter.  the delay time (and corresponding cutoff
; frequency) is set with the rotary encoder (MOD2).  there are 7 different
; cutoff frequencies, with rotations to the right increasing the cutoff
; frequnecy.  changing the cutoff frequency restarts the program to blank
; the accumulation buffer.  the delay time is set with the pot (MOD1).  the
; adc is oversampled 256 times and deadbanded to reduce jitter.  turning the
; pot all the way to the right overlaps the delay and lowpass buffers,
; causing some distortions.

; register usage - may be redefined in other sections
;
; r0  adc accumulation fractional byte
; r1  adc accumulation lsb
; r2  adc accumulation msb
; r3  
; r4  left lsb out
; r5  left msb out
; r6  left lsb in
; r7  left msb in
; r8  right output lsb
; r9  right output msb
; r10 accumulation lsb
; r11 accumulation mlb
; r12 accumulation mhb
; r13 accumulation msb
; r14 rotary encoder counter
; r15 adc/switch sample counter
; r16 temporary swap register
; r17 temporary swap register
; r18 null register 
; r19 cutoff frequency
; r20 temporary register
; r21 
; r22 actual delay lsb
; r23 actual delay msb
; r24 write address lsb
; r25 write address msb
; r26 desired delay lsb
; r27 desired delay msb
; r28 read address lsb
; r29 read address msb
; r30 jump location for interrupt lsb
; r31 jump location for interrupt msb
; t   rotary encoder state bit

;program starts here first time and after buffer changes
ldi r30,$1c ; set jump location to program start
ldi r16,$08 ; set lowpass buffer size to mid range
ldi r19,$03 ; initialize cutoff frequency to midrange

restart_000055: ; restart location for clearing memory

clr r24 ; clear write register
clr r25
clr r18 ; setup r18 as null register for carry addition and ddr setting
ldi r17,$ff ; setup r17 for ddr setting

clear_000055: ; clear lowpass buffer
; required to ensure an accurate accumulation
out portd,r24 ; set address
sts porth,r25
out portg,r18 ; 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
inc r24 ; increment write register - only clears first 256 bytes
brne clear_000055 ; continue until end of buffer reached

cleardone_000055: ; reset registers

mov r24,r16 ; set buffer size for lowpass
clr r28 ; set read address
clr r29
clr r10 ; initialize accumulation registers
clr r11
clr r12
clr r13
reti ; finish with initialization and wait for next interrupt

; program starts here every time but first
; initiate data transfer to codec
sbi portb,portb0 ; toggle slave select pin
out spdr,r5 ; send out left channel msb
cbi portb,portb0

;increment sram addreses
adiw r25:r24,$01 ; increment write address
adiw r29:r28,$01 ; increment read address

wait1_000055: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait1_000055
in r7,spdr ; recieve in left channel msb
out spdr,r4 ; send out left channel lsb

wait2_000055: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait2_000055
in r6,spdr ; recieve in left channel lsb
out spdr,r9 ; send out right channel msb

;fullwave rectify left data
sbrs r7,$07 ; check if negative
rjmp normalize_000055
com r6 ; invert data if negative (using ones complement to avoid problem at $8000)
com r7

normalize_000055: ; normalize data since its all positive values now

lsl r6 ; multiply data by two
rol r7 ; data is unsigned integer at this point
ldi r16,$80 ; convert to signed integer
add r7,r16

wait3_000055: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait3_000055
in r17,spdr ; recieve in right channel msb
out spdr,r8 ; send out right channel lsb

;write rectified left channel data to sram
out portd,r24 ; set address
sts porth,r25
out portg,r18 ; 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

wait4_000055: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait4_000055
in r17,spdr ; recieve in left channel lsb

;get left channel data from sram
out portd,r28 ; set address
sts porth,r29
nop ; wait input latch time of 2 clock cycles
nop
in r4,pina ; get data
in r5,pinc ; get data

;accumulate samples for lowpass
add r10,r6 ; add in current sample
adc r11,r7
sbrc r7,$07 ; check if data is negative
ldi r18,$ff ; set high bits if it is
adc r12,r18 ; r18 is cleared above
adc r13,r18
clr r18 ; reset null register
sub r10,r4 ; remove last sample in buffer
sbc r11,r5
sbrc r5,$07 ; check if data is negative
ldi r18,$ff ; set high bits if it is
sbc r12,r18 ; r18 is cleared above
sbc r13,r18
clr r18 ; reset null register

mov r4,r10 ; divide by 256 and move to ouptput register
mov r5,r11
mov r17,r12
tst r19 ; check if no dividing necessary
breq store_000055 ; keep dividing till the right size
mov r16,r19 ; move cutoff to temporary register

divide_000055: ; divide accumulation for proper scaling

asr r17 ; divide accumulation
ror r5
ror r4
dec r16 ; check if done
brne divide_000055 ; keep dividing till the right size

store_000055: ; store lowpassed data to memory

movw r9:r8,r5:r4 ; move immediate data to right output
movw r17:r16,r25:r24 ; move write address to temporary register
subi r17,$01 ; move to delay buffer location
out portd,r16 ; set address
sts porth,r17
out portg,r18 ; pull ce low,we low,and set high bits of address
ldi r20,$ff
out ddra,r20 ; set porta as output for data write
out ddrc,r20 ; set portc as output for data write
out porta,r4 ; set data
out portc,r5
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

;fetch delayed data from memory
sub r16,r22 ; subtract delay time
sbc r17,r23
out portd,r16 ; set address
sts porth,r17
nop ; wait input latch time of 2 clock cycles
nop
in r4,pina ; get data
in r5,pinc ; put delayed data to left output

rotary_000055: ; check rotary encoder and adjust cutoff frequency
; although rotary encoder is externally debounced, it is done here again.
; pin1 is sampled on a transition from high to low on pin0.  if pin1 is
; high, a left turn occured, if pin1 is low, a right turn occured.
dec r14 ; check if time to sample rotary encoder
brne shift_000055 ; continue if not
ldi r17,$40 ; adjust sample frequency to catch all rising edges (1.5ms)
mov r14,r17
lds r17,pinj ; get switch data
sbrs r17,$00 ; check if pin0 is low
rjmp edge_000055 ; check if pin0 was low on previous sample
clt ;  clear state register if back high
rjmp shift_000055 ; finish off

edge_000055: ; check for falling edge

brts shift_000055 ; do nothing if edge was already detected
set ; set t register to indicate edge detected
sbrs r17,$01 ; check if pin1 is high
rjmp increment_000055 ; increase cutoff frequency if right rotation
cpi r19,$06 ; else check if at max value
brsh shift_000055 ; finish off if at max
inc r19 ; incrementing cutoff value decreases cutoff frequency
rjmp buffer_000055 ; reset accumulation buffer

increment_000055: ; increase cutoff frequency

cpi r19,$01 ; check if cutoff at min value
brlo shift_000055 ; finish off if at min
dec r19 ; decrementing cutoff value increases cutoff frequency

buffer_000055: ; adjust buffer size

movw r29:r28,r25:r24 ; move write address to read address
ldi r16,$01 ; initialize the offset register
tst r19 ; check if any shifting is required
breq bufferload_000055
mov r17,r19 ; move cutoff to temporary register

shift1_000055: ; shift in zeros to make correct buffer size

lsl r16 ; increment buffer size
dec r17
brne shift1_000055 ; keep shifting until done

bufferload_000055: ; load buffer size

rjmp restart_000055 ; clear accumulation buffer

shift_000055: ; check if delay time is correct

cp r26,r22 ; compare desired delay to actual delay
cpc r27,r23
breq adcsample_000055 ; do nothing if the same
brlo indexdown_000055
ldi r17,$02 ; increment delay register
add r22,r17
adc r23,r18 ; r18 is cleared above
rjmp adcsample_000055

indexdown_000055:

ldi r17,$01 ; decrement delay register
sub r22,r17
sbc r23,r18 ; r18 is cleared above

adcsample_000055: ; get delay settings

lds r17,adcsra ; get adc control register
sbrs r17,adif ; check if adc conversion is complete
rjmp done_000055 ; skip adc sampling
lds r16,adcl ; get low byte adc value
lds r17,adch ; get high byte adc value
add r0,r16 ; accumulate adc samples
adc r1,r17
adc r2,r18 ; r18 is cleared above
ldi r17,$f7
sts adcsra,r17 ; clear interrupt flag
dec r15 ; countdown adc sample clock
brne done_000055 ; get delay time if its been long enough
lsr r2 ; divide adc sample by 4 to make 16b value
ror r1
ror r0
lsr r2
ror r1
ror r0

deadband_000055: ; check if adc has changed enough to warrant update

movw r17:r16,r1:r0 ; move adc sample to temporary register
sub r16,r26 ; find difference between adc sample and desired delay time
sbc r17,r27
brsh check_000055 ; check for deadband if positive
neg r16 ; invert if negative
adc r17,r18 ; r18 is cleared above
neg r17

check_000055: ; check if difference is greater than deadband

cpi r16,$40 ; check if difference is less than 1 lsb
cpc r17,r18 ; r18 cleared above
brlo empty_000055 ; do nothing if less than 1 lsb
movw r27:r26,r1:r0 ; move adc sample to delay time if large enough change
andi r26,$fe ; make sure delay time is a multiple of 2

empty_000055: ; empty accumulation registers and finish off

clr r0 ; empty adc accumulation registers
clr r1
clr r2

switchsample_000055: ; check rotary switch

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
cp r16,r31 ; check if location has changed
breq done_000055 ; finish off if no change
clr r30 ; reset jump register to new location
mov r31,r16

done_000055: ; normalize data and move to read buffer

reti ; return to waiting
