7.3 Manual Context Switch
- Define sufficient storage in RAM for the registers that must be saved
- Have the ISR copy the contents of the relevant registers to the storage area before the registers are modified
- Have the ISR restore the contents of the registers from the storage area after those registers no longer need to be used
The following code snippet is written for a Mid-range device that does not automatically save context when an interrupt occurs, such as a PIC12F609. The code assumes the device implements common memory. This code illustrates the above points and is described in the following paragraphs. Adapt this code to suite your device and the registers you wish to preserve.
#include <xc.inc>
PSECT udata_shr
saved_WREG:
DS 1 ;space for WREG in common memory
PSECT udata_bank0
mainSaveArea: ;space for other registers in banked memory
saved_STATUS:
DS 1
saved_PCLATH:
DS 1
PSECT isrVec,class=CODE,delta=2
isr:
;save context
movwf saved_WREG ;WREG saved to common memory
movf STATUS,w ;STATUS (bank selection bits) copied to WREG...
BANKSEL mainSaveArea ;...allowing a different bank to be selected...
movwf saved_STATUS ;STATUS saved
movf PCLATH,w
movwf saved_PCLATH ;PCLATH saved
;interrupt code that can use STATUS, WREG, PCLATH goes here
exitISR:
;restore context
BANKSEL mainSaveArea
movf saved_PCLATH,w
movwf PCLATH ;PCLATH restored
movf saved_STATUS,w
movwf STATUS ;STATUS restored
swapf saved_WREG,f
swapf saved_WREG,w ;WREG restored without altering STATUS
retfie
Space must be allocated for all registers that are to be preserved. This space does not need to be contiguous or even in the same bank. Storing context in common memory or in the PIC18 Access bank makes the context switching code much easier, but there might not be enough common memory for all the registers, nor might you wish to use this precious memory resource solely for interrupts.
In this example, one byte has been allocated (using the DS
directive) in
common memory (udata_shr
psect) to hold the state of the W register,
and memory was allocated in a block of bank 0 memory (udata_bank0
psect) to store the state of the STATUS and PCLATH registers. This arrangement
simplifies the context switch code, as you will see in the following discussion, but
does not use much common memory. You may, as was done in this example, define a label
with each byte of storage so that each byte can be uniquely referenced, or you may
simply create one label and use an offset from that label to access each byte in that
memory block. So, for example, mainSaveArea + 1
and
saved_PCLATH
both represent the same memory location.
The code at the beginning of the ISR copies the registers to the allocated storage. You must ensure that this code does not overwrite any register that must be preserved until the content of that register has been saved. This might mean that the registers need to be saved in a particular order.
The first instruction in the above example moves the current content of the W register
into the saved_WREG
object. Because the storage for this object was
allocated in common memory, the bank of this memory does not need to be selected. This
is important because on Mid-range devices, the bank selection bits are in the STATUS
register, which is one of the registers that must be preserved. A new bank cannot be
selected without clobbering the STATUS register; similarly, the STATUS register cannot
be saved first because that code would clobber the W register.
With the W register saved, the subsequent instruction can then load the W register with
the content of the STATUS register. And with the STATUS register now copied (although
not yet stored) the bank of the main storage block can be selected using the
BANKSEL
directive. The subsequent code can then use regular
movf
/movwf
instructions to save the remaining
registers.
The restoration code at the end of the ISR typically restores the registers in the reverse order to that in which they were saved. You must ensure that once a register has been restored, no subsequent code writes to that register, so again, these operations need to be performed in a strict order and may not be able to use some instructions.
The restoration code in this example first selects the bank of the main allocated space
and copies the saved values back to the PCLATH and STATUS registers using
movf
/movwf
instructions. Remember that with the
STATUS register restored, the bank currently selected also returns to that in effect
when the interrupt occurred. As the content of the remaining register to be restored in
this example was stored in common memory, this is of no concern.
Restoring the W register is more difficult. The instruction, movf
saved_WREG,w
, can't be used because it affects the Zero bit in the STATUS
register, which has already been restored. Instead, the example code restores the W
register using two swapf
instructions, which do not affect the STATUS
register.