< back to index

Syntax

For information about types, see Types.
For information about literals, see Literals.
For information about assembly, see Using assembly within Millfork programs.

Comments

Comments start with // and last until the end of line.

Declarations

Variable declarations

A variable declaration can happen at either top level of a file (global variables), or a top level of a function (local variables).

Syntax:

[segment(<segment>)] [volatile] [<storage>] <type> <name> [@<address>] [= <initial_value>]

For every variable x larger than a byte, extra subvariables are defined:

Constant declarations

const <type> <name> = <value>

TODO

Alias definitions

alias <alias> = <name> [!]

Sets an alias for a global name. Unless shadowed by a local name, the alias will point to the given global object:

byte x
alias a = x

void f() {
    a = 5 // writes to the global variable x
}

void f() {
    byte a
    a = 5 // writes to the local variable a
}

Aliases can be used for variables, arrays, constants, functions, and types, but not for text encodings, array formats or keywords.

If the alias definition is followed by a !, then the alias overrides any other definition of that name. This allows for overriding definitions of library functions by another library:

void f() {}
void g() {}
alias f = g!
// now the original f is removed and all calls to f will call g instead

Array declarations

An array is a continuous sequence of bytes in memory.

An array declaration can happen at either top level of a file (global arrays), or a top level of a function (local arrays). Regardless of where they were declared, arrays are considered static.

Syntax:

[segment(<segment>)] [const] array [(<element type>)] <name> [[<size>]] [align ( <alignment> )] [@<address>] [= <initial_values>]

TODO

Function declarations

A function can be declared at the top level. For more details, see Functions

import statements

import <module>

Adds a module to the program.

The module is looked up first in the current working directory, and then in the include directories.

Usually, the imported module will undergo the first phase of compilation first. This means that the constants in the imported module will be resolved first, allowing you to use them in the importing module.

The only exception to this rule is when the importing graph has a cycle, in which case the order of modules within the cycle is unspecified.

All starting modules are considered to be imported by all source files explicitly mentioned on the command line.

Statements

Expression statement

TODO

if statement

Syntax:

if <expression> {
    <body>
}
if <expression> {
    <body>
} else {
    <body>
}
if <expression> {
    <body>
} else if <expression> {
    <body>
} else {
    <body>
}

return statement

Syntax:

return
return <expression>

return[] statement (return dispatch)

Syntax examples:

return [a + b] {
   0   @ underflow
   255 @ overflow
   default @ nothing
}
return [getF()] {
   1 @ function1
   2 @ function2
   default(5) @ functionDefault
}
return [i] (param1, param2) {
   1,5,8 @ function1(4, 6)
   2     @ function2(9)
   default(0,20) @ functionDefault
}

Return dispatch calculates the value of an index, picks the correct branch, assigns some global variables and jumps to another function.

The index has to evaluate to a byte or to an enum. The functions cannot be macro and shouldn't have parameters. Jumping to a function with parameters gives those parameters undefined values.

The functions are not called, so they don't return to the function the return dispatch statement is in, but to its caller. The return values are passed along. If the dispatching function has a non-void return type different that the type of the function dispatched to, the return value is undefined.

If the default branch exists, then it is used for every missing index. If the index type is an non-empty enum, then the default branch supports all the other values. Otherwise, the default branch handles only the missing values between other supported values. In this case, you can override it with optional parameters to default. They specify the maximum, or both the minimum and maximum supported index value. In the above examples: the first example supports values 0–255, second 1–5, and third 0–20.

If the index has an unsupported value, the behaviour is formally undefined, but in practice the program will simply crash.

Before jumping to the function, the chosen global variables will be assigned parameter values. Variables have to be global byte-sized. Some simple array indexing expressions are also allowed. Parameter values have to be constants. For example, in the third example one of the following will happen:

while and do-while statements

Syntax:

while <expression> {
    <body>
}
do {
    <body>
} while <expression>

for statements

Warning: for loops are a bit buggy.

Syntax:

for <variable> , <start> , <direction> , <end> {
}
for <variable> : <enum type> {
}
for <variable> : [ <comma separated expressions> ]  {
}

break and continue statements

Syntax:

break
break for
break while
break do
break <variable>
continue
continue for
continue while
continue do
continue <variable>

asm statements

See Using 6502 assembly within Millfork programs
or Using 8080/LR35902/Z80 assembly within Millfork programs.

Work in progress: For 8086, see the 8086 support disclaimer.