# Assembler syntax

The assembler allows you to write regular 6502 assembly instructions. However, some more powerful features are of course also available.

# Labels

Labels can be defined to make it easier to refer to memory locations. Labels should consist of a valid identifier followed by a colon. A valid identifier starts with a character or underscore and may contain only characters, underscores or numbers.

For example, a label can be used to loop:

ldx #$20
label:
dex
bne label
1
2
3
4

You can also add braces after the label to aid formatting:

ldx #$20
label: {
    dex
    bne label
}
1
2
3
4
5

# Variables and constants

You can define variables and constants using the .var and .const directives respectively. They can then be used in expressions.

For example:

.const BORDER_COLOUR = $d020

lda #7
sta BORDER_COLOUR
1
2
3
4

Variables may be redefined, constants may not.

# Symbol scopes

Labels, variables and constants are symbols. Symbols are defined in scopes. A scope is defined by a block (starting with { and ending with }). As long as symbols reside in different scopes they can have duplicate names. You can use the super keyword to access symbols in outer scopes.

Sounds complicated perhaps, so here's an example:

label: {
    label: {
        jmp label
        jmp super.label
    }
}    
1
2
3
4
5
6

The first jmp will jump to the innermost label. The second jmp will jump to the outermost label.

# Automatic symbols

Some symbols are generated for you automatically.

# - and +

You can use - or + to refer to the start or the end of a block.

For instance, the following code loops 64 times:

ldx #$40
{
    dex
    bne -
}
1
2
3
4
5

# Loops

Loops may be generated using the .loop directive:

.loop 5 {
    lda #$00
    sta $0400 + index
}
1
2
3
4

The index symbol contains the current loop index (zero-based).

# Expressions

Simple calculations may be performed.

This program:

lda #5 + 10
1

Is equal to:

lda #15
1

# Operators

Supported operators are:

  • * Multiplication
  • / Division
  • % Modulo
  • << Shift left
  • >> Shift right
  • ^ Exclusive or (XOR)
  • + Addition
  • - Subtraction

You can also use parentheses, for example:

lda #(3 + 4) * 5
1

# Equality tests

Equality tests may also be performed. They will evaluate to 0 when false and 1 when true:

  • == Equality
  • != Inequality
  • > Greater than
  • >= Greater than or equal
  • < Less than
  • <= Less than or equal
  • && And
  • || Or

# Word operators

Additionally, the high or low byte of 16-bit variables may be accessed using the < and > modifiers, e.g.:

.const ADDRESS = $1234
lda #<ADDRESS   // a will now contain '$34'
ldx #>ADDRESS   // x will now contain '$12'
1
2
3

# Modifiers

There are two ways to modify a factor in an expression.

  • Prefix with ! to convert 0 into 1 and any positive number into 0
  • Prefix with - to negate the value

# Built-in functions

Currently the only built-in function is defined which evaluates to 1 if a variable or constant is defined and to 0 otherwise.

.const ADDRESS = $1234
lda defined(ADDRESS)   // a will now contain '1'
lda !defined(ADDRESS)  // a will now contain '0'
1
2
3

# String handling

It is possible to use strings in expressions and in variable definitions, e.g.:

.const MY_HELLO = "hello"
.const MY_WORLD = " world"
.const GREETING = MY_HELLO + MY_WORLD
1
2
3

It is also possible to do string interpolation using curly braces, e.g.:

.const MY_HELLO = "hello"
.const GREETING = "{MY_HELLO} world"  // <-- results in "hello world"
1
2

# Data definition

You may include data inline like so:

.byte 1, 2, 3
1

Supported data types are .byte, .word and .dword.

# Text definition

You may include text inline like so:

.text "hello"
1

The default encoding is ascii. You may also use the encodings petscii or petscreen. The latter emits Commodore-compatible screen codes. For example:

.text petscreen "abc"    // This emits $01, $02, $03
1

# Including from files

It is also possible to include data from files, like so:

.file "foo.bin"
1

The file is located relative to the source file that contains the .file directive.

# Comments

Lines may end with a C++-style // comment, like so:

nop  // hello, I am a comment
1

C-style comment blocks are also supported:

/*
   hello there!
*/
nop
1
2
3
4

C-style comments may also be nested.

# Conditional assembly

It is possible to conditionally assemble chunks of code by wrapping them in an .if block:

.const FOO = 1

.if defined(FOO) {
    nop
} else {
    brk
}
1
2
3
4
5
6
7

The else clause is optional.

# Program counter

During assembly it is possible to change the current program counter (i.e. the location where instructions are assembled to).

# Setting

You can set the program counter with the * directive, like so:

* = $0800
1

# Aligning

You can move the program counter forward to make it align on a certain number of bytes, using the .align directive:

.align 256
nop         // This will always be assembled to $xx00
1
2