Interrupt Handlers and MS-DOS

The introduction of an interrupt handler into your program brings with it considerable hardware dependence. It goes without saying (but I am saying it again here anyway) that you should avoid such hardware dependence in MS-DOS applications whenever possible, to ensure that your programs will be portable to any machine running current versions of MS-DOS and that they will run properly under future versions of the operating system.

Valid reasons do exist, however, for writing your own interrupt handler for use under MS-DOS:

To supersede the MS-DOS default handler for an internal hardware interrupt (such as divide-by-zero, BOUND exceeded, and so forth).

To supersede the MS-DOS default handler for a defined system exception, such as the critical-error handler or Ctrl-C handler.

To chain your own interrupt handler onto the default system handler for a hardware device, so that both the system's actions and your own will occur on an interrupt. (A typical example of this is the "clock-tick" interrupt.)

To service interrupts not supported by the default MS-DOS device drivers (such as the serial communications port, which can be used at much higher speeds with interrupts than with polling).

To provide a path of communication between a program that terminates and stays resident and other application software.

MS-DOS provides the following facilities to enable you to install well-behaved interrupt handlers in a manner that does not interfere with operating-system functions or other interrupt handlers:

Function Action

Int 21H Function 25H Set interrupt vector.

Int 21H Function 35H Get interrupt vector.

Int 21H Function 31H Terminate and stay resident.

These functions allow you to examine or modify the contents of the system interrupt-vector table and to reserve memory for the use of a handler without running afoul of other processes in the system or causing memory use conflicts. Section 2 of this book, "MS-DOS Functions Reference," describes each of these functions in detail, with programming examples.

Handlers for external hardware interrupts under MS-DOS must operate under some fairly severe restrictions:

Because the current versions of MS-DOS are not reentrant, a hardware interrupt handler should never call the MS-DOS functions during the actual interrupt processing.

The handler must reenable interrupts as soon as it gets control, to avoid crippling other devices or destroying the accuracy of the system clock.

A program should access the 8259A PIC with great care. The program should not access the PIC unless that program is known to be the only process in the system concerned with that particular interrupt level. And it is vital that the handler issue an end-of-interrupt code to the 8259A PIC before performing the IRET; otherwise, the processing of further interrupts for that priority level or lower priority levels will be blocked.

Restrictions on handlers that replace the MS-DOS default handlers for internal hardware interrupts or system exceptions (such as Ctrl-C or critical errors) are not quite so stringent, but you must still program the handlers with extreme care to avoid destroying system tables or leaving the operating system in an unstable state.

The following are a few rules to keep in mind when you are writing an interrupt driver:

Use Int 21H Function 25H (Set Interrupt Vector) to modify the interrupt vector; do not write directly to the interrupt-vector table.

If your program is not the only process in the system that uses this interrupt level, chain back to the previous handler after performing your own processing on an interrupt.

If your program is not going to stay resident, fetch and save the current contents of the interrupt vector before modifying it and then restore the original contents when your program exits.

If your program is going to stay resident, use one of the terminate- and-stay-resident functions (preferably Int 21H Function 31H) to reserve the proper amount of memory for your handler.

If you are going to process hardware interrupts, keep the time that interrupts are disabled and the total length of the service routine to an absolute minimum. Remember that even after interrupts are reenabled with an STI instruction, interrupts of the same or lower priority remain blocked if the interrupt was received through the 8259A PIC.