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.
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.
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.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.
#define LEDSTATE BANKMASK(LEDState/8),LEDState&7
which
could then be used as follows: BANKSEL LEDState/8
btfss LEDSTATE
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.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.