4.11.1 Integrating Assembly Language Modules

Entire functions can be coded in assembly language as separate .s (or .S) source files included into your project. They will be assembled and combined into the output image by the linker.

The following are guidelines that must be adhered to when writing a C-callable assembly routine.

  • Include the <xc.h> header file in your code. If this is included using #include, ensure the extension used by the source file is .S to ensure the file is preprocessed.
  • Select or define a suitable section for the executable assembly code (see 4.14.1 Compiler-Generated Sections for an introductory guide).
  • Select a name (label) for the routine
  • Ensure that the routine’s label is accessible from other modules
  • Use macros like _SFR_IO_ADDR to obtain the correct SFR address to use with instructions that can access the IO memory space.
  • Select an appropriate C-equivalent prototype for the routine on which argument passing can be modeled.
  • If values need to be passed to or returned from the routine, use the appropriate registers to passed the arguments.

The following example shows an assembly routine for an atmega103 device that takes an int parameter, adds this to the content of PORTD, and returns this as an int.

#include <xc.h>
.section .text
.global plus ; allow routine to be externally used
plus:
  ; int parameter in r24/5
  in r18, _SFR_IO_ADDR(PORTD) ; read PORTD
  add r24, r18 ; add to parameter
  adc r25, r1 ; add zero to MSB
  ; parameter registers are also the return location, so ready to return
  ret
.end

The code has been placed in a .text section, so it will be automatically placed in the area of memory set aside for code without you having to adjust the default linker options.

The _SFR_IO_ADDR macro has been used to ensure that the correct address was specified to instructions that read the IO memory space.

Because the C preprocessor #include directive and preprocessor macros were used, the assembly file must be preprocessed to ensure it uses a .S extension when compiled.

To call an assembly routine from C code, a declaration for the routine must be provided. Here is a C code snippet that declares the operation of the assembler routine, then calls the routine.

// declare the assembly routine so it can be correctly called
extern int plus(int);
void main(void) {
  volatile unsigned int result;
  result = plus(0x55); // call the assembly routine
}