welcome: please sign in

Revision 13 as of 2010-08-05 05:44:49

Clear message
location: DelayTutorialAsm

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

MICrODEC

Microdec Software