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., __far) or attribute syntax (e.g., __attribute__((far))). 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., __far). If this occurs, use the __attribute__((far)) 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__((far)) int a, b;
int __far c, d;

When applied to an object, the attribute’s location is irrelevant. The example above applies the Far memory 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__((far)) * p1;
long * __attribute__((far)) p2;

Here, p1 is a pointer stored in default memory that points to an int in Far memory memory. p2 is a pointer stored in Far memory 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., __far). For attribute syntax, use the attribute name with __attribute__ (e.g., __attribute__((far))).

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

near

Control storage of data object to near area

far

Control storage of data object to far area

interrupt

Used to define an interrupt function

amiga_interrupt

Amiga style interrupt function

intrinsic

Used to declare an intrinsic function

saveds

Entry point that needs to initialize the base pointer in register A4

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.

near

This specifies a data object or a pointer to a data object that resides in a Near area. Accessing a global or static object in this area saves two bytes for each machine instruction used compared to the Far area.

Register A4 is reserved to hold a base pointer to this area.

far

This specifies a data object or a pointer to a data object that can reside anywhere in memory.

The code needed to access Far memory tend to be slightly larger compared to the Near memory.

interrupt

An interrupt function is meant to serve as an interrupt handler. The interrupt attribute 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 as an argument to the interrupt attribute:

int counter;

__attribute__((interrupt(0x0064)))
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.

amiga_interrupt

The amiga_interrupt attribute defines an interrupt function intended to be used with the Amiga operating system. The Amiga interrupt has the following effects:

  1. An Amiga interrupt function cannot take any parameters

  2. Follows the register convention of an Amiga interrupt, registers D0, D1, A0, A1, A5 and A6 are considered scratch registers.

  3. The return type should be int and you normally want to return 0 to allow interrupt processing further interrupt chain.

Here is a simple example of how an Amiga interrupt definition may look:

int counter;

__attribute__((amiga_interrupt))
int irq () {
  counter++;
  return 0;
}

Note

There is no interrupt vector associated with an Amiga interrupt definition. You need to use the Amiga operating system calls to install the interrupt handler.

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/intrinsics68000.h file which contains all such valid declarations.

saveds

A saveds function sets up the A4 base address upon entry and restores the previous value of A4 when returning. This is useful for API functions called from another context where A4 may point to another base area or used for another purpose.

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.