Millfork: a middle-level programming language targeting 6502- and Z80-based microcomputers and home consoles
This project is maintained by KarolS
Syntax:
[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [<optimization hints>] [@ <address>] { <body> }
[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [<optimization hints>] [@ <address>] = <expression>
[segment (<segment>)] asm <return_type> <name> ( <params> ) [<optimization hints>] @ <address> extern
Examples:
void do_nothing() { }
inline byte two() = 2
asm void chkout(byte register(a) char) !preserves_x !preserves_y @ $FFD2 extern
segment(prgrom0) void main_loop(word w, byte x) align(fast) { // body omitted
<segment>
: segment name; if absent, then defaults to default_code_segment
as defined for the platform (usually default
)
<modifiers>
: zero or more of the following:
asm
– the function is written in assembly, not in Millfork (obligatory for extern
functions),
see Using 6502 assembly within Millfork programs#Assembly functions
or Using 8080/LR35902/Z80 assembly within Millfork programs#Assembly functions;
for 8086, see the 8086 support disclaimer.
macro
– the function is a macro,
see Macros_and inlining#Macros
inline
– the function should preferably be inlined
see Macros_and inlining#Inlining
noinline
– the function should never be inlined
interrupt
– the function is a hardware interrupt handler.
You are not allowed to call such functions directly.
The function cannot have parameters and the return type should be void
.
kernal_interrupt
– the function is an interrupt handler called from a generic vendor-provided hardware interrupt handler.
The hardware instruction handler is assumed to have preserved the CPU registers,
so this function only has to preserve the zeropage pseudoregisters.
An example is the Commodore 64 interrupt handler that calls the function at an address read from $314/$315.
Unlike hardware handlers with interrupt
, you can treat functions with kernal_interrupt
like normal functions.
On non-6502-based targets, functions marked as kernal_interrupt
don’t differ from normal functions.
const
– the function is pure and can be used in constant expressions. const
functions are not allowed to:
use constants that have been declared after them
have local variables
call non-const functions
contain any other statements other than return statements and conditional statements
<return_type>
is a valid return type, see Types
<params>
is a comma-separated list of parameters, in form type name
. Allowed types are the same as for local variables.
For assembly functions, certain parameter names are interpreted as CPU registers.
<alignment>
is either a numeric literal that is a power of 2, or keyword fast
.
The function will be allocated at the address divisible by alignment.
fast
means different things depending on the target platform:
<optimization hints>
is a list of optimization hints, separated by spaces
<address>
is a constant expression that defines where in the memory the function is or will be located.
extern
is a keyword than marks functions that are not defined in the current program,
but are likely to be available at certain address in memory.
Such functions should be marked as written in assembly and should have their parameters passed through registers.
<body>
is a newline-separated list of either Millfork or assembly statements
<expression>
is an expression. It is equivalent to a function body of form { return <expression> }
.
The address of a non-macro function f
is a constant f.addr
.
Non-macro, non-interrupt functions which have max one parameter of size max 2 bytes
and return void
or a value of size max 2 bytes,
can be accessed via a pointer.
void f() {}
void g(byte x) {}
function.void.to.void p = f.pointer
function.byte.to.void p = g.pointer
call(p)
call(p, 13)
The value of the pointer f.pointer
may not be the same as the value of the function address f.addr
.