Article View: comp.lang.modula2
Article #3460Re: Interrupt Handlers in Topspeed Modula 2 V1.17
From: Stefan Ericsson
Date: Mon, 08 Apr 1996 12:42
Date: Mon, 08 Apr 1996 12:42
510 lines
15688 bytes
15688 bytes
Martin Harvey wrote:
>
> -- Hi,
>
> I'm trying to write an interrupt handler that intercepts the keyboard
> interrupt. I've looked in the documentation that I have, and two
> alternative approaches seem to appear:
>
Hi Martin,
I wrote something that is based on some example TSR code from Topspeed and some C code that
I found some (long) time ago. Hopefully you can use something out of it.
The example code also includes a very simple test program.
The SetIntrrupt and ResetIntrrupt routines should use the DOS eqvalient instead of the
array of pointers, but this works.
Note, this code seems to fail working under Win95 (if I remember it correctly...or
maybe it was the key interrupt routine that is included in the RS232 toolkit?).
The code is however for V3.x.
Sorry for this long posting.
/Stefan
MODULE TestKey;
IMPORT IO,Lib,SYSTEM,Keyb,KeyInt;
CONST
UseKeyInt = FALSE;
VAR
r :SYSTEM.Registers;
key :CHAR;
keyrec :KeyInt.KeyRec;
key2 :Keyb.KeyRec;
stop:BOOLEAN;
ch :CHAR;
i,
sc :CARDINAL;
BEGIN
(*%F UseKeyInt *)
Keyb.Install;
(*%E *)
(*%T UseKeyInt *)
KeyInt.InstallKeyHandler;
(*%E *)
LOOP
IO.WrStr('Enter key:');
(*%F UseKeyInt *)
key2 := Keyb.RdKeyR();
IO.WrLn;
IO.WrStr('Char:');IO.WrChar( key2.char );
IO.WrStr(' ,Scan:'); IO.WrCard( key2.scan,0 );
IO.WrStr(' ,Code:'); IO.WrCard( key2.code,0 );
key := key2.char;
(*%E *)
(*%T UseKeyInt *)
WHILE NOT KeyInt.KeyPressed() DO END;
keyrec := KeyInt.RdKeyRec();
key := keyrec.char;
sc := keyrec.code;
IO.WrLn;
IO.WrStr('Char:');IO.WrChar( key ); IO.WrLn;
IO.WrStr('Scan:'); IO.WrCard( sc,0 );
(*%E *)
IO.WrLn;
IF key = CHR(27) THEN
EXIT;
END;
END;
(*%F UseKeyInt *)
Keyb.Uninstall;
(*%E *)
(*%T UseKeyInt *)
KeyInt.DeinstallKeyHandler;
(*%E *)
END TestKey.
DEFINITION MODULE KeyInt;
(*# check(stack=>off,index=>off,range=>off,overflow=>off,nil_ptr=>off) *)
(*# debug(vid=>off)*)
IMPORT Key;
(*
This module contains a replacement for the system
keyboard handler.
*)
CONST
QueueLength = 32;
TYPE
KeyFlagSet = SET OF ( RShift,LShift,Ctrl,Alt,Scroll,Num,Caps,Ins,Shift ) ;
KeyRec = RECORD
char : CHAR;
code : CARDINAL;
flags: KeyFlagSet;
END;
VAR
(*# save, data(volatile=>on) *)
isInstalled : BOOLEAN;
useOldKeyIntr : BOOLEAN; (* FALSE on setup *)
SysRequest : BOOLEAN; (* If SysReq has benn pressed *)
CtrlAltDel : BOOLEAN; (* If CtrlAltDel has been pressed *)
CtrlBreak : BOOLEAN; (* If CtrlBreak has been pressed *)
CtrlC : BOOLEAN; (* If CtrlC has been pressed *)
CtrlAltPressed : BOOLEAN; (* If CtrlAlt has been pressed *)
FuncKeyPressed : BOOLEAN; (* If some function key has been pressed *)
MoveKeyPressed : BOOLEAN; (* If Left/Right/Down or Up is pressed *)
KeyFlags : KeyFlagSet; (* Usefully key flags *)
lastKeyPress : CARDINAL; (* the last key pressed, use Key.Def *)
lastKey : KeyRec; (* the last key pressed *)
currKey : KeyRec; (* the current pressed key *)
(*# restore *)
PROCEDURE InstallKeyHandler;
PROCEDURE DeinstallKeyHandler;
PROCEDURE ClearKey( scan:CARDINAL ); (* If key is pressed *)
PROCEDURE SetKey( scan :CARDINAL ); (* Set key as pressed *)
PROCEDURE TestKey( scan :CARDINAL ):BOOLEAN; (* Test if key pressed *)
PROCEDURE ResetKey( scan :CARDINAL ); (* Set pressed := pressing *)
PROCEDURE GetLastKey():CARDINAL;
PROCEDURE ClearLastKey;
PROCEDURE ClrKeyBuff;
PROCEDURE RdKey():CHAR;
PROCEDURE RdKeyRec():KeyRec;
PROCEDURE RdKeyFlags():KeyFlagSet;
PROCEDURE KeyPressed():BOOLEAN;
PROCEDURE WaitEsc;
END KeyInt.
IMPLEMENTATION MODULE KeyInt;
(*# check(stack=>off,index=>off,range=>off,overflow=>off,nil_ptr=>off) *)
(*# debug(vid=>off)*)
(*# debug(line_num=>on) *)
IMPORT SYSTEM, Lib;
CONST
(*KBFuseful = KBFlagSet { RShift, LShift, Ctrl, Alt } ;*)
NumKeys = 128;
NumChars = 128;
(*QueueLength = 32;*)
TYPE
CharType = ARRAY [0..NumChars-1] OF CHAR;
(*KeyFlagSet = SET OF ( RShift,LShift,Ctrl,Alt,Scroll,Num,Caps,Ins,Shift ) ;*)
KeySetRec = RECORD
pressed : BOOLEAN;
pressing : BOOLEAN;
END;
KeySet = ARRAY [0..NumKeys-1] OF KeySetRec;
(*
KeyRec = RECORD
char : CHAR;
code : CARDINAL;
flags: KeyFlagSet;
END;
*)
QueueType = ARRAY [0..QueueLength-1] OF KeyRec;
VAR
Continue : PROC;
CapitalKeys : CharType;
(*# save, data(volatile=>on) *)
(*
isInstalled : BOOLEAN;
useOldKeyIntr : BOOLEAN; (* FALSE on setup *)
SysRequest : BOOLEAN; (* If SysReq has benn pressed *)
CtrlAltDel : BOOLEAN; (* If CtrlAltDel has been pressed *)
CtrlBreak : BOOLEAN; (* If CtrlBreak has been pressed *)
CtrlC : BOOLEAN; (* If CtrlC has been pressed *)
CtrlAltPressed : BOOLEAN; (* If CtrlAlt has been pressed *)
FuncKeyPressed : BOOLEAN; (* If some function key has been pressed *)
MoveKeyPressed : BOOLEAN; (* If Left/Right/Down or Up is pressed *)
KeyFlags : KeyFlagSet; (* Usefully key flags *)
lastKeyPress : CARDINAL; (* the last key pressed, use Key.Def *)
lastKey : KeyRec; (* the last key pressed *)
currKey : KeyRec; (* the current pressed key *)
count : CARDINAL; (* debug *)
*)
scanCode : CARDINAL; (* the key scan code *)
keys : KeySet; (* the key array, faster access *)
queueCount : CARDINAL; (* count of keys in queue *)
queue : QueueType; (* the circular key queue *)
head : CARDINAL; (* head queue pointer *)
tail : CARDINAL; (* tail queue pointer *)
(*# restore *)
PROCEDURE ConvertToChar( scan :CARDINAL ):KeyRec;
VAR key :KeyRec;
BEGIN
key.char := 0C;
key.flags := KeyFlags;
CASE scan OF
| 1 : key.code := 27; key.char := CHR(27);
| 15 : key.code := 9; key.char := CHR(9);
| 57 : key.code := 32; key.char := ' ';
| 28 : key.code := 13; key.char := CHR(13);
| 114 : SysRequest := TRUE;
key.code := scan + Key.Extended;
| 58..68,
87..88 : FuncKeyPressed := TRUE;
key.code := scan + Key.Extended;
| 72,75,
77,80 : MoveKeyPressed := TRUE;
key.code := scan + Key.Extended;
| 71,73,74,78,82,83,
79,81 : key.code := scan + Key.Extended;
ELSE
key.char := CapitalKeys[scan];
key.code := scan;
END;
RETURN key;
END ConvertToChar;
(* Interrupt Handlers *)
(* ================== *)
(* pragmas for interrupt handler *)
(*# save,
call(interrupt => on,
reg_param => (),
same_ds => off
)
*)
TYPE
IPROC = PROCEDURE();
VAR
Irpt[0:0], (* Interrupt table *)
SaveIrpt : ARRAY [0..255] OF IPROC ;
PROCEDURE Int9 (); (* Keyboard H/W interrupt *)
VAR
scan,status : SHORTCARD ;
key : CHAR;
BEGIN
IF useOldKeyIntr THEN
SaveIrpt[09H](); (* Call old key interrupt instead *)
ELSE
scan := SYSTEM.In(60H) ; (* Read scan code *)
status := SYSTEM.In(61H) ; (* Read status *)
SYSTEM.Out(61H,SHORTCARD(BITSET(status)+{7})); (* Send acknowledge *)
SYSTEM.Out(61H,status); (* Restore status *)
IF scan >= 128
THEN scanCode := CARDINAL(scan) - 128;
ELSE scanCode := CARDINAL(scan);
END;
(* Set the key flags *)
IF scan < 128 THEN (* key pressed *)
lastKeyPress := scanCode;
keys[scanCode].pressed := TRUE;
keys[scanCode].pressing := TRUE;
IF scanCode = 42 THEN (* left shift *)
KeyFlags := KeyFlags + KeyFlagSet{LShift};
KeyFlags := KeyFlags + KeyFlagSet{Shift};
ELSIF scanCode = 54 THEN (* right shift *)
KeyFlags := KeyFlags + KeyFlagSet{RShift};
KeyFlags := KeyFlags + KeyFlagSet{Shift};
ELSIF scanCode = 58 THEN KeyFlags := KeyFlags + KeyFlagSet{Caps};
ELSIF scanCode = 29 THEN KeyFlags := KeyFlags + KeyFlagSet{Ctrl};
ELSIF scanCode = 56 THEN KeyFlags := KeyFlags + KeyFlagSet{Alt};
ELSIF scanCode = 69 THEN (* num lock *)
IF Num IN KeyFlags
THEN KeyFlags := KeyFlags - KeyFlagSet{Num};
ELSE KeyFlags := KeyFlags + KeyFlagSet{Num};
END;
ELSIF scanCode = 70 THEN (* scroll lock *)
IF Scroll IN KeyFlags
THEN KeyFlags := KeyFlags - KeyFlagSet{Scroll};
ELSE KeyFlags := KeyFlags + KeyFlagSet{Scroll};
END;
ELSIF scanCode = 82 THEN (* insert key *)
IF Ins IN KeyFlags
THEN KeyFlags := KeyFlags - KeyFlagSet{Ins};
ELSE KeyFlags := KeyFlags + KeyFlagSet{Ins};
END;
END;
(* Check for important keys *)
IF Ctrl IN KeyFlags THEN
IF Alt IN KeyFlags THEN
CtrlAltPressed := TRUE;
IF scanCode=83 THEN CtrlAltDel := TRUE END; (* restart keys *)
ELSIF scanCode=46 THEN
CtrlC := TRUE;
END;
END;
(* Convert the scan code *)
lastKey := currKey;
currKey := ConvertToChar( scanCode );
IF queueCount < QueueLength THEN
queue[tail].char := currKey.char;
queue[tail].code := currKey.code;
queue[tail].flags := currKey.flags;
tail := (tail+1) MOD QueueLength;
INC(queueCount);
END;
ELSE (* key is released *)
keys[scanCode].pressing := FALSE;
IF scanCode = 42 THEN
KeyFlags := KeyFlags - KeyFlagSet{LShift};
ELSIF scanCode = 54 THEN
KeyFlags := KeyFlags - KeyFlagSet{RShift};
ELSIF scanCode = 29 THEN KeyFlags := KeyFlags - KeyFlagSet{Ctrl};
ELSIF scanCode = 56 THEN KeyFlags := KeyFlags - KeyFlagSet{Alt};
END;
IF NOT (RShift IN KeyFlags) AND NOT (LShift IN KeyFlags) THEN
KeyFlags := KeyFlags - KeyFlagSet{Shift};
END;
END;
SYSTEM.Out(20H,20H); (* Send EOI to timer *)
END; (* use old key interrupt *)
END Int9;
PROCEDURE Int1C () ; (* Timer Interrupt *)
VAR
base : CARDINAL ;
BEGIN
SaveIrpt[1CH]() ; (* Chain Int 1C *)
END Int1C ;
PROCEDURE Int24 () : CARDINAL ; (* Critical Error Interrupt *)
BEGIN
RETURN 0 ;
END Int24;
(*# restore *)
PROCEDURE SetIntr ( N : CARDINAL ; P : IPROC ) ;
VAR
fl : CARDINAL ;
BEGIN
fl := SYSTEM.GetFlags();
SYSTEM.DI;
SaveIrpt[N] := Irpt[N];
Irpt[N] := P;
SYSTEM.SetFlags(fl);
SYSTEM.EI;
END SetIntr ;
PROCEDURE ResetIntr ( N : CARDINAL ) ;
VAR
fl : CARDINAL;
BEGIN
fl := SYSTEM.GetFlags();
SYSTEM.DI;
Irpt[N] := SaveIrpt[N];
SYSTEM.SetFlags(fl);
SYSTEM.EI;
END ResetIntr ;
PROCEDURE ClrKeyBuff;
VAR
i :CARDINAL;
BEGIN
SYSTEM.DI;
head := 0;
tail := 0;
queueCount := 0;
FuncKeyPressed := FALSE;
MoveKeyPressed := FALSE;
CtrlC := FALSE;
CtrlBreak := FALSE;
CtrlAltDel := FALSE;
CtrlAltPressed := FALSE;
lastKey.char := 0C;
lastKey.code := 0;
lastKey.flags := KeyFlagSet{};
currKey.char := 0C;
currKey.code := 0;
currKey.flags := KeyFlagSet{};
FOR i:=0 TO NumKeys-1 DO
keys[i].pressed := FALSE;
keys[i].pressing := FALSE;
END;
SYSTEM.EI;
END ClrKeyBuff;
PROCEDURE ClearKey( scan:CARDINAL ); (* If key is pressed *)
BEGIN
IF scan < NumKeys-1 THEN
SYSTEM.DI;
keys[scan].pressed := FALSE;
SYSTEM.EI;
END;
END ClearKey;
PROCEDURE SetKey( scan :CARDINAL ); (* Set key as pressed *)
BEGIN
IF scan < NumKeys-1 THEN
SYSTEM.DI;
keys[scan].pressed := TRUE;
SYSTEM.EI;
END;
END SetKey;
PROCEDURE TestKey( scan :CARDINAL ):BOOLEAN; (* Test if key pressed *)
BEGIN
IF scan < NumKeys-1 THEN
RETURN (keys[scan].pressed = TRUE);
END;
END TestKey;
PROCEDURE ResetKey( scan :CARDINAL ); (* Set pressed := pressing *)
BEGIN
IF scan < NumKeys-1 THEN
SYSTEM.DI;
keys[scan].pressed := keys[scan].pressing;
SYSTEM.EI;
END;
END ResetKey;
PROCEDURE GetLastKey():CARDINAL;
BEGIN
RETURN lastKeyPress;
END GetLastKey;
PROCEDURE ClearLastKey;
BEGIN
lastKeyPress := 0;
END ClearLastKey;
PROCEDURE RdKeyRec():KeyRec;
VAR key :KeyRec;
BEGIN
IF queueCount > 0 THEN
key := queue[head];
head := (head+1) MOD QueueLength;
DEC(queueCount);
ELSE
key.char := 0C;
key.code := 0;
key.flags := KeyFlags;
END;
RETURN key;
END RdKeyRec;
PROCEDURE RdKey():CHAR;
VAR key :KeyRec;
BEGIN
key := RdKeyRec();
RETURN key.char;
END RdKey;
PROCEDURE RdKeyFlags():KeyFlagSet;
BEGIN
RETURN KeyFlags;
END RdKeyFlags;
PROCEDURE WaitEsc;
VAR key :KeyRec;
BEGIN
ClrKeyBuff;
LOOP
key := RdKeyRec();
IF key.code = Key.Escape THEN EXIT END;
END;
END WaitEsc;
PROCEDURE KeyPressed():BOOLEAN;
BEGIN
RETURN (queueCount # 0);
END KeyPressed;
PROCEDURE TermProc;
BEGIN
IF isInstalled THEN
ResetIntr( 09H );
END;
Continue();
END TermProc;
PROCEDURE InstallKeyHandler;
BEGIN
ClrKeyBuff;
useOldKeyIntr := FALSE;
isInstalled := TRUE;
SetIntr( 09H, Int9 );
Lib.Terminate(TermProc,Continue);
END InstallKeyHandler;
PROCEDURE DeinstallKeyHandler;
BEGIN
ResetIntr( 09H );
ClrKeyBuff;
useOldKeyIntr := TRUE;
isInstalled := FALSE;
END DeinstallKeyHandler;
VAR
i :CARDINAL;
BEGIN
isInstalled := FALSE;
useOldKeyIntr := TRUE;
lastKeyPress := 0;
FOR i:=0 TO NumChars-1 DO CapitalKeys[i]:=' ' END;
CapitalKeys :=
' 1234567890+ QWERTYUIOP ASDFGHJKL ZXCVBNM,.- 789 456+1230';
ClrKeyBuff;
END KeyInt.
Technical Details
Message-ID:
<316909A6.4DFF@osd.ericsson.se>Path:
arc1-ca.pugleaf.net!erinews.ericsson.se!usenetReferences:
<496207970wnr@harvey27.demon.co.uk>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!erinews.ericsson.se!usenet From: Stefan Ericsson <etxsjen@osd.ericsson.se> Newsgroups: comp.lang.modula2 Subject: Re: Interrupt Handlers in Topspeed Modula 2 V1.17 Date: Mon, 08 Apr 1996 14:42:14 +0200 Organization: Ericsson Research & Development AB Message-ID: <316909A6.4DFF@osd.ericsson.se> References: <496207970wnr@harvey27.demon.co.uk> NNTP-Posting-Host: pcsjen.ericsson.se Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-Mailer: Mozilla 2.0GoldB2 (Win95; I) To: mch24@harvey27.demon.co.uk