I'm creating this post both as a way to help others get started with FXCore and as a way for me to get more familiar with the chip and its instruction set. I'll be posting my findings in a series of topics on this post, if you have questions or suggestions please let me know and I can update as needed.
GETTING STARTED
Part 1: Registers
A register is a memory location that stores a single value for use in the program. You can put pretty much anything in a register - an audio sample, a control signal value, whatever.
The FV-1 has 32 registers, REG0-REG31. It supports indirect addressing only, so you'll need to load the register into the accumulator first.
Let's say we want to read in a register, scale it by the value of a pot, save it back to the original register. Here's how you do that with FV-1:
Code: Select all
RDAX REG0, 1.000 ; load the contents of register 0 into the accumulator and multiply by 1.00
MULX POT0 ; multiply value in accumulator by pot 0
WRAX REG0, 0.000 ; write scaled value back to register 0, multiply accumulator by 0.00 / clear it
On FXCore we have only 16 registers, r0-r15 (note the difference in names!) and you can actually do things directly to register values without loading them in the accumulator first. BUT we can't use the pot values directly, so we have a few extra steps. Here's how you'd do the same thing as above but on FXCore:
Code: Select all
cpy_cs acc32, pot0_smth ; copy the smoothed value of pot 0 into the 32-bit accumulator. pot0 is a "special" register so use cpy_cs
multrr r0, acc32 ; multiply register 0 by the value in the accumulator - the new value is placed in acc32 automatically. use multrr to do register * register
cpy_cc r0, acc32 ; copy accumulator back into r0. acc32 and r0 are both "core" registers so use cpy_cc
You could also use a second register as the initial holding place for the pot value like this:
Code: Select all
cpy_cs r1, pot0_smth ; copy the smoothed value of pot 0 into core register r1. pot0 is a "special" register so use cpy_cs
multrr r0, r1 ; multiply register 0 by register 1 - the new value is placed in acc32 automatically. use multrr to do register * register
cpy_cc r0, acc32 ; copy accumulator back into r0. acc32 and r0 are both "core" registers so use cpy_cc
On FV-1, if you want to start a program with a value in a register you have to initialize it yourself like this
Code: Select all
SKP run, 3 ; run the following section once, then on each cycle we skip these 4 lines
SOF 0.00, 0.585 ; load accumulator with 0 + 0.585
WRAX reg0, 0.00 ; store to register 1 and clear accumulator
WRAX reg1, 0.00 ; store 0 to register 1
; rest of program follows
FXCore has a really neat feature that initializes core registers (CREGs) without eating your program space. They're processed in the assembler and then saved in a special section of the FXCore's internal memory.
Code: Select all
.creg r0 0.585 ; set register 0 to 0.585, note that we don't put a comma between these two terms
.creg r1 0.00 ; set register 1 to 0, we don't really need to do this, see below
You might see the same registers used over and over in the example programs, usually called something like "temp" or "temp2." Why would you do this? Well, the FXCore has half the registers of the FV-1. There are some tricks to get more registers on FXCore (see MREGs and Lookup Tables, below) but let's assume for the moment we just have the 16. My rule of thumb is "if the value has to survive to the next program cycle it needs its own register." Otherwise you're better off learning how to reuse registers to keep as many free as you can. Those extra registers might be the key to getting the effect you want, after all!
Here's an example that uses a pot to crossfade or mix between two signals. With the pot at counter-clockwise you get one signal, clockwise you get the other, and in the middle you get some mix of both.
Code: Select all
; register names - you don't have to do this but it helps!
.rn fade r0 ; assign name "fade" to r0
.rn wet r1 ; assign name "wet" to r1
.rn temp r2 ; assign name "temp" to r2
; grab the mix pot value and treat the wet signal - this assumes the output of the effect is in the "wet" register
cpy_cs fade, pot0_smth ; put value of pot 0 in "fade." note we don't have to put anything into the accumulator
multrr wet, fade ; multiply wet signal by fade, so it gets louder as the pot goes clockwise. result in acc32
cpy_cc wet, acc32 ; store result from acc32 back into wet, lets us re-use a register!
; now get the clean signal, scale and mix it with the wet from above
cpy_cs temp, in0 ; get clean signal and put it in the "temp" register so we can do stuff with it
neg fade ; change the sign of "fade" so that it ranges -0.999 to 0, result is in acc32
addsi acc32, 0.999 ; add 0.999 to acc32, so now the pot ranges from 1 to 0 for the dry signal
multrr acc32, temp ; scale the dry signal in temp by the reversed pot
adds acc32, wet ; add scaled wet back in, result is in acc32
cpy_sc out0, acc32 ; output acc32 to output 0.
We use "fade," "wet," and "temp" twice each in this section so while they don't have to survive the next sample they do need to survive for a few lines in the program. You can re-use them somewhere else in the program, but we need three right here. So you do need these three, but you could easily re-use that "temp" register several times in the program, as long as it doesn't need to stick around for the next sample.
Next time we'll cover manipulating numbers for fun and profit. Want to see a specific topic covered? Let me know.
(edited to correct tab spacing in code examples and replace "common" with "core" per Frank's suggestion)