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 $4-$7 are used for parameter passing. An assembly -language function will receive parameters, and should pass arguments to called functions, in these registers.
  • Table 14-1 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 foo 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

/*
** file: ex1.S
*/
#include <xc.h>

    /* define which section (for example "text")
     * does this portion of code resides in. Typically,
     * all your code will reside in .text section as
     * shown below.
     */
   .text

    /*  This is important for an assembly programmer. This
     * directive tells the assembler that don't optimize
     * the order of the instructions as well as don't insert
     * 'nop' instructions after jumps and branches.
     */
   .set noreorder

/*********************************************************************
* asmFunction(int bits)
* This function clears the specified bites in IOPORT A.
********************************************************************/
.global asmFunction
.ent asmFunction
asmFunction:
   /* function prologue - save registers used in this function
     * on stack and adjust stack-pointer
     */
   addiu   sp, sp, -4
   sw      s0, 0(sp)

   la      s0, LATACLR
   sw      a0, 0(s0)      /* clear specified bits */

   la      s0, PORTA
   lw      s1, 0(s0)
   la      s0, cVariable
   sw      s1, 0(s0)      /* keep a copy */

   /* function epilogue - restore registers used in this function
     * from stack and adjust stack-pointer
     */
   lw      s0, 0(sp)
   addiu   sp, sp,

   addu    s1, ra, zero
   jal     foo
   nop
   addu    ra, s1, zero
   nop
   /* return to caller */
   jr      ra
   nop
.end asmFunction

  .bss
  .global asmVariable
  .align 2
asmVariable: .space 4

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, foo, and how to access a C defined variable, cVariable.

/*
 * file: ex2.c
 */
#include <xc.h>
extern void asmFunction(int bits);
extern unsigned int asmVariable;
volatile unsigned int cVariable = 0;
volatile unsigned int jak = 0;

int
main(void) {
    TRISA = 0;
    LATA = 0xC6FFul;
    asmFunction(0xA55Au);
    while (1) {
        asmVariable++;
    }
}

void
foo(void) {
    jak++;
}

In the C file, ex2.c, external references to symbols declared in an assembly file are declared using the standard extern keyword; note that asmFunction is a void 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.