The 65xx family of microprocessors, consisting of the MOS Technology 6502 and its derivatives, the WDC 65C02, WDC 65C802 and WDC 65C816, and CSG 65CE02, all handle interrupts in a similar fashion. There are three hardware interrupt signals common to all 65xx processors and one software interrupt, the instruction. The WDC 65C816 adds a fourth hardware interrupt—, useful for implementing virtual memory architectures—and the software interrupt instruction (also present in the 65C802), intended for use in a system with a coprocessor of some type (e.g., a floating-point processor).
Interrupt types + 65xx interrupt vector locationsInterruptVector (hexadecimal)LSBMSB/FFFEFFFFFFFCFFFDFFFAFFFBFFF8FFF9FFF4FFF5 The hardware interrupt signals are all active low, and are as follows: RESETa reset signal, level-triggered NMIa non-maskable interrupt, edge-triggered IRQa maskable interrupt, level-triggered ABORTa special-purpose, non-maskable interrupt (65C816 only, see below), level-triggered
The detection of a signal causes the processor to enter a system initialization period of six clock cycles, after which it sets the interrupt request disable flag in the status register and loads the program counter with the values stored at the processor initialization vector (–) before commencing execution. If operating in native mode, the 65C816/65C802 are switched back to emulation mode and stay there until returned to native mode under software control.
+ 65C816/65C802 native mode interrupt vector locationsInterruptVector (hexadecimal)LSBMSB00FFEE00FFEFNone00FFEA00FFEB00FFE800FFE900FFE600FFE700FFE400FFE5 The detection of an or signal, as well as the execution of a instruction, will cause the same overall sequence of events, which are, in order: The processor completes the current instruction and updates registers or memory as required before responding to the interrupt. 65C816/65C802 when operating in native mode: The program bank register (, the part of the address bus) is pushed onto the hardware stack. The most significant byte (MSB) of the program counter () is pushed onto the stack. The least significant byte (LSB) of the program counter is pushed onto the stack. The status register () is pushed onto the stack. The interrupt disable flag is set in the status register. 65C816/65C802: is loaded with . is loaded from the relevant vector (see tables).
The behavior of the 65C816 when is asserted differs in some respects from the above description and is separately discussed below.
Note that the processor does not push the accumulator and index registers on to the stack—code in the interrupt handler must perform that task, as well as restore the registers at the termination of interrupt processing, as necessary. Also note that the vector for is the same as that for in all eight bit 65xx processors, as well as in the 65C802/65C816 when operating in emulation mode. When operating in native mode, the 65C802/65C816 provide separate vectors for and .
When set, the interrupt request disable flag (the bit in the status register) will disable detection of the signal, but will have no effect on any other interrupts (however, see below section on the instruction implemented in WDC CMOS processors). Additionally, with the 65(c)02 or the 65C816/65C802 operating in emulation mode, the copy of the status register that is pushed on to the stack will have the flag set if a (software interrupt) was the cause of the interrupt, or cleared if an was the cause. Hence the interrupt service routine must retrieve a copy of the saved status register from where it was pushed onto the stack and check the status of the flag in order to distinguish between an and a . This requirement is eliminated when operating the 65C802/65C816 in native mode, due to the separate vectors for the two interrupt types.
interrupt The 65C816's interrupt input is intended to provide the means to redirect program execution when a hardware exception is detected, such as a page fault or a memory access violation. Hence the processor's response when the input is asserted (negated) is different from when and/or are asserted. Also, achieving correct operation in response to requires that the interrupt occur at the proper time during the machine cycle, whereas no such requirement exists for or .
When is asserted during a valid memory cycle, that is, when the processor has asserted the and/or status outputs, the following sequence of events will occur: The processor completes the current instruction but does not change the registers or memory in any way—the computational results of the completed instruction are discarded. An abort interrupt does not literally abort an instruction. The program bank (, see above) is pushed to the stack. The most significant byte (MSB) of the aborted instruction's address is pushed onto the stack. The least significant byte (LSB) of the aborted instruction's address is pushed onto the stack. The status register is pushed onto the stack. The interrupt disable flag is set in the status register. is loaded with . The program counter is loaded from the vector (see tables).
As the address pushed to the stack is that of the aborted instruction rather than the contents of the program counter, executing an (ReTurn from Interrupt) following an interrupt will cause the processor to return to the aborted instruction, rather than the next instruction, as would be the case with the other interrupts.
In order for the processor to correctly respond to an abort, system logic must assert (negate) the input as soon as a valid address has been placed on the bus and it has been determined that the address constitutes a page fault, memory access violation or other anomaly (e.g., attempted execution of a privileged instruction). Hence the logic must not assert until the processor has asserted the or signals. Also, must remain asserted until the fall of the phase-two clock and then be immediately released. If these timing constraints are not observed, the abort interrupt handler itself may be aborted, causing registers and/or memory to be changed in a possibly-undefined manner.
Interrupt anomalies In the NMOS 6502 and derivatives (e.g., 6510), the simultaneous assertion of a hardware interrupt line and execution of was not accounted for in the design—the instruction will be ignored in such a case. Also, the status of the decimal mode flag in the processor status register is unchanged following an interrupt of any kind. This behavior can potentially result in a difficult to locate bug in the interrupt handler if decimal mode happens to be enabled at the time of an interrupt. These anomalies were corrected in all CMOS versions of the processor.
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 typically achieved by using only the MPU hardware stack for storage (though there are other possible methods).
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 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 . 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 () and direct (zero) page () 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 (). 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 binary mode by clearing decimal flag The instruction is necessary because, as previously noted, NMOS versions of the 6502 do not clear the (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 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 and 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 and . Pushing the accumulator when it is set to 8 bits will not preserve , which could result in a loss of transparency should the ISR change in any way. Therefore, the accumulator must always be set to 16 bits before being pushed or pulled if the ISR will be using . 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 PHD ; save direct page pointer REP #%00110000 ; select 16 bit registers