5. Data storage¶
This chapter discusses controlling data placement and its impact on application efficiency.
The 68000 is a 32 bits architecture that can address a linear 4GB address range which is shared by code and data.
As there is a good amount of storage registers and your statically allocated data is probably not more than 64K, you can normally use the Small data model which reserves one register as a base address for addressing static data. This typically saves two bytes of program space for every instruction that access static data.
5.1. Ways to store data¶
Data can be allocated as auto, static, or on the heap. Variable scope and the need for allocation during runtime determine placement.
As a general rule, use auto-allocated variables whenever possible. This offers the compiler maximum flexibility for resource allocation closest to the core, which typically results in the most efficient data access.
Auto variables¶
Auto variables include function parameters and local variables not
defined with static. The compiler attempts to allocate these in
processor registers; otherwise, the stack is used.
Auto variables are allocated only when used. Their registers can be reused for other auto variables or temporary data once no longer needed. This reuse also applies to stack locations.
Auto variables can have multiple live ranges; a variable with the same name might be used in distinct parts of a function. Internally, these are treated as different variables and may be allocated to various locations, potentially not existing between live ranges.
All auto variables associated with a function are deallocated upon function exit.
Note
If you take the address of an auto variable, you can pass its pointer to other functions, which is useful for temporary storage. However, avoid using such pointers outside their scope, as auto variables are deallocated on function exit. Be aware that taking an address allocates the variable on the stack, potentially increasing access cost compared to other auto variables.
Static variables¶
Global, module, or function static variables are allocated in global memory, occupying space for the application’s duration.
Their visibility varies. A global variable is universally visible but
requires extern for use. A module static variable, declared with
static at file scope, is visible within one compilation unit.
A static local variable within a function’s scope is only visible
there, retaining its value across function calls. Use static to
differentiate it from an auto variable.
Dynamically allocated¶
A dynamically allocated variable is retrieved from a heap using the malloc function. This is useful when the required data size is unknown at program startup.
Note
Dynamically allocated variables are a potential problem in memory constrained systems if the program is left running for a long time due to heap fragmentation.
5.2. Address spaces¶
The compiler provides multiple address spaces, which are addressable memory areas with specific properties:
Address width for pointers
Width of the associated integral index type
Different instruction sequences for accessing various address spaces
An extension keyword or type attribute name
Section names tied to the address space for linking control
Address space attributes are always active in the compiler.
The Calypsi C compiler tool chain for the 68000 provides two address spaces, near and far.
All pointers to data memory are 32 bits wide and occupy 4 bytes of memory when stored in memory. The keywords and address space attributes are intended to describe a storage location to help placement in appropriate sections.
Near address space¶
The near address space is a memory area reachable from a base
address held in register A4. This can be used in either the Small
or Large data models, but it is not available in the Far-only data
model.
You can have a maximum of 64K data in the near address space. If you run out of space you may select a couple of larger data objects and move them to the far address space (see below). 64K can be considered quite a lot of memory for storing reasonable small static data objects. Stack and heap allocated objects are allocated outside this 64K memory range.
Far address space¶
The far address space makes use of the full 32 bit address range. The amount of static data objects can be up to 4GB. Direct access to the far address space costs an additional two extra bytes for each instruction compared to using the near address space.
Note
While doing a direct access to a far object costs two extra bytes, there is no difference when using a pointer to the near or the far address area, they have the same cost.
Summary¶
The following table summarizes the available address spaces.
Memory type |
Keyword |
Address range |
Pointer size |
Index type |
|---|---|---|---|---|
Near |
|
|
32 bits |
|
Far |
|
|
32 bits |
|
Note
The Near memory type allocates data in a 64K bytes large area pointed
to by a base pointer held in register A4. Pointers and address
arithmetics are performed using 32 bit operations.
Syntax¶
An address space attribute keyword such as __far is
a type qualifier. Syntactically it works the same as other C language
defined type qualifier, e.g. const and volatile.
The following declaration defines four variables in the Far memory address space:
__far int a, b;
int __far c, d;
The __far type qualifier applies to the closest type,
int in this example. In C, the order between type qualifiers and
types does not matter; they convey the same meaning.
Pointers¶
A pointer in C points to something in memory. Both the pointer itself
and what it points to have types. As an example, the type char *
is a pointer to a char.
Pointer types are easier to understand if you read them from right to
left. The * is a pointer, so char * reads from right to left
as “pointer to char”. This order of reading is especially useful when
you mix in type qualifiers in pointer types, as it makes it a lot
easier to read and understand what the type means.
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.
Structures¶
You can place a structure in a specified address space. This means that all its members are in that address space. You cannot override individual structure members using an address space keyword. It is however possible to have members of the structure that point to a different address space.
struct tag {
int __far * p;
int value;
};
struct tag __far myTag;
This is however not allowed:
struct tag {
int * __far p; /* incorrect */
int __far value; /* incorrect */
};