7 RUN-TIME ENVIRONMENT

This chapter contains the following sections:

Startup Code
Register Usage
Section Usage
Stack
Heap
~hp~Allocating Heap
~hp~Heap Size Restrictions For Different Memory Models
Floating Point Arithmetic
Special Floating Point Values
Trapping Floating Point Exceptions
Floating Point Trap Handling API
Interrupt Functions
Assembly Language Interfacing

7.1 Startup Code

When linking your C modules with the library, you automatically link the object module, containing the C startup code. This module is called start.obj and is present in every C library (once for every compiler model).

Because this module specifies the run-time environment of your C application, you might want to edit it to match your needs. Therefore, this module is delivered in assembly source in the file start.src in the lib/src subdirectory. Typically, you will copy the template startup file to your own directory and edit it. The startup code contains preprocessor symbols that can be interpreted by asxa when you want to make your own version of the object file.

The following table shows all the macros (defines) that can be set for start.src. Within EDE you can control these options from the EDE | Processor Options | Startup tabs.

Define Description
Model selection
MODEL_TINY Defines the tiny memory model for the C compiler (default)
MODEL_SMALL Defines the small memory model for the C compiler
MODEL_MEDIUM Defines the medium memory model for the C compiler
MODEL_COMPACT Defines the compact memory model for the C compiler
MODEL_LARGE Defines the large memory model for the C compiler
MODEL_HUGe Defines the huge memory model for the C compiler
Initialization
__NOINITSEG If defined, C variables are not initialized at startup
__NOINITBIT If defined, the bit-addressable range is not cleared at startup. (default not defined)
__NOINITARG If defined, disable initilization of argc and argv
__NOAVOIDNULL If defined, no section is defined to prevent pointers to be allocated at address 0 (NULL). (default not defined)
__NOEXIT If defined, C exit() and abort() are not supported in startup
__NOWATCHDOG If defined, disable watch dog code
__ESWEN If defined, write through ES in User mode is enabled
Configuration bits SCR
__PT1 __PT0 Peripheral Clock
coreXA SmartXA
0 0 oscillator/4 oscillator/4 (default)
0 1 oscillator/16 oscillator/3
1 0 oscillator/64 oscillator/2
1 1 reserved oscillator/1
__CM Defined to 0, because only "native" mode XA is supported
__PZ 1 for Page zero mode (default)
0 for Large memory mode
__EEFAST 1 EEPROM fast timing is active (SmartXA only)
0 EEPROM fast timing is inactive (default)
Configuration bits Reset PSW
__SM 1 for System mode (default)
0 for User mode
__TM 1 for Trace Mode
0 for XA debugging feature are disabled (default)
__RS Register bank number [0..3] (default 0)
__IM Interrupt priority (0-15), 15 indicates highest priority (default)
__C Carry flag (default 0)
__AC Auxiliary carry (default 0)
__V Overflow flag (default 0)
__N Negative flag (default 0)
__Z Zero flag (default 0)
Configuration bits DMCR (SmartXA only)
__SSS1 __SSS0 Size of segment 0 (bytes)
0 0 256
0 1 512
1 0 1024
1 1 Maximal
__USS1 __USS0 Size of segment 1-14 (bytes)
0 0 128
0 1 256
1 0 512
1 1 Maximal
Configuration Memory Management Unit (SmartXA only)
__MUBLKHI0 Highest block number window 0 (default 255)
__MUBLKHI1 Highest block number window 1 (default 255)
__MUBAS0 Relocation offset window 0 (default 0)
__MUBAS1 Relocation offset window 1 (default 0)

Table 7-1: Defines used in start.src

You must select the configuration you are using by defining one of the above models using the -D option of the control program. If you do not specify a model the default is MODEL_TINY.

The invocation for the medium model (using the ccxa control program) is:

In the C startup code an absolute code section is defined for setting up the power on vector and the C environment. The power-on vector contains a jump to the __START label. This PUBLIC label may not be removed, since it is referred to by the C compiler. It is also used as the default start address of the application (see the start keyword in the locator description language DELFEE). The code space for all non used interrupt vectors may be occupied by small user code section. When this is not wanted, you should reserve the space for all non used interrupt vectors in the locator description file. Thus preventing lcxa from using this area for a user code section.

The stack is defined in the locator description file (xa.dsc in directory etc) with the keyword stack, which results in a section called stack. The description file tells lcxa to allocate the stack after all other data section and the heap. See the section Stack for detailed information on the stack.

The heap is defined in the description file with the keyword heap, which results in a section called heap. The heap area is allocated in IDATA. See the section Heap for detailed information on heap management.

The startup code takes care of clearing global RAM variables (_near, _far and _huge) and initializing C variables, residing in the different RAM areas. The startup code copies the initial values of initialized C variables from ROM to RAM, using a locator generated table (also known as the 'copy table') and a run-time library function.

When everything described above has been executed, your C application is called, using the public label _main, which has been generated by cxa for the C function main().

When the C application 'returns', which is not likely to happen in an embedded environment, the program performs a Power Down by setting the PD bit. When using a debugger, it can be useful to set a breakpoint on the __EXIT label, indicating the program has reached the end, or the library function exit() has been called.

The following table shows the locator labels used in the startup code.

Locator Label Description
__START start label, mentioned in description file (xa.dsc)
__INITSEG label copy table init function
_main start label user C program
_exit start label of exit() function
__EXIT exit() function jumps to this place
__lc_bs locator begin of stack label
__lc_es locator end of stack label
__lc_gb_CS locator begin of data group CS label
__lc_gb_DS locator begin of data group DS label
__lc_gb_ES locator begin of data group ES label
__lc_gb_PC0 locator begin of data group PC0 label
__lc_gb_PC1 locator begin of data group PC1 label
RESET_VECTOR Reset vector of the application, locator description file contains absolute address

Table 7-2: Locator labels used in startup code

7.2 Register Usage

The XA architecture defines a total of 16 word registers in the register file. In the baseline XA core, only R0 through R7 are implemented. Additional global registers, R8 through R15, are reserved and may be implemented in specific XA derivatives.

Code within the run-time libraries does not need registers for indirect access. This makes registers R8 through R15 very useful as an interface to the run-time procedures. When R8 through R15 are used in the run-time library, the need to save R0 through R7 within the source that calls the run-time procedures is reduced.

So the number of available registers has a significant influence on the code generation strategy. A strategy that assumes that only 8 registers are available generates code that can be used on the 8 and the 16 register XA derivatives. However, for the 16 register derivatives the code is not optimal. A strategy that assumes that 16 registers are available generates code that only runs on 16 register XA derivatives. The XA C compiler generates code that can be used on both the 8 and 16 register XA derivatives.

cxa uses the following registers for C function return types:

Return type Register Description
bit R0.0 (carry)
char R0L (accumulator)
short/int R0 (R0H high byte, R0L low byte)
long / float R1 R0 (R1 high word, R0 low word)
double R3 R2 R1 R0 (R3 high word, R0 low word)
_near / _romc_cs / _rom_pc0/_rom_pc1 / _edata pointer R0 (R0H high byte, R0L low byte)
_rom pointer R0 (R0H high byte, R0L low byte)
(Tiny or small memory model_
_rom pointer R1 R0 (R1 high word, R0 low word)
(Compact, medium or large memory model)
_far/_far_es/_huge pointer R1 R0 (R1 high word, R0 low word)

Table 7-3: Register usage

Section 3.5 , Parameter Passing and Function Return, contains a detailed description of the register usage whith parameter passing.

7.3 Section Usage

cxa uses a large number of section. This section contains a list of all possible section names of a complete C application:

BIT

modulename_INI_BI initialized static/global user C _bit type variables

mname_CLR_BI cleared static/global user C _bit type variables

mname_varname_BI BSEG AT xx
absolute sections for variables placed using the _at() keyword

BI@groupname join user C _bit type variables to data group

DATA

modulename_INI_NE initialized static/global user C variables residing in _data/_near

mname_CLR_NE cleared static/global user C variables residing in _data/_near

mname_varname_NE DSEG AT xx
absolute sections for variables placed using the _at() keyword

NE@groupname join user C variables residing in _data/_near to data group

BITADDR

modulename_INI_BA initialized static/global user C variables residing in _bdat

mname_CLR_BA cleared static/global user C variables residing in _bdat

mname_varname_BA DBSEG AT xx
absolute sections for variables placed using the _at() keyword

BA@groupname join user C variables residing in _bdat to data group

IDATA

modulename_INI_ID initialized static/global user C variables residing in _idat or _far (tiny or small memory model)

mname_CLR_ID cleared static/global user C variables residing in _idat or _far (tiny or small memory model)

mname_varname_ID ISEG AT xx
absolute sections for variables placed using the _at() keyword

ID@groupname join user C variables residing in _idat to data group

FA@groupname join user C variables residing in _far (tiny or small memory model) to data group

HDATA INSEGMENT

modulename_INI_FA initialized static/global user C variables residing in _far

modulename_INI_FE initialized static/global user C variables residing in _far_es

mname_CLR_FA cleared static/global user C variables residing in _far

mname_CLR_FE cleared static/global user C variables residing in _far_es

mname_varname_FA HSEG INSEGMENT AT xx
absolute sections for variables placed in _far using the _at() keyword

mname_varname_FE HSEG INSEGMENT AT xx
absolute sections for variables placed in _far_es using the _at() keyword

FA@groupname join user C variables residing in _far to data group

FE@groupname join user C variables residing in _far_es to data group

HDATA

modulename_INI_HU initialized static/global user C variables residing in _huge

mname_CLR_HU cleared static/global user C variables residing in _huge

mname_varname_HU HSEG AT xx
absolute sections for variables placed using the _at() keyword

HU@groupname join user C variables residing in _huge to data group

EDATA NOCLEAR

modulename_NCLR_ED static/global user C variables residing in _edata. These variables are not cleared or initialized at startup. (SmartXA only)

mname_varname_ED ESEG AT xx
absolute sections for variables placed using the _at() keyword

CODE / HCODE

CSEG AT 0H absolute code section for power on vector and startup code.

HCSEG AT 0H absolute hcode section for power on vector and startup code.

modulename_PR user C functions, C library functions

CXA_RTL_PR run-time library functions, C Startup Code

mname_varname_PR CSEG AT xx
absolute code sections for interrupt vectors

mname_varname_PR HCSEG AT xx
absolute hcode sections for interrupt vectors

CODE ROMDATA / HCODE ROMDATA

modulename_RO user C variables residing in _rom

modulename_RC user C variables residing in _rom_cs

modulename_IR switch tables residing in one ROMDATA segment

HCODE INSEGMENT ROMDATA

modulename_RO0 user C variables residing in _rom_pc0

modulename_RO1 user C variables residing in _rom_pc1

HCODE INSEGMENT

modulename_PR0 program code residing in _rom_pc0

modulename_PR1 program code residing in _rom_pc1

XSHORT

modulename_INI_XS initialized static/global user C variables residing in _xdat

mname_CLR_XS cleared static/global user C variables residing in _xdat

mname_varname_XS XSSEG AT xx
absolute sections for variables placed using the _at() keyword

XS@groupname join user C variables residing in _xdat to data group

If you use the -R option (or renamesec pragma), to specify the name cxa must use for a certain section, this name is added to this list. Note that lcxa produces a locator map (suffix .map) which shows the addresses of all sections used in the application.

7.4 Stack

The stack is defined in the locator description file (xa.dsc in directory etc) with the keyword stack, which results in a section called stack. The description file tells lcxa to allocate the stack after all other data sections and the heap.

The stack size can be controlled with the keyword length= size in the description file. If you do not specify the stack size, the locator will allocate the rest of the available memory for the stack, as done in the startup code.

In EDE you can enter the system stack size in the System stack size field in the Stack tab of the EDE | Linker/Locator Options ... menu item. You can enter the location of the stack in the System stack address field.

You can use the locator defined labels __lc_bs and __lc_es in your application to retrieve the beginning and end address of the stack. Please note that the locator will only allocate a stack section if the application refers to one of the locator defined symbols __lc_bs or __lc_es. Remember that there must be enough space allocated for the stack, which grows downwards.

The following diagram shows the stack frame when using reentrant functions. The processor mode, user-mode or system-mode, does not influence the stack frame.

Figure 7-1: Stack diagram

The stack saves the return addresses of functions, non-register automatic and parameter variables of reentrant functions.

Automatics and parameters are all accessed using the stack pointer register. The stack pointer SP points to the last item pushed on the stack.

The stack frame also contains a so-called virtual frame pointer (fp). The virtual frame pointer points to the lower byte of the function's return address. In case of an _interrupt function fp points to PSWL. No on-chip register is allocated to serve as frame pointer. It is the debuggers task to calculate the virtual frame pointer for a function. All stack offsets in the debug info are relative to this virtual frame pointer. To be able to access automatic variables, the debugger needs to know two offsets, the stack size and the stack pointer adjust.

The stack size is passed as a function constant by the compiler. The stack size is always 0 (zero), because stack pointer adjust information is also generated in the function prologue. The stack pointer adjust reflects the number of pushes/pops done since the functions prologue.

Be aware that an interrupt function pushes both the PC and the current value of the PSW on the stack.

7.5 Heap

The heap is a reserved area in the data space. The heap area is needed when you use dynamic memory management library functions.

7.5.1 Allocating Heap

The heap is only needed when dynamic memory management library functions are used: malloc(), calloc(), free() and realloc(). Only if you use one of the memory allocation functions listed above, the locator automatically allocates a heap, as specified in the locator description file with the keyword heap.

You can place the heap section anywhere in memory, using the locator description file. To allocate the heap area., you must define a special section called heap. With the keyword length=size you can specify teh size of the heap. If you do not specify the heap size and yet refer to it (e.g. call malloc()), the locator will allocate the rest of the available IDATA for the heap.

In EDE you can enter the size and the location of the heap in the Heap size and Heap address fields in the Heap tab of the EDE | Linker/Locator Options ... menu item.

The locator defined labels __lc_bh and __lc_eh (beginning and end of heap) are used by the library function sbrk(), which is called by malloc() when memory is needed from the heap.

After editing, you must process the C startup file with both mppxa and asxa to make the correct object file. For a detailed description, see the section Startup Code.

Example part of the locator description file defining the heap size and location:

The special heap section is only allocated when its locator labels are used in the program.

7.5.2 Heap Size Restrictions For Different Memory Models

The heap size is restricted to 64Kb for tiny and small memory models, because the XA runs in page zero mode. So, only data segment zero is available for heap allocation.

The total amount of memory that can be allocated with malloc, calloc and realloc is restricted to 64Kb for the medium and compact memory models. The pointer returned by malloc() is a 16-bit pointer with 16-bit arithmetic, conform the default storage specifiers of the medium and compact memory models. It is not allowed to qualify the pointer of these ANSI-C compliant malloc functions but non ANSI-C compliant huge malloc functions are available that are not restricted to 64Kb. These functions use the _huge storage qualifier and are called halloc, calloc, hrealloc and hfree. It is not allowed to use halloc functions simultaneously with malloc functions. Both use the same heap, but with different pointer sizes.

For the large memory model the total amount of memory that can be allocated with malloc, calloc and realloc is not restricted to 64Kb. However, each block allocated cannot be larger than 64Kb, to prevent 64Kb page boundary overrides. In the large memory model pointers are 24-bit with 16-bit arithmetic. Default pointers are qualified _far in the large memory model. For this model are also non ANSI-C compliant huge malloc functions are available. The huge malloc functions use _huge pointers and are not restricted to 64Kb per allocated block of memory. The huge malloc functions are called halloc, calloc, hrealloc and hfree. It is not allowed to use halloc functions simultaneously with malloc functions. Although their pointer sizes are equal, halloc and malloc use different algorithms for the allocation of their memory.

For the huge memory model there are no restrictions other than the amount of available heap space for the allocation of memory with the malloc functions, because default pointers are qualified _huge. This makes non ANSI-C huge malloc functions superfluous.

7.6 Floating Point Arithmetic

Floating point arithmetic support for the cxa is included in software as a separate set of libraries. When linking, the desired floating point library must be specified after the C library. The libraries are reentrant, and only use temporary program stack memory.

To ensure portability of floating point arithmetic, floating point arithmetic for the cxa has been implemented adhering to the IEEE-754 standard for floating point arithmetic. See the IEEE Standard for Binary Floating-Point Arithmetic document, as published in 1985 by the IEEE Computer Society, for more details on these floating point arithmetic definitions. This document is referred to as IEEE-754 in this manual.

cxa supports both single and double precision floating point operations, usable via the ANSI C types float and double respectively. For the sole purpose of speed, also a non-trapping library is included for each memory model. For the library name syntax, see section C Libraries.

It is possible to intercept floating point exceptional cases and, if desired, handle them with an application defined exception handler. The intercepting of floating point exceptions is referred to as 'trapping'. Examples of how to install a trap handler are included.

7.6.1 Special Floating Point Values

Below is a list of special, IEEE-754 defined, floating point values as they can occur during run-time.

Special value Sign Exponent Mantissa
+0.0 (Positive Zero) 0 all zeros all zeros
-0.0 (Negative Zero) 1 all zeros all zeros
+INF (Positive Infinite) 0 all ones all zeros
-INF (Negative Infinite) 1 all ones all zeros
NaN (Not a number) 0 all ones all ones

Table 7-4: Special floating point values

7.6.2 Trapping Floating Point Exceptions

Two floating point run-time libraries are delivered for every memory model:

The distinction is made by adding an additional 't' to the name of the library comprising trap handling. The m must be replaced by one of the C memory models ('t' tiny, 's' small, 'm' medium, 'c' compact or 'l' large). By specifying the -fptrap option to the control program ccxa, the trapping type floating point library is linked into your application. If this option is not specified, the floating point library without any trapping mechanism is used when linking.

In EDE you can specify to use the trapping type floating point library by enabling the Floating point trap/exception handling check box in the Misc tab of the EDE | C Compiler Options | Project Options ... menu item.

The floating point libraries without trapping mechanism execute faster, but the result of a floating point operation is undefined when any operand or result is not in range.

IEEE-754 Trap Handler

In the IEEE-754 standard a trap handler is defined, which is invoked on (specified) exceptional events, passing along much information about the event. To install your own trap handler, use the library call _fp_install_trap_handler. When installing your own exception handler, you will have to select on which types of exceptions you want to have your handler invoked, using the function call _fp_set_exception_mask. See further below for more details on the floating point library exception handling function interface.

This approach has been included as an example in the fptrap subdirectory of the examples directory, in the file fptrap.c.

SIGFPE Signal Handler

In ANSI-C the regular approach of dealing with floating point exceptions is by installing a so-called signal handler by means of the ANSI-C library call signal. If such a handler is installed, floating point exceptions cause this handler to be invoked. To have the signal handler for the SIGFPE signal actually become operational with the provided floating point libraries, a (very) basic version of the IEEE-754 exception handler must be installed (see example below) which will raise the desired signal by means of the ANSI-C library function call raise. For this to be achieved, the function call _fp_install_trap_handler is present. When installing your own exception handler, you will have to select on which types of exceptions you want to receive a signal, using the function call _fp_set_exception_mask. See further below for more details on the floating point library exception handling function interface.

There is no way to specify any information about the context or nature of the exception to the signal handler. Just that a floating point exception occurred can be detected. See therefor the IEEE-754 trap handler discussion above if you want more control over floating point results.

This approach has been included as an example in the fpsigfpe subdirectory of the examples directory, in the file fpsigfpe.c.

Example:

7.6.3 Floating Point Trap Handling API

For purposes of dealing with floating point arithmetic exceptions, the following library calls are available:

A pair of functions to get or set the mask which controls which type of floating point arithmetic exceptions are either ignored or passed on to the trap handler. The types of possible exception flag bits are defined as:

while,

is the OR of all possible flags. See below for an explanation of each flag.

A pair of functions for examining or presetting the status word containing the accumulation of all floating point exception types which occurred so far. See the possible exception type flags above.

This function call expects a pointer to a function, which in turn expects a pointer to a structure of type _fp_exception_info_t. The members of _fp_exception_info_t are:

The member d is not present when specifying the -F option to the C compiler.

The following table lists all the exception code flags, the corresponding error description and result:

Error Description Exception Flag Default Result with Trapping
Invalid Operation EFINVOP NaN
Division by zero EFDIVZ +INF or -INF
Overflow EFOVFL +INF or -INF
Underflow EFUNFL zero
Inexact EFINEXT undefined

INF Infinite which is the largest absolute floating point
number, being always: -INF < every finite number < +INF
NAN Not a Number, a symbolic entity encoded in floating point format.

Table 7-5: Exception Type Flag Codes

To ensure all exception types are specified, you can specify EFALL to a function, which is the binary OR of all above enlisted flags.

7.7 Interrupt Functions

Interrupt functions can be implemented directly in C, by using the _interrupt(n) function qualifier. A function declared with this qualifier differs from a normal function definition in a number of ways:

1. The appropriate interrupt vector, consisting of an interrupt function entry label and a PSW value. The PSW value placed in the interrupt vector table can be defined with the interrupt function qualifier _using. For more details, see section Interrupt and Using. The vector may be suppressed with the -v option or the #pragma novector or interrupt vector -1. The vector range of the XA is 0-70; other interrupt vectors are not supported by the XA core.Interrupt and Using

See figure 3-3 in section Interrupt and Using for a list of interrupt vectors. See the 16-bit 80C51XA Microcontrollers Data Handbook for more details.

2. All registers that might possibly be corrupted during the execution of the interrupt function are saved on function entry and restored on function exit. Normally, only the registers directly used by the interrupt function will be saved.

3. The function is terminated with a RETI instruction instead of a RET instruction.

Example:

; XA C compiler vx.y rz     SN00105922-012 (c) year TASKING, Inc.
; options: -s -gn -Ms
$CASE
$ZPAGE
      NAME  INTERPT
; interpt.c    1  int  count;
; interpt.c    2
; interpt.c    3  _interrupt(1) _using( 0x8f00 )
; interpt.c    4  void  handler ( void )
; interpt.c    5  {
      PUBLIC      _handler
INTERPT_PR  SEGMENT  CODE
      RSEG  INTERPT_PR
_handler:
      PUSH.W  R1
      PUSH.W  R0
; interpt.c    6        count++;
      MOV.W  R0,#_count
      MOV.W  R0,[R0]
      ADD.W  R0,#01H
      MOV.W  R1,#_count
      MOV.W  [R1],R0
; interpt.c    7  }
      POP.W  R0
      POP.W  R1
      RETI
      CSEG AT 04H
      DW    0008FH,_handler

INTERPT_CLR_FA    SEGMENT    IDATA CLEAR
      RSEG   INTERPT_CLR_FA
      PUBLIC      _count
      ALIGN  1
_count:      DS   2
      END

7.8 Assembly Language Interfacing

Assembly language functions can be called from C and vice versa. The names used by cxa are case sensitive, so you must tell asxa to act case sensitive too, using the $CASE control. cxa prepends an underscore for the name of the C variable, to distinguish these names from the XA registers. So, any names used or defined in XA C must have a leading underscore in assembly code. Internal compiler symbols (run-time library) use two underscores.

When using strict ANSI, and when you call an assembly routine that has a name of e.g. 50 characters, you get a link error "UNRESOLVED EXTERNAL". The reason for it is that the C compiler truncates names to 32 characters, but the assembler and linker do not. The solution is, when calling assembly routines, use names of 31 characters or less (if you do not count the leading '_' for a moment). The same rule applies when you call a C function from your assembly code.

Section 3.5, Parameter Passing and Function Return describes how parameter passing is supported by cxa. If you do not want parameter passing in registers (e.g. existing assembly function expecting parameters on the stack) you must use the keyword _stackparm (as function qualifier) in the full C prototype of the assembly language function.

The quickest (and most reliable) way to make an assembly language function, which must conform to XA C, is to make the body of this function in C, and compile this module with the memory model used by all other C modules. If the assembly function must return something, specify the return type in the 'assembler function' using C syntax, and let it return something. If parameters are used, force code generation for accessing these parameters with a dummy statement (e.g. an assignment) or declare the parameter as volatile and just access it:

Now compile this module, using the correct memory model. The compiler makes the correct frame, and you can edit the generated assembly module, to make the real assembly function inside this frame.

A second method to create an interface to assembly is to make use of the feature of the compiler to have inline assembly.

Assembly lines in the C source must be introduced by a '#pragma asm', the end is indicated by a '#pragma endasm'. For example:

When the assembly does not change any registers, like in the example above, also '#pragma asm_noflush' can be used instead of '#pragma asm'. For an explanation of the used pragmas see the section Pragmas.


Copyright © 2000 TASKING, Inc.