7.2 Defining And Using Bits

The interrupt routine shown in this chapter modifies a flag that is used by main-line code to set the state of the LED. The flag's state is set by the ISR. As this flag only needs to hold a true or false value, it was defined as a bit object to save on storage space and make checking its contents more efficient.

Bit objects are created in a similar way to ordinary objects, but a special flag must be used with the psect that holds them. The single bit-wide object called LEDState is defined by the following lines of code:
PSECT	bitbss,bit,class=BANK1,space=1
LEDState:
    DS          1           ;a single bit used to hold the required LED state

The inclusion of the bit flag with the psect definition instructs the linker that the units of address within this psect are bits, not bytes. This means that the DS 1 directive, which allocates one unit of storage, is reserving a single bit, not a single byte. The LEDState object, then, can hold only a single bit.

The linker will allocate 8 bit objects to each byte of memory, so if there were additional allocations made to the above psect, like in the following:
PSECT	bitbss,bit,class=BANK1,space=1
LEDState:
    DS          1           ;a single bit used to hold the required LED state
otherState:
    DS          1           ;a single bit for some other purpose
then these two bits would reside in the same byte of memory, but would, of course, be at different bit positions within that byte.
Any symbol defined within a bit psect (for example LEDState) represents a bit address, not a byte address, and this affects how these symbols should be used in instructions. The PIC instructions that perform bit operations, such as bcf or btfss, require a byte address operand followed by a bit position within that byte. A bit object that is located at bit address 0x283, for example, is located at byte address 0x283/8, which is 0x50, and at bit position 0x283&7, which is position #3. If you are using a bit object with a bit instruction, then you will need to divide the bit address by 8 to obtain the byte (file register) address used in the instruction, and you will need to perform a bitwise AND with 7 to obtain the bit position within that byte. For example, the main-line example code that reads the desired state of the LED uses:
    BANKSEL     LEDState/8
    btfss       BANKMASK(LEDState/8),LEDState&7

Note that the BANKSEL directive also requires a byte address argument, so the bit address of LEDState was divided by 8 for this instruction as well. The BANKMASK() macro has also been used with the file register operand to the btfss instruction in the usual way, but make sure this macro is acting on the byte address of bit objects, that is, the bit address divided by 8.

You can, if desired, create a preprocessor macro to make the file address and bit position of an object more readable, for example
#define  LEDSTATE  BANKMASK(LEDState/8),LEDState&7
which could then be used as follows:
    BANKSEL     LEDState/8
    btfss       LEDSTATE
You will notice that bits within SFRs are predefined in the supplied header files in a similar way to LEDSTATE, above, and hence, they can be used in the same way. For example, the code sequence in the interrupt:
    btfsc       TMR0IE
    btfss       TMR0IF
    goto        notTimerInt ;not a timer interrupt
    bcf         TMR0IF
uses both the TMR0IE and TMR0IF SFR bits, both of which expand to the byte address that holds them (that being the addresses of PIE0 and PIR0, respectively) and their bit position within those bytes. These SFR bits are available once you include <xc.inc> into your source file.
The link address of any symbol defined in a bit psect is printed in the list and map files as a bit address. Do not compare such addresses to other byte addresses when checking memory allocation. You can confirm which psects used the bit flag in the map file by looking for the Scale value. For bit psects, this will indicate a value of 8 and be left empty for non-bit psects. In the map file produced for this example project, the bitbss psect is shown.
      Name                       Link     Load   Length Selector   Space Scale
build/default/debug/main.o
      config                     8007     8007        5        0       4
      isrVec                        4        4        A        8       0
      resetVec                      0        0        3        0       0
      bitbss                      500       A0        1       A0       1     8
      code                          E        E       1D        8       0

Notice the link address of 0x500. This is a bit address. The load address, however, is converted to a byte address and is shown as 0xA0.

A bit psect can be linked anywhere in the data memory. To highlight how banking works with bit objects, the psect containing the flag in this example was associated with the BANK1 linker class, which means that it was automatically placed somewhere in the bank 1 data memory. To access bit objects more efficiently, try to place them in the common memory or in the Access bank on PIC18 devices.