Attachment 'tremolo_stereo.asm'
Download 1 ; program: tremolo-stereo.asm
2 ; UID = 000057 - unique id to eliminate conflicts between variables
3 ; memory is not used
4 ; stereo in and out
5 ; pot (MOD1) controls the lfo frequency
6 ; pushbutton (on MOD2) controls phase between left and right output
7
8 ; program overview
9 ;
10 ; data is read in from the codec, and multiplied by an lfo that is internally
11 ; generated using a 512s/16b sinewave lookup table. the frequency of this
12 ; lfo is determined by the adc input, which is oversampled 256 times and
13 ; compared to the previous value with a deadband. this helps reduce jitter.
14 ; the left and right channels are mutiplied by opposite signals, so the
15 ; sound bounces back and forth between left and right. depressing the
16 ; pushbutton (on MOD2) changes this so that the left and right are in phase.
17
18 ; register usage - may be redefined in other sections
19 ;
20 ; r0 multiply result lsb
21 ; r1 multiply result msb
22 ; r2 accumulation lsb
23 ; r3 accumulation mlb
24 ; r4 left lsb out/accumulation mhb
25 ; r5 left msb out/accumulation msb
26 ; r6 right in/out lsb
27 ; r7 right in/out msb
28 ; r8 adc accumulator fractional byte
29 ; r9 adc accumulator lsb
30 ; r10 adc accumulator msb
31 ; r11 lfo control signal fractional byte
32 ; r12 lfo control signal lsb
33 ; r13 lfo control signal msb
34 ; r14 null register
35 ; r15 switch/adc sample counter
36 ; r16 temporary swap register
37 ; r17 temporary swap register
38 ; r18 sine wave buffer/multiply lsb
39 ; r19 sine wave buffer/multiply msb
40 ; r20 multiply swap register
41 ; r21 multiply swap register
42 ; r22 sinetable lookup address lsb
43 ; r23 sinetable lookup address mlb
44 ; r24 left data in lsb
45 ; r25 left data in msb
46 ; r26 sinetable lookup address mhb
47 ; r27 sinetable lookup address msb
48 ; r28 phase indicator
49 ; r29
50 ; r30 jump location for interrupt lsb
51 ; r31 jump location for interrupt msb
52 ; t pushbutton state indicator
53
54 ;program starts here first time
55 ; intialize registers
56 ldi r30,$07 ; increment z pointer to new jump location
57 clr r14 ; clear null register
58 ldi r16,$10 ; initialize lfo rate
59 ldi r17,$00
60 movw r13:r12,r17:r16 ; move to lfo rate register
61 clr r28 ; initialize phase mode register
62 reti ; finish with initialization and wait for next interrupt
63
64 ;program starts here every time but first
65 ; initiate data transfer to codec
66 sbi portb,portb0 ; toggle slave select pin
67 out spdr,r5 ; send out left channel msb
68 cbi portb,portb0
69
70 wait1_000057: ; check if byte has been sent
71
72 in r17,spsr
73 sbrs r17,spif
74 rjmp wait1_000057
75 in r25,spdr ; recieve in left channel msb
76 out spdr,r4 ; send out left channel lsb
77
78 wait2_000057: ; check if byte has been sent
79
80 in r17,spsr
81 sbrs r17,spif
82 rjmp wait2_000057
83 in r24,spdr ; recieve in left channel lsb
84 out spdr,r7 ; send out right channel msb
85
86 wait3_000057: ; check if byte has been sent
87
88 in r17,spsr
89 sbrs r17,spif
90 rjmp wait3_000057
91 in r7,spdr ; recieve in right channel msb
92 out spdr,r6 ; send out right channel lsb
93
94 wait4_000057: ; check if byte has been sent
95
96 in r17,spsr
97 sbrs r17,spif
98 rjmp wait4_000057
99 in r6,spdr ; recieve in right channel lsb
100
101 ;vco generation
102 movw r17:r16,r31:r30 ; store z register
103 ;get sample 1
104 add r22,r11 ; increment sinetable address
105 adc r23,r12
106 adc r26,r13
107 adc r27,r14 ; r14 is cleared above
108 movw r31:r30,r27:r26 ; move to z register for data fetch
109 lsl r30 ; adjust pointer for 16b fetch
110 rol r31
111 andi r31,$03 ; limit value to 10b (512 samples x 2 bytes)
112 ori r31,$48 ; set to memory address location where table is stored
113 lpm r18,z+ ; get sine value lsb, increment z register
114 lpm r19,z ; get sine value msb
115 sbrc r27,$01 ; flip sign for half of the values
116 rjmp interpolate_000057 ; interpolate if even
117 neg r18 ; invert if odd
118 adc r19,r14 ; r14 is cleared above
119 neg r19
120
121 interpolate_000057: ; multiply sample 1 by distance
122
123 movw r21:r20,r23:r22 ; get distance from sample 1
124 com r20 ; invert distance
125 com r21
126 mulsu r19,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
127 movw r5:r4,r1:r0 ; store high bytes result for later
128 mul r18,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
129 movw r3:r2,r1:r0 ; store low byets for later
130 mulsu r19,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
131 sbc r5,r14 ; r14 is cleared above - subtract sign bit
132 add r3,r0 ; accumulate result
133 adc r4,r1
134 adc r5,r14 ; r14 is cleared above
135 mul r21,r18 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
136 add r3,r0 ; accumulate result
137 adc r4,r1
138 adc r5,r14 ; r14 is cleared above
139
140 ;get sample 2
141 adiw r27:r26,$01 ; set to next sample
142 movw r31:r30,r27:r26 ; move to z register for data fetch
143 lsl r30 ; adjust pointer for 16b fetch
144 rol r31
145 andi r31,$03 ; limit value to 10b (512 samples x 2 bytes)
146 ori r31,$48 ; set to memory address location where table is stored
147 lpm r18,z+ ; get sine value lsb, increment z register
148 lpm r19,z ; get sine value msb
149 sbrc r27,$01 ; flip sign for half of the values
150 rjmp interpolate1_000057
151 neg r18
152 adc r19,r14 ; r14 is cleared above
153 neg r19
154
155 interpolate1_000057: ; multiply sample 2 by distance and accumulate
156
157 sbiw r27:r26,$01 ; reset address
158 movw r31:r30,r17:r16 ; restore z register
159 mulsu r19,r23 ; (signed)Ah * (unsigned)Bh - multiply high bytes
160 add r4,r0 ; accumulate result
161 adc r5,r1
162 mul r18,r22 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
163 add r2,r0 ; accumulate result
164 adc r3,r1
165 adc r4,r14 ; r14 is cleared above
166 adc r5,r14
167 mulsu r19,r22 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
168 sbc r5,r14 ; r14 is cleared above - subtract sign bit
169 add r3,r0 ; accumulate result
170 adc r4,r1
171 adc r5,r14 ; r14 is cleared above
172 mul r23,r18 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
173 add r3,r0 ; accumulate result
174 adc r4,r1
175 adc r5,r14 ; r14 is cleared above
176
177 ;add in offset so lfo is always positive
178 ; (the difference between tremolo and ring modulation)
179 ldi r16,$80 ; turn signed value to unsigned value
180 add r5,r16
181
182 ;multiply left data by lfo
183 movw r21:r20,r5:r4 ; move lfo to multiply register
184 movw r19:r18,r25:r24 ; move left data to multiply register
185 mulsu r19,r21 ; (signed)ah * (unsigned)bh
186 movw r5:r4,r1:r0
187 mul r18,r20 ; (unsigned)al * (unsigned)bl
188 movw r3:r2,r1:r0
189 mulsu r19,r20 ; (signed)ah * (unsigned)bl
190 sbc r5,r14 ; r14 is cleared above
191 add r3,r0 ; accumulate result
192 adc r4,r1
193 adc r5,r14 ; r14 is cleared above
194 mul r21,r18 ; (unsigned)bh * (unsigned)al
195 add r3,r0 ; accumulate result
196 adc r4,r1
197 adc r5,r14 ; r14 is cleared above
198
199 ;check if in or out of phase mode
200 sbrs r28,$00 ; check if phase inversion bit is set
201 rjmp rightmultiply_000057 ; skip inversion if in phase
202 com r20 ; else invert lfo signal
203 com r21 ; ones complement used to avoid $0000 problem
204
205 rightmultiply_000057: ; multiply right data by lfo
206
207 movw r19:r18,r7:r6 ; move right data to multiply register
208 mulsu r19,r21 ; (signed)ah * (unsigned)bh
209 movw r7:r6,r1:r0
210 mul r18,r20 ; (unsigned)al * (unsigned)bl
211 movw r3:r2,r1:r0
212 mulsu r19,r20 ; (signed)ah * (unsigned)bl
213 sbc r7,r14 ; r14 is cleared above
214 add r3,r0 ; accumulate result
215 adc r6,r1
216 adc r7,r14 ; r14 is cleared above
217 mul r21,r18 ; (unsigned)bh * (unsigned)al
218 add r3,r0 ; accumulate result
219 adc r6,r1
220 adc r7,r14 ; r14 is cleared above
221
222 adcsample_000057: ; sample adc for lfo rate
223
224 lds r17,adcsra ; get adc control register
225 sbrs r17,adif ; check if adc conversion is complete
226 rjmp done_000057 ; skip adc sampling
227 lds r16,adcl ; get low byte adc value
228 lds r17,adch ; get high byte adc value
229 add r8,r16 ; accumulate adc samples
230 adc r9,r17
231 adc r10,r14 ; r14 is cleared above
232 ldi r17,$f7
233 sts adcsra,r17 ; clear interrupt flag
234 dec r15 ; countdown adc sample clock
235 brne done_000057 ; get delay time if its been long enough
236 lsr r10 ; divide adc value by 4 to make 16b value
237 ror r9
238 ror r8
239 lsr r10
240 ror r9
241 ror r8
242 movw r17:r16,r9:r8 ; move adc sample to temporary register
243 clr r20
244 ldi r21,$01 ; add in offset of min lfo rate ($000100)
245 add r17,r21
246 adc r20,r14 ; r14 is cleared above
247 sub r16,r11 ; find difference between adc sample and current lfo rate
248 sbc r17,r12
249 sbc r20,r13
250 brsh check_000057 ; check for deadband if positive
251 com r16 ; else invert if negative
252 com r17 ; only 1 lsb error doing it this way
253 com r20
254
255 check_000057: ; check if difference is greater than deadband
256
257 cpi r16,$40 ; check if difference is less than 1 adc lsb
258 cpc r17,r14 ; r14 cleared above
259 cpc r20,r14
260 brlo empty_000057 ; do nothing if less than 1 adc lsb
261 mov r11,r8 ; else move to lfo rate register
262 mov r12,r9
263 mov r13,r10
264 add r12,r21 ; add in offset
265 adc r13,r14 ; r14 is cleared above
266
267 empty_000057: ; empty accumulation registers and finish off
268
269 clr r8 ; empty accumulation registers
270 clr r9
271 clr r10
272
273 ;check if phase mode should change
274 lds r16,pinj ; get switch data
275 sbrs r16,$02 ; check if pushbutton is released
276 rjmp edge_000057 ; check if pushbutton was low on previous sample
277 clt ; clear state register if back high
278 rjmp switchsample_000057 ; finish off
279
280 edge_000057: ; check for falling edge
281
282 brts adcsample_000057 ; do nothing if edge was already detected
283 set ; set t register to indicate edge detected
284 ldi r17,$01 ; toggle phase inversion mode
285 eor r28,r17
286
287 switchsample_000057: ; check rotary switch for new function
288
289 andi r16,$78 ; mask off rotary switch
290 lsr r16 ; adjust switch position to program memory location
291 lsr r16
292 ldi r17,$02
293 add r16,r17
294 cpse r16,r31 ; check if location has changed
295 clr r30 ; reset jump register to intial state
296 mov r31,r16
297
298 done_000057:
299
300 reti ; return to waiting
301
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.