4.1.3 Address Masking

All assembly identifiers represent a full address. The upper bits of a file register address represent the bank number of the object. Similarly, the upper bits of a program memory address represent the page number of the destination label. Such addresses might not be immediately usable as operands to some PIC instructions and might need to be masked or truncated to fit within the instruction's address field.

There are two ways of handling addresses when they are used as an instruction operand. The first is to have the linker automatically truncate these addresses to suite the instruction. The second is to manually mask the bank or page bits from the address. Both approaches have advantages. Automatic truncation is by far the easiest to use and does not clutter your assembly source with ancillary expressions in instruction operands, but this truncation is applied to all operands of all instructions in your program, and it might hide genuine errors that would otherwise be issued. Masking operands manually must be performed whenever needed but can be done in such a way that it confirms assumptions you have made regarding the location of the object, increasing the reliability of your program.

To have the linker automatically truncate all addresses to fit the instruction, use the -Wl,--fixupoverflow=action option and one of the warn, lstwarn, or ignore action arguments. See 6.1.31 Fixupoverflow Linker Option for more information on this option. Note that this option (except when an action argument of error is used) will suppress fixup overflow errors associated with all instructions in your program. It is recommended that you select an action argument of warn or lstwarn so that the generated warnings will help you confirm that there is no potential for program failure. If you are using this option to suppress fixup overflow errors, you do not need to mask the addresses used with any instructions, for example:
BANKSEL  flags            ;select data bank of flags
subwf    flags            ;use flags without masking its address
PAGESEL  myFunc           ;select the page of myFunc
call     myFunc           ;use myFunc without masking its address
To manually mask a file register address (data memory), use the BANKMASK() macro. To manually mask a program address used with the call and goto flow control instructions, use the PAGEMASK() macro. Both these macros AND out the bank or page information in the address using a suitable device-specific mask. They are available once you include <xc.inc> into a source module. Use of these macros (rather than manually using the AND operator, &) increases code portability across Microchip devices, since they adjust the mask to suit the bank or page size of the target device. The following code masks the addresses used by the subwf and call instructions:
BANKSEL  flags            ;select data bank of flags
subwf    BANKMASK(flags)  ;remove bank bits from address to prevent fixup overflow
PAGESEL  myFunc           ;select the page of myFunc
call     PAGEMASK(myFunc) ;remove page bits from address to prevent fixup overflow

Rather than ANDing out the bank information in an address using either the BANKMASK() or PAGEMASK() macros, the address can be XORed ( ^ operator) with a bitmask that represents the expected bank or page bits in the address. If the address falls in the bank or page that was expected, then the upper bits of the bitmask and address will XOR to zero; if this is not the case, the XOR will produce a non-zero component in the upper bits of the address and trigger a fixup overflow error from the linker (assuming this error has not been disabled by the -Wl,--fixupoverflow option).

The following example for a 16Fxxxx device (which has a data bank size of 0x80) selects bank 2 before accessing the symbol flags. The flags symbol has been XORed with the mask 0x100, which represents the bitmap of bank 2 addresses with the address offset zeroed.
movlb  2    ;select bank 2
subwf  flags^0x100
If flags was linked at address 0x153 (a bank 2 address), then 0x153^0x100 will result in the value 0x53, which fits into the address field of the subwf instruction. If flags was accidentally linked to address 0x34 (a bank 0 address), then 0x34^0x100 yields the result 0x134, which is too large to fit into the 7-bit wide address field of the subwf instruction and which will trigger a fixup overflow error, alerting you to the problem.

Do not use the BANKMASK() or PAGESEL() macros with any instruction that expects its operand to be a full address, such as the PIC18’s movff instruction for example.

Note that address masking is a fundamentally different operation to bank or page selection. Neither the -Wl,--fixupoverflow option nor the BANKMASK() or PAGEMASK() macros select the bank or page of the object being accessed, called, or jumped to. Regardless of how you handle address masking, you must always ensure that your program contains the instructions to select the correct bank or page when required, as described in 4.1.2 Bank And Page Selection. The one exception is when you use the fcall and/or ljmp pseudo instructions (see 4.1.7 Long Jumps And Calls), which perform both page selection and address masking for you.