BitMagic The Complete Development Environment for the Commander X16.

Directives

Every directive starts with a .. Parameters are positional in the order shown, or given as name=value pairs. A lone _ skips a positional parameter.

.segment ZP $02 $fd
.segment name=ZP, address=$02, maxsize=$fd
.segment ZP $02 _ _ app_general   ; skip maxsize and filename

Types

The type is not a type in any real sense. The compiler never checks it, and it plays no part in expressions, where a name always resolves to a plain address or value. It is used for two things only: reserving the right number of bytes, and telling the debugger how to show the variable (its size, whether it is an array, whether it is a pointer).

.var, .constvar and .padvar take a type. The base types are:

Type Size Meaning
byte / sbyte 1 Unsigned / signed 8-bit value.
short / ushort 2 Signed / unsigned 16-bit value.
int / uint 4 Signed / unsigned 32-bit value.
long / ulong 8 Signed / unsigned 64-bit value.
string n A run of bytes, one per character.
proc 2 The address of a procedure.
ptr 2 A bare 2-byte address, with no target type.

Arrays put the count in square brackets. byte[16] is sixteen bytes; int[4] is four 32-bit values; string[10] is a fixed ten-byte string.

Pointers are the word ptr after a type: byte ptr, ushort ptr, string ptr. A pointer is always a 2-byte word holding the address of a value of that type.

.var      ushort ptr  cursor    $0400   ; a 2-byte variable, initialised to $0400
.constvar uint ptr    counter   $c000   ; names $c000 as a uint pointer; emits nothing
.padvar   ushort ptr  next              ; reserves 2 bytes for a ushort pointer

Array and pointer combine, with the bracket first: byte[16] ptr is an array of sixteen byte pointers (32 bytes), not one pointer to a sixteen-byte block. Put ptr before the bracket and the bracket is ignored.

Segments

See Segment for the full picture.

.segment

Parameter Type Optional Description
name string false Segment name. If it already exists, switches to it and applies any other parameters given.
address number true Start address. Only settable before the segment has data; can’t be changed once set explicitly. If omitted, follows on from the current segment.
maxsize number true Maximum size. The compiler errors if the segment grows past this. No limit if omitted.
filename string true File to write for this segment.
scope string true Default scope for the segment. A new scope is created if not given.
.segment BSS $0400 $0400          ; 0x400 bytes of uninitialised space, no file
.segment DATA $2000 _ data.bin    ; written to data.bin

.endsegment

No parameters. Switches back to the Main segment and the Main scope.

Scopes and procedures

See Scope for how names are resolved.

.scope

Parameter Type Optional Description
name string true Scope name. If omitted, an anonymous name is generated.

Opens a named constant scope. Scopes are a flat namespace under App; they do not nest.

.endscope

No parameters. Returns to the enclosing procedure’s scope.

.proc

Parameter Type Optional Description
name string true Procedure name. If omitted, an anonymous name is generated.

A named block with its own scope and an entry-point constant of the same name, so jmp myproc works. Procedures can be nested: a .proc inside a .proc becomes a child of it.

.proc clear_screen
    ; ...
    rts
.endproc

    jsr clear_screen

.endproc

No parameters. Closes the current procedure and adds an endproc constant pointing just past its code, useful for placing data after it.

Constants and variables

.const

Parameter Type Description
name string Constant name.
value expression Value. See Expressions.

Defines a named constant. A single line can define several:

.const width 40
.const height=25, cells=width*height

.var

Parameter Type Optional Description
type type false See Types.
name string false Variable name.
value expression true Initial value. Defaults to 0.

A way to put typed data into the segment. It reserves space for the variable and writes its initial value, then advances the segment write position past it.

.segment DATA $2000 _ data.bin
    .var byte lives 3
    .var short score 0

.constvar

Parameter Type Optional Description
type type false See Types.
name string false Name.
value expression true Address the pointer holds. Defaults to 0.

Defines a typed constant that points at an address: a typed alias for a fixed location, such as a hardware register or a zero-page slot. No data is written.

.constvar byte ptr  vera_ctrl  $9f25   ; name for $9f25, shown as a byte in the debugger
.constvar ushort    score      $0400   ; a 16-bit value living at $0400

.padvar

Parameter Type Optional Description
type type false See Types.
name string false Name.

Reserves space for a typed variable and advances the segment write position by its size, but writes no data. It is for uninitialised storage, so it belongs in a segment with no filename (a BSS area), where you are only carving RAM into named slots rather than producing bytes for a file. Using it in a segment that is written to disk would pad the file with blank bytes.

.segment BSS $0400 $0400        ; RAM at $0400, no filename
    .padvar ushort   score      ; $0400
    .padvar byte     lives      ; $0402
    .padvar byte[8]  sprite_x   ; $0403 to $040a

Segment layout

.org

Parameter Type Description
address number Address to advance the write position to.

Moves the segment write position forward to address. Errors if the position is already past it.

.org $2000          ; the next byte lands at $2000
.byte $ff

.pad

Parameter Type Description
size number Number of bytes to skip.

Advances the write position by size bytes.

.pad $100           ; leave a 256-byte gap

.align

Parameter Type Description
boundary number Alignment boundary.

Advances the write position until it is a multiple of boundary.

.align $100         ; advance to the next page boundary
.tiles:             ; tile data, now page aligned

Data

.byte

Emits one or more bytes.

.byte $00, $11, $12, $13

.word

Emits one or more 16-bit words, little-endian.

.word $1234, label, label + 2

.code

Emits raw bytes exactly like .byte. The output is identical; the difference is how the debugger treats them. .code bytes are disassembled as instructions, .byte bytes are shown as data.

Use it when you are producing instruction bytes directly rather than writing mnemonics: opcodes emitted by a template, a hand-encoded instruction, or a patch table the CPU will jump into. Anything that gets executed should be .code or a normal opcode line, so that stepping and the disassembly view line up.

.code $4c, <target, >target    ; jmp target, encoded by hand

Debugging directives

These affect a debug session, not the output binary.

.breakpoint

No parameters. Sets a breakpoint on the next line, the same as clicking the gutter there.

.stop

No parameters. Marks the next line as a place the debugger may stop while stepping. Use it inside a .nostop block to poke a hole where you do want to land.

.nostop

Parameter Type Description
enabled bool true to start skipping, false to stop.

Turns “don’t stop here” on or off for the lines that follow. While it is on, stepping runs through those lines without landing on them, except where a .stop says otherwise. Useful for wrapping boilerplate or a macro’s output.

.exception

No parameters. Raises a debugger exception at this point, so an exception breakpoint can catch it. Use it to flag an error path.

.debugload

Parameter Type Description
filename string The file whose debug info to apply.
address number Where that file now sits in memory.

Tells the debugger that your program has loaded filename into memory at address, so it applies that file’s symbols and source map there. It does not load the file.

The debugger picks up files loaded through the KERNAL on its own. .debugload is for the cases it can’t see: a program that loads code with its own SD-card routine, a custom loader, or a copy from a cartridge. Put it on the line after the load has finished.

.debugalias

Parameter Type Description
type type See Types.
name string The name to show in the debugger.
value source Where the value lives: a memory address or a CPU register.

Adds a named variable to the debugger, of the given type, read from value. Nothing is emitted into the binary.

value is a source, not a number: it points at something the machine already holds and gives it a meaningful, typed name. The name follows the same scope rules as a label or constant, so a .debugalias inside a .proc belongs to that proc.

.map

Parameter Type Description
filename string Source file to point at.
line number Line within that file.

Redirects the source map for the following line to filename and line, so a stepped instruction jumps to the original source rather than the generated file. The Template Engine emits this; it is not normally written by hand.