Skip to content
Lorenzi edited this page Apr 12, 2021 · 8 revisions

Code blocks

You can use braces {} to create code blocks with a sequence of expressions. You can create and assign to local variables using =. There's no need for expression separators, but you can use a comma , if needed. The last expression in the block is automatically returned.

For example:

#ruledef
{
    ; from the 6502 instruction set
    bpl {addr} =>
    {
        reladdr = addr - $ - 2
        0x10 @ reladdr`8
    }
}

Assert

Asserts can be used in a code block to validate and reject certain instruction arguments. If the condition given to assert() turns out to be false, an error is thrown.

For example:

#ruledef
{
    ; from the 6502 instruction set
    bpl {addr} =>
    {
        reladdr = addr - $ - 2
        assert(reladdr <=  0x7f)
        assert(reladdr >= !0x7f)
        0x10 @ reladdr`8
    }
}

Rule Cascading

When an instruction matches multiple rules, you can use assert() to select the best possible binary representation. Make sure the asserted conditions don't overlap, otherwise the assembler may throw an error.

For example, we can write:

#ruledef
{
    mov {value} =>
    {
        assert(value >= 0)
        assert(value <= 0xff)
        0x10 @ value`8
    }

    mov {value} =>
    {
        assert(value >= 0x100)
        assert(value <= 0xffff)
        0x11 @ value`16
    }

    mov {value} =>
    {
        assert(value >= 0x10000)
        assert(value <= 0xffffff)
        0x12 @ value`24
    }
}

If the arguments to the instruction cannot be resolved in the first pass, which can happen if you were using a label that will only be defined later, 0 or a value from the previous pass are used. Further passes will refine the label value until it stops changing, and the final form of the instruction is chosen.

asm Blocks

To reuse an instruction's binary code in another instruction, you can use asm blocks:

#ruledef
{
    mov {reg}, {value} => 0x11 @ reg`8 @ value`8
    zero {reg} => asm { mov reg, 0 }
}

In the above example, the zero instruction is really just an alias for moving zero into a register, so the instruction body just calls the other instruction with an asm block to avoid repetition.

The asm block works just like any other expression, so you can, for example, use concatenation:

#ruledef
{
    mov {reg}, {value} => 0x11 @ reg`8 @ value`8
    zero_all => asm { mov 0, 0 } @ asm { mov 1, 0 } @ asm { mov 2, 0 }
}

Or, equivalently, you can put multiple instructions inside a single asm block, one per line:

#ruledef
{
    mov {reg}, {value} => 0x11 @ reg`8 @ value`8
    zero_all => asm
    {
        mov 0, 0
        mov 1, 0
        mov 2, 0
    }
}