🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.lang.modula2
Article #3460

Re: Interrupt Handlers in Topspeed Modula 2 V1.17

#3460
From: Stefan Ericsson
Date: Mon, 08 Apr 1996 12:42
510 lines
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!usenet
References: <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