On another thread an interesting question was raised (the thread was about dynamic array sizes).
The compiler generated some rather interesting code, I'm not sure if it is correct.
The problem:
The Atmega328 datasheet, page 15 says:
When using the SEI instruction to enable interrupts, the instruction following SEI will be executed before any pending interrupts, as shown in this example.
The example they give is:
__enable_interrupt(); /* set Global Interrupt Enable */
__sleep(); /* enter sleep, waiting for interrupt */
/* note: will enter sleep before any pending interrupt(s) */
Or, in assembler:
sei ; set Global Interrupt Enable
sleep ; enter sleep, waiting for interrupt
The important thing is that after executing sei() the processor guarantees that the next instruction will be executed (before any interrupts are processed).
First question
Does this also apply, if interrupts are already enabled? (Not that I care that much). For example:
sei ; set Global Interrupt Enable
sei ; set Global Interrupt Enable
sleep ; enter sleep, waiting for interrupt
Since interrupts are already enabled by the first sei instruction, does the processor always execute the next instruction (the sleep) in this case? Or, does this "next instruction will always be executed" only apply if the sei() instruction actually turned interrupts on?
Second question
What if you turn interrupts on in a different way? For example:
in r16, SREG ; store SREG value
cli ; disable interrupts
; // do stuff here
out SREG, r16 ; restore SREG value (I-bit)
sleep ; go to sleep
In this case we are not literally using sei() - we are turning interrupts on with an "out" instruction. But does that count as "the next instruction will be executed"?
If the answer is "no, that only applies to the sei() instruction", then this code generated by the compiler (for dynamic array allocation inside a function) is wrong:
f8: 0f b6 in r0, 0x3f ; 63 // save SREG
fa: f8 94 cli // disable interrupts
fc: 3e bf out 0x3e, r19 ; 62 // change stack register high byte
fe: 0f be out 0x3f, r0 ; 63 // restore SREG, possibly turning on interrupts
100: 2d bf out 0x3d, r18 ; 61 // change stack register low byte
One guesses that the compiler outputs the code this way to have interrupts off for the shortest possible time. But where is the supporting documentation that says this is OK?
If the answer is "yes, anything that turns interrupts on causes the next instruction to be always executed" then some code can be made more efficient. For example, digitalWrite currently does this:
uint8_t oldSREG = SREG;
19e: 9f b7 in r25, 0x3f ; 63
cli();
1a0: f8 94 cli
*out &= ~bit;
1a2: 8c 91 ld r24, X
1a4: 30 95 com r19
1a6: 83 23 and r24, r19
1a8: 8c 93 st X, r24
SREG = oldSREG;
1aa: 9f bf out 0x3f, r25 ; 63
1ac: 08 95 ret
By using assembler you could rejig it to turn interrupts on an instruction earlier, eg. something like:
19e: 9f b7 in r25, 0x3f ; 63 // save SREG
1a0: f8 94 cli // turn interrupts off
1a2: 8c 91 ld r24, X
1a4: 30 95 com r19
1a6: 83 23 and r24, r19
1aa: 9f bf out 0x3f, r25 ; 63 // restore SREG
1a8: 8c 93 st X, r24 // do one final thing which won't be interrupted
1ac: 08 95 ret