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