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.
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.