Jump to content

Writing your first FV-1 program

From Sandbox Pedal Wiki

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
POT0POT2 The three control knobs, 0.0 to 1.0
REG0REG31 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 MEM directive allocates space in the FV-1's 32768 words of delay memory: MEM delay 16000. It creates three symbols — delay (start), delay# (end) and delay^ (midpoint). RDA and WRA read 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 WRAX coefficients.
  • 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. WLDS and friends belong behind a SKP RUN guard.
  • 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. CHO interpolation is approximated. Confirm modulation effects on hardware.

See also[edit | edit source]