Writing your first FV-1 program
This guide builds up a working tremolo from nothing, one step at a time. Every example here assembles cleanly and can be pasted straight into the Sandbox FV-1 Editor.
You do not need a pedal to follow along — the simulator will play all of it.
How an FV-1 program works[edit | edit source]
The FV-1 runs your program once per audio sample, 32768 times a second. There is no main loop that you write; the chip loops for you.
That gives you a hard budget of 128 instructions. Anything shorter is padded with NOPs automatically. There is no way to spend more time on one sample than another.
Almost everything flows through a single accumulator (ACC). You read a value into it, do arithmetic on it, and write it somewhere. Most instructions multiply by a coefficient on the way past.
The registers you will use first:
| Register | Meaning |
|---|---|
ADCL, ADCR |
Audio input, left and right |
DACL, DACR |
Audio output, left and right |
POT0–POT2 |
The three control knobs, 0.0 to 1.0 |
REG0–REG31 |
General-purpose storage |
Coefficients are fixed-point fractions in the range −1.0 to just under 1.0. You cannot write 1.0 and expect exactly one in every field, which is why you will see 0.999 in places where you might expect 1.
Step 1: pass-through[edit | edit source]
The smallest useful program copies input to output.
RDAX ADCL, 1.0 ; ACC = left input WRAX DACL, 0.0 ; left output = ACC, then clear ACC RDAX ADCR, 1.0 ; ACC = right input WRAX DACR, 0.0 ; right output = ACC, then clear ACC
Two instructions per channel. RDAX reads a register into the accumulator, multiplying by the coefficient on the way in. WRAX writes the accumulator to a register and then multiplies what remains in ACC by its coefficient.
That second coefficient is the part that catches people out. WRAX DACL, 0.0 means "write ACC to the left DAC, then zero the accumulator" — which is what you want before starting work on the other channel. Using WRAX DACL, 1.0 would leave the value in ACC and the right channel would come out as a mix of both.
Assemble this and play it through the simulator. It should sound like nothing at all, which is the correct result.
Step 2: a volume control[edit | edit source]
Now put POT0 in charge of the level.
RDAX ADCL, 1.0 MULX POT0 ; ACC = ACC * POT0 WRAX DACL, 0.0 RDAX ADCR, 1.0 MULX POT0 WRAX DACR, 0.0
MULX multiplies the accumulator by the contents of a register. Because POT0 reads 0.0 to 1.0, this is a straightforward volume knob.
Open the SIM panel and move the POT0 slider while it plays.
Step 3: an LFO[edit | edit source]
A tremolo is a volume control that moves on its own, so the next piece is a low-frequency oscillator.
The FV-1 has two sine LFOs (SIN0, SIN1) and two ramp LFOs (RMP0, RMP1). You start a sine LFO with WLDS:
WLDS SIN0, 40, 32767 ; LFO 0: rate 40, full amplitude
This only needs to happen once, not on every one of the 32768 samples per second. The idiom for that is a conditional skip on the RUN flag, which is clear on the very first pass through the program and set on every pass after:
SKP RUN, loop WLDS SIN0, 40, 32767 loop:
On the first sample, RUN is clear, so the skip does not happen and the LFO is set up. On every subsequent sample the program jumps straight over WLDS to the loop label.
To read the LFO's current value, use CHO RDAL:
CHO RDAL, SIN0 ; ACC = the sine LFO, swinging -1.0 to +1.0
Step 4: shaping the LFO into a gain[edit | edit source]
The LFO swings from −1.0 to +1.0, but a gain needs to be positive. SOF — scale and offset — fixes that in one instruction. SOF a, b computes ACC = ACC × a + b.
CHO RDAL, SIN0 ; -1.0 .. +1.0 SOF 0.5, 0.5 ; halve it, shift it up: now 0.0 .. 1.0
Next, depth. Multiplying by POT1 scales the swing down, and a second SOF flips it so the gain sits just below unity at minimum depth:
MULX POT1 ; 0.0 .. depth SOF -1.0, 0.999 ; (1 - depth) .. 1.0
With POT1 at zero the gain is a constant 0.999 — effectively off. With POT1 fully up the gain swings across the full range.
The finished tremolo[edit | edit source]
; Simple tremolo ; POT0 = rate, POT1 = depth EQU mod REG0 ; Start the sine LFO once, on the first pass only SKP RUN, loop WLDS SIN0, 40, 32767 loop: ; POT0 sets the LFO rate RDAX POT0, 0.5 SOF 1.0, 0.02 WRAX SIN0_RATE, 0.0 ; Read the LFO and fold it into a 0..1 gain CHO RDAL, SIN0 SOF 0.5, 0.5 MULX POT1 SOF -1.0, 0.999 WRAX mod, 0.0 ; Apply the gain to both channels RDAX ADCL, 1.0 MULX mod WRAX DACL, 0.0 RDAX ADCR, 1.0 MULX mod WRAX DACR, 0.0
Sixteen instructions, out of the 128 available.
Two new things appear here. EQU mod REG0 gives REG0 a readable name — the assembler substitutes it, and it costs nothing at runtime. Writing to SIN0_RATE changes the LFO rate while the program runs, which is what turns POT0 into a rate knob; the SOF 1.0, 0.02 keeps the rate from reaching zero at the bottom of the sweep.
Load it into the simulator, press Play, and move POT0 and POT1.
Where to go next[edit | edit source]
- Delay memory. The
MEMdirective allocates space in the FV-1's 32768 words of delay memory:MEM delay 16000. It creates three symbols —delay(start),delay#(end) anddelay^(midpoint).RDAandWRAread and write it. - The built-in examples. Load File… in the editor offers Pass-through, Delay, Chorus and Tremolo. Reading working code is the fastest way to pick up the idioms.
- The instruction reference. The HELP tab lists the full instruction set with operand formats.
- Spin's documentation. The FV-1 datasheet and application note AN-0001 are the authoritative source on LFO behaviour and the fixed-point formats.
A few things that bite beginners[edit | edit source]
- Forgetting to clear ACC. If a channel sounds like it has the other channel bleeding into it, check your
WRAXcoefficients. - Coefficient range. Coefficients are fractions below 1.0. If you need gain above unity, do it in stages or use a format that allows it.
- Setup code running every sample.
WLDSand friends belong behind aSKP RUNguard. - Running out of instructions. 128 is a hard ceiling. If you hit it, the answer is usually a cheaper filter, not a cleverer one.
- Trusting the simulator on chorus.
CHOinterpolation is approximated. Confirm modulation effects on hardware.