20.1 Mixing Assembly Language and C Variables and Functions
The following guidelines indicate how to interface separate assembly language modules with C modules.
- Follow the register conventions described in Register Conventions. In particular, registers r0-r3 are used for parameter passing. An assembly language function will receive parameters, and should pass arguments to called functions, in these registers.
- The table in Register Conventions describes which registers must be saved across non-interrupt function calls.
- Interrupt functions must preserve all registers. Unlike a normal function call, an interrupt may occur at any point during the execution of a program. When returning to the normal program, all registers must be as they were before the interrupt occurred.
- Variables or functions declared within a separate assembly file that
will be referenced by any C source file should be declared as global using the assembler
directive
.global. Undeclared symbols used in assembly files will be treated as externally defined.
The following example shows how to use variables and functions in both assembly language and C regardless of where they were originally defined.
The file ex1.c defines cFunction and
cVariable to be used in the assembly language file. The C file also
shows how to call an assembly function, asmFunction, and how to access the
assembly defined variable, asmVariable.
Mixing C and Assembly
.syntax unified
.cpu cortex-m7
.thumb
.global asmVariable
.type asmVariable,%object
.data
.align 2
asmVariable:
.space 4
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ char *asmFunction (char *s)
@ {
@ asmVariable = 0;
@ if (s) {
@ char *d = s, c;
@ while ((c = *d)) {
@ if (cFunction (c)) {
@ *d = c & cVariable;
@ ++asmVariable;
@ }
@ ++d;
@ }
@ }
@ return s;
@ }
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
.global asmFunction
.type asmFunction,%function
.text
.align 1
.thumb_func
asmFunction:
@ if the input string is not NULL
cbnz r0, .L_not_NULL
@ set 'asmVariable' to zero and return
ldr r1, =asmVariable
str r0, [r1]
bx lr
.L_not_NULL:
@ r4-r7 are callee-saved registers
@ LR contains the return address
@ r0 is the first argument and also the return value of the
function
push {r0, r4-r7, lr}
@ d = s;
mov r4, r0
@ r6 - the value of the C variable (8-bit AND mask)
@ r7 - counter of changed chars
ldr r6, =cVariable
movs r7, #0
ldrb r6, [r6]
.L_while:
@ while ((c = *d))
ldrb r5, [r4]
cbz r5, .L_end_while
@ if (cFunction (c))
mov r0, r5
bl cFunction
cbz r0, .L_next_char
@ *d = c & cVariable;
ands r5, r5, r6
strb r5, [r4]
@ ++ the number of changed chars
adds r7, r7, #1
.L_next_char:
@ ++d;
adds r4, r4, #1
b .L_while
.L_end_while:
@ write the no. of changes to 'asmVariable'
ldr r0, =asmVariable
str r7, [r0]
@ return s;
pop {r0, r4-r7, pc}
.pool
.size asmFunction, .-asmFunction
The file ex1.S defines asmFunction and
asmVariable as required for use in a linked application. The
assembly file also shows how to call a C function, cFunction, and how
to access a C defined variable, cVariable.
#include <xc.h>
#include <stdio.h>
extern int asmVariable;
extern char *asmFunction (char *s);
char cVariable = 0xDF;
char cFunction (char c)
{
return c >= 'a' && c <= 'z';
}
int main()
{
char s[] = "heLLo, wOrlD!";
printf ("%s\n", s);
char *d = asmFunction (s);
printf ("%s\nchanges: %d", d, asmVariable);
return 0;
}
In the C file, ex2.c, although for the function
declaration this isn’t required, note that asmFunction is a
char * function and is declared accordingly.
In the assembly file, ex1.S, the symbols
asmFunction and asmVariable are made globally
visible through the use of the .global assembler directive and can be
accessed by any other source file.
