12. Extended attributes¶
An attribute is a property attachable to functions, data objects,
or types, specified as a keyword. Standard C includes built-in
keywords like const and volatile.
Extended attributes provide access to behaviors or properties beyond Standard C. They are either target-specific or useful for embedded systems.
12.1. Overview¶
Attributes can be applied with either keyword syntax (e.g.,
__zpage) or attribute syntax (e.g.,
__attribute__((zpage))). Both are functionally equivalent,
but the C parser may not accept the keyword form in some situations.
Usage is largely a matter of preference. Preprocessor macros can rename attributes for portability, enabling them to be toggled off for other targets or renamed to match different compilers or targets.
Note
The C parser sometimes produces unexpected errors with the keyword
form of attributes (e.g., __zpage). If this occurs,
use the __attribute__((zpage)) form instead.
12.2. Using attributes¶
Type attributes can be applied to type declarations, following the same
syntax as type qualifiers like const and volatile.
Syntax for data objects¶
You can apply attributes to data objects as follows:
__attribute__((zpage)) int a, b;
int __zpage c, d;
When applied to an object, the attribute’s location is irrelevant.
The example above applies the Zero page attribute to all
defined objects (a, b, c, and d).
Syntax for pointer types¶
Attributes can also be applied to pointer types, where their location is significant. This determines whether the pointer itself is constant or what it points to is constant.
The easiest way to decipher attributes in function types is to read the type from right to left.
int __attribute__((zpage)) * p1;
long * __attribute__((zpage)) p2;
Here, p1 is a pointer stored in default memory that points to an
int in Zero page memory. p2 is a pointer stored
in Zero page memory that points to a long in default
memory.
12.3. Attribute reference¶
This section goes through all available extension keywords and attributes.
Summary of attributes¶
The following table summarizes available attributes. For the keyword
form, prefix with two underscores (e.g., __zpage). For
attribute syntax, use the attribute name with __attribute__ (e.g.,
__attribute__((zpage))).
Attribute name |
Description |
|---|---|
|
Specifies alignment for data objects or functions |
|
Specifies the section name for a data object or function |
|
Controls storage of data objects to the zero page |
|
Defines an interrupt function |
|
Defines a Commodore 64 kernal-friendly interrupt |
|
Declares an intrinsic function |
|
Relaxes preserving registers |
|
Controls storage of data objects to far memory (MEGA65 only) |
|
Controls storage of data object to huge memory (MEGA65 only) |
Description of attributes¶
This section describes each attribute in detail.
aligned¶
This attribute can be applied to functions, global and static data objects to force a certain minimal alignment.
The aligned attribute takes an argument which is the alignment to
use:
__attribute__((aligned(16))) struct sprite ship;
Note
Certain data types may impose an alignment by themselves. The actual alignment is choosen so that all alignment constraints are satisfied.
section¶
This attribute can be applied to functions, global and static data objects to control the name of the section it is placed in.
The section attribute takes an argument which is the section name
to use:
// Place in vram
__attribute__((section("vram")))
const char tiles[256] = { .. };
__attribute__((section("trueCode")))
long foo () {
return 42;
}
See Description of pragma directives for how you can specify a section for multiple functions, global and static data objects.
zpage¶
Specifies a data object or a pointer to a data object residing in the zero page. Zero page addressing yields shorter, faster code and is particularly efficient for 6502 pointer operations.
Note
Auto variables are typically allocated to registers or zero page
pseudo-registers, automatically providing the benefits of zero page
addressing. The zpage attribute is best suited for smaller,
frequently used static data objects.
far¶
The far attribute specifies data in memory beyond the 64K provided
by the base 6502. It is available only when the MEGA65 is enabled.
On the MEGA65, the 45GS02 CPU provides additional addressing
capabilities. The amount of far data objects is limited only by
available memory.
A single far object can be at most 65535 bytes large.
huge¶
The huge attribute specifies data in memory beyond the 64K provided
by the base 6502. It is available only when the MEGA65 is enabled.
On the MEGA65, the 45GS02 CPU provides additional addressing
capabilities. The amount of huge data objects is limited only by
available memory.
Note
The huge attribute typically generates larger applications than
the far attribute. Use huge only if absolutely necessary.
interrupt¶
An interrupt function serves as an interrupt handler and has the following effects:
An interrupt function cannot take parameters
It preserves all registers used.
Exiting the interrupt function uses a different instruction sequence than normal functions
The interrupt attribute may optionally be given a vector address as an argument
The interrupt vector is specified as an argument to the interrupt attribute:
int counter;
__attribute__((interrupt(0xfffe)))
void irq () {
counter++;
}
Note
The vector argument is optional. Omitting it results in no vector
section entry being generated for that interrupt function. All vector
sections can also be suppressed using the --no-vector-sections
command-line option.
Note
It is strongly recommended to keep interrupt functions small and simple. Avoid making function calls from an interrupt function, as this requires preserving scratch registers, which is costly. If function calls are necessary, consider forcing them to be inline expanded. See Function inlining for more information.
kernal_interrupt¶
The kernal_interrupt attribute defines an interrupt function for
the Commodore 64 kernal. It can also be used by Commander X16, as they
share the same mechanism.
Kernal interrupt functions must be installed using the
_Kernal_set_interrupt_handler() function, included in the C
library.
#include <kernal/interrupt.h>
__attribute__((kernal_interrupt))
void myirq () {
counter++;
}
int main () {
__kernal_vector_t old_vector;
old_vector = _Kernal_set_interrupt_handler(myirq);
... application code
_Kernal_restore_interrupt_handler(old_vector);
return 0;
}
Installing a new vector writes it to location 0x0314 and saves the
previous one in a library-internal variable. The kernal interrupt
function exits by jumping via this saved pointer.
Before exiting your application, restore the old vector.
intrinsic¶
The intrinsic attribute declares built-in functions. This applies
only to intrinsic functions already known to the compiler. Typically,
you use this by including the calypsi/intrinsics6502.h file, which
contains all such valid declarations.
task¶
The task attribute can be applied to functions like main, which
mark the application entry point. Such functions are typically not
called from C code. Applying the task attribute relaxes register
preservation, potentially saving stack space and reducing application
size by a small amount.