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 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).
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?
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