There are several possibilities to protect critical sections of code from being interrupted:
1. Temporarily disabling and enabling interrupts:
void function(void) { EA = 0; // atomic_section... EA = 1; }
Disadvantage: This will change the status of EA if interrupts had not been enabled before.
2. Saving and restoring EA: http://www.keil.com/forum/1080/
bit ea_saved; void function(void) { ea_saved = EA; EA = 0; // atomic_section... EA = ea_saved; }
Disadvantage: if this code can be interrupted between ea_saved=EA and EA=0. If the interrupt service routine decides to disable interrupts, the interrupts will become enabled again when doing EA=ea_saved after the critical section.
3. Using #pragma disable http://www.keil.com/support/man/docs/c51/c51_disable.htm http://www.keil.com/forum/1080/
#pragma disable void function (void) { // atomic_section... }
Advantage: Avoids the above problem because the JBC opcode is used to evaluate the current state of EA and clear EA with the same instruction:
; FUNCTION function (BEGIN) SETB C JBC EA,IS_CLEARED CLR C IS_CLEARED: PUSH PSW // atomic_section... POP PSW MOV EA,C RET ; FUNCTION function (END)
Disadvantage: Can be applied to whole functions only. Requires 1 Byte on the stack.
4. Combination of 2. and 3.
#include <intrins.h> #define ATOMIC_SECTION_BEGIN { ea_saved = 1; if (!(_testbit_(EA))) { ea_saved = 0; } } #define ATOMIC_SECTION_END { EA = ea_saved; } #define INTERRUPTS_DISABLE { EA = 0; ea_saved = 0; } #define INTERRUPTS_ENABLE { EA = 1; ea_saved = 1; } bit ea_saved; void function(void) { ATOMIC_SECTION_BEGIN // atomic_section... ATOMIC_SECTION_END }
This results in
ATOMIC_SECTION_BEGIN SETB ea_saved JBC EA,?C0007?HB CLR ea_saved ?C0007?HB: // atomic_section... ATOMIC_SECTION_END MOV C,ea_saved MOV EA,C
Advantages: Pure source code, no inline assembly. Atomic section can remain inline, no sub-function required. Only one bit for status saving required.