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
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:
ccxa -c -DMODEL_MEDIUM start.src
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
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.
cxa uses a large number of section. This section contains a list of all possible section names of a complete C application:
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
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
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
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
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
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
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
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
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
modulename_RO0 user C variables residing in _rom_pc0
modulename_RO1 user C variables residing in _rom_pc1
modulename_PR0 program code residing in _rom_pc0
modulename_PR1 program code residing in _rom_pc1
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.
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.
The heap is a reserved area in the data space. The heap area is needed when you use dynamic memory management library functions.
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:
amode idata { section selection=w; heap length=1000; }
The special heap section is only allocated
when its locator labels are used in the program.
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.
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.
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
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.
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.
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:
#include <float.h> #include <signal.h> static void pass_fp_exception_to_signal( _fp_exception_info_t *info ) { info; /* suppress parameter not used warning */ /* cause SIGFPE signal to be raised */ raise( SIGFPE ); /* * now continue the program * with the unaltered result */ }
For purposes of dealing with floating point arithmetic exceptions, the following library calls are available:
#include <float.h> int _fp_get_exception_mask( void ); void _fp_set_exception_mask( int );
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:
EFINVOP EFDIVZ EFOVFL EFUNFL EFINEXCT
while,
EFALL
is the OR of all possible flags. See below for an explanation of each flag.
#include <float.h> int _fp_get_exception_status( void ); void _fp_set_exception_status( int );
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.
#include <float.h> void _fp_install_trap_handler( void (*) (_fp_exception_info_t * ) );
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:
exception
EFINVOP EFDIVZ EFOVFL EFUNFL EFINEXCT
operation
_OP_ADDITION _OP_SUBTRACTION _OP_COMPARISON _OP_EQUALITY _OP_LESS_THAN _OP_LARGER_THAN _OP_MULTIPLICATION _OP_DIVISION _OP_CONVERSION
source_format destination_format
_TYPE_SIGNED_CHARACTER _TYPE_UNSIGNED_CHARACTER _TYPE_SIGNED_SHORT_INTEGER _TYPE_UNSIGNED_SHORT_INTEGER _TYPE_SIGNED_INTEGER _TYPE_UNSIGNED_INTEGER _TYPE_SIGNED_LONG_INTEGER _TYPE_UNSIGNED_LONG_INTEGER _TYPE_FLOAT _TYPE_DOUBLE
operand1 /* left side of binary or */ /* right side of unary */ operand2 /* right side for binary */ result
typedef union _fp_value_union_t { char c; unsigned char uc; short s; unsigned short us; int i; unsigned int ui; long l; unsigned long ul; float f; #if ! _SINGLE_FP double d; #endif } _fp_value_union_t;
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.
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.
; 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
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:
int _stackparm assem( char volatile a, char c, int i ) { a; return( c + i ); }
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:
int assem( char c, int i ) { int j; j = i; #pragma asm MOV.B P2,#01 #pragma endasm j = c; }
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.