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))).

Table 12.1 Extended attributes summary

Attribute name

Description

aligned(nn)

Specify alignment of data object or function

section("name")

Specify section name to use for a data object or function

tiny

Control storage of data object to direct page

near

Control storage of data object to near bank

far

Control storage of data object to far memory

huge

Control storage of data object to huge memory

interrupt

Used to define an interrupt function

intrinsic

Used to declare an intrinsic function

saveds

Entry point that needs to initialize the direct page and data bank registers

task

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:

  1. An interrupt function cannot take any parameters

  2. An interrupt function will preserve all registers used

  3. Leaving the interrupt function uses a different instruction sequence compared to normal functions

  4. 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.