millfork

Millfork: a middle-level programming language targeting 6502- and Z80-based microcomputers and home consoles

This project is maintained by KarolS

< back to index

Operators

Unlike in high-level languages, operators in Millfork have limited applicability. Not every well-formed expression is actually compilable. Most expressions involving single bytes compile, but for larger types usually you need to use in-place modification operators.
Further improvements to the compiler may increase the number of acceptable combinations.

On 6502-like targets, certain expressions require the commandline flag -fzp-register (.ini equivalent: zeropage_register) to be enabled. They will be marked with (zpreg) next to them. The flag is enabled by default, but you can disable it if you need to.

Precedence

Millfork has different operator precedence compared to most other languages. From highest to lowest it goes:

You cannot use two different operators at the same precedence levels without using parentheses to disambiguate. It is to prevent confusion about whether a + b & c << d means (a + b) & (c << d) ((a + b) & c) << d or something else.
The only exceptions are + and -, and $+ and $-. They are interpreted as expected: 5 - 3 + 2 == 4 and 5 $- 3 $+ 2 == 4.
Note that you cannot mix $+ and $- with + and -.

Certain operators (/, %%, <<, >>, $<<, $>>, >>>>, :, !=) cannot have more than 2 parameters, i.e. x / y / z will not compile.

The decimal operators have two different forms:

Argument types

In the descriptions below, arguments to the operators are explained as follows:

Split-word operator

Expressions of the shape h:l where h and l are of type byte, are considered expressions of type word.
If and only if both h and l are assignable expressions, then h:l is also an assignable expression.

Indirect field access operator

->

TODO

Binary arithmetic operators

Bitwise operators

Decimal arithmetic operators

These operators work using the decimal arithmetic (packed BCD).

On Ricoh-based targets (e.g. Famicom) they require the zeropage register to have size at least 4

Comparison operators

These operators (except for !=) can accept more than 2 arguments. In such case, the result is true if each comparison in the group is true. Note you cannot mix those operators, so a <= b < c is not valid.

WARNING: Currently in cases like a < f() < b, f() may be evaluated an undefined number of times (the current implementation calls it twice, but do not rely on this behaviour).

The == and != operators also work for non-arithmetic types.

Currently, >, <, <=, >= operators perform signed comparison if any of the types of their arguments is signed, and unsigned comparison otherwise.

Assignment and in-place modification operators

WARNING: Unlike other languages, Millfork does not provide any guarantees about how many times the left hand side will be evaluated. An expression of form a[f()] += b may call f an undefined number of times.

There are no ||=, &&= or >>>>= operators.

Indexing

While Millfork does not consider indexing an operator, this is a place as good as any to discuss it.

An expression of form a[i], where i is an expression of type byte, is:

On 8080-like targets, and on 6502 if the zeropage register is enabled, i can also be of type word.

An expression of form a[i], where i is an expression of a enumeration type, is:

Note that you cannot access a whole array element if it’s bigger than 2 bytes (except in a simple assignment), but you can access its fields or take its pointer:

array(int32) a[6]

a[2]          // not ok
a[2] = 4      // ok, assignments are an exception
x = a[2]      // ok, assignments are an exception
a[2].b0       // ok
a[2].loword   // ok
a[2].pointer  // ok     
a[2].addr     // ok
a[2].b0.addr  // ok, equal to the above on little-endian targets

Built-in functions