home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 14 / CDACTUAL.iso / cdactual / demobin / share / program / Pascal / TSRSRC30.ZIP / MEMU.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1991-10-15  |  18.4 KB  |  701 lines

  1. {**************************************************************************
  2. *   MEMU - utility unit for TSR Utilities.                                *
  3. *   Copyright (c) 1991 Kim Kokkonen, TurboPower Software.                 *
  4. *   May be freely distributed and used but not sold except by permission. *
  5. ***************************************************************************}
  6.  
  7. {$R-,S-,I-,V-,B-,F-,A-,E-,N-,G-,X-}
  8.  
  9. unit MemU;
  10.   {-Miscellaneous memory functions needed for TSR Utilities}
  11.  
  12. interface
  13.  
  14. {Following may change when WATCH reassembled. Check WATCH.MAP}
  15. const
  16.   {Offsets into resident copy of WATCH.COM for data storage}
  17.   WatchOfs = $80;             {Location of length of command line}
  18.   WatchOffset = $81;          {Location of start of command line}
  19.   NextChange = $104;
  20.   ChangeVectors = $580;
  21.   OrigVectors = $980;
  22.   CurrVectors = $D80;
  23.   WatchId = 'TSR WATCHER';    {ID placed in WATCH command line}
  24.   MaxChanges = 128;           {Maximum number of vector changes stored in WATCH}
  25.  
  26. const
  27.   MaxBlocks = 256;            {Max number of DOS allocation blocks supported}
  28.  
  29. type
  30.   OS =
  31.     record
  32.       O, S : Word;
  33.     end;
  34.  
  35.   NameArray = array[1..8] of Char;
  36.  
  37.   McbPtr = ^Mcb;
  38.   Mcb =
  39.     record
  40.       Id : Char;
  41.       Psp : Word;
  42.       Len : Word;
  43.       Unused : array[1..3] of Byte;
  44.       Name : NameArray;
  45.     end;
  46.  
  47.   Block =
  48.   record                      {Store info about each memory block}
  49.     mcb : Word;
  50.     psp : Word;
  51.     releaseIt : Boolean;
  52.   end;
  53.  
  54.   BlockType = 0..MaxBlocks;
  55.   BlockArray = array[1..MaxBlocks] of Block;
  56.  
  57.   McbGroup =
  58.   record
  59.     Count : Word;
  60.     Mcbs : array[1..MaxBlocks] of
  61.            record
  62.              mcb : Word;
  63.              psp : Word;
  64.            end;
  65.   end;
  66.  
  67.   ChangeBlock =
  68.   record                      {Store info about each vector takeover}
  69.     VecNum : byte;
  70.     case ID : byte of
  71.       0, 1 : (VecOfs, VecSeg : Word);
  72.       2    : (SaveCode : array[1..6] of byte);
  73.       $FF  : (PspAdd : Word);
  74.   end;
  75.   {
  76.   ID is interpreted as follows:
  77.     00 = ChangeBlock holds the new pointer for vector vecnum
  78.     01 = ChangeBlock holds pointer for vecnum but the block is disabled
  79.     02 = ChangeBlock holds the code underneath the vector patch
  80.     FF = ChangeBlock holds the segment of a new PSP
  81.   }
  82.   ChangeArray = array[0..maxchanges] of changeblock;
  83.  
  84. const
  85.   RBR = 0; {Receiver buffer register offset}
  86.   THR = 0; {Transmitter buffer register offset}
  87.   BRL = 0; {Baud rate low}
  88.   BRH = 1; {Baud rate high}
  89.   IER = 1; {Interrupt enable register}
  90.   IIR = 2; {Interrupt identification register}
  91.   LCR = 3; {Line control register}
  92.   MCR = 4; {Modem control register}
  93.   LSR = 5; {Line status register}
  94.   MSR = 6; {Modem status register}
  95.  
  96. type
  97.   {Structure of a device driver header}
  98.   DeviceHeader =
  99.     record
  100.       NextHeaderOffset : Word;    {Offset address of next device in chain}
  101.       NextHeaderSegment : Word;   {Segment address of next device in chain}
  102.       Attributes : Word;          {Device attributes}
  103.       StrategyEntPt : Word;       {Offset in current segment - strategy}
  104.       InterruptEntPt : Word;      {Offset in current segment - interrupt}
  105.       DeviceName : array[1..8] of Char; {Name of the device}
  106.     end;
  107.   DeviceHeaderPtr = ^DeviceHeader;
  108.   DeviceArray = array[1..256] of DeviceHeaderPtr;
  109.  
  110.   FileRec =
  111.     record
  112.       OpenCnt : Word;
  113.       OpenMode : Word;
  114.       Attribute : Byte;
  115.       Unknown1 : Word;
  116.       DCB : Pointer;
  117.       InitCluster : Word;
  118.       Time : Word;
  119.       Date : Word;
  120.       Size : LongInt;
  121.       Pos : LongInt;
  122.       BeginCluster : Word;
  123.       CurCluster : Word;
  124.       Block : Word;
  125.       Unknown2 : Byte;            {Varies with DOS version beyond here}
  126.       Name : array[0..7] of Char;
  127.       Ext : array[0..2] of Char;
  128.       Unknown3 : array[0..5] of Byte;
  129.       Owner : Word;
  130.       Unknown4 : Word;
  131.     end;
  132.  
  133.   SftRecPtr = ^SftRec;
  134.   SftRec =
  135.     record
  136.       Next : SftRecPtr;
  137.       Count : Word;
  138.       Files : array[1..255] of FileRec;
  139.     end;
  140.  
  141.   DosRec =
  142.     record
  143.       McbSeg : Word;
  144.       FirstDPB : Pointer;
  145.       FirstSFT : SftRecPtr;
  146.       ClockDriver : Pointer;
  147.       ConDriver : Pointer;
  148.       MaxBlockBytes : Word;
  149.       CachePtr : Pointer;
  150.       DriveTable : Pointer;
  151.       Unknown2 : Pointer;
  152.       Unknown3 : Word;
  153.       BlockDevices : Byte;
  154.       LastDrive : Byte;
  155.       NullDevice : DeviceHeader;
  156.     end;
  157.   DosRecPtr = ^DosRec;
  158.  
  159.   ComRec =  {State of the communications system}
  160.     record
  161.       Base : Word;
  162.       IERReg : Byte;
  163.       LCRReg : Byte;
  164.       MCRReg : Byte;
  165.       BRLReg : Byte;
  166.       BRHReg : Byte;
  167.     end;
  168.   ComArray = array[1..2] of ComRec;
  169.  
  170. const
  171.   Digits : array[0..$F] of Char = '0123456789ABCDEF';
  172.   DosDelimSet : set of Char = ['\', ':', #0];
  173.  
  174. var
  175.   DosV : Byte;       {Major DOS version number}
  176.   DosList : Pointer; {Pointer to DOS list of lists}
  177.   Mcb1 : McbPtr;     {First MCB in system}
  178.  
  179. function GetDosListPtr : Pointer;
  180.   {-Return address of DOS list of lists}
  181.  
  182. function GetUmbLinkStatus : Boolean;
  183.   {-Return status of DOS 5 upper memory block link}
  184.  
  185. function SetUmbLinkStatus(On : Boolean) : Word;
  186.   {-Change state of DOS 5 upper memory block link}
  187.  
  188. function DosVersion : Byte;
  189.   {-Return major DOS version number}
  190.  
  191. function TopOfMemSeg : Word;
  192.   {-Return segment of top of memory}
  193.  
  194. function HiMemAvailable(DosV : Byte) : Boolean;
  195.   {-Return True if HiMem is available}
  196.  
  197. function HexB(B : Byte) : String;
  198.   {-Return hex string for byte}
  199.  
  200. function HexW(W : Word) : String;
  201.   {-Return hex string for word}
  202.  
  203. function HexPtr(P : Pointer) : string;
  204.   {-Return hex string for pointer}
  205.  
  206. function StUpcase(s : String) : String;
  207.   {-Return the uppercase string}
  208.  
  209. function JustFilename(PathName : String) : String;
  210.   {-Return just the filename of a pathname}
  211.  
  212. function JustName(PathName : String) : String;
  213.   {-Return just the name (no extension, no path) of a pathname}
  214.  
  215. function Extend(S : String; Len : Byte) : String;
  216.   {-Truncate or pad S to length Len}
  217.  
  218. function SmartExtend(S : String; Len : Byte) : String;
  219.   {-Truncate or pad S to length Len; end with '...' if truncated}
  220.  
  221. function Asc2Str(Name : NameArray) : String;
  222.   {-Convert array[1..8] of char to string}
  223.  
  224. procedure StripNonAscii(var S : String);
  225.   {-Return an empty string if input contains non-ASCII characters}
  226.  
  227. function CommaIze(L : LongInt; Width : Byte) : String;
  228.   {-Convert L to a string and add commas for thousands}
  229.  
  230. function HasEnvironment(M : McbPtr) : Boolean;
  231.   {-Return True if M has an associated environment block}
  232.  
  233. function NameFromEnv(M : McbPtr) : String;
  234.   {-Return M's name from its environment (already known to exist)}
  235.  
  236. function NameFromMcb(M : McbPtr) : String;
  237.   {-Return name from the Mcb (DOS 4+ only)}
  238.  
  239. function MasterCommandSeg : Word;
  240.   {-Return PSP segment of master COMMAND.COM}
  241.  
  242. function WatchPspSeg : Word;
  243.   {-Find copy of WATCH.COM in memory, returning its PSP segment or zero}
  244.  
  245. procedure FindTheBlocks(var Blocks : BlockArray;
  246.                         var BlockMax : BlockType;
  247.                         var StartMcb : Word;
  248.                         var CommandSeg : Word);
  249.   {-Scan memory for the allocated memory blocks}
  250.  
  251. procedure StuffKey(W : Word);
  252.   {-Stuff one key into the keyboard buffer}
  253.  
  254. procedure StuffKeys(Keys : string; ClearFirst : Boolean);
  255.   {-Stuff up to 16 keys into keyboard buffer}
  256.  
  257. function ExistFile(path : String) : Boolean;
  258.   {-Return true if file exists}
  259.  
  260.   {=======================================================================}
  261.  
  262. implementation
  263.  
  264.   function GetDosListPtr : Pointer; Assembler;
  265.     {-Return address of DOS list of lists}
  266.   asm
  267.     mov     ah,$52
  268.     int     $21
  269.     mov     dx,es
  270.     mov     ax,bx
  271.   end;
  272.  
  273.   function GetUmbLinkStatus : Boolean; Assembler;
  274.     {-Return status of DOS 5 upper memory block link}
  275.   asm
  276.     mov     ax,$5802
  277.     int     $21
  278.   end;
  279.  
  280.   function SetUmbLinkStatus(On : Boolean) : Word; Assembler;
  281.     {-Change state of DOS 5 upper memory block link}
  282.   asm
  283.     mov     ax,$5803
  284.     mov     bl,On
  285.     xor     bh,bh
  286.     int     $21
  287.     jc      @1
  288.     xor     ax,ax
  289. @1:
  290.   end;
  291.  
  292.   function DosVersion : Byte; Assembler;
  293.     {-Return major DOS version number}
  294.   asm
  295.     mov     ah,$30
  296.     int     $21
  297.   end;
  298.  
  299.   function TopOfMemSeg : Word;
  300.     {-Return segment of top of memory}
  301.   var
  302.     KBRAM : Word;
  303.   begin
  304.     asm
  305.       int $12
  306.       mov KBRAM,ax
  307.     end;
  308.     TopOfMemSeg := KBRAM shl 6;
  309.   end;
  310.  
  311.   function HiMemAvailable(DosV : Byte) : Boolean;
  312.     {-Return True if HiMem is available}
  313.   begin
  314.     HiMemAvailable := (DosV >= 5) and (DosV < 10);
  315.   end;
  316.  
  317.   function HexB(B : Byte) : String;
  318.     {-Return hex string for byte}
  319.   begin
  320.     HexB[0] := #2;
  321.     HexB[1] := Digits[B shr 4];
  322.     HexB[2] := Digits[B and $F];
  323.   end;
  324.  
  325.   function HexW(W : Word) : String;
  326.     {-Return hex string for word}
  327.   begin
  328.     HexW[0] := #4;
  329.     HexW[1] := Digits[Hi(W) shr 4];
  330.     HexW[2] := Digits[Hi(W) and $F];
  331.     HexW[3] := Digits[Lo(W) shr 4];
  332.     HexW[4] := Digits[Lo(W) and $F];
  333.   end;
  334.  
  335.   function HexPtr(P : Pointer) : string;
  336.     {-Return hex string for pointer}
  337.   begin
  338.     HexPtr := HexW(OS(P).S)+':'+HexW(OS(P).O);
  339.   end;
  340.  
  341.   function StUpcase(s : String) : String;
  342.     {-Return the uppercase string}
  343.   var
  344.     i : Byte;
  345.   begin
  346.     for i := 1 to Length(s) do
  347.       s[i] := UpCase(s[i]);
  348.     StUpcase := s;
  349.   end;
  350.  
  351.   function JustFilename(PathName : String) : String;
  352.     {-Return just the filename of a pathname}
  353.   var
  354.     I : Word;
  355.   begin
  356.     I := Word(Length(PathName))+1;
  357.     repeat
  358.       Dec(I);
  359.     until (PathName[I] in DosDelimSet) or (I = 0);
  360.     JustFilename := Copy(PathName, I+1, 64);
  361.   end;
  362.  
  363.   function JustName(PathName : String) : String;
  364.     {-Return just the name (no extension, no path) of a pathname}
  365.   var
  366.     DotPos : Byte;
  367.   begin
  368.     PathName := JustFilename(PathName);
  369.     DotPos := Pos('.', PathName);
  370.     if DotPos > 0 then
  371.       PathName := Copy(PathName, 1, DotPos-1);
  372.     JustName := PathName;
  373.   end;
  374.  
  375.   function Extend(S : String; Len : Byte) : String;
  376.     {-Truncate or pad S to length Len}
  377.   begin
  378.     if Length(S) < Len then
  379.       FillChar(S[Length(S)+1], Len-Length(S), ' ');
  380.     S[0] := Char(Len);
  381.     Extend := S;
  382.   end;
  383.  
  384.   function SmartExtend(S : String; Len : Byte) : String;
  385.     {-Truncate or pad S to length Len; end with '...' if truncated}
  386.   begin
  387.     if Length(S) > Len then
  388.       SmartExtend := copy(S, 1, Len-3)+'...'
  389.     else
  390.       SmartExtend := Extend(S, Len);
  391.   end;
  392.  
  393.   function Asc2Str(Name : NameArray) : String;
  394.     {-Convert array[1..8] of char to string}
  395.   var
  396.     I : Integer;
  397.   begin
  398.     I := 1;
  399.     while (I <= 8) and (Name[I] <> #0) and (Name[I] <> ' ') do begin
  400.       Asc2Str[I] := Name[I];
  401.       Inc(I);
  402.     end;
  403.     Asc2Str[0] := Char(I-1);
  404.   end;
  405.  
  406.   procedure StripNonAscii(var S : String);
  407.     {-Return an empty string if input contains non-ASCII characters}
  408.   var
  409.     I : Integer;
  410.     Ok : Boolean;
  411.   begin
  412.     Ok := True;
  413.     I := 1;
  414.     while Ok and (I <= Length(S)) do begin
  415.       case S[I] of
  416.         #0..#31, #126..#255 : Ok := False;
  417.       end;
  418.       Inc(I);
  419.     end;
  420.     if not Ok then
  421.       S := '';
  422.   end;
  423.  
  424.   function CommaIze(L : LongInt; Width : Byte) : String;
  425.     {-Convert L to a string and add commas for thousands}
  426.   var
  427.     I : Word;
  428.     Len : Word;
  429.     S : String[19];
  430.   begin
  431.     Str(L, S);
  432.     Len := Length(S);
  433.     I := Len;
  434.     while I > 1 do begin
  435.       if (Len+1-I) mod 3 = 0 then
  436.         insert(',', S, I);
  437.       dec(I);
  438.     end;
  439.     while Length(S) < Width do
  440.       insert(' ', S, 1);
  441.     CommaIze := S;
  442.   end;
  443.  
  444.   function HasEnvironment(M : McbPtr) : Boolean;
  445.     {-Return True if M has an associated environment block}
  446.   var
  447.     N : McbPtr;
  448.     EnvSeg : Word;
  449.     Done : Boolean;
  450.   begin
  451.     EnvSeg := MemW[M^.Psp:$2C];
  452.     N := Mcb1;
  453.     repeat
  454.       if (N^.Psp = M^.Psp) and (EnvSeg = OS(N).S+1) then begin
  455.         HasEnvironment := True;
  456.         Exit;
  457.       end;
  458.       Done := (N^.Id = 'Z');
  459.       N := Ptr(OS(N).S+N^.Len+1, 0);
  460.     until Done;
  461.     HasEnvironment := False;
  462.   end;
  463.  
  464.   function NameFromEnv(M : McbPtr) : String;
  465.     {-Return M's name from its environment (already known to exist)}
  466.   type
  467.     CharArray = array[0..32767] of Char;
  468.     CharArrayPtr = ^CharArray;
  469.   var
  470.     E : Word;
  471.     Eptr : CharArrayPtr;
  472.     Name : String[79];
  473.     Nlen : Byte absolute Name;
  474.   begin
  475.     Eptr := Ptr(MemW[M^.Psp:$2C], 0);
  476.     E := 0;
  477.     repeat
  478.       if Eptr^[E] = #0 then begin
  479.         Inc(E);
  480.         if Eptr^[E] = #0 then begin
  481.           {found end of environment}
  482.           Inc(E, 3);
  483.           Nlen := 0;
  484.           while (Nlen < 63) and (Eptr^[E] <> #0) do begin
  485.             Inc(Nlen);
  486.             Name[Nlen] := Eptr^[E];
  487.             Inc(E);
  488.           end;
  489.           StripNonAscii(Name);
  490.           NameFromEnv := JustName(Name);
  491.           Exit;
  492.         end;
  493.       end;
  494.       Inc(E);
  495.     until (E > 32767);
  496.     NameFromEnv := '';
  497.   end;
  498.  
  499.   function NameFromMcb(M : McbPtr) : String;
  500.     {-Return name from the Mcb (DOS 4+ only)}
  501.   var
  502.     Name : String[79];
  503.   begin
  504.     Name := Asc2Str(M^.Name);
  505.     StripNonAscii(Name);
  506.     NameFromMcb := Name;
  507.   end;
  508.  
  509.   function MasterCommandSeg : Word;
  510.     {-Return PSP segment of master COMMAND.COM}
  511.   var
  512.     curmcb : mcbptr;
  513.     mseg : word;
  514.     par : word;
  515.   begin
  516.     {First block}
  517.     curmcb := mcb1;
  518.     repeat
  519.       curmcb := ptr(OS(curmcb).s+curmcb^.len+1, 0);
  520.       par := memw[curmcb^.psp:$16];
  521.       mseg := OS(curmcb).s;
  522.       if (par = curmcb^.psp) and (mseg+1 = curmcb^.psp) then begin
  523.         MasterCommandSeg := curmcb^.psp;
  524.         exit;
  525.       end;
  526.     until curmcb^.id = 'Z';
  527.     MasterCommandSeg := 0;
  528.   end;
  529.  
  530.   function WatchPspSeg : Word;
  531.     {-Find copy of WATCH.COM in memory, returning its PSP segment or zero}
  532.   label
  533.     ExitPoint;
  534.   var
  535.     Status : Word;
  536.     M : McbPtr;
  537.     Done : Boolean;
  538.     LinkStatus : Boolean;
  539.  
  540.     function HasIdString(Segment, Offset : Word; Id : String) : Boolean;
  541.       {-Return True if Id appears at Segment:Offset}
  542.     var
  543.       P : ^String;
  544.     begin
  545.       P := Ptr(Segment, Offset);
  546.       HasIdString := (P^ = Id);
  547.     end;
  548.  
  549.   begin
  550.     {Access high memory if available}
  551.     if HiMemAvailable(DosV) then begin
  552.       LinkStatus := GetUmbLinkStatus;
  553.       Status := SetUmbLinkStatus(True);
  554.     end;
  555.     M := Mcb1;
  556.     repeat
  557.       if HasIdString(M^.Psp, WatchOfs, WatchId) then begin
  558.         WatchPspSeg := M^.Psp;
  559.         goto ExitPoint;
  560.       end;
  561.       Done := (M^.Id = 'Z');
  562.       M := Ptr(OS(M).S+M^.Len+1, 0);
  563.     until Done;
  564.     {Not found if we get here}
  565.     WatchPspSeg := 0;
  566. ExitPoint:
  567.     if HiMemAvailable(DosV) then
  568.       Status := SetUmbLinkStatus(LinkStatus);
  569.   end;
  570.  
  571.   procedure FindTheBlocks(var Blocks : BlockArray;
  572.                           var BlockMax : BlockType;
  573.                           var StartMcb : Word;
  574.                           var CommandSeg : Word);
  575.     {-Scan memory for the allocated memory blocks}
  576.   const
  577.     MidBlockID = $4D;         {Byte DOS uses to identify part of MCB chain}
  578.     EndBlockID = $5A;         {Byte DOS uses to identify last block of MCB chain}
  579.   var
  580.     mcbSeg : Word;         {Segment address of current MCB}
  581.     nextSeg : Word;        {Computed segment address for the next MCB}
  582.     gotFirst : Boolean;       {True after first MCB is found}
  583.     gotLast : Boolean;        {True after last MCB is found}
  584.     idbyte : Byte;            {Byte that DOS uses to identify an MCB}
  585.  
  586.     procedure StoreTheBlock(var mcbSeg, nextSeg : Word;
  587.                             var gotFirst, gotLast : Boolean);
  588.       {-Store information regarding the memory block}
  589.     var
  590.       nextID : Byte;
  591.       PspAdd : Word;       {Segment address of the current PSP}
  592.       mcbLen : Word;       {Size of the current memory block in paragraphs}
  593.  
  594.     begin
  595.  
  596.       PspAdd := MemW[mcbSeg:1]; {Address of program segment prefix for MCB}
  597.       mcbLen := MemW[mcbSeg:3]; {Size of the MCB in paragraphs}
  598.       nextSeg := Succ(mcbSeg+mcbLen); {Where the next MCB should be}
  599.       nextID := Mem[nextSeg:0];
  600.  
  601.       if gotLast or (nextID = EndBlockID) or (nextID = MidBlockID) then begin
  602.         inc(BlockMax);
  603.         gotFirst := True;
  604.         with Blocks[BlockMax] do begin
  605.           mcb := mcbSeg;
  606.           psp := PspAdd;
  607.         end;
  608.         {Store master COMMAND.COM segment}
  609.         if CommandSeg = 0 then
  610.           if (McbSeg+1 = PspAdd) and (MemW[PspAdd:$16] = PspAdd) then
  611.             CommandSeg := PspAdd;
  612.       end;
  613.     end;
  614.  
  615.   begin
  616.  
  617.     {Initialize}
  618.     StartMCB := OS(MCB1).S;
  619.     mcbSeg := StartMCB;
  620.     gotFirst := False;
  621.     gotLast := False;
  622.     BlockMax := 0;
  623.     CommandSeg := 0;
  624.  
  625.     {Scan all memory until the last block is found}
  626.     repeat
  627.       idbyte := Mem[mcbSeg:0];
  628.       if idbyte = MidBlockID then begin
  629.         StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast);
  630.         if gotFirst then
  631.           mcbSeg := nextSeg
  632.         else
  633.           inc(mcbSeg);
  634.       end else if gotFirst and (idbyte = EndBlockID) then begin
  635.         gotLast := True;
  636.         StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast);
  637.       end else
  638.         {Start block was invalid}
  639.         gotLast := True;
  640.     until gotLast;
  641.   end;
  642.  
  643.   const
  644.     KbdStart = $1E;
  645.     KbdEnd = $3C;
  646.   var
  647.     KbdHead : Word absolute $40 : $1A;
  648.     KbdTail : Word absolute $40 : $1C;
  649.  
  650.   procedure StuffKey(W : Word);
  651.     {-Stuff one key into the keyboard buffer}
  652.   var
  653.     SaveKbdTail : Word;
  654.   begin
  655.     SaveKbdTail := KbdTail;
  656.     if KbdTail = KbdEnd then
  657.       KbdTail := KbdStart
  658.     else
  659.       Inc(KbdTail, 2);
  660.     if KbdTail = KbdHead then
  661.       KbdTail := SaveKbdTail
  662.     else
  663.       MemW[$40:SaveKbdTail] := W;
  664.   end;
  665.  
  666.   procedure StuffKeys(Keys : string; ClearFirst : Boolean);
  667.     {-Stuff up to 16 keys into keyboard buffer}
  668.   var
  669.     Len : Byte;
  670.     I : Byte;
  671.   begin
  672.     if ClearFirst then
  673.       KbdTail := KbdHead;
  674.     Len := Length(Keys);
  675.     if Len > 16 then
  676.       Len := 16;
  677.     for I := 1 to Length(Keys) do
  678.       StuffKey(Ord(Keys[I]));
  679.   end;
  680.  
  681.   function ExistFile(path : String) : Boolean;
  682.     {-Return true if file exists}
  683.   var
  684.     f : file;
  685.   begin
  686.     Assign(f, path);
  687.     Reset(f);
  688.     if IoResult = 0 then begin
  689.       ExistFile := True;
  690.       Close(f);
  691.     end else
  692.       ExistFile := False;
  693.   end;
  694.  
  695. begin
  696.   DosV := DosVersion;
  697.   DosList := GetDosListPtr;     {pointer to dos list of lists}
  698.   Mcb1 := Ptr(MemW[OS(DosList).S:OS(DosList).O-2], 0); {first Mcb}
  699. end.
  700.  
  701.