; Default program 8
;
; Phase shifter from AN-5
; Mono in/out
;
; S1:S0 select 2,4,6 or 8 stages
; POT0 : rate
; POT1 : sweep range
; POT2 : resonance (feedback)
; POT3 : depth (mix)
; mr0 - mr7 are the AP delays
; mr8 - mr11 hold the result from each 2 stages for later selection
.rn temp r0 ; Temporary register
.rn feedback r1 ; Holds feedback
.rn kcoeff r2 ; K coefficient for all-pass filters
.rn input r3 ; Input from in0
.rn count r4 ; Window for the PWM
.rn bright r5 ; Number of states in count the LED is on, i.e. PWM width
.equ lfomax 2058874 ; LFO max about 5Hz at 32K, see an-3 for equations
.equ maxfb 0.875 ; Max feedback level
.equ rangebase -0.45*32767 ; We want the K for the APs to range from about -0.5 to -0.95
; but the "wrld" instruction want an unsigned 16-bit value so we
; play a trick with the assembler, we calculate the 16-bit signed
; number but use the ".l" extension when we use the value in wrdld
; as adding .l forces the assembler to treat the number as an
; integer and mask off the lower 16-bits as an unsigned number
; First set up the rate pot
cpy_cs temp, pot0_smth ; read POT0 into temp
multrr temp, temp ; square it so more control at lower range, result in acc32
wrdld temp, lfomax.u ; since wrdld uses just a 16-bit value use upper 16 bits, we could
; load the full 32-bit number but the max lfo speed is not critical
; in this program so losing the lower 16-bits is not an issue
multrr acc32, temp ; multiply pot by max range
cpy_sc lfo0_f, acc32 ; write it to LFO0 frequency control
; get the sin wave range -0.45 to -0.95 save in kcoeff
; NOTE: Phasers want a positive feed forward and negative feed back
; so make K negative as apma inverts in the feedback path and apmb does not
cpy_cs temp, lfo0_s ; read in sin wave ranges -1.0 to +1.0 (well, almost)
sra temp, 2 ; divide by 4 via right shift so ranges +/- 0.25
addsi acc32, -0.25 ; now ranges 0 to -0.5
cpy_cs temp, pot1_smth ; get the range pot
multrr temp, acc32 ; scale, result in acc32
wrdld temp, rangebase.l ; load in the base of -0.45 that we force the assembler to treat as unsigned
adds temp, acc32 ; add base so range can go from -0.45 to -0.95
cpy_cc kcoeff, acc32 ; save K in kcoeff for the APs
; get source and add feedback
cpy_cs input, in0 ; read from channel 0, put in input as we will want it later and working from core
; registers is faster than mregs
adds input, feedback ; add feedback from feedback
; shift 6 bits down to avoid clipping APs, recover at end.
sra acc32, 6 ; divide by 64 for headroom
; Do the 8 all passes saving the result every 2 APs for later selection
apma kcoeff, mr0 ; AP 1
apmb kcoeff, mr0
apma kcoeff, mr1 ; AP 2
apmb kcoeff, mr1
cpy_mc mr8, acc32 ; Save result for 2 stages
apma kcoeff, mr2 ; AP 3
apmb kcoeff, mr2
apma kcoeff, mr3 ; AP 4
apmb kcoeff, mr3
cpy_mc mr9, acc32 ; Save result for 4 stages
apma kcoeff, mr4 ; AP 5
apmb kcoeff, mr4
apma kcoeff, mr5 ; AP 6
apmb kcoeff, mr5
cpy_mc mr10, acc32 ; Save result for 6 stages
apma kcoeff, mr6 ; AP 7
apmb kcoeff, mr6
apma kcoeff, mr7 ; AP 8
apmb kcoeff, mr7
cpy_mc mr11, acc32 ; Save result for 8 stages
; Look at the switches and select the number of stages accordingly
cpy_cs temp, switch ; read in the switch sfr
andi temp, sw0|sw1 ; only keep S0 and S1
cpy_cc temp, acc32 ; save them
andi acc32, 0 ; clear acc32
ori acc32, 0x0008 ; put 8 into acc32
add acc32, temp ; add the switchs to acc32 so ranges 8 to 11 which happens
; to be the MRs used to save the AP results
cpy_cmx temp, acc32 ; use acc32 as the pointer to the mreg to read
sls temp, 6 ; multiply by 64 to recover from the initial shift, result in acc32
; use POT3 for mix level, full CCW is all dry (in0), full CW is full phase shifter output (fpo)
subs acc32, input ; acc32 = fpo - in0
cpy_cs temp, pot3_smth ; POT3 placed in temp
multrr temp, acc32 ; acc32 = acc32 * POT3 = POT3*(fpo - in0)
adds acc32, input ; acc32 = acc32 + in0 = POT3*(fpo - in0) + in0
; write to output
cpy_sc out0, acc32 ; output it!
cpy_sC out1, acc32
; adjust feedback level
cpy_cs r1, pot2_smth ; Read POT2
multrr acc32, r1 ; Multiply the output by the feedback
wrdld temp, maxfb*32768 ; We defined maxfb above as a decimal but need it to be unsigned 16-bit for wrdld
multrr acc32, temp ; Multiply by limit
cpy_cc feedback, acc32 ; save it in feedback for next time
; The PWM value becomes updated every 256 samples translating to a
; PWM frequency of 125Hz @32k with 8 bit resolution.
; While this is not exactly a high resolution PWM it might still
; good enough for generating basic control voltages in some applications.
; For driving the LEDs in this case it is perfectly enough.
cpy_cs acc32, samplecnt ; Get the sample counter
andi acc32, 0xFF ; Mask b[7:0]
jnz acc32, doPWM ;
; Reload new PWM value from LFO0_s into "bright"
cpy_cs temp, lfo0_s ; read in sin wave ranges -1.0 to +1.0 (well, almost)
sra temp, 1 ; /2 to +/- 1/2
addsi acc32, 0.5 ; ranges 0 to 1
sra acc32, 23 ; shift the PWM value in place
cpy_cc bright, acc32 ; save it
doPWM:
; Performing the decrement prior to driving the LED makes sure
; that the LED can go completly off.
addi bright, -1 ; subtract 1 from on count
cpy_cc bright, acc32 ; Save updated "bright"
xor acc32, acc32 ; Clear acc32 for the LED off case
jneg bright, doLED ;
ori acc32, 1 ; Set acc32[0] for the LED on case
doLED:
set user0|0, acc32 ; set the usr1 output per the acc32 LSB