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 14.2 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 14.2 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.