Thursday, 6 March 2014

Hardware interrupt programs

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

{$ASMMODE ATT}
{$MODE FPC}

uses
        crt,
        go32;

const
        kbdint = $9;

var
        oldint9_handler : tseginfo;
        newint9_handler : tseginfo;

        clickproc : pointer;
        backupDS : Word; external name '___v2prt0_ds_alias';

procedure int9_handler; assembler;
asm
        cli
        pushl %ds
        pushl %es
        pushl %fs
        pushl %gs
        pushal
        movw %cs:backupDS, %ax
        movw %ax, %ds
        movw %ax, %es
        movw dosmemselector, %ax
        movw %ax, %fs
        call *clickproc
        popal
        popl %gs
        popl %fs
        popl %es
        popl %ds
        ljmp %cs:oldint9_handler
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);
        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;

No comments:

Post a Comment