; program: tremolo-stereo.asm
; UID = 000057 - unique id to eliminate conflicts between variables
; memory is not used
; stereo in and out
; pot (MOD1) controls the lfo frequency
; pushbutton (on MOD2) controls phase between left and right output

; program overview
;
; data is read in from the codec, and multiplied by an lfo that is internally
; generated using a 512s/16b sinewave lookup table.  the frequency of this
; lfo is determined by the adc input, which is oversampled 256 times and
; compared to the previous value with a deadband.  this helps reduce jitter.
; the left and right channels are mutiplied by opposite signals, so the
; sound bounces back and forth between left and right.  depressing the
; pushbutton (on MOD2) changes this so that the left and right are in phase.

; register usage - may be redefined in other sections
;
; r0  multiply result lsb
; r1  multiply result msb
; r2  accumulation lsb
; r3  accumulation mlb
; r4  left lsb out/accumulation mhb
; r5  left msb out/accumulation msb
; r6  right in/out lsb
; r7  right in/out msb
; r8  adc accumulator fractional byte
; r9  adc accumulator lsb
; r10 adc accumulator msb
; r11 lfo control signal fractional byte
; r12 lfo control signal lsb
; r13 lfo control signal msb
; r14 null register
; r15 switch/adc sample counter
; r16 temporary swap register
; r17 temporary swap register
; r18 sine wave buffer/multiply lsb
; r19 sine wave buffer/multiply msb
; r20 multiply swap register
; r21 multiply swap register
; r22 sinetable lookup address lsb
; r23 sinetable lookup address mlb
; r24 left data in lsb
; r25 left data in msb
; r26 sinetable lookup address mhb
; r27 sinetable lookup address msb
; r28 phase indicator
; r29 
; r30 jump location for interrupt lsb
; r31 jump location for interrupt msb
; t   pushbutton state indicator

;program starts here first time
; intialize registers
ldi r30,$07 ; increment z pointer to new jump location
clr r14 ; clear null register
ldi r16,$10 ; initialize lfo rate
ldi r17,$00
movw r13:r12,r17:r16 ;  move to lfo rate register
clr r28 ; initialize phase mode register
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

wait1_000057: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait1_000057
in r25,spdr ; recieve in left channel msb
out spdr,r4 ; send out left channel lsb

wait2_000057: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait2_000057
in r24,spdr ; recieve in left channel lsb
out spdr,r7 ; send out right channel msb

wait3_000057: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait3_000057
in r7,spdr ; recieve in right channel msb
out spdr,r6 ; send out right channel lsb

wait4_000057: ; check if byte has been sent

in r17,spsr
sbrs r17,spif
rjmp wait4_000057
in r6,spdr ; recieve in right channel lsb

;vco generation
movw r17:r16,r31:r30 ; store z register
;get sample 1
add r22,r11 ; increment sinetable address
adc r23,r12
adc r26,r13
adc r27,r14 ; r14 is cleared above
movw r31:r30,r27:r26 ; move to z register for data fetch
lsl r30 ; adjust pointer for 16b fetch
rol r31
andi r31,$03 ; limit value to 10b (512 samples x 2 bytes)
ori r31,$48 ; set to memory address location where table is stored
lpm r18,z+ ; get sine value lsb, increment z register
lpm r19,z ; get sine value msb
sbrc r27,$01 ; flip sign for half of the values
rjmp interpolate_000057 ; interpolate if even
neg r18 ; invert if odd
adc r19,r14 ; r14 is cleared above
neg r19

interpolate_000057: ; multiply sample 1 by distance

movw r21:r20,r23:r22 ; get distance from sample 1
com r20 ; invert distance
com r21
mulsu r19,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
movw r5:r4,r1:r0 ; store high bytes result for later
mul	r18,r20	; (unsigned)Al * (unsigned)Bl ; multiply low bytes
movw r3:r2,r1:r0 ; store low byets for later
mulsu r19,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
sbc	r5,r14 ; r14 is cleared above - subtract sign bit
add	r3,r0 ; accumulate result
adc	r4,r1
adc	r5,r14 ; r14 is cleared above
mul r21,r18 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
add	r3,r0 ; accumulate result
adc	r4,r1
adc	r5,r14 ; r14 is cleared above

;get sample 2
adiw r27:r26,$01 ; set to next sample
movw r31:r30,r27:r26 ; move to z register for data fetch
lsl r30 ; adjust pointer for 16b fetch
rol r31
andi r31,$03 ; limit value to 10b (512 samples x 2 bytes)
ori r31,$48 ; set to memory address location where table is stored
lpm r18,z+ ; get sine value lsb, increment z register
lpm r19,z ; get sine value msb
sbrc r27,$01 ; flip sign for half of the values
rjmp interpolate1_000057
neg r18
adc r19,r14 ; r14 is cleared above
neg r19

interpolate1_000057: ; multiply sample 2 by distance and accumulate

sbiw r27:r26,$01 ; reset address
movw r31:r30,r17:r16 ; restore z register
mulsu r19,r23 ; (signed)Ah * (unsigned)Bh - multiply high bytes
add r4,r0 ; accumulate result
adc r5,r1
mul	r18,r22	; (unsigned)Al * (unsigned)Bl ; multiply low bytes
add r2,r0 ; accumulate result
adc r3,r1
adc r4,r14 ; r14 is cleared above
adc r5,r14
mulsu r19,r22 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
sbc	r5,r14 ; r14 is cleared above - subtract sign bit
add	r3,r0 ; accumulate result
adc	r4,r1
adc	r5,r14 ; r14 is cleared above
mul r23,r18 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
add	r3,r0 ; accumulate result
adc	r4,r1
adc	r5,r14 ; r14 is cleared above

;add in offset so lfo is always positive
; (the difference between tremolo and ring modulation)
ldi r16,$80 ; turn signed value to unsigned value
add r5,r16

;multiply left data by lfo
movw r21:r20,r5:r4 ; move lfo to multiply register
movw r19:r18,r25:r24 ; move left data to multiply register
mulsu r19,r21 ; (signed)ah * (unsigned)bh
movw r5:r4,r1:r0
mul	r18,r20	; (unsigned)al * (unsigned)bl
movw r3:r2,r1:r0
mulsu r19,r20 ; (signed)ah * (unsigned)bl
sbc	r5,r14 ; r14 is cleared above
add	r3,r0 ; accumulate result
adc	r4,r1
adc	r5,r14 ; r14 is cleared above
mul r21,r18 ; (unsigned)bh * (unsigned)al
add	r3,r0 ; accumulate result
adc	r4,r1
adc	r5,r14 ; r14 is cleared above

;check if in or out of phase mode
sbrs r28,$00 ; check if phase inversion bit is set
rjmp rightmultiply_000057 ; skip inversion if in phase
com r20 ; else invert lfo signal
com r21 ; ones complement used to avoid $0000 problem

rightmultiply_000057: ; multiply right data by lfo

movw r19:r18,r7:r6 ; move right data to multiply register
mulsu r19,r21 ; (signed)ah * (unsigned)bh
movw r7:r6,r1:r0
mul	r18,r20	; (unsigned)al * (unsigned)bl
movw r3:r2,r1:r0
mulsu r19,r20 ; (signed)ah * (unsigned)bl
sbc	r7,r14 ; r14 is cleared above
add	r3,r0 ; accumulate result
adc	r6,r1
adc	r7,r14 ; r14 is cleared above
mul r21,r18 ; (unsigned)bh * (unsigned)al
add	r3,r0 ; accumulate result
adc	r6,r1
adc	r7,r14 ; r14 is cleared above

adcsample_000057: ; sample adc for lfo rate

lds r17,adcsra ; get adc control register
sbrs r17,adif ; check if adc conversion is complete
rjmp done_000057 ; skip adc sampling
lds r16,adcl ; get low byte adc value
lds r17,adch ; get high byte adc value
add r8,r16 ; accumulate adc samples
adc r9,r17
adc r10,r14 ; r14 is cleared above
ldi r17,$f7
sts adcsra,r17 ; clear interrupt flag
dec r15 ; countdown adc sample clock
brne done_000057 ; get delay time if its been long enough
lsr r10 ; divide adc value by 4 to make 16b value
ror r9
ror r8
lsr r10
ror r9
ror r8
movw r17:r16,r9:r8 ; move adc sample to temporary register
clr r20
ldi r21,$01 ; add in offset of min lfo rate ($000100)
add r17,r21
adc r20,r14 ; r14 is cleared above
sub r16,r11 ; find difference between adc sample and current lfo rate
sbc r17,r12
sbc r20,r13
brsh check_000057 ; check for deadband if positive
com r16 ; else invert if negative
com r17 ; only 1 lsb error doing it this way
com r20

check_000057: ; check if difference is greater than deadband

cpi r16,$40 ; check if difference is less than 1 adc lsb
cpc r17,r14 ; r14 cleared above
cpc r20,r14
brlo empty_000057 ; do nothing if less than 1 adc lsb
mov r11,r8 ; else move to lfo rate register
mov r12,r9
mov r13,r10
add r12,r21 ; add in offset
adc r13,r14 ; r14 is cleared above

empty_000057: ; empty accumulation registers and finish off

clr r8 ; empty accumulation registers
clr r9
clr r10

;check if phase mode should change
lds r16,pinj ; get switch data
sbrs r16,$02 ; check if pushbutton is released
rjmp edge_000057 ; check if pushbutton was low on previous sample
clt ;  clear state register if back high
rjmp switchsample_000057 ; finish off

edge_000057: ; check for falling edge

brts adcsample_000057 ; do nothing if edge was already detected
set ; set t register to indicate edge detected
ldi r17,$01 ; toggle phase inversion mode
eor r28,r17

switchsample_000057: ; check rotary switch for new function

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_000057:

reti ; return to waiting


