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.,
__tiny) or attribute syntax (e.g.,
__attribute__((tiny))). 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., __tiny). If this occurs,
use the __attribute__((tiny)) 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__((tiny)) int a, b;
int __tiny c, d;
When applied to an object, the attribute’s location is irrelevant.
The example above applies the Direct 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__((tiny)) * p1;
long * __attribute__((tiny)) p2;
Here, p1 is a pointer stored in default memory that points to an
int in Direct page memory. p2 is a pointer stored
in Direct 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., __tiny). For
attribute syntax, use the attribute name with __attribute__ (e.g.,
__attribute__((tiny))).
Attribute name |
Description |
|---|---|
|
Specify alignment of data object or function |
|
Specify section name to use for a data object or function |
|
Control storage of data object to direct page |
|
Control storage of data object to near bank |
|
Control storage of data object to far memory |
|
Control storage of data object to huge memory |
|
Used to define an interrupt function |
|
Used to declare an intrinsic function |
|
Entry point that needs to initialize the direct page and data bank registers |
|
Relaxes preserving registers |
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.
tiny¶
This specifies a data object or a pointer to a data object that resides in the direct page. Direct page addressing results in shorter and faster code. It is especially efficient when working with pointers on the 65816.
Note
Auto variables are normally allocated to registers or direct page
pseudo registers which means you will automatically get the
benefits of direct page addressing when using auto
variables. The tiny attribute is best used for smaller
frequently used static data objects.
near¶
This specifies a data object or a pointer to a data object that
resides in a single 64K bank. Near bank addressing is a middle ground
that is fairly efficient, but not as efficient as using the direct page.
It results in shorter code than the far and huge attributes
and being 64K it provides ample space.
far¶
This specifies a data object or a pointer to a data object that can reside anywhere in memory. The only limitation is that a single data object can be at most 64K minus one byte large.
The code needed to access far memory tends to be somewhat larger than the near bank, but the 65816 is still capable of using various addressing modes for reasonable good performance.
far24¶
This specifies a data object or a pointer to a data object that can reside anywhere in memory. The only limitation is that a single data object can be at most 64K minus one byte large.
A far24 pointer is similar to far with the difference that
it occupies 24 bits in memory.
Note
The far24 attribute is intended to be used when writing to data
structures where you actually want a write to be exactly 24 bits.
This may be required by hardware or function API. The compiler will
normally allocate 32 bits for wide pointers such as far and
huge in order to make more efficient code on the 65816.
huge¶
This specifies a data object or a pointer to a data object that can reside anywhere in memory and that has no size limitations other than the addressable 16MB range.
The generated code for using this attribute tends to be larger than
any of the other alternatives. At best you can get something that is
the same or similar as far, but in many cases the code will be
larger and slower.
interrupt¶
An interrupt function is meant to serve as an interrupt handler. The interrupt has the following effects:
An interrupt function cannot take any parameters
An interrupt function will preserve all registers used
Leaving the interrupt function uses a different instruction sequence compared to normal functions
The interrupt attribute may optionally be given a vector address as an argument
The interrupt vector specified is given as an argument to the interrupt attribute:
int counter;
__attribute__((interrupt(0xfffe)))
void irq () {
counter++;
}
Note
The vector argument is optional. Omitting it means that there will be
no vector section entry generated for that interrupt function.
You can also suppress all vector sections from being generated by
using the --no-vector-sections command-line option.
Note
It is strongly recommended to make your interrupt functions small and simple. Also avoid making function calls from an interrupt function. A function call means the interrupt needs to preserve scratch registers, which is costly. If you must use function calls, consider forcing them to be inline expanded See Function inlining for more information.
intrinsic¶
The intrinsic attribute is used to declare intrinsic built-in
functions. This can only be done on intrinsic functions that is
already known to the compiler. Normally you use this by including the
calypsi/intrinsics65816.h file which contains all such valid declarations.
saveds¶
A saveds function is useful for API functions called from another
context. The direct page and data bank are initialized to suit the
current runtime environment. The previous values of these registers
are restored when the function returns. This makes it possible to call
an API function from another context where different values may be
used for these base registers.
Note
For saveds API calls, you should generally prevent arguments
from being passed on the direct page. Use either a single register
argument or the simple_call calling convention to force arguments
onto the stack. Direct page differences when using saveds mean
accessing caller-placed arguments there will not work.
Note
When using saveds for API functions you most likely want to use
the saveds attribute on C written interrupt functions as
well, as interrupts may be triggered at any time.
task¶
A task attribute can be used on functions such as main which
is the start of the application. You will not normally call such
functions from any C code. In that case you can apply the task
attribute which relaxes preserving registers that would otherwise be
saved on the stack. This can save a little stack space and will make
the application a tiny bit smaller.