Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Tools

Creating TSRs Programs With Turbo Pascal: Part II


JUN89: CREATING TSRS PROGRAMS WITH TURBO PASCAL: PART II

Ken Pottebaum is a professional mechanical engineer for the Small Disk Division of Imprimis, a subsidiary of Control Data Corp. He can be reached at 321 Redbud, Yukon, OK 70399.


This is the second part of a two-part article about creating TSR (Terminate-and-Stay-Resident) programs with Turbo Pascal 5.0 and TSRUnit. The first part dealt with the inside workings of TSRUnit. This part focuses on how to use TSRUnit and provides an example program. Although the example program is simple, it illustrates methods for accessing files, printing, inserting characters into the keyboard input stream, and other useful tasks. If you missed the first part of the series, you can still create your own TSR programs because the INTERFACE section of TSRUnit (Listing One) provides information about how to use the unit.

Using TSRUnit

In order to use TSRUnit, your program must provide a function for TSRUnit to call when the unit pops up and also perform a call to TSRInstall.

The function that TSRUnit calls when it pops up may have any valid function name. For simplicity, I will refer to this function as DemoTasks. (DemoTasks is also the name for this function in the example program TSRDemo, which is discussed later in this article.) DemoTasks must be compiled as a far function (without any parameters) that returns a WORD value. The WORD value is the number of characters to be inserted into the keyboard input stream. (The character insertion option is a special feature provided by TSRUnit and is explained later, along with the other features illustrated in the example program.) Generally speaking, the far function can contain any valid Turbo Pascal statement, including calls to other procedures and functions. Common sense is important, however, when any of the routines from the standard DOS UNIT are used. For example, it would not make sense to use the Keep procedure because Keep has already been used. Also, if you use any of the procedures that change interrupt vectors, be sure to restore those procedures before exiting DemoTasks.

As shown in Listing One, the call to TSRInstall requires four parameters. The first parameter is a character string that contains the name of the TSR; this name is displayed when the TSR is installed. The second parameter is a procedure-type parameter that specifies the function that TSRUnit calls when the TSR is popped up. The last two parameters specify the default hot key combination that is pressed in order to pop up the TSR. The first of the hot key parameters contains a byte code that represents the shift keys that must be pressed while the key for the character specified in the last parameter is pressed. The shift key code can be any combination of the following constants: AltKey, CtrlKey, LeftKey, and RightKey. The constants stand for the Alt, Ctrl, Left Shift, and Right Shift keys, respectively. Combinations of two or more shift keys may be formed by using either the OR or + operators to combine the constants. The valid characters for the last parameter are 0-9 and A-Z.

Example TSR

The example program, TSRDEMO.PAS, is shown in Listing Two. Although Turbo Pascal normally defaults the stack size to 16K, the compiler memory directive may be used to set the stack and heap sizes. TSRDemo uses the compiler directive to reduce its memory requirements by specifying a 2K stack and no heap. To find the minimum stack size with which your program will work, simply run the program with different stack sizes. In order for the stack size check to be valid, test all of the program's features (unless you already know which feature of the program uses the most stack space). If you know the worst case, limit the testing to the worst case operations. Set the stack size for your program slightly higher than the minimum size for which the program worked, in order to allow for variations in stack usage that occur due to differences in versions of DOS and BIOS used and due to the presence of other TSR programs. Although you can reduce memory usage by specifying a stack that is only as large as necessary, do not confuse this step with avoiding using the stack altogether -- the use of the stack for local variables and for passing information is an efficient allocation of memory.

When TSRDemo is popped up, its routine, DemoTasks, is called from TSRUnit. DemoTasks creates a window on the screen, displays some instructions, and displays miscellaneous information about the computer system. It then echoes the keyboard input to the screen until a key that activates a demonstration of one of DemoTasks' special features is pressed. DemoTasks provides four special features: file accessing, printing, accessing the saved screen image, and inserting characters.

File Accessing

When the F1 key is pressed, the standard file-handling routines are used to write a short message to the TSRDEMO.DAT file. It is advisable to disable the compiler I/O error-checking directive, and to use the IOResult function to check for errors when files are accessed.

Printing

When F2 is pressed, DemoTasks performs the operations that are required in order to print a short message. Programs normally use the standard PRINTER unit to automatically assign, open, and close the printer device. The TSR installation process causes a printer device that was opened in this fashion to be closed. Therefore, the TSR program must treat the printer as a normal file device, and use the Assign, Rewrite, and Close procedures to open and close it.

Printing from a TSR poses a special hazard. If the TSR writes to the printer when the printer is not ready, an error occurs. When the standard IOResult function is used, the error is reported, but damage that may be caused by the error is not prevented. The consequence of this "device not ready" error is that the computer may crash when the TSR is popped back down. An easy way to prevent this problem is to always check the printer status before using the printer. TSRUnit provides two auxiliary functions for this purpose: PrinterOkay and PrinterStatus. PrinterOkay returns a TRUE value if the printer is selected, the printer has paper, and no I/O or time-out error has occurred. PrinterStatus returns the actual status byte for the printer.

Accessing Saved Screen Text

When F3 is pressed, the program prompts you for the row number of the saved screen text to be displayed. TSRUnit provides the pointer TSRScrPtr, which points to the saved screen image. In addition, TSRUnit provides two auxiliary routines that return the contents of one row of a text image. The function ScreenLineStr returns all of the characters contained on the line in a character string. The procedure ScreenLine returns an array of records, where each record contains a character and the character's display attribute.

Inserting Characters

DemoTasks' fourth special feature is the insertion of characters into the keyboard input stream. When F8 is pressed, you are prompted to specify the characters to be inserted. After you type the characters, the TSR pops back down and inserts them into the keyboard input stream. A useful application of this feature is to insert a result obtained from the TSR into a spreadsheet or word processor program.

In order for TSRUnit to insert the characters, the pointer TSRChrPtr must be set to point to the first character to be inserted, and the return value for DemoTasks must be set to the number of characters. The only restriction on the number of characters to be inserted is that all of the characters must reside in the same 64K segment of memory. Because the example program restricts the number of characters to 255, it stores the characters in string InsStr and puts the address of InsStr[1] in TSRChrPtr.

Overriding Default Hot Keys

To allow the users of TSRs to avoid hot key conflicts with other programs, TSRUnit permits the default hot key combination to be overridden when the TSR is installed. To replace the hot key combination, include some command-line parameters along with the TSR's program name. The format for the parameters is

[/A][/C][/R][/L][/"[K["]]]

The square brackets surround optional items. (Do not include the brackets.) Spaces or characters between the parameters are ignored and the parameters may be listed in any order. Although the shift key parameters (/A, /C,/R, and /L) are cumulative, only the last character key specified is used. The command to install TSRDemo with hot key combination Alt, Left Shift, T is

TSRDEMO A/L/"T

Caution: The Effects of Run-Time Errors

Although it is always good programming technique to take steps to prevent run-time errors from occurring, it is especially important to do so in a TSR program. Depending upon the error-handling methods used, the occurrence of an error can cause the execution of the TSR to be aborted. In some cases, execution will resume at the DOS prompt level. In more severe cases, the computer will "hang up" either immediately, or later when the TSR is popped back down.

Several approaches can be used to deal with run-time error situations. The example program TSRDemo illustrates two approaches. One of these methods is to replace Turbo Pascal's standard I/O operation error-checking code with code that simply displays an error message. (The default error-checking code terminates the program after displaying an error message.) The standard I/O error checking is disabled with the compiler directive {$I-}. As an example of a second type of error checking, TSRDemo uses TSRUnit's PrinterOkay function to verify that the printer is ready before writing to it. This approach can also be used to prevent numerical errors, such as divide-by-zero errors. A third method, which is too complicated to cover in this article, is to intercept the error handling routines.

One important category of errors that should be prevented from occurring is that of "device not ready" errors -- this type of error causes the computer to "hang up" either when the error occurs or when the TSR pops back down. The most likely cause for this type of error is an attempt to use either a diskette drive or a printer when it is not ready. To prevent either of these events from occurring, the example program takes the precaution of querying the user and checking the printer status.

Availability

All source code for articles in this issue is available on a single disk. To order, send $14.95 (Calif. residents add sales tax) to Dr. Dobb's Journal, 501 Galveston Dr., Redwood City, CA 94063, or call 800-356-2002 (from inside Calif.) or 800-533-4372 (from outside Calif.). Please specify the issue number and format (MS-DOS, Macintosh, Kaypro).

Creating TSRs with Turbo Pascal, Part II by Ken Pottebaum

[Listing One]


UNIT TSRUnit; {Create TSR programs with Turbo Pascal 5.0 & TSRUnit}
INTERFACE {=======================================================}
{
The author and any distributor of this software assume no responsi-
bility for damages resulting from this software or its use due to
errors, omissions, incompatibility with other software or with
hardware, or misuse; and specifically disclaim any implied warranty
of fitness for any particular purpose or application.
}
USES DOS, CRT;
CONST
{*** Shift key combination codes.                                 }
  AltKey = 8;  CtrlKey = 4;  LeftKey = 2;  RightKey = 1;

  TSRVersion : WORD = $0203;       {Low byte.High byte = 2.03     }

TYPE
  String80  = STRING[80];
  ChrWords  = RECORD CASE INTEGER OF
                  1: ( W: WORD );
                  2: ( C: CHAR; A: BYTE );
              END;
  LineWords = ARRAY[1..80] OF ChrWords;
  WordFuncs = FUNCTION : WORD;

VAR
  TSRScrPtr : POINTER; {Pointer to saved screen image.            }
  TSRChrPtr : POINTER; {Pointer to first character to insert.     }
  TSRMode   : BYTE;    {Video mode --------- before TSR popped up.}
  TSRWidth  : BYTE;    {Number of screen columns-- " "    "    " .}
  TSRPage   : BYTE;    {Active video page number-- " "    "    " .}
  TSRColumn : BYTE;    {Cursor column number ----- " "    "    " .}
  TSRRow    : BYTE;    {Cursor row number -------- " "    "    " .}
{
** Procedure for installing the TSR program.                      }
PROCEDURE TSRInstall( TSRName : STRING;   {Name or title for TSR. }
                      TSRFunc : WordFuncs;{Ptr to FUNCTION to call}
                      ShiftComb: BYTE;    {Hot key--shift key comb}
                      KeyChr   : CHAR );  {Hot Key--character key.}
{
  ShiftComb and KeyChr specify the default hot keys for the TSR.
  ShiftComb may be created by adding or ORing the constants AltKey,
  CtrlKey, LeftKey, and RightKey together.  KeyChr may be
  characters 0-9 and A-Z.

  The default hot keys may be overridden when the TSR is installed
  by specifying optional parameters on the command line.  The
  parameter format is:
                       [/A] [/C] [/R] [/L] [/"[K["]]]
  The square brackets surround optional items--do not include them.
  Any characters between parameters are ignored. The order of the
  characters does not matter; however, the shift keys specified are
  cummulative and the last character key "K" specified is the used.
}
{
** Functions for checking status of printer LPT1.                 }
FUNCTION PrinterOkay:   BOOLEAN; {Returns TRUE if printer is okay.}
FUNCTION PrinterStatus: BYTE;    {Returns status of printer.
  Definition of status byte bits (1 & 2 are not used), if set then:
 Bit: -- 7 ---  ---- 6 ----  -- 5 ---  -- 4 ---  -- 3 --  --- 0 ---
      Not busy  Acknowledge  No paper  Selected  I/O Err. Timed-out
}
{
** Routines for obtaining one row of screen characters.           }
FUNCTION ScreenLineStr( Row: BYTE ): String80; {Returns char. str.}
PROCEDURE ScreenLine( Row: BYTE; VAR Line: LineWords; {Returns    }
                                 VAR Words: BYTE );   {chr & color}





<a name="0121_000e"><a name="0121_000e">
[Listing Two]

PROGRAM TSRDemo;  {An example TSR program created using TSRUnit.   }

{$M $0800,0,0}   {Set stack and heap size for demo program.        }

USES CRT, DOS, TSRUNIT; {Specify the TSRUNIT in the USES statement.}
                        {Do not use the PRINTER unit, instead treat}
                        {the printer like a file; i.e. use the     }
                        {Assign, Rewrite, and Close procedures.    }

CONST  DemoPgmName : STRING[16] = 'TSR Demo Program';

VAR
  Lst      : TEXT;      {Define variable name for the printer.     }
  TextFile : TEXT;      {  "        "     "    "   a data file.    }
  InsStr   : STRING;    {Storage for characters to be inserted into}
                        {keyboard input stream--must be a gobal or }
                        {heap variable.                            }

FUNCTION IOError: BOOLEAN;    {Provides a message when an I/O error}
VAR  i : WORD;                {occurs.                             }
BEGIN
  i       := IOResult;
  IOError := FALSE;
  IF i <> 0 THEN BEGIN
    Writeln('I/O Error No. ',i);
    IOError := TRUE;
  END;
END;  {OurIOResult.}
{
***** Demo routine to be called when TSRDemo is popped up.
      be compiled as a FAR FUNCTION that returns a WORD containing
      the number of characters to insert into the keyboard input
      stream.
}
{$F+} FUNCTION DemoTasks: WORD; {$F-}
CONST
  FileName : STRING[13] = ' :TSRDemo.Dat';
  EndPos = 40;
  Wx1 = 15; Wy1 = 2;   Wx2 = 65; Wy2 = 23;
VAR
  Key, Drv          : CHAR;
  Done, IOErr       : BOOLEAN;
  InputPos, RowNumb : INTEGER;
  DosVer            : WORD;
  InputString       : STRING;

  PROCEDURE ClearLine; {Clears current line and resets line pointer}
  BEGIN
    InputString := '';     InputPos := 1;
    GotoXY( 1, WhereY );   ClrEol;
  END;

BEGIN
  DemoTasks   := 0;             {Default to 0 characters to insert.}
  Window( Wx1, Wy1, Wx2, Wy2 ); {Set up the screen display.        }
  TextColor( Black );
  TextBackground( LightGray );
  LowVideo;
  ClrScr;                      {Display initial messages.          }
  Writeln;
  Writeln('  Example Terminate & Stay-Resident (TSR) program');
  Writeln(' --written with Turbo Pascal 5.0 and uses TSRUnit.');
  Window( Wx1+1, Wy1+4, Wx2-1, Wy1+12);
  TextColor( LightGray );
  TextBackground( Black );
  ClrScr;                      {Display function key definitions.  }
  Writeln;
  Writeln('    Function key definitions:');
  Writeln('        [F1]  Write message to TSRDEMO.DAT');
  Writeln('        [F2]    "     "     to printer.');
  Writeln('        [F3]  Read from saved screen.');
  Writeln('        [F8]  Exit and insert text.');
  Writeln('        [F10] Exit TSR and keep it.');
  Write(  '        or simply echo your input.');

                               {Create active display window.      }
  Window( Wx1+1, Wy1+14, Wx2-1, Wy2-1 );
  ClrScr;
                               {Display system information.        }
  Writeln('TSRUnit Version: ', Hi(TSRVersion):8, '.',
                               Lo(TSRVersion):2 );
  Writeln('Video Mode, Page:', TSRMode:4, TSRPage:4 );
  Writeln('Cursor Row, Col.:', TSRRow:4, TSRColumn:4 );

  DosVer := DosVersion;
  Writeln('DOS Version:     ', Lo(DosVer):8, '.', Hi(DosVer):2 );

  InputString := '';          {Initialize variables.               }
  InputPos    := 1;
  Done        := False;

  REPEAT                      {Loop for processing keystrokes.     }
    GotoXY( InputPos, WhereY );    {Move cursor to input position. }
    Key := ReadKey;                {Wait for a key to be pressed.  }
    IF Key = #0 THEN BEGIN         {Check for a special key.       }
      Key := ReadKey;              {If a special key, get auxiliary}
      CASE Key OF                  {byte to identify key pressed.  }

{Cursor Keys and simple editor.}
{Home}  #71: InputPos := 1;
{Right} #75: IF InputPos > 1 THEN Dec( InputPos );
{Left}  #77: IF (InputPos < Length( InputString ))
                OR ((InputPos = Length( InputString ))
                    AND (InputPos < EndPos )) THEN Inc( InputPos );
{End}   #79: BEGIN
               InputPos := Succ( Length( InputString ) );
               IF InputPos > EndPos THEN InputPos := EndPos;
             END;
{Del}   #83: BEGIN
               Delete( InputString, InputPos, 1 );
               Write( Copy( InputString, InputPos, EndPos ), ' ');
             END;

{Function Keys--TSRDemo's special features.}
{F1}    #59: BEGIN                 {Write short message to a file. }
               ClearLine;
               REPEAT
                 Write('Enter disk drive:  ',FileName[1] );
                 Drv := UpCase( ReadKey );  Writeln;
                 IF Drv <> #13 THEN FileName[1] := Drv;
                 Writeln('Specifying an invalid drive will cause your');
                 Write('system to crash.  Use drive ',
                        FileName[1], ': ?  [y/N] ');
                 Key := UpCase( ReadKey );  Writeln( Key );
               UNTIL Key = 'Y';
               Writeln('Writing to ',FileName );
               {$I-}                         {Disable I/O checking.}
               Assign( TextFile, 'TSRDemo.Dat' );
               IF NOT IOError THEN BEGIN     {Check for error.     }
                 Rewrite( TextFile );
                 IF NOT IOError THEN BEGIN
                   Writeln(TextFile,'File was written by TSRDemo.');
                   IOErr := IOError;
                   Close( TextFile );
                   IOErr := IOError;
                 END;
               END;
               {$I+}                 {Enable standard I/O checking.}
               Writeln('Completed file operation.');
             END;  {F1}

{F2}    #60: BEGIN {Print a message, use TSRUnit's auxiliary       }
                   {function PrinterOkay to check printer status.  }
               ClearLine;
               Writeln('Check printer status, then print if okay.');
               IF PrinterOkay THEN BEGIN  {Check if printer is okay}
                 Assign( Lst, 'LPT1' );   {Define printer device.  }
                 Rewrite( Lst );          {Open printer.           }
                 Writeln( Lst, 'Printing performed from TSRDemo');
                 Close( Lst );            {Close printer.          }
               END
               ELSE Writeln('Printer is not ready.');
               Writeln( 'Completed print operation.' );
             END;  {F2}

{F3}    #61: BEGIN {Display a line from the saved screen image--not}
                   {valid if the TSR was popped up while the       }
                   {display was in a graphics mode.                }
               ClearLine;
               CASE TSRMode OF    {Check video mode of saved image.}
                 0..3,
                 7: BEGIN
                      {$I-}
                      REPEAT
                        Writeln('Enter row number [1-25] from ');
                        Write('which to copy characters:  ');
                        Readln( RowNumb );
                      UNTIL NOT IOError;
                      {$I+}
                      IF RowNumb <= 0 THEN RowNumb := 1;
                      IF RowNumb > 25 THEN RowNumb := 25;
                      Writeln( ScreenLineStr( RowNumb ) );
                    END;
               ELSE Writeln('Not valid for graphics modes.');
               END;  {CASE TSRMode}
             END;  {F3}
{F8}    #66: BEGIN {Exit and insert string into keyboard buffer.}
               ClearLine;
               Writeln('Enter characters to insert;');
               Writeln('Up to 255 character may be inserted.');
               Writeln('Terminate input string by pressing [F8].');
               InsStr := '';
               REPEAT                     {Insert characters into a}
                 Key := ReadKey;          {until [F8] is pressed.  }
                 IF Key = #0 THEN BEGIN     {Check for special key.}
                   Key := ReadKey;          {Check if key is [F8]. }
                   IF Key = #66 THEN Done := TRUE; {[F8] so done.  }
                 END
                 ELSE BEGIN {Not special key, add it to the string.}
                   IF Length(InsStr) < Pred(SizeOf(InsStr)) THEN
                   BEGIN
                     IF Key = #13 THEN Writeln
                     ELSE Write( Key );
                     InsStr := InsStr + Key;
                   END
                   ELSE Done := TRUE; {Exceeded character limit.   }
                 END;
               UNTIL Done;
               DemoTasks := Length( InsStr );  {Return no. of chr. }
               TSRChrPtr := @InsStr[1];        {Set ptr to 1st chr.}
             END;  {F8}

{F10}   #68: Done := TRUE; {Exit and Stay-Resident.                }

      END;  {CASE Key}
    END  {IF Key = #0}
    ELSE BEGIN   {Key pressed was not a special key--just echo it. }
      CASE Key OF
{BS}    #08: BEGIN  {Backspace}
               IF InputPos > 1 THEN BEGIN
                 Dec( InputPos );
                 Delete( InputString, InputPos, 1 );
                 GotoXY( InputPos, WhereY );
                 Write( Copy( InputString, InputPos, EndPos ), ' ');
               END;
             END;  {BS}
{CR}    #13: BEGIN  {Enter}
               Writeln;
               InputString := '';
               InputPos    := 1;
             END;  {CR}
{Esc}   #27: ClearLine;
      ELSE
        IF Length( InputString ) >= EndPos THEN
          Delete( InputString, EndPos, 1 );
        Insert( Key, InputString, InputPos );
        Write( Copy( InputString, InputPos, EndPos ) );
        IF InputPos < EndPos THEN
          Inc( InputPos );
      END;  {CASE...}
    END;  {ELSE BEGIN--Key <> #0}
    UNTIL Done;
END;  {DemoTasks.}

BEGIN
  TSRInstall( DemoPgmName, DemoTasks, AltKey, 'E' );
END.  {TSRDemo.}





<a name="0121_000f"><a name="0121_000f">
[Listing One from May, 1989]

UNIT TSRUnit; {Create TSR programs with Turbo Pascal 5.0 & TSRUnit}
(*
The author and any distributor of this software assume no responsi-
bility for damages resulting from this software or its use due to
errors, omissions, incompatibility with other software or with
hardware, or misuse; and specifically disclaim any implied warranty
of fitness for any particular purpose or application.
*)

{$B-,F-,I+,R-,S+} {Set compiler directives to normal values.}

INTERFACE {=======================================================}
USES DOS, CRT;
CONST
{*** Shift key combination codes.                                 }
  AltKey = 8;  CtrlKey = 4;  LeftKey = 2;  RightKey = 1;

  TSRVersion : WORD = $0202;       {Low byte.High byte = 2.02     }

TYPE
  String80  = STRING[80];
  ChrWords  = RECORD CASE INTEGER OF
                  1: ( W: WORD );
                  2: ( C: CHAR; A: BYTE );
              END;
  LineWords = ARRAY[1..80] OF ChrWords;

VAR
  TSRScrPtr : POINTER; {Pointer to saved screen image.            }
  TSRChrPtr : POINTER; {Pointer to first character to insert.     }
  TSRMode   : BYTE;    {Video mode --------- before TSR popped up.}
  TSRWidth  : BYTE;    {Number of screen columns-- " "    "    " .}
  TSRPage   : BYTE;    {Active video page number-- " "    "    " .}
  TSRColumn : BYTE;    {Cursor column number ----- " "    "    " .}
  TSRRow    : BYTE;    {Cursor row number -------- " "    "    " .}
{
** Procedure for installing the TSR program.                      }
PROCEDURE TSRInstall( PgmName,            {Ptr to a char. string. }
                      PgmPtr   : POINTER; {Ptr to FUNCTION to call}
                      ShiftComb: BYTE;    {Hot key--shift key comb}
                      KeyChr   : CHAR );  {Hot Key--character key.}
{
  ShiftComb and KeyChr specify the default hot keys for the TSR.
  ShiftComb may be created by adding or ORing the constants AltKey,
  CtrlKey, LeftKey, and RightKey together.  KeyChr may be
  characters 0-9 and A-Z.

  The default hot keys may be overridden when the TSR is installed
  by specifying optional parameters on the command line.  The
  parameter format is:
                       [/A] [/C] [/R] [/L] [/"[K["]]]
  The square brackets surround optional items--do not include them.
  Any characters between parameters are ignored. The order of the
  characters does not matter; however, the shift keys specified are
  cummulative and the last character key "K" specified is the used.
}
{
** Functions for checking status of printer LPT1.                 }
FUNCTION PrinterStatus: BYTE;    {Returns status of printer.      }
FUNCTION PrinterOkay:   BOOLEAN; {Returns TRUE if printer is okay.}

{
** Routines for obtaining one row of screen characters.           }
FUNCTION ScreenLineStr( Row: BYTE ): String80; {Returns char. str.}
PROCEDURE ScreenLine( Row: BYTE; VAR Line: LineWords; {Returns    }
                                 VAR Words: BYTE );   {chr & color}

IMPLEMENTATION {==================================================}
VAR
  BuffSize, InitCMode : WORD;
  NpxFlag             : BOOLEAN;
  Buffer              : ARRAY[0..8191] OF WORD;
  NpxState            : ARRAY[0..93] OF BYTE;
  RetrnVal, InitVideo : BYTE;

CONST    {Offsets to items contained in PROCEDURE Asm.            }
  UnSafe = 0;    Flg   = 1;     Key     = 2;     Shft  = 3;
  StkOfs = 4;    StkSs = 6;     DosSp   = 8;     DosSs = 10;
  Prev  = 12;    Flg9  = 13;    InsNumb = 14;
  Dos21 = $10;         Dos25  = Dos21+4;      Dos26  = Dos25+4;
  Bios9 = Dos26+4;     Bios16 = Bios9+4;      DosTab = Bios16+4;
  Our21 = DosTab+99;   Our25  = Our21+51;     Our26  = Our25+24;
  Our09 = Our26+24;    Our16  = Our09+127+8;  InsChr = Our16+180-8;
  PopUp = InsChr+4;    Pgm    = PopUp+4;

PROCEDURE Asm; {Inline code--data storage and intercept routines. }
INTERRUPT;
BEGIN
INLINE(
{***  Storage for interrupt vectors.                              }
      {Dos21:  }  >0/>0/    {DOS func. intr vector.               }
      {Dos25:  }  >0/>0/    {DOS abs. disk read intr. vector.     }
      {Dos26:  }  >0/>0/    {DOS abs. sector write intr.vector.   }
      {Bios9:  }  >0/>0/    {BIOS key stroke intr. vector.        }
      {Bios16: }  >0/>0/    {BIOS buffered keybd. input intr.vect.}

      {DosTab: ARRAY[0..98] OF BYTE = {Non-reetrant DOS functions.}
      0/0/0/0/0/0/0/0/  0/0/0/0/0/1/1/1/  1/1/1/1/1/1/1/1/
      1/1/1/1/1/1/1/1/  1/1/1/1/1/1/0/1/  1/1/1/1/1/1/1/0/
      1/0/0/0/0/0/1/1/  1/1/1/1/1/1/1/1/  1/1/1/1/1/1/1/1/
      0/0/0/0/0/0/1/1/  0/0/0/0/1/0/1/1/  0/1/1/1/1/0/0/0/  0/0/0/

{*** OurIntr21 ******* Intercept routine for DOS Function Intr.***}
{  0} $9C/               { PUSHF            ;Save flags.          }
{  1} $FB/               { STI              ;Enable interrupts.   }
{  2} $80/$FC/$63/       { CMP  AH,63H      ;Assume unsafe if new }
{  5} $73/<22-7/         { JNB  IncF        ;function--skip table.}
{  7} $50/               { PUSH AX          ;Save registers.      }
{  8} $53/               { PUSH BX          ;Load offset to table.}
{  9} $BB/>DosTab/       { MOV  BX,[DosTab]                       }
{ 12} $8A/$C4/           { MOV  AL,AH       ;Load table entry     }
{ 14} $2E/               { CS:              ;index.               }
{ 15} $D7/               { XLAT             ;Get value from table.}
{ 16} $3C/$00/           { CMP  AL,0        ;If TRUE then set flag}
{ 18} $5B/               { POP  BX          ;Restore registers.   }
{ 19} $58/               { POP  AX          ;                     }
{ 20} $74/$17/           { JZ   JmpDos21    ;Jump to orig. intr.  }
{ 22} $2E/          {IncF: CS:              ;                     }
{ 23} $FE/$06/>UnSafe/   { INC  [UnSafe]    ;Set UnSafe flag.     }
{ 27} $9D/               { POPF             ;Restore flags.       }
{ 28} $9C/               { PUSHF            ;                     }
{ 29} $2E/               { CS:              ;                     }
{ 30} $FF/$1E/>Dos21/    { CALL FAR [Dos21] ;Call orig. intr.     }
{ 34} $FB/               { STI              ;Enable interrupts.   }
{ 35} $9C/               { PUSHF            ;Save flags.          }
{ 36} $2E/               { CS:              ;                     }
{ 37} $FE/$0E/>UnSafe/   { DEC  [UnSafe]    ;Clear UnSafe flag.   }
{ 41} $9D/               { POPF             ;Restore flags.       }
{ 42} $CA/$02/$00/       { RETF 2           ;Return & remove flag.}

{ 45} $9D/      {JmpDos21: POPF             ;Restore flags.       }
{ 46} $2E/               { CS:              ;                     }
{ 47} $FF/$2E/>Dos21/    { JMP FAR [Dos21]  ;Jump to orig. intr.  }
{ 51}
{*** OurIntr25 ********** Intercept routine for DOS Abs. Read *** }
{  0} $9C/               { PUSHF            ;Save flags.          }
{  1} $2E/               { CS:              ;                     }
{  2} $FE/$06/>UnSafe/   { INC  [UnSafe]    ;Set UnSafe flag.     }
{  6} $9D/               { POPF             ;Restore flags.       }
{  7} $9C/               { PUSHF            ;                     }
{  8} $2E/               { CS:              ;                     }
{  9} $FF/$1E/>Dos25/    { CALL FAR [Dos25] ;Call DOS abs. read.  }
{ 13} $83/$C4/$02/       { ADD  SP,2        ;Clean up stack.      }
{ 16} $9C/               { PUSHF            ;Save flags.          }
{ 17} $2E/               { CS:              ;                     }
{ 18} $FE/$0E/>UnSafe/   { DEC  [UnSafe]    ;Clear UnSafe flag.   }
{ 22} $9D/               { POPF             ;Restore flags.  Leave}

{ 23} $CB/               { RETF             ;old flags on the stk.}
{ 24}
{*** OurIntr26 ********** Intercept routine for DOS Abs. Write ***}
{  0} $9C/               { PUSHF            ;Save flags.          }
{  1} $2E/               { CS:              ;                     }
{  2} $FE/$06/>UnSafe/   { INC  [UnSafe]    ;Set UnSafe flag.     }
{  6} $9D/               { POPF             ;Restore flags.       }
{  7} $9C/               { PUSHF            ;                     }
{  8} $2E/               { CS:              ;                     }
{  9} $FF/$1E/>Dos26/    { CALL FAR [Dos26] ;Call DOS abs. write. }
{ 13} $83/$C4/$02/       { ADD  SP,2        ;Clean up stack.      }
{ 16} $9C/               { PUSHF            ;Save flags.          }
{ 17} $2E/               { CS:              ;                     }
{ 18} $FE/$0E/>UnSafe/   { DEC  [UnSafe]    ;Clear UnSafe flag.   }
{ 22} $9D/               { POPF             ;Restore flags.  Leave}
{ 23} $CB/               { RETF             ;old flags on the stk.}
{ 24}

{*** OurIntr9 ********** Intercept for BIOS Hardware Keyboard Intr}
{  0} $9C/               { PUSHF            ;Entry point.         }
{  1} $FB/               { STI              ;Enable interrupts.   }
{  2} $1E/               { PUSH DS          ;                     }
{  3} $0E/               { PUSH CS          ;DS := CS;            }
{  4} $1F/               { POP  DS          ;                     }
{  5} $50/               { PUSH AX          ;Preserve AX on stack.}
{  6} $31/$C0/           { XOR  AX,AX       ;Set AH to 0.         }
{  8} $E4/$60/           { IN   AL,60h      ;Read byte from keybd }
{ 10} $3C/$E0/           { CMP  AL,0E0h     ;If multi-byte codes, }
{ 12} $74/<75-14/        { JE   Sfx         ;then jump and set    }
{ 14} $3C/$F0/           { CMP  AL,0F0h     ;multi-byte flag, Flg9}
{ 16} $74/<75-18/        { JE   Sfx         ;                     }
{ 18} $80/$3E/>Flg9/$00/ { CMP  [Flg9],0    ;Exit if part of      }
{ 23} $75/<77-25/        { JNZ  Cfx         ;multi-byte code.     }
{ 25} $3A/$06/>Key/      { CMP  AL,[Key]    ;Exit if key pressed  }
{ 29} $75/<88-31/        { JNE  PreExit     ;is not hot key.      }

{ 31} $50/               { PUSH AX          ;Hot key was pressed, }
{ 32} $06/               { PUSH ES          ;check shift key      }
{ 33} $B8/$40/$00/       { MOV  AX,0040h    ;status byte.  First  }
{ 36} $8E/$C0/           { MOV  ES,AX       ;load BIOS segment.   }
{ 38} $26/               { ES:              ;                     }
{ 39} $A0/>$0017/        { MOV  AL,[0017h]  ;AL:= Shift key status}
{ 42} $07/               { POP  ES          ;Restore ES register. }
{ 43} $24/$0F/           { AND  AL,0Fh      ;Clear unwanted bits. }
{ 45} $3A/$06/>Shft/     { CMP  AL,[Shft]   ;Exit if not hot key  }
{ 49} $58/               { POP  AX          ;shift key combination}
{ 50} $75/<88-52/        { JNE  PreExit     ;(Restore AX first).  }

                         {                  ;Hot Keys encountered.}
{ 52} $3A/$06/>Prev/     { CMP  AL,[Prev]   ;Discard repeated hot }
{ 56} $74/<107-58/       { JE   Discard     ;key codes.           }
{ 58} $A2/>Prev/         { MOV  [Prev],AL   ;Update Prev.         }
{ 61} $F6/$06/>Flg/3/    { TEST [Flg],3     ;If Flg set, keep key }
{ 66} $75/<99-68/        { JNZ  JmpBios9    ;& exit to orig. BIOS }
{ 68} $80/$0E/>Flg/1/    { OR   [Flg],1     ;9.  Else set flag and}
{ 73} $EB/<107-75/       { JMP SHORT Discard;discard key stroke.  }

{ 75} $B4/$01/       {Sfx: MOV  AH,1        ;Load AH with set flag}
{ 77} $88/$26/>Flg9/ {Cfx: MOV  [Flg9],AH   ;Save multi-byte flag.}
{ 81} $C6/$06/>Prev/$FF/ { MOV  [Prev],0FFh ;Change prev key byte.}
{ 86} $EB/<99-88/        { JMP SHORT JmpBios9                     }

{ 88} $3C/$FF/   {PreExit: CMP  AL,0FFh     ;Update previous key  }
{ 90} $74/<99-92/        { JE   JmpBios9    ;unless key is buffer-}
{ 92} $3C/$00/           { CMP  AL,0        ;full code--a 00h     }
{ 94} $74/<99-96/        { JZ   JmpBios9    ;0FFh                 }
{ 96} $A2/>Prev/         { MOV [Prev],AL    ;Update previous key. }

{ 99} $58/      {JmpBios9: POP  AX          ;Restore registers and}
{100} $1F/               { POP  DS          ;flags.               }
{101} $9D/               { POPF             ;                     }
{102} $2E/               { CS:              ;                     }
{103} $FF/$2E/>Bios9/    { JMP  [Bios9]     ;Exit to orig. intr 9.}

{107} $E4/$61/   {Discard: IN   AL,61h      ;Clear key from buffer}
{109} $8A/$E0/           { MOV  AH,AL       ;by resetting keyboard}
{111} $0C/$80/           { OR   AL,80h      ;port and sending EOI }
{113} $E6/$61/           { OUT  61h,AL      ;to intr. handler     }
{115} $86/$E0/           { XCHG AH,AL       ;telling it that the  }
{117} $E6/$61/           { OUT  61h,AL      ;key has been         }
{119} $B0/$20/           { MOV  AL,20h      ;processed.           }
{121} $E6/$20/           { OUT  20h,AL      ;                     }
{123} $58/               { POP  AX          ;Restore registers and}
{124} $1F/               { POP  DS          ;flags.               }
{125} $9D/               { POPF             ;                     }
{126} $CF/               { IRET             ;Return from interrupt}
{127}

{*** OurIntr16 ***** Intercept routine for Buffered Keyboard Input}
{  0} $58/     {JmpBios16: POP  AX          ;Restore AX, DS, and  }
{  1} $1F/               { POP  DS          ;FLAGS registers then }
{  2} $9D/               { POPF             ;exit to orig. BIOS   }
{  3} $2E/               { CS:              ;intr. 16h routine.   }
{  4} $FF/$2E/>Bios16/   { JMP  [Bios16]    ;                     }

{  8} $9C/     {OurIntr16: PUSHF            ;Preserve FLAGS.      }
{  9} $FB/               { STI              ;Enable interrupts.   }
{ 10} $1E/               { PUSH DS          ;Preserve DS and AX   }
{ 11} $50/               { PUSH AX          ;registers.           }
{ 12} $0E/               { PUSH CS          ;DS := CS;            }
{ 13} $1F/               { POP  DS          ;                     }
{ 14} $F6/$C4/$EF/       { TEST AH,EFh      ;Jmp if not read char.}
{ 17} $75/<48-19/        { JNZ  C3          ;request.             }

                         {*** Intercept loop for Read Key service.}
{ 19} $F6/$06/>Flg/1/ {C1: TEST [Flg],1     ;If pop up Flg bit is }
{ 24} $74/<29-26/        { JZ   C2          ;set then call INLINE }
{ 26} $E8/>122-29/       { CALL ToPopUp     ;pop up routine.      }
{ 29} $F6/$06/>Flg/16/{C2: TEST [Flg],10h   ;Jmp if insert flg set}
{ 34} $75/<48-36/        { JNZ  C3          ;                     }
{ 36} $FE/$C4/           { INC  AH          ;Use orig. BIOS       }
{ 38} $9C/               { PUSHF            ;service to check for }
{ 39} $FA/               { CLI              ;character ready.     }
{ 40} $FF/$1E/>Bios16/   { CALL FAR [Bios16];Disable interrupts.  }
{ 44} $58/               { POP  AX          ;Restore AX and save  }
{ 45} $50/               { PUSH AX          ;it again.            }
{ 46} $74/<19-48/        { JZ   C1          ;Loop until chr. ready}

{ 48} $F6/$06/>Flg/17/{C3: TEST [Flg],11h   ;Exit if neither bit  }
{ 53} $74/<-55/          { JZ   JmpBios16   ;of Flg is set.       }
{ 55} $F6/$06/>Flg/$01/  { TEST [Flg],1     ;If pop up Flg bit is }
{ 60} $74/<65-62/        { JZ   C4          ;set then call INLINE }
{ 62} $E8/>122-65/       { CALL ToPopUp     ;pop up routine.      }
{ 65} $F6/$06/>Flg/$10/{C4:TEST [Flg],10h   ;Exit unless have     }
{ 70} $74/<-72/          { JZ   JmpBios16   ;characters to insert.}
{ 72} $F6/$C4/$EE/       { TEST AH,0EEh     ;If request is not a  }
{ 75} $75/<-77/          { JNZ  JmpBios16   ;chr. request, exit.  }

                         {*** Insert a character.                 }
{ 77} $58/               { POP  AX          ;AX := BIOS service no}
{ 78} $53/               { PUSH BX          ;Save BX and ES.      }
{ 79} $06/               { PUSH ES          ;                     }
{ 80} $C4/$1E/>InsChr/   { LES  BX,[InsChr] ;PTR(ES,BX) := InsChr;}
{ 84} $26/               { ES:              ;AL := InsChr^;       }
{ 85} $8A/$07/           { MOV  AL,[BX]     ;                     }
{ 87} $07/               { POP  ES          ;Restore ES and BX.   }
{ 88} $5B/               { POP  BX          ;                     }
{ 89} $F6/$C4/$01/       { TEST AH,01h      ;IF AH IN [$01,$11]   }
{ 92} $B4/$00/           { MOV  AH,00h      ;   THEN ReportOnly;  }
{ 94} $75/<114-96/       { JNZ  ReportOnly  ;Set Scan code to 0.  }
{ 96} $FE/$06/>InsChr/   { INC  [InsChr]    ;Inc( InsChr );       }
{100} $FF/$0E/>InsNumb/  { DEC  [InsNumb]   ;Dec( InsNumb );      }
{104} $75/<111-106/      { JNZ  SkipReset   ;IF InsNumb = 0 THEN  }
{106} $80/$26/>Flg/$EF/  { AND  [Flg],0EFh  ; Clear insert chr flg}
{111} $1F/     {SkipReset: POP  DS          ;Restore BX, DS, and  }
{112} $9D/               { POPF             ;FLAGS, then return   }
{113} $CF/               { IRET             ;from interrupt.      }

{114} $1F/    {ReportOnly: POP  DS          ;Report char. ready.  }
{115} $9D/               { POPF             ;Restore DS and FLAGS.}
{116} $50/               { PUSH AX          ;Clear zero flag bit  }
{117} $40/               { INC  AX          ;to indicate a        }
{118} $58/               { POP  AX          ;character ready.     }
{119} $CA/>0002/         { RETF 2           ;Exit & discard FLAGS }

                         {*** Interface to PopUpCode Routine.     }
{122} $50/       {ToPopUp: PUSH AX          ;Save AX.             }
{123} $FA/               { CLI              ;Disable interrupts.  }
{124} $F6/$06/>UnSafe/$FF/{TEST [UnSafe],0FFh ;IF UnSafe <> 0     }
{129} $75/<177-131/      { JNZ  PP2           ;      THEN Return. }
{131} $A0/>Flg/          { MOV  AL,[Flg]    ;Set in-use bit; clear}
{134} $24/$FE/           { AND  AL,0FEh     ;pop up bit of Flg.   }
{136} $0C/$02/           { OR   AL,2        ;Flg := (Flg AND $FE) }
{138} $A2/>Flg/          { MOV  [Flg],AL    ;        OR 2;        }
                         {                  ;**Switch to our stack}
{141} $A1/>StkOfs/       { MOV  AX,[StkOfs] ;Load top of our stack}
{144} $87/$C4/           { XCHG AX,SP       ;Exchange it with     }
{146} $A3/>DosSp/        { MOV  [DosSp],AX  ;stk.ptr, save old SP.}
{149} $8C/$16/>DosSs/    { MOV  [DosSs],SS  ;Save old SS.         }
{153} $8E/$16/>StkSs/    { MOV  SS,[StkSs]  ;Replace SS with our  }
{157} $FB/               { STI              ;SS. Enable interrupts}

{158} $9C/               { PUSHF            ;Interrupt call to pop}
{159} $FF/$1E/>PopUp/    { CALL FAR [PopUp] ;up TSR routine.      }

{163} $FA/               { CLI              ;Disable interrupts.  }
{164} $8B/$26/>DosSp/    { MOV  SP,[DosSp]  ;Restore stack ptr    }
{168} $8E/$16/>DosSs/    { MOV  SS,[DosSs]  ;SS:SP.  Clear in-use }
{172} $80/$26/>Flg/$FD/  { AND  [Flg],0FDh  ;bit of Flg.          }

{177} $FB/           {PP2: STI              ;Enable interrupts.   }
{178} $58/               { POP  AX          ;Restore AX.          }
{179} $C3 );             { RET              ;Return.              }
{180}
END; {Asm.} {END corresponds to 12 bytes of code--used for storage}

PROCEDURE PopUpCode; {Interface between the BIOS intercept        }
INTERRUPT;           {routines and your TSR function.             }
CONST  BSeg = $0040;   VBiosOfs = $49;
TYPE
  VideoRecs = RECORD
                VideoMode                      : BYTE;
                NumbCol, ScreenSize, MemoryOfs : WORD;
                CursorArea      : ARRAY[0..7] OF WORD;
                CursorMode                     : WORD;
                CurrentPage                    : BYTE;
                VideoBoardAddr                 : WORD;
                CurrentMode, CurrentColor      : BYTE;
              END;
VAR
  Regs         : Registers;
  VideoRec     : VideoRecs;
  KeyLock      : BYTE;
  ScrnSeg      : WORD;
BEGIN
  SwapVectors;                            {Set T.P. intr. vectors.}
  Move( Ptr(BSeg,VBiosOfs)^, VideoRec,    {Get Video BIOS info.   }
        SizeOf(VideoRec) );
  WITH VideoRec, Regs DO BEGIN
    IF (VideoMode > 7) OR                  {Abort pop up if unable}
       (ScreenSize > BuffSize) THEN BEGIN  {to save screen image. }
      SwapVectors;                         {Restore intr. vectors.}
      Exit;
    END;
    KeyLock := Mem[BSeg:$0017];            {Save lock key states. }
    IF VideoMode = 7 THEN ScrnSeg := $B000 {Save screen--supports }
    ELSE ScrnSeg := $B800;                 {text, MGA & CGA modes.}
    Move( PTR( ScrnSeg, MemoryOfs )^, Buffer, ScreenSize );
    AX := InitVideo;                       {If in graphics mode,  }
    IF (VideoMode >=4)                     {switch to text mode.  }
       AND (VideoMode <= 6) THEN Intr( $10, Regs );
    AX := $0500;                           {Select display page 0.}
    Intr( $10, Regs );
    CX := InitCMode;                       {Set cursor size.      }
    AH := 1;
    Intr( $10, Regs );

    TSRMode   := VideoMode;              {Fill global variables   }
    TSRWidth  := NumbCol;                {with current information}
    TSRPage   := CurrentPage;
    TSRColumn := Succ( Lo( CursorArea[CurrentPage] ) );
    TSRRow    := Succ( Hi( CursorArea[CurrentPage] ) );

    IF NpxFlag THEN                      {Save co-processor state.}
      INLINE( $98/ $DD/$36/>NpxState );  {WAIT FSAVE [NpxState]   }
{
*** Call user's program and save return code--no. char. to insert.
}
    INLINE( $2E/$FF/$1E/>Pgm/   { CALL FAR CS:[Pgm]   }
            $2E/$A3/>InsNumb ); { MOV CS:[InsNumb],AX }

    IF Mem[CSeg:InsNumb] > 0 THEN BEGIN  {Have char. to insert.   }
      MemL[CSeg:InsChr] := LONGINT( TSRChrPtr );
      Mem[CSeg:Flg]     := Mem[CSeg:Flg] OR $10;
    END;
{
*** Pop TSR back down--Restore computer to previous state.
}
    IF NpxFlag THEN                      {Restore co-prcssr state.}
      INLINE( $98/ $DD/$36/>NpxState );  {WAIT FSAVE [NpxState]   }

    Mem[BSeg:$17] :=                     {Restore key lock status.}
      (Mem[BSeg:$17] AND $0F) OR (KeyLock AND $F0);

    IF Mem[BSeg:VBiosOfs] <> VideoMode THEN BEGIN
      AX := VideoMode;                   {Restore video mode.     }
      Intr( $10, Regs );
    END;
    AH := 1;  CX := CursorMode;          {Restore cursor size.    }
    Intr( $10, Regs );
    AH := 5;  AL := CurrentPage;         {Restore active page.    }
    Intr( $10, Regs );
    AH := 2;  BH := CurrentPage;         {Restore cursor positon. }
    DX := CursorArea[CurrentPage];
    Intr( $10, Regs );                   {Restore screen image.   }
    Move( Buffer, PTR( ScrnSeg, MemoryOfs )^, ScreenSize );

    SwapVectors;                        {Restore non-T.P. vectors.}
  END;
END;  {PopUp.}
{
***** Printer Functions:
}
FUNCTION PrinterStatus: BYTE;             {Returns status of LPT1.}
{ Definition of status byte bits (1 & 2 are not used), if set then:
 Bit: -- 7 ---  ---- 6 ----  -- 5 ---  -- 4 ---  -- 3 --  --- 0 ---
      Not busy  Acknowledge  No paper  Selected  I/O Err. Timed-out
}
VAR Regs  : Registers;
BEGIN
  WITH Regs DO BEGIN
    AH := 2;  DX := 0;    {Load BIOS function and printer number. }
    Intr( $17, Regs );    {Call BIOS printer services.            }
    PrinterStatus := AH;  {Return with printer status byte.       }
  END;
END; {PrinterStatus.}

FUNCTION PrinterOkay: BOOLEAN;  {Returns TRUE if printer is okay. }
VAR  S : BYTE;
BEGIN
  S := PrinterStatus;
  IF ((S AND $10) <> 0) AND ((S AND $29) = 0) THEN
    PrinterOkay := TRUE
  ELSE PrinterOkay := FALSE;
END;  {PrinterOkay.}
{
***** Procedures to obtain contents of saved screen image.
}
PROCEDURE ScreenLine( Row: BYTE; VAR Line: LineWords;
                                 VAR Words: BYTE );
BEGIN
  Words := 40;                        {Determine screen line size.}
  IF TSRMode > 1 THEN  Words := Words*2;          {Get line's     }
  Move( Buffer[Pred(Row)*Words], Line, Words*2 ); {characters and }
END;  {ScreenLine.}                               {colors.        }

FUNCTION ScreenLineStr( Row: BYTE ): String80; {Returns just chars}
VAR
  Words, i   : BYTE;
  LineWord   : LineWords;
  Line       : String80;
BEGIN
  ScreenLine( Row, LineWord, Words );   {Get chars & attributes.  }
  Line := '';                           {Move characters to string}
  FOR i := 1 TO Words DO Insert( LineWord[i].C, Line, i );
  ScreenLineStr := Line;
END;  {ScreenString.}
{
***** TSR Installation procedure.
}
PROCEDURE TSRInstall( PgmName, PgmPtr: POINTER;
                      ShiftComb: BYTE; KeyChr: CHAR );
CONST
  ScanChr = '+1234567890++++QWERTYUIOP++++ASDFGHJKL+++++ZXCVBNM';
  CombChr = 'RLCA"';
VAR
  PgmNamePtr, PlistPtr: ^STRING;
  i, j, k             : WORD;
  Regs                : Registers;
  Comb, ScanCode      : BYTE;
BEGIN
  IF Ofs( Asm ) <> 0 THEN EXIT;           {Offset of Asm must be 0}
  MemW[CSeg:StkSs]  := SSeg;              {Save pointer to top of }
  MemW[CSeg:StkOfs] := Sptr + 308;        {TSR's stack.           }
  MemL[CSeg:PopUp]  := LONGINT(@PopUpCode); {Save PopUpCode addr. }
  MemL[CSeg:Pgm]    := LONGINT(PgmPtr);     {Save PgmPtr.         }
  PgmNamePtr        := PgmName;        {Convert ptr to string ptr.}

  Writeln('Installing Stay-Resident program: ',PgmNamePtr^);
{
*****  Save intercepted interrupt vectors: $09, $16, $21, $25, $26.
}
  GetIntVec( $09, POINTER( MemL[CSeg:Bios9] ) );
  GetIntVec( $16, POINTER( MemL[CSeg:Bios16] ) );
  GetIntVec( $21, POINTER( MemL[CSeg:Dos21] ) );
  GetIntVec( $25, POINTER( MemL[CSeg:Dos25] ) );
  GetIntVec( $26, POINTER( MemL[CSeg:Dos26] ) );
{
***** Get equipment list and video mode.
}
  WITH Regs DO BEGIN
    Intr( $11, Regs );                  {Check equipment list for }
    NpxFlag := (AL AND 2) = 2;          {math co-processor.       }
    AH      := 15;                      {Get current video mode   }
    Intr( $10, Regs );                  {and save it for when TSR }
    InitVideo := AL;                    {is activated.            }
    AH := 3; BH := 0;                   {Get current cursor size  }
    Intr( $10, Regs );                  {and save it for when TSR }
    InitCMode := CX;                    {is activated.            }
  END;  {WITH Regs}
{
***** Get info. on buffer for saving screen image.
}
  BuffSize := SizeOf( Buffer );
  TSRScrPtr := @Buffer;
{
*** Determine activation key combination.
}
  Comb := 0;  i := 1;                       {Create ptr to        }
  PlistPtr := Ptr( PrefixSeg, $80 );        {parameter list.      }
  WHILE i < Length( PlistPtr^ ) DO BEGIN    {Check for parameters.}
    IF PlistPtr^[i] = '/' THEN BEGIN        {Process parameter.   }
      Inc( i );
      j := Pos( UpCase( PlistPtr^[i] ), CombChr );
      IF (j > 0) AND (j < 5) THEN Comb := Comb OR (1 SHL Pred(j))
      ELSE IF j <> 0 THEN BEGIN             {New activation char. }
        Inc( i );   k := Succ( i );
        IF i > Length(PlistPtr^) THEN KeyChr := #0
        ELSE BEGIN
          IF ((k <= Length(PlistPtr^)) AND (PlistPtr^[k] = '"'))
             OR (PlistPtr^[i] <> '"') THEN KeyChr := PlistPtr^[i]
          ELSE KeyChr := #0;
        END;  {ELSE BEGIN}
      END;  {ELSE IF ... BEGIN}
    END; {IF PlistPtr^[i] = '/'}
    Inc( i );
  END;  {WHILE ...}
  IF Comb = 0 THEN Comb := ShiftComb;  {Use default combination.  }
  IF Comb = 0 THEN Comb := AltKey;     {No default, use [Alt] key.}
  ScanCode := Pos( UpCase( KeyChr ), ScanChr );  {Convert char. to}
  IF ScanCode < 2 THEN BEGIN                     {scan code.      }
    ScanCode := 2;  KeyChr := '1';
  END;
  Mem[CSeg:Shft] := Comb;             {Store shift key combination}
  Mem[CSeg:Key]  := ScanCode;         {and scan code.             }
{
*** Output an installation message:  Memory used & activation code.
}
  Writeln( 'Memory used is approximately ',
   ( ($1000 + Seg(FreePtr^) - PrefixSeg)/64.0):7:1,' K (K=1024).');
  Writeln(
'Activate program by pressing the following keys simultaneously:');
  IF (Comb AND 1) <> 0 THEN Write(' [Right Shift]');
  IF (Comb AND 2) <> 0 THEN Write(' [Left Shift]');
  IF (Comb AND 4) <> 0 THEN Write(' [Ctrl]');
  IF (Comb AND 8) <> 0 THEN Write(' [Alt]');
  Writeln(' and "', KeyChr, '".');
{
*** Intercept orig. interrupt vectors; Then exit and stay-resident.
}
  SetIntVec( $21, Ptr( CSeg, Our21 ) );
  SetIntVec( $25, Ptr( CSeg, Our25 ) );
  SetIntVec( $26, Ptr( CSeg, Our26 ) );
  SetIntVec( $16, Ptr( CSeg, Our16 ) );
  SetIntVec( $09, Ptr( CSeg, Our09 ) );
  SwapVectors;                           {Save turbo intr.vectors.}
  MemW[CSeg:UnSafe] := 0;                {Allow TSR to pop up.    }
  Keep( 0 );                             {Exit and stay-resident. }
END;  {TSRInstall.}
END.  {TSRUnit.}











Copyright © 1989, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.