DelayTutorial.asm
[Discussion of code elements, needed revision etc, can go here]
1 ; ****************************************************************************
2 ; * program: delay-16b-rotary.asm *
3 ; * UID = dan_practice - unique id to eliminate conflicts between variables *
4 ; * 16b address space (.7s delay time) *
5 ; * stereo data *
6 ; * pot controlled delay time (0s - .7s) *
7 ; ****************************************************************************
8 ; *
9 ; * PROGRAM OVERVIEW
10 ; *
11 ; * data is read in from memory and written to the codec at the same time
12 ; * new data is written to the memory from the codec. the rotary encoder
13 ; * increments or decrements the read address, adjusting the delay time.
14 ; *
15 ; * register usage - may be redefined in other sections
16 ; *
17 ; * r0
18 ; * r1
19 ; * r2 left lsb out
20 ; * r3 left msb out
21 ; * r4 right lsb out
22 ; * r5 right msb out
23 ; * r6 left lsb in
24 ; * r7 left msb in
25 ; * r8 right lsb in
26 ; * r9 right msb in
27 ; * r10
28 ; * r11
29 ; * r12 desired delay lsb
30 ; * r13 desired delay msb
31 ; * r14
32 ; * r15 switch sample counter
33 ; * r16 temporary swap register
34 ; * r17 temporary swap register
35 ; * r18
36 ; * r19
37 ; * r20
38 ; * r21
39 ; * r22 write address third byte/null register
40 ; * r23
41 ; * r24 write address lsb
42 ; * r25 write address msb
43 ; * r26 actual delay lsb
44 ; * r27 actual delay msb
45 ; * r28 read address lsb
46 ; * r29 read address msb
47 ; * r30 jump location for interrupt lsb
48 ; * r31 jump location for interrupt msb
49 ; ****************************************************************************
50
51
52 ; * NOTES: - all unprocessed (dry) raw data comes in off of the SPI port, via spdr
53 ; - all processed (wet) data goes out over the SPI port, also via spdr
54 ; - all external memory addressing happens over ports D and H
55 ; - all data we are saving and reading back (like our delay audio) travels over ports A and C
56 ; - I am going to try to notate
57
58
59
60
61 ; **********************
62 ; **** PROGRAM START
63 ; **********************
64
65 ; initiate data transfer to codec
66
67 sbi portb, portb0 ; set bit portb0 (aka DACLRC aka DAC sample rate left/right clock)
68 ;
69 ; **NOTE: from WM8731 data sheet, page 36:
70 ; "DACLRC is an alignment clock that controls whether Left or Right channel
71 ; data is present on DACTAT"
72
73 ; (now on SPI - LEFT MSB) *********************************************************************************************
74
75 out spdr, r3 ; send out processed left channel msb (or whatever's currently in r3) to SPI data register
76 cbi portb, portb0 ; clear DACLRC
77
78 adiw r25:r24, 0x01 ; increment write address pointer by 0x01
79 adiw r29:r28, 0x01 ; increment read address pointer by 0x01
80 ldi r22, 0x00 ; setup write high byte with null
81
82
83 ; **********************
84 ; **** wait1
85 ; **********************
86
87 wait1_UID: ; wait to see if processed left channel msb has been sent
88
89 in r17, spsr ; move spi status register to r17 so we can look at it
90 sbrs r17, spif ; skip next instruction if the SPIF (spi interrupt flag) bit is set
91 ; **NOTE: from atmega3250P data sheet, page 162:
92 ; "When a serial transfer is complete, the SPIF flag is set"
93 rjmp wait1_UID ; aka: loop until SPIF is set
94 ; and then...
95
96 ; ### after wait1, new spi data ready
97
98 in r7, spdr ; move spi data register into r7 (remember spdr is read/write, page 162)
99 ; this moves the incoming (dry) left channel msb to r7
100
101 ; (end of wet/dry left msb transfer) ***********************************************************************************
102
103 ; (now on SPI - LEFT LSB) ***********************************************************************************
104
105 out spdr, r2 ; send the processed (wet) left lsb out
106
107 ; ### retrieve stored left channel data from SRAM:
108
109 out portd, r28 ; move read address lsb pointer to portd (see schematic)
110 sts porth, r29 ; move read address msb pointer to portH
111 ; **NOTE: - this is a special instruction because the I/O portH register is wayyy up there in the sram
112 ; - 'out' completes in one cycle, 'sts' completes in two
113
114 nop ; read address msb hits the port, now wait for (1)
115 nop ; a latch time of two cycles... (2)
116
117 ; DING! your data is ready, please pick it up!
118 in r2, pina ; (see schematic) D0-7, aka lsb, is on port A, now stored in r2
119 in r3, pinc ; and D8-15, aka msb, is on port C, now stored in r3
120
121 ; ### left channel SRAM retrieve completed
122
123 ; ### now we've got a little time to kill, so...
124 adiw r29:r28, 0x01 ; increment read address pointer by 1
125
126
127 ; **********************
128 ; **** wait2
129 ; **********************
130
131 wait2_UID: ; wait to see if wet left channel lsb has been sent
132
133 in r17, spsr ; spi status reg. to r17 (looking familiar?)
134 sbrs r17, spif ; skip next if SPIF bit not set...
135 rjmp wait2_UID ; loop until SPIF set
136
137 ; **NOTE: and now a couple of words about rjmp:
138 ; - it can only move within 2k of memory (ie, can't jump all the way to the start, or end, of a long program)
139 ; - it doesn't do anything to the stack (so if it loops a thousand times, the stack isn't going to overflow)
140 ; - it takes two cycles
141
142 ; ### after wait2 is finished, new spi data ready
143
144 in r6, spdr ; receive in dry left channel lsb
145 ; **NOTE: that gives us both bytes of the incoming left channel, and both wet bytes have been sent
146 ; we can now move on to the right channel
147
148 ; (end of wet/dry left lsb transfer) ************************************************************************************
149
150 ; (now on SPI - RIGHT MSB) ************************************************************************************
151
152 out spdr, r5 ; send out (wet) right msb
153
154 ; ### writing (dry) left channel to SRAM:
155 out portd, r24 ; update data address ports to reflect where we want to write
156 sts porth, r25 ; remember 'out' and 'sts'?
157
158 out portg, r22 ; woah, portG? what's this? Check out the SRAM data sheet and the schematic:
159 ; r22 is currently 0x00, so we are pulling CE and WE low, and writing our high address bits to zero
160 ; CE (chip enable) and WE (write enable) are asserted low
161
162 ; **NOTE: from AS7C4098 data sheet, page 2:
163 ; "Data on input pins io1-io16 is written on the rising edge of WE..."
164 ; "To avoid bus contention, external devices should drio i/o pins only after
165 ; outputs have been disabled with OE (output enable) or WE"
166 ; "A read cycle is accomplished by asserting OE and CE, with WE high."
167
168 ldi r17, 0xFF ; prepare a salad of ones
169 out ddra, r17 ; send them to the portA direction register
170 out ddrC, r17 ; and to the portC direction register
171 ; (see ATmega3250P data sheet, page 65, 'Switching Between Input and Output')
172
173 out porta, r6 ; with these two 'out' instructions
174 out portc, r7 ; we send left channel dry data directly to the sram
175
176
177 sbi portg, portg2 ; and as soon as we pull WE (write enable) on portG_2 high,
178 ; zzztt! a single audio sample is written into memory, just like that.
179
180
181
182 ; ### left channel SRAM write completed
183
184
185 out ddra, r22 ; now, we happen to know that r22 contains a bunch of zeros
186 out ddrc, r22 ; so we can use that register to set ports A and C back to an input state.
187
188
189
190
191 ; **********************
192 ; **** wait3
193 ; **********************
194
195 wait3_UID: ; meanwhile, back at the codec, we are still transferring the right channel lsb
196
197 in r17, spsr ; check out the spsr, check out the spif,
198 sbrs r17, spif ; we know what we're doing here now, right?
199 rjmp wait3_UID ; loop until transfer completed
200
201 ; ### end of wait3, new data ready!
202
203 in r9, spdr ; recieve in (dry) right channel msb
204
205 ; (end of wet/dry right msb transfer) ***********************************************************************************
206
207 ; (now on SPI - RIGHT LSB) ***********************************************************************************
208
209 out spdr, r4 ; send out (wet) right channel lsb
210
211 ; ### retrieve stored right channel data from SRAM
212 out portd, r28 ; set up the address we want to read from
213 sts porth, r29 ; on ports d and h
214
215 nop ; killing time again...
216 nop ; two cycles, while the SRAM latches the address
217
218 ; and once more, ding! our data is now waiting on the SRAM data lines
219 in r4, pina ; right lsb
220 in r5, pinc ; right msb (this is audio data we saved in the past)
221
222 ; ### right channel SRAM data retrieval completed
223
224 adiw r25:r24, 0x01 ; increment the write address (we're going to be using that pointer next)
225
226
227 ; **********************
228 ; **** wait4
229 ; **********************
230
231 wait4_UID: ; checking up on that SPI transfer
232
233 in r17, spsr
234 sbrs r17, spif
235 rjmp wait4_UID ; loop until SPIF is set
236
237 ; ### end of wait4, let's get our dry audio in
238 in r8, spdr ; that's the right channel lsb
239
240 ; (end of wet/dry right lsb transfer) ***********************************************************************************
241 ; now that we have both bytes of the right channel dry audio, we can store it in the SRAM
242
243 ; ### writing (dry) right channel to SRAM
244
245 out portd, r24 ; give the sram the write address, lsb
246 sts porth, r25 ; now msb
247
248 out portg, r22 ; pull WE low, (CE is already low, WE was high for read operations)
249 ldi r17, 0xFF ; prepare a bevy of ones
250 out ddra, r17 ; set porta as output
251 out ddrc, r17 ; set portc as output
252 out porta, r8 ; put the data on the ports, lsb
253 out portc, r9 ; msb
254 sbi portg, portg2 ; zzzt! pull WE high and write the data
255
256 out ddra, r22 ; reconfigure porta as input
257 out ddrc, r22 ; reconfigure portc as input
258
259
260 ; **********************************************************************************
261 ; **** check rotary encoder and adjust delay time
262 ; **********************************************************************************
263 ; * The rotary encoder is externally debounced, so we don't have to do that here.
264 ; * You'll see it in the schematic labeled MOD2 on portJ0, portJ1, and portJ2.
265 ; *
266 ; * The encoder's pin1 is sampled on a transition from high to low on pin0.
267 ; * if pin1 is high, a left turn occured, if pin1 is low, a right turn occured.
268 ; **********************************************************************************
269
270 dec r15 ; well, let's do a little debouncing:
271 brne adjust_UID ; brne checks the Z register,
272 ; if r15 was not zero after the last operation, it will branch us to adjust_UID
273
274 ldi r17, 0x40 ; prepare a constant in r17
275 mov r15, r17 ; put 0x40 in the sample freq bin to catch all rising edges (results in 1.5ms sampletime)
276 lds r17, pinj ; move port J data into r17
277 sbrs r17, PINJ0 ; skip next if PINJ0 is set
278 rjmp edgecheck_UID ; if it's not set, is it a falling edge?
279
280 clt ; clear T reg (in SREG)
281 rjmp switchsample_UID ; done looking at mod2, look at the program selector
282
283 ; **********************
284 ; **** edgecheck
285 ; **********************
286
287 edgecheck_UID: ; checks for falling edge
288 brts switchsample_UID ; if the T flag in SREG is set, assume the edge was already detected
289 set ; otherwise set the T flag
290 sbrs r17, PINJ1 ; check if PINJ1 is high
291 rjmp upcount_UID ; if PINJ0 has just gone low and PINJ1 is low, a right turn has transpired
292 ; upcount will therefore increase the delay
293 ; otherwise, PINJ1 is high and a left turn has transpired, so we should decrease the delay
294 ; **** downcount
295 ldi r17, 0x01 ; load our decrement amount into r17 (0x01 = 256 samples = 0.006s)
296 sub r13, r17 ; decrement delay MSB
297
298 rjmp switchsample_UID ; done looking at mod2, look at the program selector
299
300 ; **** upcount
301 upcount_UID: ; increment delay register
302 ldi r17, 0x01 ; load increment amount into r17
303 add r13, r17 ; increment MSB
304
305
306 ; **********************
307 ; **** switchsample
308 ; **********************
309
310 switchsample_UID: ; sample the program select dial
311
312 lds r31, pinj ; put switch data into jump location MSB reg
313 andi r31, 0x78 ; mask off rotary encoder 0b01111000
314 ldi r17, 0x02 ; 0x02 into r17
315 lsr r31 ; shift r31 to the right
316 lsr r31 ; shift again
317 add r31, r17 ; convert switch position data to program memory location
318
319
320 ; **********************
321 ; **** adjust
322 ; **********************
323
324 adjust_UID: ; since we've only changed the desired delay in the previous section, we need to implement that delay
325 ; this checks to see if the delay time is correct,
326 ; and if it's not it makes an effort to move slightly closer to the correct delay
327
328 andi r26, 0xFE ; is the delay time even?
329 cp r26, r22 ; compare actual delay lsb to zero
330 cpc r27, r13 ; compare with carry actual delay msb with desired delay msb
331 breq done_UID ; If equal, head to done, yay!
332 brsh indexdown_UID ; If the same or higher, branch to indexdown
333 ; otherwise, we can assume it is too low
334 ; **** indexup
335 adiw r27:r26, 0x04 ; so increment delay register by 0x04
336 rjmp done_UID ; and head to the end
337
338 ; **** indexdown
339 indexdown_UID:
340 sbiw r27:r26, 0x02 ; decrement delay reg by 0x02
341
342
343
344 ; **********************
345 ; **** done
346 ; **********************
347
348 done_UID: ; It's been a long hard row to hoe, but we did it!
349 ; oh wait, what? I'm going to have to do it again? from the beginning?
350 ; but I still need to get my pointers all lined up!
351
352 movw r29:r28, r25:r24 ; sync write destination and read address
353 sub r28, r26 ; now subtract by the delay in samples (first lsb)
354 sbc r29, r27 ; subtract msb with carry from previous
355
356 reti ; return from interrupt so we can get back to our idling
357