Writing interrupt service routines

mspgcc allows interrupt service routines to be written efficiently in C. To access the interrupt features of mspgcc the header file

#include <signal.h>

should be included in any source files where interrupt service routines are defined.

To make a routine an interrupt service routine, define it as follows:

interrupt (INTERRUPT_VECTOR) IntServiceRoutine(void)
{
    /* Any normal C code */
}
where "INTERRUPT_VECTOR" is replaced with the name of the actual vector to be serviced. Definitions for these may be found in the header files. The generated code will save any registers used within the interrupt routine, and use the "RETI", rather than the usual "RET" instruction, to exit from the routine. The vector table will automatically point to the routine. Further interrupt related attributes are also recognised:
interrupt (INTERRUPT_VECTOR) [wakeup, enablenested] IntServiceRoutine(void)
{
    /* Any normal C code */
}
The "wakeup" attribute makes the compiler alway force exit from any low power modes that may be in force at exit from the routine. See later for ways to gain greater control of the lower power modes. "enablenested" causes an interrupt enable instruction to be inserted before the function prologue. This allows other higher priority interrupts to be serviced while handling the current one. Use this feature with care if you use it in conjunction with "wakeup"!

Although interrupt service routines normally accept no arguments, it is possible to define a function with the "interrupt" attribute and an argument list. The compiler will correctly allow for the extra register (r2) pushed on the stack, when accessing parameters on the stack. Parameters passed in registers are, obviously, unaffected by this. The ability to define functions in this way is provided for completeness. Their usefulness may be limited.

It should be noted that there is a performance hit associated with any function calls within an interrupt service routine (unless the function is of the "inline" type, which does not result in an actual function call instruction). Any call requires the compiler save register r12, r13, r14 and r15 on the stack during the function call. For example, something as simple as:

uint32_t localtime;

void incloctime()
{
    localtime++;
}

interrupt(BASICTIMER_VECTOR) isr()
{
    incloctime();
}
will cause the overhead of saving these registers to occur, even though none of them are using within the called function. In this case, declaring "incloctime" as "static inline" will make things much more efficient.

For every device, the macros "NOVECTOR" and "RESET_VECTOR" are defined. If an interrupt service routine is declared as

interrupt (NOVECTOR) [wakeup, enablenested] IntServiceRoutine(void)
{
    /* Any normal C code */
}
GCC will not assign an interrupt vector for this routine. The code generated for the routine itself will be just the same as for any real interrupt vector. Similarly the macro "RESET_VECTOR()" can be used as the vector name when the standard reset start-up routine needs to be replaced with a customised one.