sdcc correctly issues an EI instruction at the end of a __critical block or function (see bug 1160666, fixed in 2006). At the end of a __critical __interrupt function, sdcc issues a RETN to return from a non-maskable interrupt, which restores the state of the z80's interrupt flag IFF1 to whatever it was before the CPU accepted the non-maskable interrupt. At the end of a maskable interrupt, sdcc issues a RETI instruction, but omits the required EI to re-enable interrupts. According to the manual (see http://www.zilog.com/docs/z80/um0080.pdf , page 23): "When the CPU accepts a maskable interrupt, both IFF1 and IFF2 are automatically reset, inhibiting further interrupts until the programmer issues a new El instruction." Presently, a function to handle a maskable interrupt must manually ensure that interrupts are re-enabled by inserting __asm("ei"); or equivalent.
$ sdcc -v
SDCC : mcs51/gbz80/z80/z180/r2k/r3ka/ds390/TININative/ds400/hc08/s08 3.2.1 #8413 (Feb 6 2013) (Linux)
$ sdcc -mz80 interrupt.c
Example code
Compiled example code
This one makes me wonder if nobody is using interrupts on the Z80 or is everybody inserting the EI him/herself? And if the latter, is this then a feature request that requires discussion first?
And what about a __critical __interrupt(1) function? This will issue a superfluous DI, but does use EI. It appears that I misunderstood the interrupt mechanism of the Z80 almost 8 years ago when I implemented this and in 7 years nobody noticed!
Last edit: Maarten Brock 2013-12-22
Well, I never used the __interrupt or __critical stuff. Since I have to write the interrupt table myself anyway, I always just wrote all the interrupt-related stuff in assembler. For a long time I did the same for I/O, since for some of it the timing matters - my Z80 code was free of any SDCC-specific keywords.
Philipp
Last edit: Maarten Brock 2016-04-28
And while discussing Z80 interrupt service routines. I'm surprised nobody asked for the implementation of __using for the Z80 ISR's through the use of EX Af,AF' and EXX. It seems cheaper than pushing and popping all registers.
Fixed in revision #9583.
Philipp