I'm developing firmware for the Microchip PIC16F882 microcontroller using their MPLAB X IDE and the free version of their XC8 C compiler. I'm running into a small problem when trying to add a customized interrupt handler.
Because the free version of the compiler is unreasonably incompetent, I'm writing most of the interrupt handler in assembly, so that I can squeeze out the last few cycles. This works very well so far, but now I want to remove the last bit of overhead: the built-in interrupt wrapper normally placed at address 4 (this PIC has a fixed interrupt vector).
The wrapper is designed to handle generic C functions, so it spends some effort and memory on saving data and registers I do not use.
The manual is not obvious here, and I've tried two things:
Absolute Psects
MPLAB XC8 is able to determine the address bounds of absolute psects and uses this information to ensure that the code produced from C source by the code generator does not use memory required by the assembly code. The code generator will reserve any memory used by the assembly code prior to compiling C source.
I modified their example to place my interrupt handler on address 4:
PSECT my_isr,class=CODE,delta=2,space=0,abs,ovrld
ORG 4
my_isr:
...
-P spec
Using a custom linker flag I can place a section where I want. Instead of messing with the ORG 4
directive I added this to the linker flags:
-Pmy_isr=4
The problem: Backup reset condition flags
Both of these correctly places the code at address 4. Problem solved!
...not so fast...
Normally, the first instruction placed at address 0
by the compiler and linker is a GOTO
to the initialization routine. Then, if there's no interrupt handler, it places "random" things there, apparently it likes to continue with tables and such.
If an interrupt handler is defined in C, the first instruction is still a GOTO
. Then at address 4 is the interrupt wrapper code, which eventually jumps to your interrupt handler. This is completely as expected.
The problem is with this little button called Backup reset condition flags:
Preserve Power-down and Time-out STATUS bits at start up (PIC10/12/16 only).
When I select that, the compiler inserts code at address 0 that saves the relevant bits from the STATUS
register before jumping to the initialization code:
0000 0000 NOP
0001 0183 CLRF STATUS
0002 0803 MOVF STATUS,w
0003 00A0 MOVWF ___resetbits
0004 2919 GOTO __initialization
Note that the code ends at address 4, inclusive.
If I add an interrupt handler in C, the compiler knows that address 4 is forbidden, and instead replaces the above code with a NOP
and a GOTO
:
0000 0000 NOP
0001 2810 GOTO 0x0010
...
0010 0183 CLRF STATUS
0011 0803 MOVF STATUS,w
0012 00A0 MOVWF ___resetbits
0013 2919 GOTO __initialization
But if I implement my own interrupt handler as described above, while enabling the option to save the status register, the compiler generates code at address 0 to 4, which my code then promptly overwrites, leading to a garbled mess that won't boot:
0000 0000 NOP
0001 0183 CLRF STATUS
0002 0803 MOVF STATUS,w
0003 00A0 MOVWF ___resetbits
0004 00F0 MOVWF 0x70 ; Oops! Should've been a GOTO!
0005 0E03 SWAPF STATUS,w
...
I've tried using the GLOBAL
directive to make sure that my handler is seen by my main.c
, but that only results in an overlap warning, not an actual solution to the problem.
How do I tell the compiler that I have a custom interrupt handler?