Interfacing C/C++ with assembly language functions is straightforward if you follow the calling conventions defined in Section 7.3, and the register conventions defined in Section 7.2. C/C++ code can access variables and call functions defined in assembly language, and assembly code can access C/C++ variables and call C/C++ functions.
Follow these guidelines to interface assembly language and C:
- All functions, whether they are written in C/C++ or assembly language, must follow the register conventions outlined in Section 7.2.
- Dedicated registers modified by a function must be preserved. Dedicated registers include:
| XAR1 | R4H (FPU only) | |
| XAR2 | R5H (FPU only) | |
| XAR3 | R6H (FPU only) | |
| SP | R7H (FPU only) | |
If the SP is used normally, it does not need to be preserved explicitly. The assembly function is free to use the stack as long as anything that is pushed on the stack is popped back off before the function returns (thus preserving the SP).
Any register that is not dedicated can be used freely without being preserved.
- The stack pointer (SP) must be even-aligned by the parent function prior to making a call to the child function. This is done by incrementing the stack pointer by 1, if necessary. If needed, the coder should increment the SP before making the call.
- The stack is aligned at function boundary.
- Interrupt routines must save all the registers they use. For more information, see Section 7.7.
- When you call a C/C++ function from assembly language, load the designated registers with arguments and push the remaining arguments onto the stack as described in Section 7.3.1.
When accessing arguments passed in from a C/C++ function, these same conventions apply.
- Longs and floats are stored in memory with the least significant word at the lower address.
- Structures are returned as described in Section 7.3.2.
- No assembly module should use the .cinit section for any purpose other than autoinitialization of global variables. The C/C++ startup routine assumes that the .cinit section consists entirely of initialization tables. Disrupting the tables by putting other information in .cinit can cause unpredictable results.
- The compiler prepends an underscore ( _ ) to the beginning of all identifiers. In assembly language modules, you must use the prefix _ for all objects that are to be accessible from C/C++. For example, a C/C++ object named x is called _x in assembly language. For identifiers that are to be used only in an assembly language module or modules, any name that does not begin with an underscore can be safely used without conflicting with a C/C++ identifier.
- The compiler assigns linknames to all external objects. Thus, when you write assembly language code, you must use the same linknames as those assigned by the compiler. See Section 6.12 for details.
- Any object or function declared in assembly language that is accessed or called from C/C++ must be declared with the .def or .global directive in the assembly language modifier. This declares the symbol as external and allows the linker to resolve references to it.
Likewise, to access a C/C++ function or object from assembly language, declare the C/C++ object with the .ref or .global directive in the assembly language module. This creates an undeclared external reference that the linker resolves.
- Because compiled code runs with the PAGE0 mode bit reset, if you set the PAGE0 bit to 1 in your assembly language function, you must set it back to 0 before returning to compiled code.
- If you define a structure in assembly and access it in C using extern struct, the structure should be blocked. The compiler assumes that structure definitions are blocked to optimize the DP load. So the definition should honor this assumption. You can block the structure by specifying the blocking flag in the .usect directive. See the TMS320C28x Assembly Language Tools User’s Guide for more information on these directives.