DelayShortAsm

DelayTutorial.asm

[Discussion of code elements, needed revision etc, can go here]

; ****************************************************************************
; * program: delay-16b-rotary.asm                                            *
; * UID = dan_practice - unique id to eliminate conflicts between variables  *
; * 16b address space (.7s delay time)                                       *
; * stereo data                                                              *
; * pot controlled delay time (0s - .7s)                                     *
; ****************************************************************************
; * 
; * PROGRAM OVERVIEW
; * 
; * data is read in from memory and written to the codec at the same time
; * new data is written to the memory from the codec.  the rotary encoder
; * increments or decrements the read address, adjusting the delay time.
; * 
; * register usage - may be redefined in other sections
; * 
; * r0  
; * r1  
; * r2  left lsb out
; * r3  left msb out
; * r4  right lsb out
; * r5  right msb out
; * r6  left lsb in
; * r7  left msb in
; * r8  right lsb in
; * r9  right msb in
; * r10 
; * r11 
; * r12 desired delay lsb
; * r13 desired delay msb
; * r14 
; * r15 switch sample counter
; * r16 temporary swap register
; * r17 temporary swap register
; * r18 
; * r19 
; * r20 
; * r21 
; * r22 write address third byte/null register
; * r23 
; * r24 write address lsb
; * r25 write address msb
; * r26 actual delay lsb
; * r27 actual delay msb
; * r28 read address lsb
; * r29 read address msb
; * r30 jump location for interrupt lsb
; * r31 jump location for interrupt msb
; ****************************************************************************

; * NOTES: - all unprocessed (dry) raw data  comes in off of the SPI port, via spdr
;          - all processed (wet) data goes out over the SPI port, also via spdr
;          - all external memory addressing happens over ports D and H 
;          - all data we are saving and reading back (like our delay audio) travels over ports A and C
;          - I am going to try to notate 

; **********************
; **** PROGRAM START
; **********************

; initiate data transfer to codec

sbi portb, portb0   ; set bit portb0  (aka DACLRC aka DAC sample rate left/right clock)
                    ;  
                    ; **NOTE: from WM8731 data sheet, page 36: 
                    ;           "DACLRC is an alignment clock that controls whether Left or Right channel 
                    ;            data is present on DACTAT" 

; (now on SPI - LEFT MSB)  *********************************************************************************************

out spdr, r3        ; send out processed left channel msb (or whatever's currently in r3) to SPI data register
cbi portb, portb0   ; clear DACLRC

adiw r25:r24, 0x01  ; increment write address pointer by 0x01
adiw r29:r28, 0x01  ; increment  read address pointer by 0x01
ldi  r22, 0x00      ; setup write high byte with null

; **********************
; **** wait1 
; **********************

wait1_UID:     ; wait to see if processed left channel msb has been sent

in   r17, spsr ; move spi status register to r17 so we can look at it
sbrs r17, spif ; skip next instruction if the SPIF (spi interrupt flag) bit is set
               ; **NOTE: from atmega3250P data sheet, page 162:
               ;            "When a serial transfer is complete, the SPIF flag is set" 
rjmp wait1_UID ; aka: loop until SPIF is set
               ; and then...

; ### after wait1, new spi data ready

in r7, spdr    ; move spi data register into r7  (remember spdr is read/write, page 162)
               ; this moves the incoming (dry) left channel msb to r7

; (end of wet/dry left msb transfer) ***********************************************************************************

; (now on SPI - LEFT LSB)            ***********************************************************************************

out spdr, r2   ; send the processed (wet) left lsb out

; ### retrieve stored left channel data from SRAM: 

out portd, r28 ; move read address lsb pointer to portd (see schematic) 
sts porth, r29 ; move read address msb pointer to portH
               ; **NOTE: - this is a special instruction because the I/O portH register is wayyy up there in the sram
               ;         - 'out' completes in one cycle, 'sts' completes in two

nop ; read address msb hits the port, now wait for  (1)  
nop ; a latch time of two cycles...                 (2)

               ; DING!  your data is ready, please pick it up! 
in r2, pina    ; (see schematic) D0-7,  aka lsb, is on port A, now stored in r2
in r3, pinc    ;             and D8-15, aka msb, is on port C, now stored in r3

; ###  left channel SRAM retrieve completed

; ### now we've got a little time to kill, so...
adiw r29:r28, 0x01    ; increment read address pointer by 1

; **********************
; **** wait2 
; **********************

wait2_UID:      ; wait to see if wet left channel lsb has been sent

in   r17, spsr  ; spi status reg. to r17 (looking familiar?)
sbrs r17, spif  ; skip next if SPIF bit not set...
rjmp wait2_UID  ; loop until SPIF set

                ; **NOTE: and now a couple of words about rjmp:
                ;       - it can only move within 2k of memory (ie, can't jump all the way to the start, or end, of a long program) 
                ;       - it doesn't do anything to the stack  (so if it loops a thousand times, the stack isn't going to overflow)
                ;       - it takes two cycles

; ### after wait2 is finished, new spi data ready

in r6, spdr     ; receive in dry left channel lsb 
                ; **NOTE: that gives us both bytes of the incoming left channel, and both wet bytes have been sent
                ;         we can now move on to the right channel

; (end of wet/dry left lsb transfer) ************************************************************************************

; (now on SPI - RIGHT MSB)           ************************************************************************************

out spdr, r5    ; send out (wet) right msb

; ### writing (dry) left channel to SRAM:
out portd, r24  ; update data address ports to reflect where we want to write
sts porth, r25  ; remember 'out' and 'sts'?

out portg, r22  ; woah, portG? what's this?  Check out the SRAM data sheet and the schematic:
                ;    r22 is currently 0x00, so we are pulling CE and WE low, and writing our high address bits to zero
                ;    CE (chip enable) and WE (write enable) are asserted low
                   
                ; **NOTE:  from AS7C4098 data sheet, page 2:
                ;            "Data on input pins io1-io16 is written on the rising edge of WE..." 
                ;            "To avoid bus contention, external devices should drio i/o pins only after 
                ;               outputs have been disabled with OE (output enable) or WE"
                ;            "A read cycle is accomplished by asserting OE and CE, with WE high."  

ldi r17, 0xFF   ; prepare a salad of ones 
out ddra, r17   ; send them to the portA direction register
out ddrC, r17   ; and to the portC direction register       
                ; (see ATmega3250P data sheet, page 65, 'Switching Between Input and Output')

out porta, r6   ; with these two 'out' instructions 
out portc, r7   ; we send left channel dry data directly to the sram
                

sbi portg, portg2  ; and as soon as we pull WE (write enable) on portG_2 high,
                   ; zzztt!  a single audio sample is written into memory, just like that.
                   
          

; ### left channel SRAM write completed

out ddra, r22   ; now, we happen to know that r22 contains a bunch of zeros 
out ddrc, r22   ; so we can use that register to set ports A and C back to an input state. 

; **********************
; **** wait3
; **********************

wait3_UID:          ; meanwhile, back at the codec, we are still transferring the right channel lsb

in   r17, spsr      ; check out the spsr, check out the spif,
sbrs r17, spif      ; we know what we're doing here now, right?  
rjmp wait3_UID      ; loop until transfer completed

; ### end of wait3, new data ready!

in r9, spdr         ; recieve in (dry) right channel msb

; (end of wet/dry right msb transfer) ***********************************************************************************

; (now on SPI - RIGHT LSB)            ***********************************************************************************

out spdr, r4        ; send out (wet) right channel lsb

; ### retrieve stored right channel data from SRAM
out portd, r28      ; set up the address we want to read from
sts porth, r29      ; on ports d and h

nop                 ; killing time again... 
nop                 ; two cycles, while the SRAM latches the address

                    ; and once more, ding!  our data is now waiting on the SRAM data lines
in r4, pina         ; right lsb
in r5, pinc         ; right msb    (this is audio data we saved in the past)

; ### right channel SRAM data retrieval completed

adiw r25:r24, 0x01  ; increment the write address (we're going to be using that pointer next)

; **********************
; **** wait4
; **********************

wait4_UID:          ; checking up on that SPI transfer

in r17, spsr
sbrs r17, spif
rjmp wait4_UID      ; loop until SPIF is set

; ### end of wait4, let's get our dry audio in
in r8, spdr         ; that's the right channel lsb

; (end of wet/dry right lsb transfer) ***********************************************************************************
; now that we have both bytes of the right channel dry audio, we can store it in the SRAM

; ### writing (dry) right channel to SRAM

out portd, r24      ; give the sram the write address, lsb
sts porth, r25      ; now msb

out portg, r22      ; pull WE low, (CE is already low, WE was high for read operations)
ldi r17, 0xFF       ; prepare a bevy of ones
out ddra, r17       ; set porta as output
out ddrc, r17       ; set portc as output
out porta, r8       ; put the data on the ports, lsb
out portc, r9       ; msb
sbi portg, portg2   ; zzzt!  pull WE high and write the data

out ddra, r22       ; reconfigure porta as input
out ddrc, r22       ; reconfigure portc as input

; **********************************************************************************
; **** check rotary encoder and adjust delay time
; **********************************************************************************
; * The rotary encoder is externally debounced, so we don't have to do that here.
; * You'll see it in the schematic labeled MOD2 on portJ0, portJ1, and portJ2.
; * 
; * The encoder's pin1 is sampled on a transition from high to low on pin0.  
; * if pin1 is high, a left turn occured, if pin1 is low, a right turn occured.
; **********************************************************************************

dec r15                 ; well, let's do a little debouncing:
brne adjust_UID         ; brne checks the Z register,
                        ; if r15 was not zero after the last operation, it will branch us to adjust_UID

ldi r17, 0x40           ; prepare a constant in r17                 
mov r15, r17            ; put 0x40 in the sample freq bin to catch all rising edges (results in 1.5ms sampletime)
lds r17, pinj           ; move port J data into r17
sbrs r17, PINJ0         ; skip next if PINJ0 is set
rjmp edgecheck_UID      ; if it's not set, is it a falling edge?

clt                     ; clear T reg (in SREG)
rjmp switchsample_UID   ; done looking at mod2, look at the program selector

; **********************
; **** edgecheck 
; **********************

edgecheck_UID:          ; checks for falling edge
brts switchsample_UID   ; if the T flag in SREG is set, assume the edge was already detected
set                     ; otherwise set the T flag
sbrs r17, PINJ1         ; check if PINJ1 is high
rjmp upcount_UID        ; if PINJ0 has just gone low and PINJ1 is low, a right turn has transpired
                        ; upcount will therefore increase the delay
                        ; otherwise, PINJ1 is high and a left turn has transpired, so we should decrease the delay
; **** downcount
ldi r17, 0x01           ; load our decrement amount into r17  (0x01 = 256 samples = 0.006s)
sub r13, r17            ; decrement delay MSB 

rjmp switchsample_UID   ; done looking at mod2, look at the program selector

; **** upcount
upcount_UID:            ; increment delay register
ldi r17, 0x01           ; load increment amount into r17
add r13, r17            ; increment MSB

; **********************
; **** switchsample
; **********************

switchsample_UID:       ; sample the program select dial

lds  r31, pinj          ; put switch data into jump location MSB reg
andi r31, 0x78          ; mask off rotary encoder 0b01111000
ldi  r17, 0x02          ; 0x02 into r17
lsr  r31                ; shift r31 to the right
lsr  r31                ; shift again
add r31, r17            ; convert switch position data to program memory location

; **********************
; **** adjust
; **********************

adjust_UID:             ; since we've only changed the desired delay in the previous section, we need to implement that delay
                        ; this checks to see if the delay time is correct,
                        ; and if it's not it makes an effort to move slightly closer to the correct delay 

andi r26, 0xFE          ; is the delay time even?
cp r26, r22             ; compare actual delay lsb to zero
cpc r27, r13            ; compare with carry actual delay msb with desired delay msb
breq done_UID           ; If equal, head to done, yay!  
brsh indexdown_UID      ; If the same or higher, branch to indexdown
                        ; otherwise, we can assume it is too low
; **** indexup
adiw r27:r26, 0x04      ; so increment delay register by 0x04
rjmp done_UID           ; and head to the end

; **** indexdown
indexdown_UID:
sbiw r27:r26, 0x02      ; decrement delay reg by 0x02

; **********************
; **** done
; **********************

done_UID:               ; It's been a long hard row to hoe, but we did it!  
                        ; oh wait, what?  I'm going to have to do it again?  from the beginning?  
                        ; but I still need to get my pointers all lined up!    

movw r29:r28, r25:r24       ; sync write destination and read address
sub r28, r26            ; now subtract by the delay in samples (first lsb)
sbc r29, r27            ; subtract msb with carry from previous

reti                    ; return from interrupt so we can get back to our idling 

MICrODEC

Microdec Software

Last edited 2010-07-18 by DanielMcAnulty · 2 revisions