This guide walks through your first SBBE programs, from a single return value to loops and function calls. By the end, you will understand how values flow through the operand stack and how to express common patterns.

Your first program

The simplest SBBE program pushes a constant and returns it:

func $main() -> i32 {
entry:
    ldi 42
    ret
}

Every SBBE program needs at least one function. $main is the entry point (by convention). The -> i32 means it returns a 32-bit integer. The body has one block (entry:) with two instructions: ldi 42 pushes the integer 42, and ret pops it as the return value.

Understanding the stack

SBBE is a stack machine. There are no named registers. Every instruction either pushes a value onto an implicit operand stack, pops values from it, or both.

To compute (3 + 4) * 2:

func $main() -> i32 {
entry:
    ldi 3        // stack: [3]
    ldi 4        // stack: [3, 4]
    add.s i32    // stack: [7]        — pops 4 and 3, pushes 7
    ldi 2        // stack: [7, 2]
    mul.s i32    // stack: [14]       — pops 2 and 7, pushes 14
    ret          //                   — pops 14 as the return value
}

Binary operations always pop the right operand first (b = pop(); a = pop()), then push the result. This means you push operands in the natural left-to-right order.

Local variables

When you need to reuse a value or your expression doesn’t fit a straight-line stack sequence, use local variables. Declare them with var at the top of the function, then use ld/str to access them by name:

func $main() -> i32 {
    var $x i32
    var $y i32

entry:
    ldi 10
    str $x         // x = 10
    ldi 20
    str $y         // y = 20
    ld $x
    ld $y
    add.s i32      // x + y = 30
    ret
}

Function parameters are also locals, accessed by index with ldl:

func $add(i32, i32) -> i32 {
entry:
    ldl 0          // first parameter
    ldl 1          // second parameter
    add.s i32
    ret
}

Control flow with blocks and jumps

SBBE uses labeled blocks and explicit jumps, like assembly. There is no if/else or while — you build those from jmp (unconditional) and jmp.if (conditional, pops an i32 and jumps if nonzero).

Here is a loop that computes 1 + 2 + ... + 10:

func $main() -> i32 {
    var $n i32
    var $acc i32

entry:
    ldi 10
    str $n
    ldi 0
    str $acc
    jmp check

check:
    ld $n
    eqz i32        // push 1 if n == 0, else 0
    jmp.if done    // jump to done if n == 0
    jmp body

body:
    ld $acc
    ld $n
    add.s i32
    str $acc        // acc += n
    ld $n
    ldi 1
    sub.s i32
    str $n          // n -= 1
    jmp check

done:
    ld $acc
    ret             // return 55
}

Calling functions

Push arguments in declaration order, then call:

func $square(i32) -> i32 {
entry:
    ldl 0
    ldl 0
    mul.s i32
    ret
}

func $main() -> i32 {
entry:
    ldi 7
    call $square   // pushes 49
    ret
}

The return value (if any) is left on the caller’s stack after the call.

Calling external functions

Use extern func to declare a function provided by the host environment:

extern func $puts(ptr) -> i32

func $main() -> i32 {
entry:
    ldi 0          // address of string in memory
    call $puts
    drop           // discard puts return value
    ldi 0
    ret
}

The host sets up the string data in linear memory before execution. The VM’s extern binding system (or a native linker) connects $puts to the actual implementation.

Branchless conditionals with sel

For simple conditionals where both sides are cheap to compute, sel avoids a branch entirely:

func $min(i32, i32) -> i32 {
entry:
    ldl 0          // a
    ldl 1          // b
    ldl 0          // a (candidate if true)
    ldl 1          // b (candidate if false)
    lt.s i32       // a < b ?
    sel            // push(cond ? a : b)
    ret
}

sel pops three values: condition, then the two candidates. If the condition is nonzero, it pushes the first candidate; otherwise the second.

Next steps

Now that you understand the basics, explore the other guides:

Or dive into the Instruction Set reference for the complete list of operations.