3.3 File Register Address Masking

Most PIC file register instructions require the bank of the object being accessed to be preselected and the bank value of the destination address removed to prevent build errors. There are two approaches you can follow to remove the bank value from an address operand.
  • Use the -Wl,--fixupoverflow=ignore|warn|lstwarn option to request that the linker automatically truncate all instruction operands to a size that suits the instruction. This approach is the most compatible with MPASM code, but all instruction operands are truncated.
  • Use the -Wl,--fixupoverflow==error option and mask out the bank value from the address using the BANKMASK() macro. This approach is safer as it only truncates the addresses you specify.

Note that some instructions require operands with a full address, for example the movff and movffl PIC18 instructions and this section, does not apply to these instructions. Note also that although address masking using the latter of the above techniques is not required if the object being accessed is in bank 0 (since its bank value will already be 0), it is good practice to mask all address operands in case the object is moved later in program development.

The value of a label in data memory (for example the label associated with an object or variable) is always the full address of where the label was positioned. The upper bits of such an address come from the bank value of the label (its bank number, which is preloaded into the appropriate register by the BANKSEL directive) and the lower order bits being the offset into that bank. Most PIC instructions accessing data memory require only the offset within a bank to be specified for the file register operand, with the bank value having been preselected by an instruction sequence executed earlier. For these instructions, the upper bits of the address operand, which indicate the bank value, must be removed (zeroed). The PIC Assembler will issue a fixup overflow error (for symbolic operands) or warning (for absolute operands) should it detect that these instructions have an operand with superfluous bank information present.

For both approaches shown above, you must ensure that the required bank is selected before accessing the memory location. This is typically done using the BANKSEL assembler directive. The operation of this directive is identical in MPASM and the PIC Assembler.

If you decide to follow the first approach, use the -Wl,--fixupoverflow=action option and one of the warn, lstwarn, or ignore action arguments. The lstwarn argument is the default if the option not specified. See the MPLAB® XC8 PIC® Assembler User's Guide for full information on this feature.

To have the PIC Assembler mimic the behavior of MPASM, use the ignore action argument. This will tell the linker to truncate instruction operands with no warning, similar to how MPASM operates. Note, however, that the linker will truncate all operands for all instructions in your program. In some situations, an operand that is too large to fit the relevant field in the instruction might indicate a flaw in the program design. It is recommended that you select an action argument of warn or lstwarn, or both (colon-separated), as these modes of operation will have the linker issue a warning when it truncates a value so that you can confirm there is no potential for program failure. If you are using this option with any of the warn, lstwarn, or ignore action arguments, object labels can be used in instructions without modification, as shown in the following comparison table, where example MPASM code and the equivalent PIC-AS code are identical.
Table 3-2. Migrating file register address operands when using the first migration approach (-Wl,--fixupoverflow= ignore|warn|lstwarn)
MPASM codeEquivalent PIC-AS code
copy:
  BANKSEL  src    ;select src bank
  movf     src,w  ;move from src
  BANKSEL  dst    ;select dst bank
  movwf    dst    ;move to dst
copy:
  BANKSEL  src   ;select src bank
  movf     src,w ;move from src
  BANKSEL  dst   ;select dst bank
  movwf    dst   ;move to dst
To remove the bank value following the second approach, perform a bitwise AND of the address operand using the BANKMASK() macro, available once you include <xc.inc>. This macro will perform the AND operation using the correct mask value, based on the selected device. Next, specify the -Wl,--fixupoverflow=error option, which will force the linker to generate an error should it encounter any operand that has not had its bank value removed. Using the BANKMASK() macro is the most portable way to manually mask an address and its use is shown in the following migration table.
Table 3-3. Migrating file register address operands when using the second migration approach (-Wl,--fixupoverflow= error)
MPASM codeEquivalent PIC-AS code
copy:
  BANKSEL src    ;select src bank
  movf    src,w  ;move from src
  BANKSEL dst    ;select dst bank
  movwf   dst    ;move to dst
#include <xc.inc>

copy:
  BANKSEL src              ;select src bank
  movf    BANKMASK(src),w  ;masked src address
  BANKSEL dst              ;select dst bank
  movwf   BANKMASK(dst)    ;masked dst address

In addition to the above migration approaches, there is an alternate way that addresses can be masked using an XOR operation. This gives you the opportunity to perform additional checks that ensure your assumptions about the location of objects are correct.

Use the -Wl,--fixupoverflow=error option and XOR the address with a value that will clear the expected bank value but leave the bank offset unchanged. Such a value will have zeros in the bank offset locations and specify the bit pattern of the bank in which the object is expected to be located as the bank value. So, for example, XOR the address operand with the mask 0x100 on PIC18 devices if the operand is assumed to be the address of an object in bank 1; XOR with 0x300 if it meant to be an object in bank 3. On Mid-range devices, XOR with 0x80 for bank 1 objects; 0x180 for bank 3 objects. On Baseline devices, XOR with 0x20 for bank 1 objects; 0x60 for bank 3 objects, etc. In the following Mid-range example, an error will be produced if src is not in bank 1 or if dst is not in bank 2.
copy:
  BANKSEL 1            ;select src bank (or use 'BANKSEL src')
  movf    src^080h,w   ;masked bank 1 address
  BANKSEL 2            ;select dst bank (or use 'BANKSEL dst')
  movwf   dst^0100h    ;masked bank 2 address
You can also XOR the address operand with a symbolic value. In the following Mid-range example, the programmer has changed the program so that scr and dst are now in the same bank. As a result, only one bank selection sequence was used before accessing both these objects. An XOR can be performed using these two symbols to ensure that the assumption regarding their location is valid. In the last instruction, the expression src&0FF80h obtains the bank value for src. This value is then XORed with the full address of dst.
#include <xc.inc>

copy:
  BANKSEL  src                ;select src bank
  movf     BANKMASK(src),w    ;move from masked src address
  movwf    dst^(src&0FF80h)   ;move to masked dst address in the same bank as src

If scr and dst in the above code are linked in the same bank as it was assumed, then the XOR of the bits representing their bank values will be 0, effectively masking the bank value from the operand address to the movwf instruction. If src and dst are instead linked into different banks, the XOR will yield non-zero bits in the bank value that will trigger an error, thus confuting the programmer's assumption and preventing the code from failing at runtime. This is a case where it does not matter what bank these objects are in, but they must both be in the same bank for the code to execute correctly.