Quick Interrupts by Michael Sinz [Editor's note: This article was written from the programmer's perspective, and doesn't discuss any of the hardware issues. See the ``Appendix K: Zorro Expansion Bus'' section of the third edition of the Amiga Hardware Reference Manual for more information.] One of the features of the Zorro III bus is the Quick Interrupt, also known as the vectored interrupt. This feature allows Zorro III hardware to supply a vector number to the system when an interrupt occurs. The system uses this vector number to go directly to an interrupt routine. Conventional Amiga Interrupts The Amiga handles normal interrupts from Zorro II cards using an interrupt server chain. There are two interrupts available from the Zorro II bus, the PORTS and EXTER interrupt server chains. If a driver for a Zorro II card needs to use an interrupt, it adds an interrupt routine to the appropriate chain. When the interrupt occurs, Exec calls each routine in the interrupt chain, which are sorted in priority order. Exec continues until it finds the routine that corresponds to the device that triggered the interrupt. The server chain allows several routines to share a single interrupt. This means that several devices trigger the same interrupt, so each interrupt routine must do some processing to determine if its card triggered the interrupt or if some other source caused the interrupt. For example, an interrupt routine might examine a register on its card to determine that the card triggered the interrupt. Although this scheme allows unrelated pieces of software to easily share an interrupt, it can make the interrupt overhead rather high. These two interrupt server chains also handle interrupts from the CIA chips, which are used to trigger a variety of events. As a result, these server chains can contain a multitude of interrupt routines. Consider what happens when a Zorro II card generates a PORTS interrupt. Exec has to perform some set up and then step through the PORTS server chain. Exec calls each interrupt routine in priority order looking for the routine that services this interrupt. If there are 20 interrupt routines of higher priority than the card's interrupt routine in the server chain, Exec has to call 20 other routines before it gets to the correct routine. Zorro III Quick Interrupts Quick interrupts avoid the overhead involved in Exec's interrupt server chains. Exec only helps set up the quick interrupt, which it does via the exec.library function ObtainQuickVector() (see the Autodoc at the end of this article). Once Exec has set up the quick interrupt routine, it does not intervene. Unlike conventional Amiga interrupt routines, which are called as subroutines from Exec's main interrupt code, the Amiga jumps directly to the quick interrupt routine using a private vector. This behavior requires quick interrupt routines to take some special precautions. There are two important differences between a conventional Amiga interrupt routine and a quick quick interrupt routine. A quick interrupt routine must save and restore all of the registers it changes, including D0, D1, A0, and A1. It must do this because, unlike regular interrupt routines, Exec doesn't do it for you. Also, a quick interrupt routine ends with a RTE (return from exception) instruction. If your quick interrupt routine is 100% self-contained and does not access any operating system structures or routines, then the work is rather simple. Just save the registers you use, perform your interrupt processing, restore the registers, and end with an RTE. If, however, the routine needs to call the OS or use an OS structure, it must check if the interrupt has been delayed. This is necessary in case the interrupt hit the CPU just after the CPU had told the hardware to hold off interrupts (see the Autodoc for ObtainQuickVector() to find out how to perform this test). As the Amiga OS is a dynamic operating system, quick interrupts are allocated by the OS. If your hardware/software wants to use a quick interrupt, it must allocate a vector with ObtainQuickVector(). This routine accepts a pointer to the quick interrupt code (not a pointer to an Interrupt structure). If Exec installed the vector, ObtainQuickVector() returns the vector number. When the quick interrupt occurs, the Zorro III card sends this vector number to the CPU, which tells the CPU where the interrupt code is. ObtainQuickVector() returns 0 if there are no more vectors. Since the number of vectors is limited, any Zorro III device that uses quick interrupts must be able to fall back to the Amiga's conventional interrupt scheme. The LVO for ObtainQuickVector() was added for V39, but it was not fully implemented until after the initial release. This means the OS that currently ships with the Amiga 4000 and Amiga 1200, Release 3.00, will always return 0 (no SetPatch currently exists to correct this, but a future SetPatch may do so). ObtainQuickVector() only works in the developer releases of the OS that follwed the initial release. Since a Zorro III device driver must handle the case where it cannot obtain a vector, this function should never cause a hardware product to fail. There is no reliable way to obtain a vector before V39. exec.library/ObtainQuickVector exec.library/ObtainQuickVector NAME Function to obtain an install a Quick Interrupt vector (V39) SYNOPSIS vector=ObtainQuickVector(interruptCode) d0 a0 ULONG ObtainQuickVector(APTR); FUNCTION This function will install the code pointer into the quick interrupt vector it allocates and returns to you the interrupt vector that your Quick Interrupt system needs to use. This function may also return 0 if no vectors are available. Your hardware should be able to then fall back to using the shared interrupt server chain should this happen. The interrupt code is a direct connect to the physical interrupt. This means that it is the responsibility of your code to do all of the context saving/restoring required by interrupt code. Also, due to the performance of the interrupt controller, you may need to also watch for "false" interrupts. These are interrupts that come in just after a DISABLE. The reason this happens is because the interrupt may have been posted before the DISABLE hardware access is completed. For example: myInt: move.l d0,-(sp) ; Save d0... move.w _intenar,d0 ; Get interrupt enable state btst.l #INTB_INTEN,d0 ; Check if pending disable bne.s realInt ; If not, do real one... exitInt: move.l (sp)+,d0 ; Restore d0 rte ; Return from int... ; realInt: ; Now do your int code... d0 is already saved ; ALL other registers need to be saved if needed ; This includes a0/a1/d0/d1 as this is an interrupt ; and not a function call... ; bra.s exitInt ; Exit interrupt... If your interrupt will not play with system (OS) structures and your own structures are safe to play with you do not need to check for the disable. It is only needed for when the system is in disable but that "one last interrupt" still got through. NOTE This function was not implemented fully until V39. Due to a miscue it is not safe to call in V37 EXEC. (Sorry) INPUTS A pointer to your interrupt code. This code is not an EXEC interrupt but is directly connected to the hardware interrupt. Thus, the interrupt code must not modify any registers and must return via an RTE. RESULTS The 8-bit vector number used for Zorro-III Quick Interrupts If it returns 0, no quick interrupt was allocatable. The device should at this point switch to using the shared interrupt server method. SEE ALSO