welcome: please sign in
location: DelayShortAsm

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 

MICrODEC

Microdec Software

DelayShortAsm (last edited 2010-07-18 03:00:46 by DanielMcAnulty)