[Back to TSR SWAG index] [Back to Main SWAG index] [Original]

    Unit TSRUnit; {Create TSR Programs With Turbo Pascal 5.0 & TSRUnit}

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

    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 = $0204;       {Low Byte.High Byte = 2.04     }

    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}

    Implementation {==================================================}
    Var
      BuffSize, InitCMode : Word;
      NpxFlag             : Boolean;
      Buffer              : Array[0..8191] of Word;
      NpxState            : Array[0..93] of Byte;
      RetrnVal, InitVideo : Byte;
      TheirFunc           : WordFuncs;

    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+27;
      Our09 = Our26+27;    Our16  = Our09+127+8;  InsChr = Our16+180-8;
      PopUp = InsChr+4;

    Procedure Asm1; {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} $68/>Our25+19/     { PUSH Our25+19    ;Clean up stack with- }
    { 16} $C2/$02/$00/       { RET  2           ;out changing flags.  }
    { 19} $9C/               { PUSHF            ;Save flags.          }
    { 20} $2E/               { CS:              ;                     }
    { 21} $FE/$0E/>UnSafe/   { DEC  [UnSafe]    ;Clear UnSafe flag.   }
    { 25} $9D/               { POPF             ;Restore flags.  Leave}
    { 26} $CB/               { RETF             ;old flags on the stk.}
    { 27}
    {*** 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} $68/>Our26+19/     { PUSH Our26+19    ;Clean up stack with- }
    { 16} $C2/$02/$00/       { RET  2           ;out changing flags.  }
    { 19} $9C/               { PUSHF            ;Save flags.          }
    { 20} $2E/               { CS:              ;                     }
    { 21} $FE/$0E/>UnSafe/   { DEC  [UnSafe]    ;Clear UnSafe flag.   }
    { 25} $9D/               { POPF             ;Restore flags.  Leave}
    { 26} $CB/               { RETF             ;old flags on the stk.}
    { 27}

    {*** 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, NumbChr : 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.
    }
        NumbChr := TheirFunc;
        MemW[CSeg:InsNumb] := NumbChr;
        if NumbChr > 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( TSRName: String; TSRFunc: WordFuncs;
                          ShiftComb: Byte; KeyChr: Char );
    Const
      ScanChr = '+1234567890++++QWERTYUIOP++++ASDFGHJKL+++++ZXCVBNM';
      CombChr = 'RLCA"';
    Var
      PlistPtr         : ^String;
      i, j, k          : Word;
      Regs             : Registers;
      Comb, ScanCode   : Byte;
    begin
      if ofs( Asm1 ) <> 0 then Exit;           {offset of Asm must be 0}
      MemW[CSeg:StkSs]  := SSeg;              {Save Pointer to top of }
      MemW[CSeg:Stkofs] := Sptr + 562;        {TSR's stack.           }
      MemL[CSeg:PopUp]  := LongInt(@PopUpCode); {Save PopUpCode addr. }
      TheirFunc         := TSRFunc;           {& their TSR func. addr.}
      Writeln('Installing Stay-Resident Program: ',TSRName );
    {
    *****  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.}

    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.}

[Back to TSR SWAG index] [Back to Main SWAG index] [Original]