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, int (16 bit) and long (32 bit) integer division,
multiplication & modulus and
floating-point operations are implemented
using external support routines. If an interrupt service routine needs
to do any of these operations 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.13.1.
Calling other functions from an interrupt service routine 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 nonreentrant 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.8 about Overlaying and section
3.11 about Functions using private
register banks.