Apr 21, 2012 7:03 AM
Delay effect
-
Like (0)
Hi,
I'm trying to make a clean digital delay as a starting point for some other effects. To do this, I am trying to set up a circular buffer using modulo addressing. However, in the modulus (ie: m0, m1... m7) can have a max value of 32768, meaning I am limited to about 0.8 s of delay.
Does anyone know how to use the Address Generation Unit (AGU) with a larger modulo or perhaps stacking several modulo buffers or if there is another approach altogether? Any example code too would be great.
Thanks!
Mike
In the 56300 family manual, section 4 is about the Address Generation Unit (AGU) and 4.5.3 explains modulo addressing.
Hi Mike,
the short answer is: yes, its possible. The long answer: its not that easy. You can't take benefit of the AGU directly since you cant't use the modifier registers for modulo adressing as you said since they are limited to 15-bit word length. I thought about that for a minute and got this simple idea:
1.) Use linear adressing - set the modifier to $xxFFFF
2.) You have to create your own modulo buffer. To do so you have to manage the pointer administration overhead by yourself - the classic approach.
3.) You can use a single pointer (any Rx corresponding to the Mx modifier) and read an old sample before overwriting it by the actual one.
4.) After each read/write procedure, increment the pointer and check if it has reached the end position (delay offset) for your delay buffer depth.
5.) If so reset the pointer to the original address
Here an idea for a code snippet (not tested)
bufstart equ $1000
delay equ $FFFF
bufend equ (bufstart + delay)
org x;
test ds 1
org p:
...
; initialization
move #bufstart,r1
move #$FFFFFF,m1
...
; this goes to loop
...
; get input value to x0
move x:(r1),x1 ; read sample out of delay buffer
move x0,x(r1)+ ; write input sample to delay line
; all the following is buffer management overhead - get your modulo addressing by hand.
move #bufend,x1 ; get buffer end address
move r1,a ; get the current delay line address for test
cmp x1,a ; see if pointer has reached the buffer end address - essentially the comparison (cmp) is a subtraction by x1 of a
brpl noreset ; if not, do not reset pointer - check if the result is positiv (pl). If so don't reset the pointer.
; You may also try brnz (branch is not zero) to match the buffer end point more exactly
move #bufstart,r1 ; if yes, set pointer to #bufstart
noreset
... ; more code goes here
Please give feedback if you could solve the problem. A good idea might be to check the code using the simulator. I will do so soon...
Best regards
Christian
Hi Mike,
now I've checked my 'homebrewn' modulo and removed some bugs. The following code is to be tested by the simulator:
; Assembler directives
bufstart equ $1000
delay equ $000F
bufend equ bufstart+delay
org p:$0 ; Look for proper code memory base address on real silicon
; This code goes to initialization
move #bufstart,r1 ; Here the pointer to the arbitrary length modulo buffer is placed.
; The restrictive rules for modulo addressing are no longer valid...
move #$FFFFFF,m1 ; ..since we use the linear addressing mode
; This code goes to main loop
loop
; Get input value to x0 in advance - this code isn't listed here!
move x:(r1),x1 ; read sample out of delay buffer
move x0,x:(r1)+ ; write new input sample to delay line
; all the following is buffer management overhead - get your modulo addressing by hand.
; Anyway this isn't too much overhead compared to the strong modulo addressing rules!
; We just check if the pointer r1 has reached the 'modulo' buffer end
; - if so, we just reset it to the original address location
move #bufend,x1 ; get buffer end address
move r1,a ; get the current delay line address for test if modulo buffer end is reached now
cmp x1,a ; see if pointer has reached the buffer end address
; - essentially the comparison (cmp) is a subtraction by x1 of a
jne noreset ; if not, do not reset pointer - check if the result is not equal (ne).
; If not so don't reset the pointer by jumping over the next instruction.
move #bufstart,r1 ; if so, reset pointer to #bufstart
noreset
jmp loop
; more code goes here
Hi Christian,
I just got around to trying this today and this approach works great, thanks. I took Flange 0.95 from Luke Durback as the base code and implemented the manual buffer pointer management. This resulted in a flanger with a really long predelay - long enough to make it distinct like in a delay pedal. The flange effect with the very short oscillating delay is still applied to the repeats. I'll have to post some code once it's cleaned up.
Cheers,
Mike
page 132,60
include "..\Ioequ.inc"
include "..\vectors4.inc"
list
;---------------------------------------------------------------------------------------------------
;This is a modification of Luke Durback's Flange 0.95 to extend the parameter "minimum delay",
;set by knob 4 in order to achieve a standard guitar delay effect (A flanger is also a delay
;but with a very short (order of ms) modulating delay time creating a comb filter as a result.
;To make the normal delay, I extend this minimum delay time into the order of seconds.
;The problem was that simply increasing the size of constant "MinDelayLimit" beyond a certain
;point does not work as modulo buffer addressing can have a max value of 32768, limiting delay
;time to about 0.8 s. The solution in this code is circular buffer management that is implemented manually as overhead code.
;Nested three times (read after main delay, read after flange delay, write) between the lines
;"mk buffer management" and "mk end buffer management" is this code. The constants section has also changed to reflect
;the longer delay times and additional buffer allocation.
;The multiple voice loop has been disabled. What occurs is the minimum delay (first read) offers
;the perceptible digital delay, followed by the second short flange delay applied to the repeat
;of the main delay. The flange delay depth is set by knob 5, and its oscillation frequency by knob 1.
;You'll hear what I mean.
;
;Start of Luke's code:
;
; Flanger 0.95
; This file is setup to work with the Line 6 / Freescale Tone Core Developer s Kit.
; This effect is a simple multi-voice flange/chorus. The code is based on Line 6 s 2 Band Stereo EQ.
;
;
; Knobs
; *1st knob sets the lfo frequency
; *2nd knob sets the depth (how much of the delayed sample is added to the current sample)
; *3rd knob sets the feedback (how much of the processed sample is added into the delay buffer)
; *4th knob sets the minimum delay
; *5th knob sets the delay width
; *6th knob sets the separation between voices
;
; Switch 1 the number of voices: 1, 2, or 3
;
; Switch 2 sets the lfo shape
; *position 0 - sine
; *position 1 - exponential
; *position 2 - linear
;
; Flange 0.95:
; Luke Durback, March 14, 2009
;
;
; 2 Band Stereo EQ:
; Line 6, November 30, 2007
; Copyright (c) 2008 Line 6 Inc
; Line 6 Confidential Information
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; Constants
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
BufferSize equ 35000 ; the buffer size, originally 4096
DelayLimit equ 300 ; the limit on the delay length, originally 300
MinDelayLimit equ 30000 ; the limit on the delay offst, originally 600
SeperationLimit equ 600 ; the limit on the seperation between voices, originally 600
; make sure that
; BufferSize > DelayLimit + MinDelayLimit + 2*SeperationLimit, and then some (increase till it works)
LFOIncMin equ $0 ;originally $40
LFOIncLimit equ $2000 ;originally $2000
AccessSRAMWord0 equ $982245 ; 1 0 011 00 0 00100 0 100100 01 01
; DMA Control Register for channel 0
; bit(s)value description
; [23] = DE = 1 DMA Operation disabled (Trigger DMA transfer)
; [22] = DIE = 0 DMA Interrupt disabled
; [21:19] = DTM[2:0] = 011 triggered by DE, DE=0 after done
; [18:17] = DPR[1:0] = 00 priority level 0
; [16] = DCON = 0 Continuous mode disabled
; [15:11] = DRS[4:0] = 00100 Transfer done from channel 0
; [10] = D3D = 0 non 3-d mode
; [9:4] = DAM[5:0] = 100100 no update s/d
; [3:2] = DDS[1:0] = 01 Y memory destination
; [1:0] = DSS[1:0] = 01 Y memory source
AccessSRAMWord1 equ $982A45 ; 1 0 011 00 0 00101 0 100100 01 01
; DMA Control Register for channel 0
; bit(s)value description
; [23] = DE = 1 DMA Operation disabled (Trigger DMA transfer)
; [22] = DIE = 0 DMA Interrupt disabled
; [21:19] = DTM[2:0] = 011 triggered by DE, DE=0 after done
; [18:17] = DPR[1:0] = 00 priority level 0
; [16] = DCON = 0 Continuous mode disabled
; [15:11] = DRS[4:0] = 00101 Transfer done from channel 1
; [10] = D3D = 0 non 3-d mode
; [9:4] = DAM[5:0] = 100100 no update s/d
; [3:2] = DDS[1:0] = 01 Y memory destination
; [1:0] = DSS[1:0] = 01 Y memory source
;**************************************************************************
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; Variables
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
org x:$000
;...Knob registers expect a value of 0x000000 thru 0x7FFFFF from the microcontroller
Knob_1 ds 1 ; 00h
Knob_2 ds 1 ; 01h
Knob_3 ds 1 ; 02h
Knob_4 ds 1 ; 03h
Knob_5 ds 1 ; 04h
Knob_6 ds 1 ; 05h
;...Switch registers expect a value of 0,1, or 2 from the microcontroller
Switch_1 ds 1 ; 06h
Switch_2 ds 1 ; 07h
;...FootSwitch registers expect a value of 0x000000 or 0x000001 from the microcontroller
FootSwitch_TopLayer ds 1 ; 08h
FootSwitch_BottomLayer ds 1 ; 09h
;...LED registers are read by the micro and should be set to 0x000000 or 0x000001 (off and on)
LED_Red ds 1 ; 0ah
LED_Green ds 1 ; 0bh
;...RX and TX registers
LeftRx ds 1 ; 0ch Left received sample
RightRx ds 1 ; 0dh Right received sample
LeftTx ds 1 ; 0eh Left sample to transmit
RightTx ds 1 ; 0fh Right sample to transmit
SHI_StateMachine ds 1 ; 10h current state of the serial host interface
HostCommand ds 1 ; 11h current SHI command
;...Debug registers
Debug_Write_to_DSP_1 ds 1 ; 12h recieves data from the ToneCoreGUI application
Debug_Write_to_DSP_2 ds 1 ; 13h recieves data from the ToneCoreGUI application
Debug_Write_to_DSP_3 ds 1 ; 14h recieves data from the ToneCoreGUI application
Debug_Write_to_DSP_4 ds 1 ; 15h recieves data from the ToneCoreGUI application
Debug_Read_from_DSP_1 ds 1 ; 16h send data to the ToneCoreGUI application
Debug_Read_from_DSP_2 ds 1 ; 17h send data to the ToneCoreGUI application
Debug_Read_from_DSP_3 ds 1 ; 18h send data to the ToneCoreGUI application
Debug_Read_from_DSP_4 ds 1 ; 19h send data to the ToneCoreGUI application
;...FootLatch says whether the simulated latch is up or down
;...FootLatchMem is compared to the current state of FootSwitch_BottomLayer to decide
; whether the simulated latch is up or down
FootLatch ds 1 ; 1Ah
FootLatchMem ds 1 ; 1Bh
LeftInput ds 1 ; 00h
LeftOutput ds 1
Depth ds 1
LFOPhase ds 1
LFOInc ds 1
Delay ds 1
MinDelay ds 1
Feedback ds 1
MaxDelayLength ds 1
Voices ds 1
VoiceSeperation ds 1
Temp ds 16
org y:$000
;...Chan0 is for reading from SRAM
Chan0IO ds 16 ; 04h
;...Chan1 is for writing to SRAM
Chan1IO ds 16 ; 14h
org y:$200000
Buffer dsm (BufferSize)
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; Macros
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
HandleFootSwitch MACRO
;Handles the foot switch, turns on/off bypass if need be
;Use Bottom layer of FootSwitch to turn on/off analog bypass
;
; Uses b, x0, and x1
;
; take the momentary footswitch signal (pulse) and generate a latched (step) control signal
move x:FootSwitch_BottomLayer,b ; load unmodified footswitch input
move x:FootSwitch_BottomLayer,x1 ; load unmodified footswitch input
not b x:FootLatchMem,x0 ; not a (also, load the previous momentary signal into x0)
and x0,b x1,x:FootLatchMem ; x0 & a (also, store current momentary signal for next iteration)
move x:FootLatch,x0 ; load previous latched value
eor x0,b ; generate current latched (step) control signal
move b1,x:FootLatch ; store latched footswitch signal created from momentary footswitch
move x:FootLatch,b ; load latching footswitch signal
jclr #0,b,_ANALOG_BYPASS ; bit clear = bypass
movep #>$000008,x:M_PDRB ; bit 3 on = FX_ON Also Pre-Emph, Post-DeEmph Off, Direct Off
bset #0,x:LED_Green ; turn led on to indicate effect on
jmp _END_ANALOG_BYPASS
_ANALOG_BYPASS:
movep #>$000004,x:M_PDRB ; bit 2 on = DIRECT_ON, All else off
bclr #0,x:LED_Green ; turn led off to indicate effect off
_END_ANALOG_BYPASS:
ENDM
TransferYData0 MACRO SRC,DEST
; Transfer data y:(SRC) into y:(DEST) using Chan0
;;;Primarily used for reading from SRAM
move SRC,x:M_DSR0 ; Set chan1 source
move DEST,x:M_DDR0 ; Set chan1 dest
jclr #0,x:M_DSTR,* ; Wait until previous transfers are finished
movep #AccessSRAMWord0,x:M_DCR0 ; Initiate transfer for next sample
ENDM
WriteToSRAM MACRO DATA,DEST
; Store data in SRAM at y:(DEST) using Chan1
; DATA - register where sample is stored (a,b,x,y,a1,a0,b1,b0,x1,x0,y1,y0)
; DEST - r[0-5] where to store sample
;----------------
;-Uses first word of Chan1IO
;----------------
move DATA,y:Chan1IO ; Put data in Chan0IO
movep DEST,x:M_DDR1 ; Set chan0 dest
jclr #1,x:M_DSTR,* ; Wait until previous writes are finished
movep #AccessSRAMWord1,x:M_DCR1 ; Initiate write
ENDM
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; Code Start
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
org p:$4E
START
ori #$03,mr ; mask interrupts
movep #$2D0063,X:M_PCTL ; Set PLL Control Register
; Fosc = 100 MHz = (100)(3 MHz)/3
bset #14,omr ; allow address attributes line to function independently
movep #>$000040,x:M_SAICR ; 4x4 Synchronous mode (use TX frame and bit clks)
movep #$FCC304,x:M_TCCR
;THCKD -1- HCKT is an output (bit 23)
;TFSD -1- FST is output (bit 22)
;TCKD -1- internal clock source drives SCKT (bit 21)
;THCKP -1- Transmitter High Freq Clock Polarity (bit 20)
;TFSP -1- negative FST polarity (bit 19)
;TCKP -1- data & FST clocked out on falling edge (bit 18)
;TFP -3- TFP3..0 = Divide by 4 (bits 17:14)
;TDC -1- 2 words per frame (bits 13:9)
;TPSR -1- Bypass Fixed /8 Prescaler (bit 8)
;TPM -4- TPM7-0 = Divide by 5 (bits 7:0)
movep #$FCC304,x:M_RCCR
;RHCKD -1- HCKR is an output Flag 2 (bit 23)
;RFSD -1- FSR is output Flag 1 (bit 22)
;RCKD -1- SCKR is output Flag 0 (bit 21)
;RHCKP -1- Pos. High Freq Clock Polarity (bit 20)
;RFSP -1- negative FSR polarity (bit 19)
;RCKP -1- data & FSR clocked in on falling edge (bit 18)
;RFP -3- RFP3..0 = Divide by 4 (bits 17:14)
;RDC -1- 2 words per frame (bit 13:9)
;RPSR -1- Bypass Fixed /8 Prescaler (bit 8)
;RPM -4- RPM7-0 = Divide by 5 (bits 7:0)
movep #$707d00,x:M_RCR
;RE --- RX0, RX1, RX2, RX3 disabled (bit3:0=0000)
;RSHFD -0- MSB shifted first (bit6=0)
;RWA -0- word left-aligned (bit7=0)
;RMOD -1- network mode (bit9:8=01)
;RSWS 1F- 32-bit slot length, 24-bit word length (bit14:10=11111)
;RFSL -0- word-length frame sync (bit15=0)
;RFSR -0- frame sync occurs 1 clock cycle earlier (bit16=1)
; reserved (bit18:17=00)
;RPR ?-0- transmit normally, not personal reset (bit19=0)
; RIE, REDIE, REIE enabled (bit23:20=0111)
;RLIE --- bit23 RLIE
;RIE --- bit22 RIE
;REDIE --- bit21 REDIE
;REIE --- bit20 REIE
movep #$027D80,x:M_TCR
;TE --- Start w/ TX0-TX5 disabled (bit5:0=000000)
;TSHFD -0- MSB shifted first (bit6=0)
;TWA -1- word left-aligned (bit7=0)
;TMOD -1- network mode (bit9:8=01)
;TSWS 1F- 32-bit slot length, 24-bit word length (bit14:10=11111)
;TFSL -0- word length frame sync (bit15=0)
;TFSR -0- frame sync occurs 1 clock cycle earlier (bit16=1)
;PADC -1- zero padding enabled (bit17=1)
; reserved (bit18)
;TRP ?-0- transmit normally, not personal reset (bit19=0)
; TLIE, TIE, TEIE disabled (bit23:20=0000)
;TLIE --- bit23 TLIE
;TIE --- bit22 TIE
;TEDIE --- bit21 TEDIE
;TEIE --- bit20 TEIE
movep #>$000000,x:M_PDRC ; Clear Port C data
movep #>$000BF8,x:M_PCRC ; Set appropriate Port C GPIO pins for ESAI .
movep #>$000C7E,x:M_PRRC ; Set pin direction of PORT C
;sdo0 sdo1 sdo2 sdo3 sdo4 sdo5 hckt fst sckt hckr fsr sckr
;ESAI GPO GPI GPI GPI ESAI ESAI ESAI ESAI GPO GPO NC
;DAC Data Inhibit IRQA Stereo/Mono A_In B_In ADC_Data 256FS_CLK0 FS_CLK 64FS_CLK Gain Switching Codec_Reset SS_Module
movep #>$000000,x:M_PCRB ; Set up Port B for output
movep #>$00000F,x:M_PRRB ; Set up Port B for output
movep #>$000008,x:M_PDRB ; bit 0 = In_EMPH
; bit 1 = OUT_DE_EMPH
; bit 2 = DIRECT_ON
; bit 3 = FX_ON
movep #>$000003,x:M_RSMA ; Enable first 2 time slots for receive.
movep #>$000000,x:M_RSMB ;
movep #>$000003,x:M_TSMA ; Enable first 2 time slots for transmit.
movep #>$000000,x:M_TSMB
movep #>$000000,x:M_TX0 ; zero out transmitter 0
;ENABLE ESAI
bset #0,x:M_RCR ; now enable RX0
bset #0,x:M_TCR ; now enable TX0
;...Setup Expansion Port A for SRAM...
movep #$2406B5,x:M_AAR0
; [23:12] = 0x240 Address used to assert chip select
; [11:8] = 0110 Number of address bits to compare = 6
; [7] = 1 Bit Packing Enabled
; [6] = 0 Address Mux Disabled
; [5] = 1 Y Space Enabled
; [4] = 1 X Space Enabled
; [3] = 0 P Space Disabled
; [2] = 1 Active High for Address Line
; [1:0] = 01 SRAM Mode
movep #$2003B1,x:M_AAR1
; [23:12] = 0x200 Address used to assert chip select
; [11:8] = 0011 Number of address bits to compare = 3
; [7] = 1 Bit Packing Enabled
; [6] = 0 Address Mux Disabled
; [5] = 1 Y Space Enabled
; [4] = 1 X Space Enabled
; [3] = 0 P Space Disabled
; [2] = 0 Active Low for Chip Select
; [1:0] = 01 SRAM Mode
;...Bus Control Register for SRAM...
movep #$0124A5,x:M_BCR ; 0001 0010 0100 0110 0011
;bus request hold off (bit23=0)
;bus lock hold off (bit22=0)
;bus state (bit21=0)
;default area wait states (bit20:16=00001)
;area 3 wait states (bit15:13=001)
;area 2 wait states (bit12:10=001)
;area 1 wait states (bit9:5=00011)
;area 0 wait states (bit4:0=00011)
;...Set up SHI (Serial Host Interface to the MCU)...
movep #>$003001,x:M_HCKR ; Turn Data/Clk Line Filter to max, wide spike tolerance (100ns glitch)
; CPHA=1, CPOL=0 : => same as reset/power-on.
movep #>$001189,x:M_HCSR ;
;...Initialize registers
move #>$000000,x0
move x0,r0
rep #26
move x0,x:(r0)+ ; clear start of x:mem
move #>$400000,x0 ; Intialize the knob registers
move x0,x:Knob_1
move x0,x:Knob_2
move x0,x:Knob_3
move x0,x:Knob_4
move x0,x:Knob_5
move x0,x:Knob_6
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; Setup
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Setup:
; Initialize the DMA counter register, only needs to be done once
move #>$000000,x0
nop
movep x0,x:M_DCO0 ; set the number of transfers to 1 for a single word read/write
movep x0,x:M_DCO1
; Dedicate Chan0 to read from buffer & write to y:Chan0IO
movep #Chan0IO,x:M_DDR0
; Dedicate Chan1 to read from y:Chan1IO & write to buffer
movep #Chan1IO,x:M_DSR1
move #0,x0
move x0,x:LFOPhase
move x0,x:LFOInc
move x0,x:Delay
move x0,x:MinDelay
move #>DelayLimit,y0
move y0,x:MaxDelayLength
move #$000001,y0
move y0,x:Voices
move #$000010,y0
move y0,x:VoiceSeperation
move #>Buffer,r0
move #>Buffer,r1
move #>$0001ff,m3
;mkout move #>(3*BufferSize-1),m0
move #$FFFFFF,m0
;mkout move #>(3*BufferSize-1),m1
move #$FFFFFF,m1
move #>$00ffff,m2
move #>(3*BufferSize),y0
move #>3,n0
move #>Chan0IO,r6
move #>$000000,x0
move #$00ffff,m5 ; Use r5 for the MCU parameter updates.
movep x0,x:M_HTX ; Assert HREQ* pin for the MCU.
;...Initialize Peripheral Interrupt Priority Register for Audio Interrupts and SHI.
movep #$000007,x:M_IPRP ; ESAI int enabled and top Priority
; SHI int enabled and lowest Priority.
andi #$FC,mr ;enable all interrupt levels
;clear scaling bits
movep #>$000002,x:M_PDRC ; Take CODEC out of power down mode.
;------------------------------------------------------------
; Main loop
;------------------------------------------------------------
nop
nop
dor forever,LOOP
wait
nop
nop
nop
LOOP
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; 4x4 Interrupt Service Routines
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;-----------------------------
; Host Interrupt
;-----------------------------
shi_receive
movep x:M_HRX,x:HostCommand ; Get word from 8031.
movep #000000,x:M_HTX ; Assert HREQ* pin for 8031.
btst #0,x:HostCommand ; $000001 = Write 1 DSP word to X:mem.
bcs DoWriteCommand ;
btst #3,x:HostCommand ; $000008 = Send word to the 8031.
bcs DoReadCommand ;
DoWriteCommand
jclr #M_HRNE,x:M_HCSR,* ; Wait for address.
movep x:M_HRX,r5 ; Store address to write to.
movep #>000000,x:M_HTX ; Assert HREQ* pin for 8031.
jclr #M_HRNE,x:M_HCSR,* ; Wait for data
movep x:M_HRX,x:(r5) ; Write data.
movep #000000,x:M_HTX ; Assert HREQ* pin for 8031.
rti
DoReadCommand
jclr #M_HRNE,x:M_HCSR,*
movep x:M_HRX,r5 ; Store Address to read from
movep #>000000,x:M_HTX
jclr #M_HRNE,x:M_HCSR,*
movep x:M_HRX,n5
movep x:(r5),x:<<M_HTX ; Send Data at specified address to the 8031
jclr #M_HRNE,x:M_HCSR,*
movep x:M_HRX,n5
movep #>000000,x:<<M_HTX
rti
;-----------------------------
; Receive Exception Interrupt
;-----------------------------
esai_rxe_isr ; ESAI RECEIVE ISR
bclr #7,x:M_SAISR ; Read SAISR to clear receive
; overrun error flag
bclr #14,x:M_SAISR ; Read SAISR to clear transmit
; underrun error flag
movep x:M_RX0,x:RightRx ;
movep x:RightTx,x:M_TX0 ;
rti
;-----------------------------
; Receive Interrupt
;-----------------------------
esai_rx_isr
movep x:M_RX0,x:RightRx
movep x:RightTx,x:M_TX0
rti
;-----------------------------
; Receive Even Slot Interrupt
;-----------------------------
esai_rxeven_isr
; Mono effect, so no right line
movep x:M_RX0,x:LeftRx
move x:LeftOutput,x0 ;
move x0,x:LeftTx ; Transmit the output from the last sample period.
movep x:LeftTx,x:M_TX0
move x:LeftRx,a ; receive left
move a,x:LeftInput ; Save Current LeftInput sample
;Note: r5 and n5 are currently reserved for Host Interrupts
;Get some values from the knobs
;LFOInc = (Knob_1)^2 * (LFOIncLimit-LFOIncMin) + LFOIncMin
move x:Knob_1,x0
move x0,y0
mpy x0,y0,a
move a,x0
move #>(LFOIncLimit-LFOIncMin),y0
mpy x0,y0,a
move #>LFOIncMin,x0
add x0,a
move a,x:LFOInc
;Depth = Knob_2
move x:Knob_2,x0
move x0,x:Depth
;Feedback = Knob_3
move x:Knob_3,x0
move x0,x:Feedback
;MinDelay = Knob_4*MinDelayLimit + 1
move x:Knob_4,x0
move #>MinDelayLimit,y0
mpy x0,y0,a
add #>$000001,a
move a,x:MinDelay
;MaxDelay = Knob_5 * DelayLimit
move x:Knob_5,x0
move #>DelayLimit,y0
mpy x0,y0,a
move a,x:MaxDelayLength
;VoiceSeperation = Knob_6 * SeperationLimit
move x:Knob_6,x0
move #>SeperationLimit,y0
mpy x0,y0,a
move a,x:VoiceSeperation
;Voices = Switch_1 + 1
move x:Switch_1,a
move #>$0000001,x0
add x0,a
move a,x:Voices
move x:LeftInput,a ; a = x[n] = newest input sample
jsr ProcessSample
move a,x0 ;double output amplitude
add x0,a
move a,x:LeftOutput ; write the output, note by choosing a here it will round as appropriate
;increase lfo
move x:LFOInc,n2
move x:LFOPhase,r2
lua (r2)+n2,r2
move r2,x:LFOPhase
;update delay
jsr CalculateDelay
HandleFootSwitch
rti
ProcessSample:
; sample in a
;
;
;READ FIRST VOICE
move a,y:(r0) ; overwrite sample at y:(r0) with current input (only matters when delay = 0)
; which may not ever happen
move a,x1 ; put copy of sample into x1 so I can later add it back for feedback
move r0,r1 ; r0 & r1 now hold the buffer index
move x:Delay,x0 ; load delay amount in samples
move x0,b
add x0,b
add x0,b
move b,n1
lua (r1)-n1,r1 ; load delayed sample into y0
;mk buffer management
move #>Buffer,b
move r1,x0 ;get current delay address pointer to see if at modulo end of buffer
cmp x0,b ;see if pointer has reached buffer end address
jle noreset1 ;if not, do not reset pointer - check result is less than
;if not, do not reset point by jumping over next address
move #>(3*BufferSize),b
move r1,x0
add x0,b
move b,r1
noreset1
;move r1,x:Debug_Read_from_DSP_3
;move n1,x:Debug_Read_from_DSP_4
;mk end buffer management
; this line is new
;READ NEXT VOICE(S)
move x:VoiceSeperation,x0
move x0,b
add x0,b
add x0,b
move b,n1
move x:Depth,x0 ; x0 = Depth
;replace sample with (1-depth)*sample
move #>$7fffff,b
sub x0,b
move b,x0
move x0,x:Debug_Read_from_DSP_2
move a,y0
mpy x0,y0,a
;;;;;;;;;;;;;;;;;;;
; move y:(r1),y0
;new code for multiple voices
;move x:Voices,y1
;dor y1,_ProcessVoice
TransferYData0 r1,r6
jclr #0,x:M_DSTR,* ; Wait until previous transfers are finished
move y:Chan0IO,y0
lua (r1)-n1,r1 ; load delayed sample into y0
;mk buffer management
move #>Buffer,b
move r1,x0 ;get current delay address pointer to see if at modulo end of buffer
cmp x0,b ;see if pointer has reached buffer end address
jle noreset2 ;if not, do not reset pointer - check result is less than
;if not, do not reset point by jumping over next address
move #>(3*BufferSize),b
move r1,x0
add x0,b
move b,r1
noreset2
;mk end buffer management
; put depth back into x0 (since it had 1-depth a second ago)
;_ProcessVoice
;WRITE TO BUFFER
add y0,a
move x:Feedback,x0 ; put feedback multiplier into x0
move x0,x:Debug_Read_from_DSP_4
mpy x0,y0,b
move x:Depth,x0
move x0,x:Debug_Read_from_DSP_3
mac x0,x1,b
WriteToSRAM b,r0 ; put b into delay buffer
;WriteToSRAM a,r0
;lua (r0)+n0,r0 ; update delay buffer address
lua (r0)+n0,r0 ; load delayed sample into y0
;mk buffer management
move #>Buffer,x0
move #>(3*BufferSize),b ;move buffer length into b
add x0,b
move b,x0
move r0,b ;get current delay address pointer to see if at modulo end of buffer
cmp x0,b ;see if pointer has reached buffer end address
jlt noreset3 ;if not, do not reset pointer - check result is less than
;if not, do not reset point by jumping over next address
sub x0,b ;determine difference outstanding to add to bottom of buffer
move #>Buffer,x0 ;if so, reset the pointer to Buffer start location
add x0,b
move b,r0
noreset3
;mk end buffer management
;move n0,x:Debug_Read_from_DSP_2
;move r0,x:Debug_Read_from_DSP_1
rts
CalculateDelay:
; use LFOPhase to calculate the current Delay
; no register inputs, modifies x:Delay
; using x:LFOPhase
; Switch_2 selects which function to use for the lfo
move x:LFOPhase,a ; put LFOPhase into a, will be passed
; to another function
move x:Switch_2,b
cmp #1,b
jlt _Sine
jeq _Exp
jgt _LinearTriangle
_Sine
jsr SineVal
jmp _DoneCalculating
_Exp
jsr ExpVal
jmp _DoneCalculating
_LinearTriangle:
jsr AbsVal
jmp _DoneCalculating
_DoneCalculating:
; output of the previous function is a number between 0 and 1
; multiply this by x:MaxDelay and then add MinDelay to get the current
; delay amount
move x:MaxDelayLength,x0
move a,y0
move x:MinDelay,a
mac x0,y0,a
move a,x:Delay
rts
SineVal:
; returns a Bezier approximation of 1/2(Sin(a*PI)+1)
; 2(x(1-|x|)+1/2)
; let us call the input x
move a,y0 ; y0 = x
abs a ; a = |x|
move a,x0 ; x0 = |x|
move #>$7fffff,a ; a = 1
sub x0,a ; a = 1-|x|
move a,x0 ; x0 = 1-|x|
mpy x0,y0,a ; a = x*(1-|x|)
add #>$200000,a ; a = x*(1-|x|)+1/2
move a,x0 ; x0 = x*(1-|x|)+1/2
add x0,a ; a = a+x0 = 2(x(1-|x|)+1/2) and we are done
rts
ExpVal:
; calculates a 3rd order taylor approximation of (e^x-1)/(e-1)
; 9/15 = 0x4ccccc.ccccc
; 1/6 = 0x155555.55555
; formula = 9/15*(x+x^2/2+x^3/6)
abs a
move #$4ccccd,x0
move a,y0
mpy x0,y0,b
move b,x:Temp
mpy y0,y0,b
move b,y0
mpy x0,y0,b ; b = x^2
asr #1,b,b ; b = x^2/2
move #$4ccccd,x0
move b,y0
mpy x0,y0,b ; b = 9/15*x^2/2
move b,x:(Temp+1)
move a,y0
mpy y0,y0,b
move b,x0
mpy x0,y0,b ; b = x^3
move b,y0
move #$155555,x0
mpy x0,y0,b ; b = x^3/6
move #$4ccccd,x0
move b,y0
mpy x0,y0,a ; a = 9/15*x^3/6
move x:Temp,x0 ; add up previous numbers
move x:Temp,y0
add x0,a
add y0,a
rts
AbsVal:
abs a
rts
;SquareVal:
; calculate 2^a/2^0x7fffff
; move a,x0
; move a,y0
; mpy x0,y0,a
; rts
Stay in the mix and in the know.
Latest offers, special deals and insider updates.