Source: PS/2 Model 25 Technical Reference pp 27-34
(also in Model 30 techref)
Note: This pertains to the original 8086-based
PS/2 Model 25 and
Model 30. It's currently unknown what other ISA
systems support this feature (specifically the Global Rearm logic). If
any...
A standardized hardware design concept has been established to enable
multiple adapters to share an interrupt level. The integrated adapters do not
use interrupt sharing. The following describes this design concept and
discusses the programming support required.
Design Overview
Most interrupt-supporting adapters hold the IRQ line inactive and then drive
the line active to cause an interrupt. In contrast, the shared interrupt
hardware design allows the IRQ line to float high. Each adapter on the line may
cause an interrupt by pulsing the line low. The leading edge of the pulse arms
the interrupt controller; the trailing edge of the pulse causes the
interrupt.
Each adapter sharing an interrupt level must monitor the IRQ line. When any
adapter pulses the line, all other adapters on that interrupt must not issue an
interrupt request until they are rearmed.
If an adapter's interrupt is active when it is rearmed, the adapter must
reissue the interrupt. This prevents lost interrupts in case two adapters issue
an interrupt at exactly the same time and an interrupt handler issues a Global
Rearm after servicing one of them.
The following diagram shows the shared interrupt hardware logic.
Figure 1. Shared Interrupt Hardware Logic
Program Support
The interrupt-sharing program support described in the following provides
for an orderly means to:
- Link a task's interrupt handler to a chain of interrupt handlers
- Share the interrupt level while the task is active
- Unlink the interrupt handler from the chain when the task is deactivated.
Linking onto the Chain: Each newly activated task replaces the
interrupt vector in low memory with a pointer to its own interrupt handler. The
old interrupt vector is used as a forward pointer and is stored away at a fixed
offset from the new task's interrupt handler. This method of linking means the
last handler to link is the first one in the chain.
Sharing the Interrupt Level: When the new task's handler gains
control as a result of an interrupt, the handler reads the contents of the
adapter's Interrupt Status register to determine whether its adapter caused the
interrupt. If its adapter did cause the interrupt, the handler services the
interrupt, disables (clears) the interrupts (CLI), and writes
to address hex 02FX, where X corresponds to interrupt levels 2 through
7. Each adapter in the chain decodes the address, which results in a
Global Rearm. The handler then issues a non Specific End of Interrupt (EOI) and
finally issues a Return from Interrupt (IRET). If its adapter did not cause the
interrupt, the handler passes control to the next interrupt handler in the
chain.
Unlinking from the Chain: To unlink from the chain, a task must first
locate its handler's position within the chain. By starting at the interrupt
vector in low memory and using the offset of each handler's forward pointer to
find the entry point of each handier, the chain can be methodically searched
until the task finds its own handler. The forward pointer of the previous
handler in the chain is replaced by the task's pointer, removing the handler
from the chain.
Note: If the handler cannot locate its position in
the chain or, if the signature of any prior handler is not hex 4248, it must
not unlink.
Error Recovery: If the unlinking routine discovers that the interrupt
chain has been corrupted, an unlinking error recovery procedure must be in
place. Each application can incorporate its own unlinking error procedure into
the unlinking routine. One application may choose to display an error message
requiring the operator to either correct the situation or reset the system. The
application, however, must not unlink.
Precautions
The following precautions must be taken when designing hardware or programs
that use shared interrupts.
Interrupt Chaining Structure
ENTRY: JMP SHORT PAST ; Jump around structure
FPTR DD 0 ; Forward Pointer
SIGNATURE DW 424BH ; Used when unlinking to identify
; compatible interrupt handlers
FLAGS DB 0 ; Flags
FIRST EQU 80H ; Flags for being first in chain
JMP SHORT RESET
RES_BYTES DB DUP 7(0) ; Future Expansion
PAST: ... ; Actual start of code
The interrupt chaining structure is a 16-byte format containing FPTR,
SIGNATURE, RES_BYTES, and a Jump instruction to a reset routine. It begins at
the third byte from the interrupt handler's entry point. The first instruction
of every handler is a short jump around the structure to the start of the
routine.
Except for those residing in adapter ROM, handlers designed for interrupt
sharing must use hex 424B as the signature to avoid corrupting the chain due to
misidentification of an interrupt handler. Because each handler's chaining
structure is known, the forward pointers can be updated when unlinking.
The flag indicates that the handler is first in the chain and is used only
with interrupt 7. The Reset routine disables the adapter's interrupt and then
does a Far Return to the operating system.
ROM Considerations
Adapters with interrupt handlers residing in ROM must store the forward
pointer in latches or ports on the adapter. If the adapter is sharing interrupt
7, it must also store a First. Storing this flag is necessary because its
position in the chain may not always be first. Because the forward pointer is
not stored in the third byte, these handlers must contain a signature of hex
00.
Examples
In the following examples, note that interrupts are disabled before passing
control to the next handler on the chain. The next handler receives control as
if a hardware interrupt had caused it to receive control. Note also that the
interrupts are disabled before the nonspecific EOI is issued, and are not
reenabled in the interrupt handler. This ensures that the IRET is executed (at
which point the flags are restored and the interrupts reenabled) before another
interrupt is serviced. This protects the stack from excessive buildup.
Interrupt Handler Example
OUR_CARD EQU xxxx ; Location of our card's interrupt
ISB EQU xx ; Interrupt bit in our cards interrupt
; control/status register
REARM EQU 2F7H ; Global Rearm location for interrupt 7
SPC_EOI EQU 67H ; Specific EOI for interrupt 7
EOI EQU 20H ; Nonspecific EOI
OCR EQU 20H ; Location of interrupt controller
; operational control register
IMR EQU 21H ; Location of interrupt mask register
MYSEG SEGMENT PARA
ASSUME CS:MYSEG,DS:DSEG
ENTRY PROC FAR
JMP SHORT PAST ; Entry point of handler
FPTR DD 0 ; Forward Pointer
SIGNATURE DW 424BH ; Used when unlinking to identify
; compatible interrupt handlers
FLAGS DB 0 ; Flags
FIRST EQU 80H
JMP SHORT RESET
RES_BYTES DB DUP 7(0) ; Expansion
PAST: STI ; Actual start of handler code
PUSH ... ; Save needed registers
MOV DX,OUR_CARD ; Select our status register
IN AL,DX ; Read the status register
TEST AL,ISB ; Our card caused the interrupt?
JNE SERVICE ; Yes, branch to service logic
TEST CS:FLAGS,FIRST ; Are we the first ones in?
JNZ EXIT ; If yes, branch for EOI and Rearm
POP ... ; Restore registers
CLI ; Disable interrupts
JMP DWORD PTR CS:FPTR ; Pass control to next handler on chain
SERVICE: ... ; Service the interrupt
EXIT:
CLI ; Disable the interrupts
MOV AL,EOI
OUT OCR,AL ; Issue nonspecific EOI
MOV DX,REARM ; Rearm our card
OUT DX,AL
POP ... ; Restore registers
IRET
RESET: ... ; Disable our card
RET ; Return Far to operating system
ENTRY: ENDP
MYCSEG ENDS
END ENTRY
Linking Code Example
PUSH ES
CLI ; Disable interrupts
; Set forward pointer to the value of the interrupt vector in low memory
ASSUME CS:CODESEG,DS:CODESEG
PUSH ES
MOV AX,3S0FH ; DOS get interrupt vector
INT 21H ;
MOV SI,OFFSET CS:FPTR ; Set offset of our forward pointer
; in an indexable register
MOV CS:[SI],BX ; Store the old interrupt vector
MOV CS:[SI+2],ES ; in our forward pointer
CMP ES:BYTE PTR[BX],CFH ; Test for IRET
JNZ SERVECTR
MOV CS:FLAGS,FIRST ; Set up first in chain flag
SERVECTR: POP ES
PUSH DS
; Make interrupt vector in low memory point to our handler
MOV DX,OFFSET ENTRY ; Make interrupt vector point to our
; interrupt handler
MOV AX,SEG ENTRY ; If DS not = CS, get it and
MOV DS,AX ; put it in DS
MOV AX,2S0FH ; DOS set interrupt vector
INT 21H ;
POP DS
; Unmask (enable) interrupts for our level
SET7: IN AL,IMR ; Read interrupt mask register
AND AL,07FH ; Unmask interrupt level 7
OUT IMR,AL ; Write new interrupt mask
MOV AL,SPC_EOI ; Issue specific EOI for level 7
OUT OCR,AL ; to allow pending level 7 interrupts
; (if any) to be serviced
STI ; Enable interrupts
POP ES
Unlinking Code Example
PUSH DS
PUSH ES
CLI ; Disable interrupts
MOV AX,350FH ; DOS get interrupt vector
INT 21H ; ES:BX points to the first in the chain
MOV CX,ES ; Pickup segment part of interrupt vector
; Are we the first handler in the chain?
MOV AX,CS ; Get code seg into comparable register
CMP BX,OFFSET ENTRY ; Interrupt vector in low memory
; pointing to our handlers offset?
JNE UNCHAIN_A ; No, branch
CMP AX,CX ; Vector pointing to our handler's segment?
JNE UNCHAIN_A ; No, branch
; Set interrupt vector in low memory to point to the handler
; pointed to by our pointer
PUSH DS
MOV AX,CS:FPTR
MOV DX,WORD PTR CS:FPTR ; Set offset of interrupt vector
MOV DS,WORD PTR CS:FPTR[2] ; Set segment of interrupt vector
MOV AX, 250FH ; DOS set interrupt vector
INT 21H
POP DS
JMP UNCHAIN_X
UNCHAIN_A: ; CX = FPTR segment, BX = FPTR offset
CMP ES:[BX+6],4B42H ; Is handler using the appropriate
; conventions (is SIGNATURE = 424BH?)
JNE exception ; No, invoke error exception handler
LDS SI,ES:[BX+2] ; Get FPTR's segment and offset
CMP SI,OFFSET ENTRY ; Is this forward pointer pointing to
; our handler's offset?
JNE UNCHAIN_B ; No, branch
MOV CX,DS ; Is this forward pointer pointing to
CMP AX,CX ; our handler's segment?
JNE UNCHAIN_B ; No, branch
; Located our handler in the chain
MOV AX,WORD PTR CS:FPTR ; Get our FPTR's offset
MOV ES:[BX+2],AX ; Replace FPTR offset pointing to us
MOV AX,WORD PTR CS:FPTR[2] ; Get our FPTR's segment
MOV ES:[BX+4],AX ; Replace FPTR segment pointing to us
MOV AL,CS:FLAGS
AND AL,FIRST
OR ES:[BX+6],AX ; Replace offset of FPTR of handler
JMP UNCHAIN_X
UNCHAIN_B:
MOV BX,SI ; Move new offset to BX
PUSH DS
PUSH ES
JMP UNCHAIN_A ; Examine the next handler in the chain
UNCHAIN_X:
STI ; Enable interrupts
POP ES
POP DS
|