home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / TPTOOL5.ZIP / STAYRES.INC next >
Encoding:
Text File  |  1987-03-28  |  20.4 KB  |  428 lines

  1.  
  2. const stayres_tag: string[90]
  3.    = #0'@(#)CURRENT_FILE LAST_UPDATE TSR support library 1.0'#0;
  4. #log TSR support library 1.0
  5.  
  6. {-----------------------------------------------------------------------------}
  7. {                                                                             }
  8. {                                                                             }
  9. {         "  S o r r y,   D a v e,   I   C a n ' t   D o   T h a t .  "       }
  10. {                                                                             }
  11. {                                                          Arthur C. Clark    }
  12. {                                                           " 2 0 0 1 "       }
  13. {-----------------------------------------------------------------------------}
  14.  
  15. { Stayres Version 3.20i  (single include-file version)
  16.  
  17.  
  18. { Authors: Lane H. Ferris (Stay Resident/Exit Code)
  19.            Neil J. Rubenking (Directory code and ideas)
  20.            Other Public Gurus on whose shoulders we stand.
  21.  
  22. { PURPOSE:  This code will serve as a template to create other "Stay  Resident"
  23.             programs  in  Turbo  Pascal(tm).   This  code  intercepts  Int  16,
  24.             displacing original Interrupt  16  Vector  to  User  Interrupt  67.
  25.             During  execution  of  other  programs,  it  can  be invoked by the
  26.             special key combination  specified  by  "Our_Char"  (in  this  case
  27.             Alt-F10.)
  28.  
  29.   Modifications:
  30.            7. 7.85 - Replace Windows with a more simple form/less code.
  31.            7. 8.85 - Replace Window Array with Pointers/Heap form.
  32.            7.11.85 - Re-issue termination Keyboard Read / pass back to user
  33.                      Would like to back up Instruction Ptr by two bytes before
  34.                      the Int 16 ($CD16) but it might be a "long call" by
  35.                      some other Kbd interceptor (chirp chirp chrIP)... and
  36.                      thats "trouble in River City".
  37.            7.19.85 - Clean up RmWin "incorrect" attribute bugs. If screen
  38.                      isnt cleared, we get border attribute, not text attrb.
  39.                    - Remove last window at Termination Time (Ctrl-Home).
  40.            8.26.85 - Version 3.10 Changes
  41.                      1) Save 40 words in StaySave/Rstr to avoid clobbersing
  42.                         Dos Stack when entering Dos with Turbo Write(ln) caused
  43.                         by Int 21 Function 5  (Writln(Lst,..)) which re-issues
  44.                         Int 16.
  45.                      2) Change Int 68 to Int 67 to Avoid collisions with
  46.                         Dos 3.1 on an AT.
  47.                      3) Correct "Press a Key..." to accept any "Key..."
  48.                         (not just Cr).
  49.                      4) Check Int16 function. Jmp directly to Int16 if not
  50.                         a character request. Avoids 40 word Save/Restore
  51.                         overhead.
  52.            9.04.85 - Version 3.20 changes
  53.                      1) When returning to user program, pass back a fake
  54.                         "Ctrl-key" scan code to allow immediate re-execution
  55.                         of the TSR (Terminate Stay Resident) program. Also
  56.                         solves SideKick incessant bird caws.
  57.            2.14.86 - Version 3.20i changes
  58.                      1) Combined all stay-resident functions into a single
  59.                         include file.  This simplifies the job of making
  60.                         a stay-resident utility based on this package.
  61.                      2) Made a simple stay-resident program that demonstrates
  62.                         how the stayres include file is used.
  63.         ***doesnt    3) Added searching for a free interrupt vector.  This
  64.         ***work         allows you to have more than 1 resident program at
  65.         ***yet!!!       a time in memory.
  66. }
  67.  
  68.  
  69.  
  70. procedure user_stayres_procedure;  forward;   {defined later by the user}
  71.  
  72.  
  73. { * * * * * * * CONSTANTS * * * * * * * * * * * * * * * * * * * * * * }
  74.   const
  75.     Our_Char        =  113; {this is the scan code for AltF10}
  76.     Alt_F10         = #113; {Alt F10 Scan Code               }
  77.     Ctrl_Home       = #119; {Control Home Scan Code          }
  78.     Ctrl_End        = #117; {Control End Scan Code           }
  79.  
  80.     User_Int_Min    = $67; {first place to put new interrupt}
  81.     User_Int_Max    = $7F; {last  place to put new interrupt}
  82.  
  83.     Kybrd_Int       = $16; {BIOS keyboard interrupt}
  84.  
  85.  
  86. {  - - - - - - T Y P E    D E C L A R A T I O N S - - - - - - - - - - - -  }
  87.   Type
  88.     Regtype     = record Ax,Bx,Cx,Dx,Bp,Si,Di,Ds,Es,Flags:integer  end;
  89.     HalfRegtype = record Al,Ah,Bl,Bh,Cl,Ch,Dl,Dh:byte              end;
  90.  
  91. { - - - - - - - T Y P E D   C O N S T A N T S - - - - - - - - - - - - - - -}
  92.   Const
  93.     {regs is defined as a typed constant in order to get it in the code segment}
  94.  
  95.       Regs   : regtype = (Ax:0;Bx:0;Cx:0;Dx:0;Bp:0;Si:0;Di:0;Ds:0;Es:0;Flags:0);
  96.  
  97.       OurDseg: integer = 0;            {Our Data Segment Value             }
  98.       OurSseg: integer = 0;            {Our Stack Segment Value            }
  99.       DosSseg: integer = 0;            {Dos Stack Segment Value            }
  100.       Inuse  : Boolean = false;        {Recursion flag                     }
  101.  
  102.      { The following two constants *MUST* remain in the IP:CS order        }
  103.      { because StaySave uses them as a JMP target                          }
  104.       User_IntIP : integer = 0;        {Pointer to Original IP Int value   }
  105.       User_IntCS : integer = 0;        {Pointer to Original CS Int value   }
  106.  
  107.       User_Int  { : integer} = User_Int_Min;
  108.  
  109.  { - - - - - - - V A R I A B L E S - - - - - - - - - - - - - - - - - - - - - -}
  110.     Var
  111.       SaveRegs                      : regtype;
  112.       HalfRegs                      : halfregtype absolute regs;
  113.       Terminate_flag                : boolean ;
  114.       Old_Xpos,Old_Ypos             : integer ;
  115.  
  116.  
  117. {-----------------------------------------------------------------------------}
  118. {  Stay_Xit Check Terminate Keys                                              }
  119. {                                                                             }
  120. {  Clean up the Program, Free the Environment block, the program segment      }
  121. {  memory and return to Dos. Programs using this routine, must be the         }
  122. {  last program in memory, else, a hole will be left causing Dos              }
  123. {  to go GooGoo .                                                             }
  124. {-----------------------------------------------------------------------------}
  125.  
  126. Procedure Stay_Xit;
  127.    Begin { Block }
  128.             SaveRegs.Ax := $3500 + User_Int;
  129.             MsDos(SaveRegs);           {get the original Int 16 Addr   }
  130.  
  131.             SaveRegs.Ax := $2500 + Kybrd_Int;
  132.             SaveRegs.Ds := SaveRegs.Es;
  133.             SaveRegs.Dx := SaveRegs.Bx; { Reset the Keyboard interrupt addr }
  134.             MsDos(SaveRegs);            { to its original value             }
  135.  
  136.             MemW[$00:User_Int * 4] := 0 ;    { Clear User Interrupt vector  }
  137.             MemW[$00:User_Int * 4 + 2] :=0;
  138.  
  139.             Saveregs.Ax := $4900 + 0 ;       { Free Allocated Block function}
  140.             Saveregs.Es := MemW[CSeg:$2C] ;  { Free environment block       }
  141.             MsDos( Saveregs ) ;
  142.  
  143.             Saveregs.Ax := $4900 + 0 ;        { Free Allocated Block function}
  144.             Saveregs.Es := CSeg ;             { Free Program                 }
  145.             MsDos( Saveregs ) ;
  146.  
  147.             Intr($20,Regs) ;                  { Return to Dos }
  148.  
  149.    End  { StayXit };
  150. {-----------------------------------------------------------------------------}
  151.  
  152. {----------------------------------------------------------------------------}
  153. {              P R O C E S S   I N T E R R U P T                             }
  154. { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - }
  155. {  PURPOSE:  This  procedure  replaces  the  standard  keyboard  interrupt.  If
  156.             anything but <Alt>-F10 is pressed,  the key is  passed  on  to  the
  157.             standard  keyboard  interrupt.  B*U*T  when  <Alt>- F10 is pressed,
  158.             this program takes over.  The variable InUse  is  set  to  TRUE  to
  159.             ensure that this code doesn't try to run "on top of itself " AND to
  160.             indicate  to the Inline code to save/restore the original interrupt
  161.             regs.
  162. }
  163.  
  164. Procedure Process_Intr;
  165. Begin
  166.           { K e y b o a r d    Interrupt   o c c u r s   here }
  167.  
  168. {This Inline routine will save the regs and Stack for Stay resident programs.
  169.  It restores Ds and Ss from the previously saved integer constants "OurDseg"
  170.   and "OurSSeg". This is important since Dos is not re-entrant and any attempt
  171.   to use Interrupt I/O services will clobber the very stack on which the
  172.   Resident Turbo program just saved its regs. Thus, on the final return, you
  173.   and Toto will end up somewhere other than Kansas and without your Ruby Reds.
  174.    }
  175.  
  176.   Inline (
  177.  
  178.  { The following routine avoids the overhead of saving the DOS stack         }
  179.  { when the INT 16 function was not for a character request. This happens    }
  180.  { often (every four chars) as DOS checks on ^S/^Q/^C/Keypressed  ad.nausea  }
  181.  
  182.    $80/$FC/$00/                        {Cmp Ah,00     If Char request,       }
  183.    $74/$07/                            {Je   ChrRqst  enter Staysave code    }
  184.    $5D/$5D/                            {Pop  Bp/Pop Bp    Restore Bp         }
  185.    $2E/
  186.    $FF/$2E/User_IntIP/                 {Jmp Far CS:[User_IntIP]              }
  187.  
  188. {ChrRqst: }
  189.     $FA /                              {  Cli       Stop all interrupts       }
  190.                                        { Bp and Sp aready saved at Begin Stmt }
  191.     $55/                               {Push   Bp  Save again for Regpak      }
  192.     $BD/Regs/                          {Mov    Bp,offset REGS}
  193.     $2E/$89/$46/$00/                   {CS:Mov [Bp+0],AX}
  194.     $2E/$89/$5E/$02/                   {CS:Mov [Bp+2],Bx}
  195.     $2E/$89/$4E/$04/                   {CS:Mov [Bp+4],CX}
  196.     $2E/$89/$56/$06/                   {CS:Mov [Bp+6],DX}
  197.     $2E/$8F/$46/$08/                   {Pop    CS:[Bp+8] Fetch Bp from stack  }
  198.     $2E/$89/$76/$0A/                   {CS:Mov [Bp+A],SI}
  199.     $2E/$89/$7E/$0C/                   {CS:Mov [Bp+C],DI}
  200.     $2E/$8C/$5E/$0E/                   {CS:Mov [Bp+E],DS}
  201.     $2E/$8C/$46/$10/                   {CS:Mov [Bp+10],ES}
  202.     $9C/                               {PUSHF  put Flags on stack to retrieve }
  203.     $2E/$8F/$46/$12/                   {POP CS:[Bp+12]}
  204.  
  205.     { If Current SS := [OurSseg] or Inuse = True, then dont save the stack. }
  206.     { This program is being recursive.                                      }
  207.  
  208.      $2E/$80/$3E/Inuse/$01/   {Cmp  CS:[Inuse],1                          }
  209.      $74/$57/                 {Je   ReCurin             -J-U-M-P-         }
  210.  
  211.  
  212.      $2E/$8C/$16/DosSSeg/     {Mov  CS:DosSSeg,SS Save Dos Stack Segment    }
  213.      $8C/$D6/                 {Mov  Si,SS         If this is our Stack Seg  }
  214.      $8E/$C6/                 {Mov  Es,Si         Get Dos StackSeg          }
  215.      $2E/$8E/$16/OurSSeg/     {Mov  SS,CS:OurSSeg Get our Stack segment     }
  216.      $2E/$8E/$1E/OurDseg/     {Mov  Ds,CS:Our_Ds  Setup our Data Segment    }
  217.  
  218.  
  219.      $2E/$3B/$36/OurSSeg/     {Cmp  Si,CS:OurSSeg ..use current Stack ptr   }
  220.      $89/$E6/                 {Mov  Si,Sp         ..value..else reset stack }
  221.      $74/$05/                 {Je   $+5           ..to original Turbo stack }
  222.      $3E/$8B/$36/$74/$01/     {Mov  Si,Ds:[174]   ..(cf. code at B2B 3.0x)  }
  223.  
  224.      $87/$F4/                 {Xchg Sp,Si         Set new  Stack Pointer    }
  225.                                           { Save Dos/User regs for Exit     }
  226.      $2E/$FF/$76/$00/         {Push [Bp+0]  Save Ax                         }
  227.      $2E/$FF/$76/$02/         {Push [Bp+2]  Save Bx                         }
  228.      $2E/$FF/$76/$04/         {Push [Bp+4]  Save Cx                         }
  229.      $2E/$FF/$76/$06/         {Push [Bp+6]  Save Dx                         }
  230.                               {Push [Bp+8]  Save Bp                         }
  231.      $2E/$FF/$76/$0A/         {Push [Bp+A]  Save Si                         }
  232.      $2E/$FF/$76/$0C/         {Push [Bp+C]  Save Di                         }
  233.      $2E/$FF/$76/$0E/         {Push [Bp+E]  Save Ds                         }
  234.      $2E/$FF/$76/$10/         {Push [Bp+10] Save Es                         }
  235.  
  236.     { Now save 40 Words from the Dos Stack before performing any         }
  237.     { I/O or re-using the Dos stack                                      }
  238.  
  239.      $B9/>$0028/              {Mov  Cx,40  Save 40 Dos Stack words for Retn }
  240.  {Restack:}
  241.      $26/$FF/$34/             {Push Es:[Si] Our Stack := Dos Es:Si          }
  242.      $46/$46/                 {Inc  Si/Inc Si Get Next Dos Stack Word       }
  243.      $E2/$F9/                 {Loop to Restack                              }
  244.  
  245.      $2E/$8E/$16/OurSSeg/     {Mov  SS,CS:OurSSeg Set up our Stack          }
  246.      $56/                     {Push Si            Save bottom of Dos Stack  }
  247.      $2E/$8C/$5E/$0E/         {Mov  CS:[Bp+E],Ds  Set New Data Segmt in regs}
  248. {Recurin                                            Jump here if Recursion  }
  249.      $FB                      {Sti Enable Interrupts                        }
  250.  
  251.        ) ;
  252. {...........................................................................}
  253.  
  254. { Check the Int 16 request function in Ah reg:
  255.                    0 = read character from Keyboard
  256.                    1 = check character available
  257.                    2 = check shift key values
  258. }
  259.  
  260.           { HalfRegs.Ah = 0  This is a Character Request because StaySave }
  261.           { doesnt allow an enter here unless it is!                      }
  262.  
  263.  
  264.   Intr (User_Int, Regs);             { Use the DOS replaced interrupt}
  265.                                      { Get Key from Keyboard         }
  266.   If (Halfregs.Ah = Our_Char)        { Separate the tests so code    }
  267.                                      { performs efficiently.         }
  268.      then if  (not InUse) then       { Must be OUR key and not busy  }
  269.      Begin
  270.         InUse := true;                  { "dont clobber saved stack"}
  271.         Old_Xpos := WhereX;
  272.         Old_Ypos := WhereY;
  273.  
  274.         user_stayres_procedure;         {your program called here}
  275.  
  276.         GotoXY(Old_Xpos,Old_Ypos);       { Put Cursor Back                 }
  277.         Regs.Ax := $1D00;                { Give Dummy Ctrl Scan Code to    }
  278.                                          {           interrupted program   }
  279.  
  280.         InUse := false;                  { ok to restore interrupted stack }
  281.      End;
  282.  
  283.  
  284. { Inline Code to restore the stack and regs moved to the Turbo Resident
  285.   Program Stack to allow re-entrancy into the Dos Code for I/O and
  286.   recursion from built-in Turbo functions.  Arthor:      L.H. Ferris}
  287.  
  288.     inline(
  289.  
  290.     $BD/Regs/                          {Mov    Bp,offset REGS}
  291.     $2E/$8B/$46/$00/                   {CS:Mov Ax,[Bp+0]}
  292.     $2E/$8B/$5E/$02/                   {CS:Mov Bx,[Bp+2]}
  293.     $2E/$8B/$4E/$04/                   {CS:Mov Cx,[Bp+4]}
  294.     $2E/$8B/$56/$06/                   {CS:Mov Dx,[Bp+6]}
  295.  
  296.     $2E/$8B/$76/$0A/                   {CS:Mov Si,[Bp+A]}
  297.     $2E/$8B/$7E/$0C/                   {CS:Mov Di,[Bp+C]}
  298.     $2E/$8E/$5E/$0E/                   {CS:Mov DS,[Bp+E]}
  299.     $2E/$8E/$46/$10/                   {CS:Mov ES,[Bp+10]}
  300.     $2E/$FF/$76/$12/                   {Push CS:[Bp+12]}
  301.     $9D/                               {Popf}
  302.  
  303.     { If [CS:InUse]:= True,  then dont restore the stack. This program is   }
  304.     { being recursive. Else restore  Dos Stack and Program Entry registers  }
  305.  
  306.      $2E/$80/$3E/Inuse/$01/   {Cmp  byte ptr CS:[Inuse],1                   }
  307.      $74/$23/                 {Je   ReCurOut                                }
  308.  
  309.       $FA /                   { Cli      ; Stop all interrupts    }
  310.  
  311.     {  Move 40 words of our stack back to Dos Stack                }
  312.  
  313.     $5E/                     {Pop Si     Bottom of Dos Stack              }
  314.     $B9/>$0028/              {Mov Cx,40  Return 40 Dos Stack Words        }
  315.     $2E/$8E/$06/DosSSeg/     {Mov ES,CS:DosSSeg Get Dos StackSegment      }
  316.   {Restack:}
  317.     $4E/$4E/                 {Dec Si/Dec Si     Backup Dos Stack          }
  318.     $26/$8F/$04/             {Pop Es:[Si]       Dos Stack := Our Stack    }
  319.     $E2/$F9/                 {Loop to Restack                             }
  320.     $89/$F5/                 {Mov Bp,Si         Save Dos Sp across Pops   }
  321.  
  322.  
  323.     $07/                     {Pop  Es                                     }
  324.     $1F/                     {Pop  Ds                                     }
  325.     $5F/                     {Pop  Di                                     }
  326.     $5E/                     {Pop  Si                                     }
  327.     $5A/                     {Pop  Dx                                     }
  328.     $59/                     {Pop  Cx                                     }
  329.     $5B/                     {Pop  Bx                                     }
  330.     $44/$44/                 {Inc sp/Inc sp Thow old Ax value away        }
  331.  
  332.     $89/$EC/                  {Mov  Sp,Bp         Setup Dos Stack Ptr     }
  333.     $2E/$8E/$16/DosSSeg/      {Mov  SS,CS:DosSSeg Give back Dos Stack     }
  334.  
  335. {RecurOut                                 Clean up the Stack              }
  336.  
  337.     $5D/                               {Pop Bp     Throw away old dos Sp  }
  338.  
  339.     $BD/Regs/                          {Mov    Bp,offset REGS             }
  340.     $2E/$FF/$76/$12/                   {Push CS:[Bp+12]                   }
  341.     $9D/                               {Popf                              }
  342.     $5D/                               {Pop Bp  Retrieve old BP           }
  343.  
  344.     $FB/                               {Sti     Enable interrupts      }
  345.     $CA/$02/$00                        {Ret Far 002                    }
  346.         );
  347. {.......................................................................}
  348. End ;{Process_Intr}
  349.  
  350. {-----------------------------------------------------------------------}
  351.  
  352. {The main program installs the new interrupt routine and makes it permanently
  353.  resident as the keyboard interrupt.  The old keyboard interrupt is addressed
  354.  through #67H, so it can still be used.
  355.  
  356. The following dos calls are used:
  357.  
  358.  Function 25 - Install interrupt address
  359.                input al = int number,
  360.                ds:dx = address to install
  361.  Function 35 - get interrupt address
  362.                input al = int number
  363.                output es:bx = address in interrupt
  364.  Function 31 - terminate and stay resident
  365.                input dx = size of resident program obtained from the memory
  366.                allocation block at [CS:0 - $10 + 3]
  367.  Function 49 - Free Allocated Memory
  368.                input Es = Block Segment to free
  369.  Interrupt 20 - Return to invoking process
  370. }
  371.  
  372.  
  373. {-----------M A I N    B L O C K---------------------------------------------}
  374. procedure stay_resident;
  375. begin
  376.  
  377.   InUse  := false;
  378.   OurDseg:= Dseg;           { Save the Data Segment Address for Interrupts }
  379.   OurSseg:= Sseg;           { Save our Stack Segment for Interrupts        }
  380.  
  381.  
  382.   Terminate_Flag := false ;
  383.  
  384.   {now install the interrupt routine, searching for a free vector}
  385.  
  386.   repeat
  387.      SaveRegs.Ax := $3500 + User_Int;
  388.      Intr($21,SaveRegs);            {Check to make sure int not already used}
  389.  
  390. {     if SaveRegs.Es <> $00 then
  391.         User_Int := User_Int + 1;   }
  392.  
  393.   until (SaveRegs.Es = 0) or (User_Int > User_Int_max);
  394.  
  395.  
  396.   if SaveRegs.Es <> 0 then
  397.         WriteLn ('Interrupt ',User_Int,' in use -- can''t install Resident Code')
  398.   else
  399.  
  400.     begin
  401.  
  402.       SaveRegs.Ax := $3500 + Kybrd_Int;
  403.       Intr($21,SaveRegs);        {get the address of keyboard interrupt }
  404.  
  405.       SaveRegs.Ax := $2500 + User_Int;
  406.       SaveRegs.Ds := SaveRegs.Es;
  407.       SaveRegs.Dx := SaveRegs.Bx;
  408.       Intr($21,SaveRegs);       { set the user-interrupt address to point
  409.                                 { to the keyboard interrupt address }
  410.  
  411.       SaveRegs.Ax := $2500 + Kybrd_Int;
  412.       SaveRegs.Ds := CSeg;
  413.       SaveRegs.Dx := Ofs(Process_Intr);
  414.       Intr ($21,SaveRegs);        { set the keyboard interrupt to point to
  415.                                   "Process-Intr" above}
  416.  
  417.       User_IntIP := MemW[0:User_Int * 4 ];  { Location of User Interrupt IP }
  418.       User_IntCS := MemW[0:User_Int * 4 +2];{ Location of User Interrupt CS }
  419.  
  420.       {now terminate and stay resident}
  421.       SaveRegs.Ax := $3100 + 0 ;              { Terminate and Stay Resident }
  422.       SaveRegs.Dx := MemW [CSeg-1:0003] ;     { Prog_Size from Allocation Blk}
  423.       Intr ($21,SaveRegs);
  424.  
  425.     end;
  426.        { END OF RESIDENCY CODE }
  427. end;
  428.