rtel wrote on Tuesday, June 14, 2005:
There are two key points here:
1) The Yield appears only at the end of the interrupt service routine - when the interrupt has been serviced but the ISR has not completed.
2) Each task maintains its own interrupt status.
This is the sequence:
1) Task A is running when an interrupt occurs. The interrupt causes some CPU registers to be saved including the interrupt flags. Registers are saved so as to be restored by a RETI instruction. For the interrupt to have occurred the interrupt flags must have been set with interrupts enabled - but within the ISR they are disabled.
2) The ISR code executes. During the execution it is discovered that a yield is required. The yield does not occur however - just a flag is set to say one is necessary.
3) At the end of the interrupt the flag is checked which says a yield is required, and yield is called.
4) Yield saves the context of the task A - which has been interrupted. The registers are saved to the stack on top of the registers that were already pushed onto the stack when the ISR was entered. The program counter that is saved points to the end of the ISR function.
5) Yield then switches the stack pointer to point to the saved context of task B - and proceeds to pop the registers from task B context. When all the general purpose registers have been popped the task B interrupt flags and return address are left on the stack. The flags are popped first. This will return the interrupt enable status to whatever it was when task B last ran (could be enabled or disabled). Finally RET is executed which causes the return address to be popped and the program counter set to the correct place to run task B.
6) At some point task B blocks. In our example assume this makes task A the candidate to run again. Having saved the task B context the context switch this time sets the stack pointer back to the saved context of task A - the task that was interrupted.
7) The general purpose registers for task A are restored. Next the interrupt flags are restored. The interrupt flags were stored with interrupts disabled as they were saved from within the ISR. Finally the RET instruction is executed which sets the program counter back to the place where task A was last executing. In this case task A was last executing in the ISR. The next items on the stack are the registers pushed onto the stack by the processor when the interrupt occurred. [NB there may actually be some registers on the stack from the function prologue generated by the compiler, which will be popped by the epilogue generated by the compiler].
8) Task A returns to within the ISR. The ISR processing has already been performed but the ISR itself has not finished. The next instruction that task A executes is from the function epilogue - in a RETI. This pops the interrupt flags and return address from the stack - these are those placed there when the ISR was entered. The interrupt flags re-enables interrupts (remember they must have been enabled for the interrupt to get called at all) and returns task A to the place it was prior to it getting interrupt in the first place.
Hope this helps!