SDCC allows interrupt service routines to be coded in C, with some extended keywords.
void timer_isr (void) __interrupt (1) __using (1)The optional number following the __interrupt keyword is the interrupt number this routine will service. When present, the compiler will insert a call to this routine in the interrupt vector table for the interrupt number specified. If you have multiple source files in your project, interrupt service routines can be present in any of them, but a prototype of the isr MUST be present or included in the file that contains the function main. The optional (8051 specific) keyword __using can be used to tell the compiler to use the specified register bank when generating code for this function.
{
...
}
If an interrupt service routine changes variables which are accessed by other functions these variables have to be declared volatile. See http://en.wikipedia.org/wiki/Volatile_variable.
If the access to these variables is not atomic
(i.e. the processor needs more than one instruction for the access
and could be interrupted while accessing the variable) the interrupt
must be disabled during the access to avoid inconsistent data.
Access to 16 or 32 bit variables is obviously not atomic on 8 bit
CPUs and should be protected by disabling interrupts. You're not automatically
on the safe side if you use 8 bit variables though. We need an example
here: f.e. on the 8051 the harmless looking ”flags |= 0x80;”
is not atomic if flags resides in xdata. Setting ”flags |= 0x40;”
from within an interrupt routine might get lost if the interrupt occurs
at the wrong time. ”counter += 8;” is not atomic on
the 8051 even if counter is located in data memory.
Bugs like these are hard to reproduce and can cause a lot of trouble.
The return address and the registers used in the interrupt service routine are saved on the stack so there must be sufficient stack space. If there isn't variables or registers (or even the return address itself) will be corrupted. This stack overflow is most likely to happen if the interrupt occurs during the ”deepest” subroutine when the stack is already in use for f.e. many return addresses.
A special note here, integer multiplicative operators and floating-point
operations might be implemented using external support routines, depending
on the target architecture. If an interrupt service routine needs
to do any of these operations on a target where functions are non-reentrant
by default, then the support routines (as mentioned in a following
section) will have to be recompiled using the —stack-auto
option and the source file will need to be compiled using the —int-long-reent
compiler option.
Note, the type promotion required by ANSI C
can cause 16 bit routines to be used without
the programmer being aware of it. See f.e. the cast (unsigned
char)(tail-1) within the if clause in section 3.11.2.
Calling other functions from an interrupt service routine on a target
where functions are non-reentrant by default is not recommended, avoid
it if possible. Note that when some function is called from an interrupt
service routine it should be preceded by a #pragma nooverlay
if it is not reentrant. Furthermore non-reentrant functions should
not be called from the main program while the interrupt service routine
might be active. They also must not be called from low priority interrupt
service routines while a high priority interrupt service routine might
be active. You could use semaphores or make the function critical
if all parameters are passed in registers.
Also see section 3.7 about Overlaying and section
3.10 about Functions using
private register banks.