home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / INVOKE.ZIP / INVOKE.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1986-11-08  |  11.5 KB  |  366 lines

  1. {
  2. This program demonstrates a dynamically sized DOS shell from
  3. within a Turbo Program. The program may be compiled to use all
  4. available memory. When the DOS shell is invoked, the program
  5. will automatically release all memory above the "high water"
  6. mark of the heap, saving the Turbo stack for later recovery.
  7. As a result, the DOS shell can get up to several hundred K of
  8. RAM. When the DOS shell returns, Turbo regains the memory and
  9. restores its stack.
  10.  
  11. This concept is particularly useful for programs that use
  12. Turbo EXTENDER, since code modules there can be loaded and
  13. unloaded from the heap, and the space reused by INVOKE.
  14.  
  15. The Invoke procedure can be called from anywhere in a program,
  16. even from nested procedures.
  17.  
  18. The EXEC function used here is Bela Lubkin's. The stack and
  19. memory allocation code was written by Kim Kokkonen.
  20.  
  21. Compile with mAx heap set to A000 or larger.
  22. Cannot be run within the compiler, compile to COM file.
  23.  
  24. Version 1.1, changes NewStackSize to avoid problem on some systems, 11/4/86.
  25. }
  26.  
  27. program TestInvoke;
  28.  
  29. const                         {DO NOT REDUCE}
  30.   NewStackSize = 1700;        {Turbo Stack size (bytes) to keep while in DOS shell (>700)}
  31.   MinDOSspace = 20000;        {Minimum bytes for DOS shell to run}
  32.   StackBufferSize = 512;      {Bytes in DOS stack buffer}
  33.  
  34. type
  35.   String255 = string[255];
  36.   Registers =
  37.   record
  38.     case Integer of
  39.       1 : (Ax, Bx, Cx, Dx, Bp, Si, Di, Ds, Es, Flags : Integer);
  40.       2 : (Al, Ah, Bl, Bh, Cl, Ch, Dl, Dh : Byte);
  41.   end;
  42.  
  43. var
  44.   {Following variables are required for the shell}
  45.   TopOfStack : Integer;
  46.   StackBuffer : array[1..StackBufferSize] of Byte;
  47.   StackSeg : Integer;
  48.   StackPtr : Integer;
  49.   NewStackSeg : Integer;
  50.   NewStackPtr : Integer;
  51.   ParasToKeep : Integer;
  52.   ParasWeHave : Integer;
  53.   ParasForDos : Integer;
  54.   ExecStatus : Integer;
  55.  
  56.   Screenadr : Integer;
  57.   Screenstore : array[1..4000] of Char;
  58.   Screenx, Screeny : Integer;
  59.   Commandstr : String255;
  60.  
  61.   {Following variable is for demonstration only}
  62.   P : ^Integer;
  63.   DOScommandLine : String255 absolute CSeg : $80;
  64.  
  65.   function ScreenAddress : Integer;
  66.     {-Return the segment of the screen memory}
  67.   var
  68.     retracemode : Boolean;
  69.     regs : Registers;
  70.   begin
  71.     regs.Ax := $0F00;
  72.     {BIOS INT 10H call to get screen type}
  73.     Intr($10, regs);
  74.     retracemode := regs.Al <> 7;
  75.     if retracemode then
  76.       {Color card}
  77.       ScreenAddress := $B800
  78.     else
  79.       ScreenAddress := $B000;
  80.   end;                        {ScreenAddress}
  81.  
  82.   function StackPointer : Integer;
  83.     {-Return the stack pointer at the point of the call}
  84.   begin
  85.     inline(
  86.       $89/$E0/                {MOV     AX,SP}
  87.       $05/$08/$00/            {ADD     AX,0008}
  88.       $89/$EC/                {MOV     SP,BP}
  89.       $5D/                    {POP     BP}
  90.       $C2/$02/$00             {RET     0002}
  91.       );
  92.   end;                        {StackPointer}
  93.  
  94.   procedure Error(message : String255);
  95.     {-Demonstration error handler}
  96.   begin
  97.     WriteLn(message);
  98.     Halt(1);
  99.   end;                        {Error}
  100.  
  101.   procedure SetBlock(paras : Integer);
  102.     {-Free up some memory above this program for a DOS shell}
  103.   var
  104.     regs : Registers;
  105.   begin                       {SetBlock}
  106.     with regs do begin
  107.       Ah := $4A;
  108.       Es := CSeg;
  109.       Bx := paras;
  110.       MsDos(regs);
  111.       if Odd(Flags) then
  112.         Error('setblock error');
  113.     end;
  114.   end;                        {SetBlock}
  115.  
  116.   procedure StoreScreen;
  117.     {-Demonstration StoreScreen, causes snow on CGA}
  118.   begin
  119.     Move(Mem[Screenadr:0], Screenstore, SizeOf(Screenstore));
  120.     Screenx := WhereX;
  121.     Screeny := WhereY;
  122.   end;                        {Storescreen}
  123.  
  124.   procedure RestoreScreen;
  125.     {-Demonstration RestoreScreen, causes snow on CGA}
  126.   begin
  127.     Move(Screenstore, Mem[Screenadr:0], SizeOf(Screenstore));
  128.     GoToXY(Screenx, Screeny);
  129.   end;                        {RestoreScreen}
  130.  
  131.   procedure Invoke(command : String255);
  132.     {-Run any DOS command. Call with command='' for a new shell}
  133.   var
  134.     m : Integer;
  135.     Ch : Char;
  136.  
  137.     function SubProcess(CommandLine : String255) : Integer;
  138.       {-From Bela Lubkin's EXEC.PAS}
  139.     const
  140.       SSSave : Integer = 0;
  141.       SPSave : Integer = 0;
  142.  
  143.     var
  144.       regs : Registers;
  145.       FCB1, FCB2 : array[0..36] of Byte;
  146.       PathName : String255;
  147.       CommandTail : String255;
  148.       ParmTable : record
  149.                     EnvSeg : Integer;
  150.                     ComLin : ^Integer;
  151.                     FCB1Pr : ^Integer;
  152.                     FCB2Pr : ^Integer;
  153.                   end;
  154.       RegsFlags : Integer;
  155.  
  156.     begin
  157.       if Pos(' ', CommandLine) = 0 then begin
  158.         PathName := CommandLine+#0;
  159.         CommandTail := ^M;
  160.       end else begin
  161.         PathName := Copy(CommandLine, 1, Pred(Pos(' ', CommandLine)))+#0;
  162.         CommandTail := Copy(CommandLine, Pos(' ', CommandLine), 255)+^M;
  163.       end;
  164.       CommandTail[0] := Pred(CommandTail[0]);
  165.       with regs do begin
  166.         FillChar(FCB1, SizeOf(FCB1), 0);
  167.         Ax := $2901;
  168.         Ds := Seg(CommandTail[1]);
  169.         Si := Ofs(CommandTail[1]);
  170.         Es := Seg(FCB1);
  171.         Di := Ofs(FCB1);
  172.         MsDos(regs);          { Create FCB 1 }
  173.         FillChar(FCB2, SizeOf(FCB2), 0);
  174.         Ax := $2901;
  175.         Es := Seg(FCB2);
  176.         Di := Ofs(FCB2);
  177.         MsDos(regs);          { Create FCB 2 }
  178.         with ParmTable do begin
  179.           EnvSeg := MemW[CSeg:$002C];
  180.           ComLin := Addr(CommandTail);
  181.           FCB1Pr := Addr(FCB1);
  182.           FCB2Pr := Addr(FCB2);
  183.         end;
  184.         inline(
  185.           $8D/$96/PathName/$42/ { <DX>:=Ofs(PathName[1]); }
  186.           $8D/$9E/ParmTable/  { <BX>:=Ofs(ParmTable);   }
  187.           $B8/$00/$4B/        { <AX>:=$4B00;            }
  188.           $1E/$55/            { Save <DS>, <BP>         }
  189.           $16/$1F/            { <DS>:=Seg(PathName[1]); }
  190.           $16/$07/            { <ES>:=Seg(ParmTable);   }
  191.           $2E/$8C/$16/SSSave/ { Save <SS> in SSSave     }
  192.           $2E/$89/$26/SPSave/ { Save <SP> in SPSave     }
  193.           $FC/                { CLD}
  194.           $FA/                { Disable interrupts      }
  195.           $CD/$21/            { Call MS-DOS             }
  196.           $FA/                { Disable interrupts      }
  197.           $2E/$8B/$26/SPSave/ { Restore <SP>            }
  198.           $2E/$8E/$16/SSSave/ { Restore <SS>            }
  199.           $FB/                { Enable interrupts       }
  200.           $5D/$1F/            { Restore <BP>,<DS>       }
  201.           $9C/$8F/$86/RegsFlags/ { RegsFlags:=<CPU flags>}
  202.           $89/$86/regs);      { Regs.AX:=<AX>;          }
  203.         if Odd(RegsFlags) then
  204.           SubProcess := Ax
  205.         else
  206.           SubProcess := 0;
  207.       end;
  208.     end;                      {SubProcess}
  209.  
  210.     function GetEnvStr(SearchString : String255) : String255;
  211.       {-Return the environment variable value}
  212.     type
  213.       env = array[0..32767] of Char;
  214.     var
  215.       eptr : ^env;
  216.       estr : String255;
  217.       done : Boolean;
  218.       i : Integer;
  219.  
  220.     begin
  221.       GetEnvStr := '';
  222.       if SearchString <> '' then begin
  223.         eptr := Ptr(MemW[CSeg:$002C], 0);
  224.         i := 0;
  225.         SearchString := SearchString+'=';
  226.         done := False;
  227.         estr := '';
  228.         repeat
  229.           if eptr^[i] = #0 then begin
  230.             if eptr^[Succ(i)] = #0 then begin
  231.               done := True;
  232.               if SearchString = '==' then begin
  233.                 estr := '';
  234.                 i := i+4;
  235.                 while eptr^[i] <> #0 do begin
  236.                   estr := estr+eptr^[i];
  237.                   i := Succ(i);
  238.                 end;
  239.                 GetEnvStr := estr;
  240.               end;
  241.             end;
  242.             if Copy(estr, 1, Length(SearchString)) = SearchString then begin
  243.               GetEnvStr := Copy(estr, Succ(Length(SearchString)), 255);
  244.               done := True;
  245.             end;
  246.             estr := '';
  247.           end
  248.           else estr := estr+eptr^[i];
  249.           i := Succ(i);
  250.         until done;
  251.       end;
  252.     end;                      {GetEnvStr}
  253.  
  254.   begin                       {Invoke}
  255.  
  256.     {Save current stack seg and ptr}
  257.     inline(
  258.       $8C/$D0/                {MOV    AX,SS}
  259.       $A3/StackSeg/           {MOV    stackseg,AX}
  260.       $89/$26/StackPtr        {MOV    stackptr,SP}
  261.       );
  262.  
  263.     {The new lower stack goes above the "high water mark" of the heap }
  264.     {Heap fragmentation may cause HeapPtr to be higher than you expect}
  265.     NewStackSeg := Succ(Seg(HeapPtr^));
  266.     NewStackPtr := NewStackSize;
  267.  
  268.     {Current DOS memory allocation read from memory control block}
  269.     ParasWeHave := MemW[Pred(CSeg):3];
  270.     ParasToKeep := Succ(NewStackSeg-CSeg)+Succ(NewStackSize shr 4);
  271.     ParasForDos := ParasWeHave-ParasToKeep;
  272.  
  273.     {See if enough memory to run DOS}
  274.     if (ParasForDos > 0) and (ParasForDos < (MinDOSspace shr 4)) then
  275.       Error('Insufficient memory to run command');
  276.  
  277.     {See if enough stack buffer to store current Turbo stack}
  278.     if succ(TopOfStack-StackPtr) > StackBufferSize then
  279.       Error('Insufficient memory for internal stack buffer');
  280.  
  281.     {Build the command string}
  282.     Commandstr := GetEnvStr('COMSPEC');
  283.     if command <> '' then
  284.       Commandstr := Commandstr+' /C '+command;
  285.  
  286.     {Store and Clear physical screen}
  287.     StoreScreen;
  288.     ClrScr;
  289.     WriteLn('Type EXIT to return to program...');
  290.     m := (ParasForDos-240) shr 6;
  291.     WriteLn('Approximate memory available: ', m, 'K');
  292.  
  293.     {Copy the top of the stack to a buffer}
  294.     Move(Mem[StackSeg:StackPtr], StackBuffer, succ(TopOfStack-StackPtr));
  295.  
  296.     {Lower stack}
  297.     inline(
  298.       $FA/                    {CLI    }
  299.       $A1/NewStackSeg/        {MOV    AX,newstackseg}
  300.       $8E/$D0/                {MOV    SS,AX}
  301.       $8B/$26/NewStackPtr/    {MOV    SP,newstackptr}
  302.       $FB                     {STI    }
  303.       );
  304.  
  305.     {Deallocate memory for DOS}
  306.     SetBlock(ParasToKeep);
  307.  
  308.     {Run the DOS command}
  309.     ExecStatus := SubProcess(Commandstr);
  310.  
  311.     {Reallocate memory from DOS}
  312.     SetBlock(ParasWeHave);
  313.  
  314.     {Restore stack seg and ptr to original values}
  315.     inline(
  316.       $FA/                    {CLI    }
  317.       $A1/StackSeg/           {MOV    AX,stackseg}
  318.       $8E/$D0/                {MOV    SS,AX}
  319.       $8B/$26/StackPtr/       {MOV    SP,stackptr}
  320.       $FB                     {STI    }
  321.       );
  322.  
  323.     {Put stack buffer back on stack}
  324.     Move(StackBuffer, Mem[StackSeg:StackPtr], succ(TopOfStack-StackPtr));
  325.  
  326.     {Allow time to see result}
  327.     if command <> '' then begin
  328.       Write('Press a key to continue ');
  329.       Read(Kbd, Ch);
  330.     end;
  331.  
  332.     {Restore screen}
  333.     RestoreScreen;
  334.  
  335.     if ExecStatus <> 0 then
  336.       Error('Error while invoking DOS');
  337.  
  338.   end;                        {Invoke}
  339.  
  340. begin                         {TestInvoke}
  341.  
  342.   {Store stack size for use in DOS shell invocation later}
  343.   TopOfStack := StackPointer;
  344.  
  345.   {Get Screen address for later}
  346.   Screenadr := ScreenAddress;
  347.  
  348.   {*****************************************************
  349.   Do any desired heap allocation and deallocation here.
  350.   WARNING: a fragmented heap will not be usable by DOS.
  351.   For Dispose or Freemem to be effective, the objects
  352.   deallocated must be at the top of the heap. Mark and
  353.   Release may also be used.
  354.   ******************************************************}
  355.   GetMem(P, 4096);
  356.   FreeMem(P, 4096);
  357.  
  358.   {Example call}
  359.   if ParamCount > 0 then
  360.     Invoke(DOScommandLine)
  361.   else
  362.     {A general command shell}
  363.     Invoke('');
  364.  
  365. end.                          {TestInvoke}
  366.