PS/2 Model 25 & 30 - Interrupt Sharing

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.

  • Hardware designers should ensure that the adapters:

    • Do not power up with an interrupt pending or enabled.

    • Do not generate interrupts that are not serviced by a handler. Generating interrupts when a handler is not active to service them causes that interrupt level to lock up. The design concept relies on the handler to clear its adapter's interrupt and issue the Global Rearm.

    • Can be disarmed so that they do not remain active after their application has terminated.

  • Programmers should:

    • Ensure that their programs contain a short routine that can be executed with the AUTOEXEC.BAT to disable their adapter's interrupts. This precaution ensures that the adapters are deactivated for a system reboot that does not clear memory.

    • Treat words as words, not as bytes.

      Note: Remember that data is stored in memory using the Intel format (word hex 424B is stored as hex 4B42).

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

Content created and/or collected by:
Louis Ohland, Peter Wendt, David Beem, William Walsh, Tatsuo Sunagawa, Jim Shorney, Tim Clarke, Kevin Bowling, Tomáš Slavotínek, and many others.

Ardent Tool of Capitalism - MAD Edition! is maintained by Tomáš Slavotínek.
Last update: 25 Nov 2022 - Changelog | Legal Info & Contact