The native keyword is a modifier for proc and func declarations that instructs the compiler to skip generating standard stack frame setup and teardown code (link a6,#N and unlk a6). This is intended for low-level, assembly-only functions where maximum performance is required and the function doesn't need stack-based local variables.
native proc function_name(__reg(regname) param: type, ...) -> return_type {
// Function body (typically asm blocks)
}
or for forward declarations:
native func function_name(__reg(regname) param: type, ...) -> return_type;
When using the native keyword, the following restrictions apply:
-
All parameters MUST use
__reg: Every parameter must be passed in a register using the__regspecifier. Stack-based parameters are not allowed. -
No local variables: The function cannot declare local variables using
var. Since there's no stack frame, there's no space to allocate locals. -
Intended for asm-only bodies: While not enforced, native functions are designed to contain primarily
asm { }blocks with direct register manipulation.
code utils:
// Native function to read a word from memory
native proc peek_w(__reg(a0) addr: long) -> long {
asm {
move.w (a0),d0 ; d0 = word at addr
ext.l d0 ; sign-extend to 32-bit
}
return;
}
// Native function for fast addition
native proc fast_add(__reg(d0) a: long, __reg(d1) b: long) -> long {
asm {
add.l d1,d0 ; result in d0
}
return;
}
With native keyword:
peek_w:
; param addr: long in a0
move.w (a0),d0 ; d0 = word at addr
ext.l d0 ; sign-extend to 32-bit
rtsWithout native keyword (traditional):
peek_w:
; param addr: long in a0
; local addr: long at -4(a4)
link a6,#-8
move.l a0,-4(a6) ; save addr from a0
move.l a4,-8(a6) ; save a4 in frame
move.l a6,a4 ; save frame pointer in a4
move.w (a0),d0 ; d0 = word at addr
ext.l d0 ; sign-extend to 32-bit
move.l -8(a6),a4 ; restore a4 from frame
unlk a6
rtsThe compiler will reject native functions that violate the requirements:
// ERROR: Parameter 'x' must use __reg
native proc bad_func(x: long) -> long {
asm {
move.l d0,d1
}
return;
}
Error message:
In native proc 'bad_func': Parameter 'x' must use __reg.
Native functions require all parameters to be register-based.
// ERROR: Local variables not allowed
native proc bad_func(__reg(d0) x: long) -> long {
var temp: long;
temp = x + 1;
return temp;
}
Error message:
In native proc 'bad_func': Local variables are not allowed.
Native functions cannot allocate stack space for local variables.
native proc read_custom_register(__reg(a0) reg_addr: long) -> word {
asm {
move.w (a0),d0
}
return;
}
native proc multiply_16x16(__reg(d0) a: word, __reg(d1) b: word) -> long {
asm {
muls.w d1,d0 ; 16x16 = 32-bit result
}
return;
}
native proc wait_cycles(__reg(d0) count: long) -> void {
asm {
.loop:
subq.l #1,d0
bne.s .loop
}
}
Using native eliminates:
link a6,#Ninstruction (4 bytes, ~18 cycles on 68000)- Register parameter save to stack (4-8 bytes per param, ~16-20 cycles per param)
- Frame pointer setup (4 bytes, ~12 cycles)
unlk a6instruction (2 bytes, ~12 cycles)- Frame restoration (4-8 bytes, ~12-20 cycles)
For a simple function with 2 register parameters:
- Traditional: ~70-80 cycles overhead
- Native: 0 cycles overhead
This is a significant improvement for functions called frequently in tight loops or time-critical code.
-
Use sparingly: Reserve
nativefor truly performance-critical code. Regular functions with stack frames are easier to debug and maintain. -
Document register usage: Clearly document which registers are used and their purpose.
-
Preserve registers: If your native function uses scratch registers, document this for callers.
-
Test thoroughly: Native functions bypass safety features, so ensure assembly code is correct.
-
Consider calling conventions: Remember that callers expect d0-d1/a0-a1 to be scratch registers, while d2-d7/a2-a6 should be preserved.
| Feature | Regular proc | Native proc |
|---|---|---|
| Stack frame | Yes (link/unlk) | No |
| Local variables | Yes | No |
| Stack parameters | Yes | No |
| Register parameters | Optional | Required |
| Overhead | ~70-80 cycles | ~0 cycles |
| Safety | High | Low |
| Use case | General purpose | Performance-critical |