Interrupts in 65xx Processors - Interrupt Handler Considerations

Interrupt Handler Considerations

A well-designed and succinct interrupt handler or interrupt service routine (ISR) will not only expeditiously service any event that causes an interrupt, it will do so without interfering in any way with the interrupted foreground task—the ISR must be "transparent" to the interrupted task (although exceptions may apply in specialized cases). This means that the ISR must preserve the microprocessor (MPU) state and not disturb anything in memory that it is not supposed to disturb. Additionally, the ISR should be fully reentrant, meaning that if two interrupts arrive in close succession, the ISR will be able to resume processing the first interrupt after the second one has been serviced. Reentrancy is achieved by using only the MPU hardware stack for storage.

Preserving the MPU state means that the ISR must assure that whatever values were in the MPU registers at the time of the interrupt are there when the ISR terminates. A part of the preservation process is automatically handled by the MPU when it acknowledges the interrupt, as it will push the program counter (and program bank in the 65C816/65C802) and status register to the stack prior to executing the ISR. At the completion of the ISR, when the RTI instruction is executed, the MPU will reverse the process. No member of the 65xx family pushes any other registers to the stack.

In most ISRs, the accumulator and/or index registers must be preserved to assure transparency and later restored as the final steps prior to executing RTI. In the case of the 65C816/65C802, consideration must be given to whether it is being operated in emulation or native mode at the time of the interrupt. If the latter, it may also be necessary to preserve the data bank (DB) and direct (zero) page (DP) registers to guarantee transparency. Also, a 65C816 native mode operating system may well use a different stack location than the application software, which means the ISR would have to preserve and subsequently restore the stack pointer (SP). Further complicating matters with the 65C816/65C802 is that the sizes of the accumulator and index registers may be either 8 or 16 bits when operating in native mode, requiring that their sizes be preserved for later restoration.

The methods by which the MPU state is preserved and restored within an ISR will vary with the different versions of the 65xx family. For NMOS processors (e.g., 6502, 6510, 8502, etc.), there can be only one method by which the accumulator and index registers are preserved, as only the accumulator can be pushed to and pulled from the stack. Therefore, the following ISR entry code is typical:

pha ;save accumulator txa pha ;save X-register tya pha ;save Y-register cld ;ensure decimal mode

The CLD instruction is necessary because, as previously noted, NMOS versions of the 6502 do not clear the D (decimal mode) flag in the status register when an interrupt occurs.

Once the accumulator and index registers have been preserved, the ISR can use them as needed. When the ISR has concluded its work, it would restore the registers and then resume the interrupted foreground task. Again, the following NMOS code is typical:

pla tay ;restore Y-register pla tax ;restore X-register pla ;restore accumulator rti ;resume interrupted task

A consequence of the RTI instruction is the MPU will return to decimal mode if that was its state at the time of the interrupt. The 65C02, and the 65C816/65C802 when operating in emulation mode, require less code, as they are able to push and pull the index registers without using the accumulator as an intermediary. They also automatically clear decimal mode before executing the ISR. The following is typical:

pha ;save accumulator phx ;save X-register phy ;save Y-register

Upon finishing up, the ISR would reverse the process:

ply ;restore Y-register plx ;restore X-register pla ;restore accumulator rti ;resume interrupted task

As previously stated, there is a little more complexity with the 65C816/65C802 when operating in native mode due to the variable register sizes and the necessity of accounting for the DB and DP registers. In the case of the index registers, they may be pushed without regard to their sizes, as changing sizes automatically sets the most significant byte (MSB) in these registers to zero and no data will be lost when the pushed value is restored, provided the index registers are the same size they were when pushed.

The accumulator, however, is really two registers: designated .A and .B. Pushing the accumulator when it is set to 8 bits will not preserve .B, which could result in a loss of transparency should the ISR change .B in any way. Therefore, the accumulator must always be set to 16 bits before being pushed or pulled if the ISR will be using .B. It is also more efficient to set the index registers to 16 bits before pushing them. Otherwise, the ISR has to then push an extra copy of the status register so it can restore the register sizes prior to pulling them from the stack.

For most ISRs, the following entry code will achieve the goal of transparency:

phb ;save current data bank (65C816 only) phd ;save zero page pointer rep #%00110000 ;select 16 bit registers pha ;save accumulator phx ;save X-register phy ;save Y-register

In the above code fragment, the symbol % is MOS Technology and WDC standard assembly language syntax for a bitwise operand.

If the ISR has its own assigned stack location, preservation of the stack pointer (SP) must occur in memory after the above pushes have occurred—it should be apparent why this is so. The following code, added to the above sequence, would handle this requirement:

tsc ;copy stack pointer to accumulator sta fgstkptr ;save somewhere in safe RAM lda isstkptr ;get ISR's stack pointer &... tcs ;set new stack location

At the completion of the ISR, the above processes would be reversed as follows:

rep #%00110000 ;select 16 bit registers tsc ;save ISR's SP... sta isstkptr ;for subsequent use lda fgstkptr ;get foreground task's SP &... tcs ;set it ply ;restore Y-register plx ;restore X-register pla ;restore accumulator pld ;restore zero page pointer plb ;restore current data bank rti ;resume interrupted task

Note that upon executing RTI, the 65C816/65C802 will automatically restore the register sizes to what they were when the interrupt occurred, since pulling the previously–saved status register sets or clears both register size bits to what they were at the time of the interrupt.

While it is possible to switch the 65C816/65C802 from native mode to emulation mode within an ISR, such is fraught with peril. In addition to forcing the accumulator and index registers to 8 bits (causing a loss of the most significant byte in the index registers), entering emulation mode will truncate the stack pointer to 8 bits and relocate the stack itself to page 1 RAM. The result is the stack that existed at the time of the interrupt will be inaccessible unless it was also in page 1 RAM and no larger than 256 bytes. In general, mode switching while servicing an interrupt is not a recommended procedure, but may be necessary in specific operating environments.

Read more about this topic:  Interrupts In 65xx Processors

Famous quotes containing the word interrupt:

    Never interrupt a murderer, madame.
    Walter Reisch (1903–1963)