next up previous contents index
Next: Software interrupts Up: Protected mode memory organization Previous: Disabling interrupts

Hardware interrupts

Hardware interrupts are generated by hardware devices when something unusual happens; this could be a keypress or a mouse move or any other action. This is done to minimize CPU time, else the CPU would have to check all installed hardware for data in a big loop (this method is called 'polling') and this would take much time.

A standard IBM-PC has two interrupt controllers, that are responsible for these hardware interrupts: both allow up to 8 different interrupt sources (IRQs, interrupt requests). The second controller is connected to the first through IRQ 2 for compatibility reasons, e.g. if controller 1 gets an IRQ 2, he hands the IRQ over to controller 2. Because of this up to 15 different hardware interrupt sources can be handled.

IRQ 0 through IRQ 7 are mapped to interrupts 8h to Fh and the second controller (IRQ 8 to 15) is mapped to interrupt 70h to 77h.

All of the code and data touched by these handlers MUST be locked (via the various locking functions) to avoid page faults at interrupt time. Because hardware interrupts are called (as in real mode) with interrupts disabled, the handler has to enable them before it returns to normal program execution. Additionally a hardware interrupt must send an EOI (end of interrupt) command to the responsible controller; this is acomplished by sending the value 20h to port 20h (for the first controller) or A0h (for the second controller).

The following example shows how to redirect the keyboard interrupt.

Example
Program Keyclick;

uses crt, 
     go32;

const kbdint = $9; 

var oldint9_handler : tseginfo;
    newint9_handler : tseginfo;

    clickproc : pointer; 

{$ASMMODE DIRECT}
procedure int9_handler; assembler;
asm
   cli
   pushal
   movw %cs:INT9_DS, %ax
   movw %ax, %ds
   movw %ax, %es
   movw U_GO32_DOSMEMSELECTOR, %ax
   movw %ax, %fs
   call *_CLICKPROC
   popal

   ljmp %cs:OLDHANDLER 

INT9_DS: .word 0
OLDHANDLER:
         .long 0
         .word 0
end;

procedure int9_dummy; begin end;

procedure clicker;
begin
     sound(500); delay(10); nosound;
end;

procedure clicker_dummy; begin end;

procedure install_click;
begin
     clickproc := @clicker;
     lock_data(clickproc, sizeof(clickproc));
     lock_data(dosmemselector, sizeof(dosmemselector));

     lock_code(@clicker, 
               longint(@clicker_dummy)-longint(@clicker));
     lock_code(@int9_handler, 
               longint(@int9_dummy)
                - longint(@int9_handler));
     newint9_handler.offset := @int9_handler;
     newint9_handler.segment := get_cs;
     get_pm_interrupt(kbdint, oldint9_handler);
     asm
        movw %ds, %ax
        movw %ax, INT9_DS
        movl _OLDINT9_HANDLER, %eax
        movl %eax, OLDHANDLER
        movw 4+_OLDINT9_HANDLER, %ax
        movw %ax, 4+OLDHANDLER
     end;
     set_pm_interrupt(kbdint, newint9_handler);
end;

procedure remove_click;
begin
     set_pm_interrupt(kbdint, oldint9_handler);
     unlock_data(dosmemselector, sizeof(dosmemselector));
     unlock_data(clickproc, sizeof(clickproc));
     unlock_code(@clicker, 
                 longint(@clicker_dummy)
                  - longint(@clicker));
     unlock_code(@int9_handler, 
                 longint(@int9_dummy)
                  - longint(@int9_handler));
end;

var ch : char;

begin
     install_click;
     Writeln('Enter any message.',
             ' Press return when finished');
     while (ch <> #13) do begin
           ch := readkey; write(ch);
     end;
     remove_click;
end.



Michael Van Canneyt
Thu Sep 10 13:59:33 CEST 1998