home *** CD-ROM | disk | FTP | other *** search
/ For Beginners & Professional Hackers / cd.iso / docum / advdos.doc / s1c13 < prev    next >
Encoding:
Text File  |  1992-04-21  |  33.3 KB  |  711 lines

  1. ────────────────────────────────────────────────────────────────────────────
  2. Chapter 13  Interrupt Handlers
  3.  
  4.   Interrupts are signals that cause the computer's central processing unit
  5.   to suspend what it is doing and transfer to a program called an interrupt
  6.   handler. Special hardware mechanisms that are designed for maximum speed
  7.   force the transfer. The interrupt handler determines the cause of the
  8.   interrupt, takes the appropriate action, and then returns control to the
  9.   original process that was suspended.
  10.  
  11.   Interrupts are typically caused by events external to the central
  12.   processor that require immediate attention, such as the following:
  13.  
  14.   ■  Completion of an I/O operation
  15.  
  16.   ■  Detection of a hardware failure
  17.  
  18.   ■  "Catastrophes" (power failures, for example)
  19.  
  20.   In order to service interrupts more efficiently, most modern processors
  21.   support multiple interrupt types, or levels. Each type usually has a
  22.   reserved location in memory, called an interrupt vector, that specifies
  23.   where the interrupt-handler program for that interrupt type is located.
  24.   This design speeds processing of an interrupt because the computer can
  25.   transfer control directly to the appropriate routine; it does not need a
  26.   central routine that wastes precious machine cycles determining the cause
  27.   of the interrupt. The concept of interrupt types also allows interrupts to
  28.   be prioritized, so that if several interrupts occur simultaneously, the
  29.   most important one can be processed first.
  30.  
  31.   CPUs that support interrupts must also have the capability to block
  32.   interrupts while they are executing critical sections of code. Sometimes
  33.   the CPU can block interrupt levels selectively, but more frequently the
  34.   effect is global. While an interrupt is being serviced, the CPU masks all
  35.   other interrupts of the same or lower priority until the active handler
  36.   has completed its execution; similarly, it can preempt the execution of a
  37.   handler if a different interrupt with higher priority requires service.
  38.   Some CPUs can even draw a distinction between selectively masking
  39.   interrupts (they are recognized, but their processing is deferred) and
  40.   simply disabling them (the interrupt is thrown away).
  41.  
  42.   The creation of interrupt handlers has traditionally been considered one
  43.   of the most arcane of programming tasks, suitable only for the elite cadre
  44.   of system hackers. In reality, writing an interrupt handler is, in itself,
  45.   straightforward. Although the exact procedure must, of course, be
  46.   customized for the characteristics of the particular CPU and operating
  47.   system, the guidelines on the following page are applicable to almost any
  48.   computer system.
  49.  
  50.   A program preparing to handle interrupts must do the following:
  51.  
  52.   1.  Disable interrupts, if they were previously enabled, to prevent them
  53.       from occurring while interrupt vectors are being modified.
  54.  
  55.   2.  Initialize the vector for the interrupt of interest to point to the
  56.       program's interrupt handler.
  57.  
  58.   3.  Ensure that, if interrupts were previously disabled, all other vectors
  59.       point to some valid handler routine.
  60.  
  61.   4.  Enable interrupts again.
  62.  
  63.   The interrupt handler itself must follow a simple but rigid sequence of
  64.   steps:
  65.  
  66.   1.  Save the system context (registers, flags, and anything else that the
  67.       handler will modify and that wasn't saved automatically by the CPU).
  68.  
  69.   2.  Block any interrupts that might cause interference if they were
  70.       allowed to occur during this handler's processing. (This is often done
  71.       automatically by the computer hardware.)
  72.  
  73.   3.  Enable any interrupts that should still be allowed to occur during
  74.       this handler's processing.
  75.  
  76.   4.  Determine the cause of the interrupt.
  77.  
  78.   5.  Take the appropriate action for the interrupt: receive and store data
  79.       from the serial port, set a flag to indicate the completion of a
  80.       disk-sector transfer, and so forth.
  81.  
  82.   6.  Restore the system context.
  83.  
  84.   7.  Reenable any interrupt levels that were blocked during this handler's
  85.       execution.
  86.  
  87.   8.  Resume execution of the interrupted process.
  88.  
  89.   As in writing any other program, the key to success in writing an
  90.   interrupt handler is to program defensively and cover all the bases. The
  91.   main reason interrupt handlers have acquired such a mystical reputation is
  92.   that they are so difficult to debug when they contain obscure errors.
  93.   Because interrupts can occur asynchronously──that is, because they can be
  94.   caused by external events without regard to the state of the currently
  95.   executing process──bugs in interrupt handlers can cause the system as a
  96.   whole to behave quite unpredictably.
  97.  
  98.  
  99. Interrupts and the Intel 80x86 Family
  100.  
  101.   The Intel 80x86 family of microprocessors supports 256 levels of
  102.   prioritized interrupts, which can be triggered by three types of events:
  103.  
  104.   ■  Internal hardware interrupts
  105.  
  106.   ■  External hardware interrupts
  107.  
  108.   ■  Software interrupts
  109.  
  110. Internal Hardware Interrupts
  111.  
  112.   Internal hardware interrupts, sometimes called faults, are generated by
  113.   certain events encountered during program execution, such as an attempt to
  114.   divide by zero. The assignment of such events to certain interrupt numbers
  115.   is wired into the processor and is not modifiable (Figure 13-1).
  116.  
  117.  
  118.   Interrupt  Vector    Interrupt            8086/88    80286     80386
  119.   level      address   trigger
  120.   ──────────────────────────────────────────────────────────────────────────
  121.   00H        00H─03H   Divide-by-zero       x          x         x
  122.   01H        04H─07H   Single step          x          x         x
  123.   02H        08H─0BH   Nonmaskable          x          x         x
  124.                        interrupt (NMI)
  125.   03H        0CH─0FH   Breakpoint           x          x         x
  126.   04H        10H─13H   Overflow             x          x         x
  127.   05H        14H─17H   BOUND exceeded                  x         x
  128.   06H        18H─1BH   Invalid opcode                  x         x
  129.   07H        1CH─1FH   Processor extension             x         x
  130.                        not available
  131.   08H        20H─23H   Double fault                    x         x
  132.   09H        24H─27H   Segment overrun                 x         x
  133.   0AH        28H─2BH   Invalid task-state              x         x
  134.                        segment
  135.   0BH        2CH─2FH   Segment not present             x         x
  136.   0CH        30H─33H   Stack segment                   x         x
  137.                        overrun
  138.   0DH        34H─37H   General protection              x         x
  139.                        fault
  140.   0EH        38H─3BH   Page fault                                x
  141.   0FH        3CH─3FH   Reserved
  142.   10H        40H─43H   Numeric coprocessor             x         x
  143.                        error
  144.   11H─1FH    44H─7FH   Reserved
  145.   ──────────────────────────────────────────────────────────────────────────
  146.  
  147.  
  148.   Figure 13-1.  Internal interrupts (faults) on the Intel 8086/88, 80286,
  149.   and 80386 microprocessors.
  150.  
  151. External Hardware Interrupts
  152.  
  153.   External hardware interrupts are triggered by peripheral device
  154.   controllers or by coprocessors such as the 8087/80287. These can be tied
  155.   to either the CPU's nonmaskable-interrupt (NMI) pin or its
  156.   maskable-interrupt (INTR) pin. The NMI line is usually reserved for
  157.   interrupts caused by such catastrophic events as a memory parity error or
  158.   a power failure.
  159.  
  160.   Instead of being wired directly to the CPU, the interrupts from external
  161.   devices can be channeled through a device called the Intel 8259A
  162.   Programmable Interrupt Controller (PIC). The CPU controls the PIC through
  163.   a set of I/O ports, and the PIC, in turn, signals the CPU through the INTR
  164.   pin. The PIC allows the interrupts from specific devices to be enabled and
  165.   disabled, and their priorities to be adjusted, under program control.
  166.  
  167.   A single PIC can handle only eight levels of interrupts. However, PICs can
  168.   be cascaded together in a treelike structure to handle as many levels as
  169.   desired. For example, 80286- and 80386-based machines with a
  170.   PC/AT-compatible architecture use two PICs wired together to obtain 16
  171.   individually configurable levels of interrupts.
  172.  
  173.   INTR interrupts can be globally enabled and disabled with the CPU's STI
  174.   and CLI instructions. As you would expect, these instructions have no
  175.   effect on interrupts received on the CPU's NMI pin.
  176.  
  177.   The manufacturer of the computer system and/or the manufacturer of the
  178.   peripheral device assigns external devices to specific 8259A PIC interrupt
  179.   levels. These assignments are realized as physical electrical connections
  180.   and cannot be modified by software.
  181.  
  182. Software Interrupts
  183.  
  184.   Any program can trigger software interrupts synchronously simply by
  185.   executing an INT instruction. MS-DOS uses Interrupts 20H through 3FH to
  186.   communicate with its modules and with application programs. (For instance,
  187.   the MS-DOS function dispatcher is reached by executing an Int 21H.) The
  188.   IBM PC ROM BIOS and application software use other interrupts, with either
  189.   higher or lower numbers, for various purposes (Figure 13-2). These
  190.   assignments are simply conventions and are not wired into the hardware in
  191.   any way.
  192.  
  193.  
  194.   Interrupt          Usage                                Machine
  195.   ──────────────────────────────────────────────────────────────────────────
  196.   00H                Divide-by-zero                       PC, AT, PS/2
  197.   01H                Single step                          PC, AT, PS/2
  198.   02H                NMI                                  PC, AT, PS/2
  199.   03H                Breakpoint                           PC, AT, PS/2
  200.   04H                Overflow                             PC, AT, PS/2
  201.   05H                ROM BIOS PrintScreen                 PC, AT, PS/2
  202.                      BOUND exceeded                       AT, PS/2
  203.   06H                Reserved                             PC
  204.                      Invalid opcode                       AT, PS/2
  205.   07H                Reserved                             PC
  206.                      80287/80387 not present              AT, PS/2
  207.   08H                IRQ0 timer tick                      PC, AT, PS/2
  208.                      Double fault                         AT, PS/2
  209.   09H                IRQ1 keyboard                        PC, AT, PS/2
  210.                      80287/80387 segment overrun          AT, PS/2
  211.   0AH                IRQ2 reserved                        PC
  212.                      IRQ2 cascade from slave 8259A PIC    AT, PS/2
  213.                      Invalid task-state segment (TSS)     AT, PS/2
  214.   0BH                IRQ3 serial communications (COM2)    PC, AT, PS/2
  215.                      Segment not present                  AT, PS/2
  216.   0CH                IRQ4 serial communications (COM1)    PC, AT, PS/2
  217.                      Stack segment overflow               AT, PS/2
  218.   0DH                IRQ5 fixed disk                      PC
  219.                      IRQ5 parallel printer (LPT2)         AT
  220.                      Reserved                             PS/2
  221.                      General protection fault             AT, PS/2
  222.   0EH                IRQ6 floppy disk                     PC, AT, PS/2
  223.                      Page fault                           AT, PS/2
  224.   0FH                IRQ7 parallel printer (LPT1)         PC, AT, PS/2
  225.   10H                ROM BIOS video driver                PC, AT, PS/2
  226.                      Numeric coprocessor fault            AT, PS/2
  227.   11H                ROM BIOS equipment check             PC, AT, PS/2
  228.   12H                ROM BIOS conventional-memory size    PC, AT, PS/2
  229.   13H                ROM BIOS disk driver                 PC, AT, PS/2
  230.   14H                ROM BIOS communications driver       PC, AT, PS/2
  231.   15H                ROM BIOS cassette driver             PC
  232.                      ROM BIOS I/O system extensions       AT, PS/2
  233.   16H                ROM BIOS keyboard driver             PC, AT, PS/2
  234.   17H                ROM BIOS printer driver              PC, AT, PS/2
  235.   18H                ROM BASIC                            PC, AT, PS/2
  236.   19H                ROM BIOS bootstrap                   PC, AT, PS/2
  237.   1AH                ROM BIOS time of day                 AT, PS/2
  238.   1BH                ROM BIOS Ctrl-Break                  PC, AT, PS/2
  239.   1CH                ROM BIOS timer tick                  PC, AT, PS/2
  240.   1DH                ROM BIOS video parameter table       PC, AT, PS/2
  241.   1EH                ROM BIOS floppy-disk parameters      PC, AT, PS/2
  242.   1FH                ROM BIOS font (characters 80H─FFH)   PC, AT, PS/2
  243.   20H                MS-DOS terminate process
  244.   21H                MS-DOS function dispatcher
  245.   22H                MS-DOS terminate address
  246.   23H                MS-DOS Ctrl-C handler address
  247.   24H                MS-DOS critical-error handler
  248.                      address
  249.   25H                MS-DOS absolute disk read
  250.   26H                MS-DOS absolute disk write
  251.   27H                MS-DOS terminate and stay resident
  252.   28H                MS-DOS idle interrupt
  253.   29H                MS-DOS reserved
  254.   2AH                MS-DOS network redirector
  255.   2BH─2EH            MS-DOS reserved
  256.   2FH                MS-DOS multiplex interrupt
  257.   30H─3FH            MS-DOS reserved
  258.   40H                ROM BIOS floppy-disk driver (if      PC, AT, PS/2
  259.                      fixed disk installed)
  260.   41H                ROM BIOS fixed-disk parameters       PC
  261.                      ROM BIOS fixed-disk parameters       AT, PS/2
  262.                      (drive 0)
  263.   42H                ROM BIOS default video driver (if    PC, AT, PS/2
  264.                      EGA installed)
  265.   43H                EGA, MCGA, VGA character table       PC, AT, PS/2
  266.   44H                ROM BIOS font (characters 00H─7FH)   PCjr
  267.   46H                ROM BIOS fixed-disk parameters       AT, PS/2
  268.                      (drive 1)
  269.   4AH                ROM BIOS alarm handler               AT, PS/2
  270.   5AH                Cluster adapter                      PC, AT
  271.   5BH                Used by cluster program              PC, AT
  272.   60H─66H            User interrupts                      PC, AT, PS/2
  273.   67H                LIM EMS driver                       PC, AT, PS/2
  274.   68H─6FH            Unassigned
  275.   70H                IRQ8 CMOS real-time clock            AT, PS/2
  276.   71H                IRQ9 software diverted to IRQ2       AT, PS/2
  277.   72H                IRQ10 reserved                       AT, PS/2
  278.   73H                IRQ11 reserved                       AT, PS/2
  279.   74H                IRQ12 reserved                       AT
  280.                      IRQ12 mouse                          PS/2
  281.   75H                IRQ13 numeric coprocessor            AT, PS/2
  282.   76H                IRQ14 fixed-disk controller          AT, PS/2
  283.   77H                IRQ15 reserved                       AT, PS/2
  284.   78H─7FH            Unassigned
  285.   80H─F0H            BASIC                                PC, AT, PS/2
  286.   F1H─FFH            Not used                             PC, AT, PS/2
  287.   ──────────────────────────────────────────────────────────────────────────
  288.  
  289.  
  290.   Figure 13-2.  Interrupts with special significance on the IBM PC, PC/AT,
  291.   and PS/2 and compatible computers. Note that the IBM ROM BIOS uses several
  292.   interrupts in the range 00H─1FH, even though they were reserved by Intel
  293.   for CPU faults. IRQ numbers refer to Intel 8259A PIC priority levels.
  294.  
  295. The Interrupt-Vector Table
  296.  
  297.   The bottom 1024 bytes of system memory are called the interrupt-vector
  298.   table. Each 4-byte position in the table corresponds to an interrupt type
  299.   (0 through 0FFH) and contains the segment and offset of the interrupt
  300.   handler for that level. Interrupts 0 through 1FH (the lowest levels) are
  301.   used for internal hardware interrupts; MS-DOS uses Interrupts 20H through
  302.   3FH; all the other interrupts are available for use by either external
  303.   hardware devices or system drivers and application software.
  304.  
  305.   When an 8259A PIC or other device interrupts the CPU by means of the INTR
  306.   pin, it must also place the interrupt type as an 8-bit number (0 through
  307.   0FFH) on the system bus, where the CPU can find it. The CPU then
  308.   multiplies this number by 4 to find the memory address of the interrupt
  309.   vector to be used.
  310.  
  311. Servicing an Interrupt
  312.  
  313.   When the CPU senses an interrupt, it pushes the program status word (which
  314.   defines the various CPU flags), the code segment (CS) register, and the
  315.   instruction pointer (IP) onto the machine stack and disables the interrupt
  316.   system. It then uses the 8-bit number that was jammed onto the system bus
  317.   by the interrupting device to fetch the address of the handler from the
  318.   vector table and resumes execution at that address.
  319.  
  320.   Usually the handler immediately reenables the interrupt system (to allow
  321.   higher-priority interrupts to occur), saves any registers it is going to
  322.   use, and then processes the interrupt as quickly as possible. Some
  323.   external devices also require a special acknowledgment signal so that they
  324.   will know the interrupt has been recognized.
  325.  
  326.   If the interrupt was funneled through an 8259A PIC, the handler must send
  327.   a special code called end of interrupt (EOI) to the PIC through its
  328.   control port to tell it when interrupt processing is completed. (The EOI
  329.   has no effect on the CPU itself.) Finally, the handler executes the
  330.   special IRET (INTERRUPT RETURN) instruction that restores the original
  331.   state of the CPU flags, the CS register, and the instruction pointer
  332.   (Figure 13-3).
  333.  
  334.   Whether an interrupt was triggered by an external device or forced by
  335.   software execution of an INT instruction, there is no discernible
  336.   difference in the system state at the time the interrupt handler receives
  337.   control. This fact is convenient when you are writing and testing external
  338.   interrupt handlers because you can debug them to a large extent simply by
  339.   invoking them with software drivers.
  340.  
  341.   ──────────────────────────────────────────────────────────────────────────
  342.   pic_ctl         equ  20h                     ; control port for 8259A
  343.                                                ; interrupt controller
  344.                   .
  345.                   .
  346.                   .
  347.                   sti                          ; turn interrupts back on,
  348.                   push  ax                     ; save registers
  349.                   push  bx
  350.                   push  cx
  351.                   push  dx
  352.                   push  si
  353.                   push  di
  354.                   push  bp
  355.                   push  ds
  356.                   push  es
  357.  
  358.                   mov   ax,cs                  ; make local data addressable
  359.                   mov   ds,ax
  360.                   .                            ; do some stuff appropriate
  361.                   .                            ; for this interrupt here
  362.                   .
  363.                   mov   al,20h                 ; send EOI to 8259A PIC
  364.                   mov   dx,pic_ctl
  365.                   out   dx,al
  366.  
  367.                   pop   es                     ; restore registers
  368.                   pop   ds
  369.                   pop   bp
  370.                   pop   di
  371.                   pop   si
  372.                   pop   dx
  373.                   pop   cx
  374.                   pop   bx
  375.                   pop   ax
  376.                   iret                         ; resume previous processing
  377.   ──────────────────────────────────────────────────────────────────────────
  378.  
  379.   Figure 13-3.  Typical handler for hardware interrupts on the 80x86 family
  380.   of microprocessors. In real life, the interrupt handler would need to save
  381.   and restore only the registers that it actually modified. Also, if the
  382.   handler made extensive use of the machine stack, it would need to save and
  383.   restore the SS and SP registers of the interrupted process and use its own
  384.   local stack.
  385.  
  386.  
  387. Interrupt Handlers and MS-DOS
  388.  
  389.   The introduction of an interrupt handler into your program brings with it
  390.   considerable hardware dependence. It goes without saying (but I am saying
  391.   it again here anyway) that you should avoid such hardware dependence in
  392.   MS-DOS applications whenever possible, to ensure that your programs will
  393.   be portable to any machine running current versions of MS-DOS and that
  394.   they will run properly under future versions of the operating system.
  395.  
  396.   Valid reasons do exist, however, for writing your own interrupt handler
  397.   for use under MS-DOS:
  398.  
  399.   ■  To supersede the MS-DOS default handler for an internal hardware
  400.      interrupt (such as divide-by-zero, BOUND exceeded, and so forth).
  401.  
  402.   ■  To supersede the MS-DOS default handler for a defined system exception,
  403.      such as the critical-error handler or Ctrl-C handler.
  404.  
  405.   ■  To chain your own interrupt handler onto the default system handler for
  406.      a hardware device, so that both the system's actions and your own will
  407.      occur on an interrupt. (A typical example of this is the "clock-tick"
  408.      interrupt.)
  409.  
  410.   ■  To service interrupts not supported by the default MS-DOS device
  411.      drivers (such as the serial communications port, which can be used at
  412.      much higher speeds with interrupts than with polling).
  413.  
  414.   ■  To provide a path of communication between a program that terminates
  415.      and stays resident and other application software.
  416.  
  417.   MS-DOS provides the following facilities to enable you to install
  418.   well-behaved interrupt handlers in a manner that does not interfere with
  419.   operating-system functions or other interrupt handlers:
  420.  
  421.   Function                             Action
  422.   ──────────────────────────────────────────────────────────────────────────
  423.   Int 21H Function 25H                Set interrupt vector.
  424.   Int 21H Function 35H                Get interrupt vector.
  425.   Int 21H Function 31H                Terminate and stay resident.
  426.   ──────────────────────────────────────────────────────────────────────────
  427.  
  428.   These functions allow you to examine or modify the contents of the system
  429.   interrupt-vector table and to reserve memory for the use of a handler
  430.   without running afoul of other processes in the system or causing memory
  431.   use conflicts. Section 2 of this book, "MS-DOS Functions Reference,"
  432.   describes each of these functions in detail, with programming examples.
  433.  
  434.   Handlers for external hardware interrupts under MS-DOS must operate under
  435.   some fairly severe restrictions:
  436.  
  437.   ■  Because the current versions of MS-DOS are not reentrant, a hardware
  438.      interrupt handler should never call the MS-DOS functions during the
  439.      actual interrupt processing.
  440.  
  441.   ■  The handler must reenable interrupts as soon as it gets control, to
  442.      avoid crippling other devices or destroying the accuracy of the system
  443.      clock.
  444.  
  445.   ■  A program should access the 8259A PIC with great care. The program
  446.      should not access the PIC unless that program is known to be the only
  447.      process in the system concerned with that particular interrupt level.
  448.      And it is vital that the handler issue an end-of-interrupt code to the
  449.      8259A PIC before performing the IRET; otherwise, the processing of
  450.      further interrupts for that priority level or lower priority levels
  451.      will be blocked.
  452.  
  453.   Restrictions on handlers that replace the MS-DOS default handlers for
  454.   internal hardware interrupts or system exceptions (such as Ctrl-C or
  455.   critical errors) are not quite so stringent, but you must still program
  456.   the handlers with extreme care to avoid destroying system tables or
  457.   leaving the operating system in an unstable state.
  458.  
  459.   The following are a few rules to keep in mind when you are writing an
  460.   interrupt driver:
  461.  
  462.   ■  Use Int 21H Function 25H (Set Interrupt Vector) to modify the
  463.      interrupt vector; do not write directly to the interrupt-vector table.
  464.  
  465.   ■  If your program is not the only process in the system that uses this
  466.      interrupt level, chain back to the previous handler after performing
  467.      your own processing on an interrupt.
  468.  
  469.   ■  If your program is not going to stay resident, fetch and save the
  470.      current contents of the interrupt vector before modifying it and then
  471.      restore the original contents when your program exits.
  472.  
  473.   ■  If your program is going to stay resident, use one of the terminate-
  474.      and-stay-resident functions (preferably Int 21H Function 31H) to
  475.      reserve the proper amount of memory for your handler.
  476.  
  477.   ■  If you are going to process hardware interrupts, keep the time that
  478.      interrupts are disabled and the total length of the service routine to
  479.      an absolute minimum. Remember that even after interrupts are reenabled
  480.      with an STI instruction, interrupts of the same or lower priority
  481.      remain blocked if the interrupt was received through the 8259A PIC.
  482.  
  483.  
  484. ZERODIV, an Example Interrupt Handler
  485.  
  486.   The listing ZERODIV.ASM (Figure 13-4) illustrates some of the principles
  487.   and guidelines on the previous pages. It is an interrupt handler for the
  488.   divide-by-zero internal interrupt (type 0). ZERODIV is loaded as a .COM
  489.   file (usually by a command in the system's AUTOEXEC file) but makes itself
  490.   permanently resident in memory as long as the system is running.
  491.  
  492.   The ZERODIV program has two major portions: the initialization portion and
  493.   the interrupt handler.
  494.  
  495.   The initialization procedure (called init in the program listing) is
  496.   executed only once, when the ZERODIV program is executed from the MS-DOS
  497.   level. The init procedure takes over the type 0 interrupt vector, prints a
  498.   sign-on message, then performs a terminate-and-stay-resident exit to
  499.   MS-DOS. This special exit reserves the memory occupied by the ZERODIV
  500.   program, so that it is not overwritten by subsequent application programs.
  501.  
  502.   The interrupt handler (called zdiv in the program listing) receives
  503.   control when a divide-by-zero interrupt occurs. The handler preserves all
  504.   registers and then prints a message to the user asking whether to continue
  505.   or to abort the program. We can use the MS-DOS console I/O functions
  506.   within this particular interrupt handler because we can safely presume
  507.   that the application was in control when the interrupt occurred; thus,
  508.   there should be no chance of accidentally making overlapping calls upon
  509.   the operating system.
  510.  
  511.   If the user enters a C to continue, the handler simply restores all the
  512.   registers and performs an IRET (INTERRUPT RETURN) to return control to the
  513.   application. (Of course, the results of the divide operation will be
  514.   useless.) If the user enters Q to quit, the handler exits to MS-DOS. Int
  515.   21H Function 4CH is particularly convenient in this case because it
  516.   allows the program to pass a return code and at the same time is the only
  517.   termination function that does not rely on the contents of any of the
  518.   segment registers.
  519.  
  520.   For an example of an interrupt handler for external (communications port)
  521.   interrupts, see the TALK terminal-emulator program in Chapter 7. You may
  522.   also want to look again at the discussions of Ctrl-C and critical-error
  523.   exception handlers in Chapters 5 and 8.
  524.  
  525.   ──────────────────────────────────────────────────────────────────────────
  526.            name      zdivide
  527.            page      55,132
  528.            title     ZERODIV--Divide-by-zero handler
  529.  
  530.   ;
  531.   ; ZERODIV.ASM--Terminate-and-stay-resident handler
  532.   ;              for divide-by-zero interrupts
  533.   ;
  534.   ; Copyright 1988 Ray Duncan
  535.   ;
  536.   ; Build:        C>MASM ZERODIV;
  537.   ;               C>LINK ZERODIV;
  538.   ;               C>EXE2BIN ZERODIV.EXE ZERODIV.COM
  539.   ;               C>DEL ZERODIV.EXE
  540.   ;
  541.   ; Usage:        C>ZERODIV
  542.   ;
  543.  
  544.   cr      equ     0dh             ; ASCII carriage return
  545.   lf      equ     0ah             ; ASCII linefeed
  546.   beep    equ     07h             ; ASCII bell code
  547.   backsp  equ     08h             ; ASCII backspace code
  548.  
  549.   _TEXT   segment word public 'CODE'
  550.  
  551.           org     100H
  552.  
  553.           assume  cs:_TEXT,ds:_TEXT,es:_TEXT,ss:_TEXT
  554.  
  555.   init    proc    near            ; entry point at load time
  556.  
  557.                                   ; capture vector for
  558.                                   ; interrupt zero...
  559.           mov     dx,offset zdiv  ; DS:DX = handler address
  560.           mov     ax,2500h        ; function 25h = set vector
  561.                                   ; interrupt type = 0
  562.           int     21h             ; transfer to MS-DOS
  563.  
  564.                                   ; print sign-on message
  565.           mov     dx,offset msg1  ; DS:DX = message address
  566.           mov     ah,9            ; function 09h = display string
  567.           int     21h             ; transfer to MS-DOS
  568.  
  569.                                   ; DX = paragraphs to reserve
  570.           mov     dx,((offset pgm_len+15)/16)+10h
  571.           mov     ax,3100h        ; function 31h = terminate and
  572.                                   ; stay resident
  573.           int     21h             ; transfer to MS-DOS
  574.  
  575.   init    endp
  576.  
  577.  
  578.   zdiv    proc    far             ; this is the divide-by-
  579.                                   ; zero interrupt handler
  580.  
  581.           sti                     ; enable interrupts
  582.  
  583.           push    ax              ; save registers
  584.           push    bx
  585.           push    cx
  586.           push    dx
  587.           push    s 
  588.           push    di
  589.           push    bp
  590.           push    ds
  591.           push    es
  592.  
  593.           mov     ax,cs           ; make data addressable
  594.           mov     ds,ax
  595.  
  596.                                   ; display message
  597.                                   ; "Continue or Quit?"
  598.           mov     dx,offset msg2  ; DS:DX = message address
  599.           mov     ah,9            ; function 09h = display string
  600.           int     21h             ; transfer to MS-DOS
  601.  
  602.   zdiv1:  mov     ah,1            ; function 01h = read keyboard
  603.           int     21h             ; transfer to MS-DOS
  604.  
  605.           or      al,20h          ; fold char to lowercase
  606.  
  607.           cmp     al,'c'          ; is it C or Q?
  608.           je      zdiv3           ; jump, it's a C
  609.  
  610.           cmp     al,'q'
  611.           je      zdiv2           ; jump, it's a Q
  612.  
  613.                                   ; illegal entry, send beep
  614.                                   ; and erase the character
  615.           mov     dx,offset msg3  ; DS:DX = message address
  616.           mov     ah,9            ; function 09h = display string
  617.           int     21h             ; transfer to MS-DOS
  618.  
  619.           jmp     zdiv1           ; try again
  620.  
  621.   zdiv2:                          ; user chose "Quit"
  622.           mov     ax,4cffh        ; terminate current program
  623.           int     21h             ; with return code = 255
  624.  
  625.   zdiv3:                          ; user chose "Continue"
  626.                                   ; send CR-LF pair
  627.           mov     dx,offset msg4  ; DS:DX = message address
  628.           mov     ah,9            ; function 09h = print string
  629.           int     21h             ; transfer to MS-DOS
  630.  
  631.                                   ; what CPU type is this?
  632.           xor     ax,ax           ; to find out, we'll put
  633.           push    ax              ; zero in the CPU flags
  634.           popf                    ; and see what happens
  635.           pushf
  636.           pop     ax
  637.           and     ax,0f000h       ; 8086/8088 forces
  638.           cmp     ax,0f000h       ; bits 12-15 true
  639.           je      zdiv5           ; jump if 8086/8088
  640.  
  641.                                   ; otherwise we must adjust
  642.                                   ; return address to bypass
  643.                                   ; the divide instruction...
  644.           mov     bp,sp           ; make stack addressable
  645.  
  646.           lds     bx,[bp+18]      ; get address of the
  647.                                   ; faulting instruction
  648.  
  649.           mov     bl,[bx+1]       ; get addressing byte
  650.           and     bx,0c7h         ; isolate mod & r/m fields
  651.  
  652.           cmp     bl,6            ; mod 0, r/m 6 = direct
  653.           jne     zdiv4           ; not direct, jump
  654.  
  655.           add     word ptr [bp+18],4
  656.           jmp     zdiv5
  657.  
  658.   zdiv4:  mov     cl,6            ; otherwise isolate mod
  659.           shr     bx,cl           ; field and get instruction
  660.           mov     bl,cs:[bx+itab] ; size from table
  661.           add     [bp+18],bx
  662.  
  663.   zdiv5:  pop     es              ; restore registers
  664.           pop     ds
  665.           pop     bp
  666.           pop     di
  667.           pop     si
  668.           pop     dx
  669.           pop     cx
  670.           pop     bx
  671.           pop     ax
  672.           iret                    ; return from interrupt
  673.  
  674.   zdiv    endp
  675.  
  676.  
  677.   msg1    db      cr,lf           ; load-time sign-on message
  678.           db      'Divide by Zero Interrupt '
  679.           db      'Handler installed.'
  680.           db      cr,lf,'$'
  681.  
  682.   msg2    db      cr,lf,lf        ; interrupt-time message
  683.           db      'Divide by Zero detected: '
  684.           db      cr,lf,'Continue or Quit (C/Q) ? '
  685.           db      '$'
  686.  
  687.   msg3    db      beep            ; used if bad entry
  688.           db      backsp,' ',backsp,'$'
  689.  
  690.   msg4    db      cr,lf,'$'       ; carriage return-linefeed
  691.  
  692.                                   ; instruction size table
  693.   itab    db      2               ; mod = 0
  694.           db      3               ; mod = 1
  695.           db      4               ; mod = 2
  696.           db      2               ; mod = 3
  697.  
  698.   pgm_len equ     $-init          ; program length
  699.  
  700.   _TEXT   ends
  701.  
  702.           end     init
  703.   ──────────────────────────────────────────────────────────────────────────
  704.  
  705.   Figure 13-4.  A simple example of an interrrupt handler for use within the
  706.   MS-DOS environment. ZERODIV makes itself permanently resident in memory
  707.   and handles the CPU's internal divide-by-zero interrupt.
  708.  
  709.  
  710.  
  711.