fpc/docs/go32ex/keyclick.pas
2005-02-14 17:13:06 +00:00

119 lines
3.4 KiB
ObjectPascal

{ This example demonstrates how to chain to a hardware interrupt.
In more detail, it hooks the keyboard interrupt, calls a user
procedure which in this case simply turns the PC speaker on and off.
Then the old interrupt is called.
}
{$ASMMODE ATT}
{$MODE FPC}
uses
crt,
go32;
const
{ keyboard is IRQ 1 -> interrupt 9 }
kbdint = $9;
var
{ holds old PM interrupt handler address }
oldint9_handler : tseginfo;
{ new PM interrupt handler }
newint9_handler : tseginfo;
{ pointer to interrupt handler }
clickproc : pointer;
{ the data segment selector }
backupDS : Word; external name '___v2prt0_ds_alias';
{ interrupt handler }
procedure int9_handler; assembler;
asm
cli
{ save all registers, because we don't know which the compiler
uses for the called procedure }
pushl %ds
pushl %es
pushl %fs
pushl %gs
pushal
{ set up to call a FPC procedure }
movw %cs:backupDS, %ax
movw %ax, %ds
movw %ax, %es
movw dosmemselector, %ax
movw %ax, %fs
{ call user procedure }
call *clickproc
{ restore all registers }
popal
popl %gs
popl %fs
popl %es
popl %ds
{ note: in go32v2 mode %cs=%ds=%es !!!}
ljmp %cs:oldint9_handler { call old handler }
{ we don't need to do anything more, because the old interrupt
handler does this for us (send EOI command, iret, sti...) }
end;
{ dummy procedure to retrieve exact length of handler, for locking
and unlocking functions }
procedure int9_dummy; begin end;
{ demo user procedure, simply clicks on every keypress }
procedure clicker;
begin
sound(500); delay(10); nosound;
end;
{ dummy procedure to retrieve exact length of user procedure for
locking and unlocking functions }
procedure clicker_dummy; begin end;
{ installs our new handler }
procedure install_click;
begin
clickproc := @clicker;
{ lock used code and data }
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));
{ fill in new handler's 48 bit pointer }
newint9_handler.offset := @int9_handler;
newint9_handler.segment := get_cs;
{ get old PM interrupt handler }
get_pm_interrupt(kbdint, oldint9_handler);
{ set the new interrupt handler }
set_pm_interrupt(kbdint, newint9_handler);
end;
{ deinstalls our interrupt handler }
procedure remove_click;
begin
{ set old handler }
set_pm_interrupt(kbdint, oldint9_handler);
{ unlock used code & data }
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.