5.12.3.4 Accessing Registers From Assembly Code

Special Function Registers (SFRs) can be accessed in assembly code using special symbols defined by the compiler. See How Do I Find The Names Used To Represent SFRs And Bits? for assistance on finding SFR names.

In separate assembly modules, the assembly header file <xc.inc> can be used to gain access to SFR definitions. Do not use this file for assembly in-line with C code as it will clash with definitions in <xc.h>. Include the <xc.inc> header file using the C preprocessor’s #include directive. Make sure you use a .S extension for the assembly source file.

The symbols for registers in this header file look similar to the identifiers provided by <xc.h> in the C domain, for example, PORTA and EECON1. They are different symbols to those in the C domain, but they will map to the same memory location as their C counterparts.

The names of bits within SFRs are available and are defined as registerName, bitNumber with address masking. This makes them usable in bit-orientated assembly instructions. So, for example, RA0 is defined as BANKMASK(PORTA), 0.

Here is an example of a Mid-range assembly module that uses SFRs.
#include <xc.inc>
GLOBAL _setports
PSECT text,class=CODE,local,delta=2
_setports:
    movlw 0xAA
    BANKSEL (PORTA)
    movwf BANKMASK(PORTA)  ;write 0xAA to PORTA
    BANKSEL (PORTB)
    bsf RB1                ;set bit #1 in PORTB

If you wish to access register definitions from assembly that is in-line with C code, ensure that the <xc.h> header is included into the C module. Information included by this header will define in-line assembly symbols as well as the usual symbols accessible from C code.

The symbols used for register names are similar to those defined by the <xc.inc> header. The example given previously could be rewritten as in-line assembly as follows.
#include <xc.h>

int main(void) {
    asm("movlw 0xAA");
    asm("BANKSEL (PORTA)");
    asm("movwf " ___mkstr(BANKMASK(PORTA)));   ;write 0xAA to PORTA
    asm("BANKSEL (PORTB)");
    asm("bsf " RB1_bit);                       ;set bit #1 in PORTB
}

Note that BANKMASK() is a predefined preprocessor macro, and thus it will not be expanded inside a C string literal. If you want to use the BANKMASK() macro within in-line assembly, convert the macro’s replacement to a string using the ___mkstr() macro (note: three leading underscore characters), defined by the inclusion of <xc.h>.

The RB1_bit operand, which represents bit #1 of PORTB, is also a predefined macro defined by the inclusion of <xc.h>. It makes use of another macro called _BIT_ACCESS, which converts the byte and bit number operands to a string, ready to be used by the in-line assembly instruction.

The code generator does not detect when SFRs could be modified by in-line assembly, so these registers might need to be preserved before the in-line assembly code and restored afterward. The list of registers used by the compiler and further information can be found in Register Usage.