File: keyclick.pas

package info (click to toggle)
fpc 1.9.4-5
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 63,532 kB
  • ctags: 93,677
  • sloc: pascal: 675,850; makefile: 219,089; xml: 9,242; perl: 7,703; yacc: 3,074; ansic: 2,275; lex: 711; sh: 406; asm: 71; csh: 34; sed: 33; cpp: 26; tcl: 7
file content (119 lines) | stat: -rw-r--r-- 3,026 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
{ 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.