home *** CD-ROM | disk | FTP | other *** search
/ Frostbyte's 1980s DOS Shareware Collection / floppyshareware.zip / floppyshareware / GLEN / TSRSRC32.ZIP / DEVICE.PAS next >
Pascal/Delphi Source File  |  1991-11-22  |  11KB  |  386 lines

  1. {
  2.  Display the DOS device driver chain.
  3.  Adapted from an assembly language program by Ray Duncan and modified by
  4.  several others.
  5.  
  6.  version 3.0 9/2/91
  7.    reorganize source code for consistency with other utilities
  8.  version 3.1 11/4/91
  9.    no change
  10.  version 3.2 11/22/91
  11.    no change
  12. }
  13.  
  14. {$R-,S-,I-,V-,B-,F-,A-,E-,N-,G-,X-}
  15. {$M 2048,0,655360}
  16.  
  17. program Device_Chain;
  18.  
  19. uses
  20.   Dos,
  21.   MemU;
  22.  
  23. const
  24.   MaxDevices = 100;               {Maximum number of devices to report}
  25.  
  26. type
  27.   {FCB used to find start of device driver chain}
  28.   FileControlBlock =
  29.     record
  30.       Drive : Byte;
  31.       Filename : array[1..8] of Char;
  32.       Extension : array[1..3] of Char;
  33.       CurrentBl : Word;
  34.       LRL : Word;
  35.       FilSizeLo : Word;
  36.       FilSizeHi : Word;
  37.       FileDate : Word;
  38.       FileTime : Word;
  39.       Other : array[0..7] of Byte;
  40.       CurRecord : Byte;
  41.       RelRecLo : Word;
  42.       RelRecHi : Word;
  43.     end;
  44.  
  45.   DisplayRec =
  46.     record
  47.       StartAddr : Pointer;
  48.       Header : DeviceHeader;
  49.     end;
  50.   DisplayArray = array[1..MaxDevices] of DisplayRec;
  51.  
  52. var
  53.   DeviceControlBlock : FileControlBlock; {File Control Block for NUL Device}
  54.   DevicePtr : ^DeviceHeader;      {Pointer to the next device header}
  55.   DeviceSegment : Word;           {Current device segment}
  56.   DeviceOffset : Word;            {Current device offset}
  57.   DeviceCount : Word;             {Number of devices}
  58.   Devices : DisplayArray;         {Sortable list of devices}
  59.   RawMode : Boolean;
  60.   NulStatus : Byte;
  61.  
  62.   procedure Abort(Msg : String);
  63.   begin
  64.     WriteLn(Msg);
  65.     Halt(1);
  66.   end;
  67.  
  68.   function FindNulDevice(Segm : Word) : Word;
  69.     {-Return the offset of the null device in the specified segment}
  70.   var
  71.     Ofst : Word;
  72.   begin
  73.     for Ofst := 0 to 65534 do
  74.       if MemW[Segm:Ofst] = $554E then
  75.         {Starts with 'NU'}
  76.         if Mem[Segm:Ofst+2] = Byte('L') then
  77.           {Continues with 'L'}
  78.           if (MemW[Segm:Ofst-6] and $801F) = $8004 then begin
  79.             {Has correct driver attribute}
  80.             FindNulDevice := Ofst-10;
  81.             Exit;
  82.           end;
  83.     Abort('Cannot find NUL device driver');
  84.   end;
  85.  
  86. var
  87.   Pivot : DisplayRec;
  88.   Swap : DisplayRec;
  89.  
  90.   function PhysAddr(X : Pointer) : LongInt;
  91.     {-Return the physical address given by pointer X}
  92.   begin
  93.     PhysAddr := (LongInt(OS(X).S) shl 4)+OS(X).O;
  94.   end;
  95.  
  96.   function Less(X, Y : DisplayRec) : Boolean;
  97.     {-Return True if address of X is less than address of Y}
  98.   begin
  99.     Less := (PhysAddr(X.StartAddr) < PhysAddr(Y.StartAddr));
  100.   end;
  101.  
  102.   procedure Sort(L, R : Word);
  103.     {-Sort device headers}
  104.   var
  105.     I : Word;
  106.     J : Word;
  107.   begin
  108.     I := L;
  109.     J := R;
  110.     Pivot := Devices[(L+R) shr 1];
  111.     repeat
  112.       {Sort by address}
  113.       while Less(Devices[I], Pivot) do
  114.         Inc(I);
  115.       while Less(Pivot, Devices[J]) do
  116.         Dec(J);
  117.       if I <= J then begin
  118.         Swap := Devices[J];
  119.         Devices[J] := Devices[I];
  120.         Devices[I] := Swap;
  121.         Inc(I);
  122.         Dec(J);
  123.       end;
  124.     until I > J;
  125.     if L < J then
  126.       Sort(L, J);
  127.     if I < R then
  128.       Sort(I, R);
  129.   end;
  130.  
  131.   procedure WriteHelp;
  132.     {-Write a simple help screen}
  133.   begin
  134.     WriteLn;
  135.     WriteLn('DEVICE produces a report showing the device drivers loaded into the system as');
  136.     WriteLn('well as how much memory each uses, and what interrupt vectors are taken over.');
  137.     WriteLn;
  138.     WriteLn('DEVICE accepts the following command line syntax:');
  139.     WriteLn;
  140.     WriteLn('  DEVICE [Options]');
  141.     WriteLn;
  142.     WriteLn('Options may be preceded by either / or -. Valid options are as follows:');
  143.     WriteLn('     /R     raw, unsorted report.');
  144.     WriteLn('     /?     write help screen.');
  145.     Halt(1);
  146.   end;
  147.  
  148.   procedure GetOptions;
  149.     {-Check for command line options}
  150.   var
  151.     Arg : String[127];
  152.     I : Integer;
  153.   begin
  154.     RawMode := False;
  155.     I := 1;
  156.     while I <= ParamCount do begin
  157.       Arg := ParamStr(I);
  158.       if Length(Arg) = 2 then
  159.         if (Arg[1] = '/') or (Arg[1] = '-') then
  160.           case Upcase(Arg[2]) of
  161.             'R' : RawMode := True;
  162.             '?' : WriteHelp;
  163.           end;
  164.       Inc(I);
  165.     end;
  166.   end;
  167.  
  168.   function GetName(Header : DeviceHeader) : String;
  169.     {-Get a device name}
  170.   const
  171.     Plural : array[Boolean] of String[1] = ('', 's');
  172.   var
  173.     Num : String[3];
  174.   begin
  175.     with Header do
  176.       if (Attributes and $8000) <> 0 then
  177.         GetName := DeviceName
  178.       else begin
  179.         Str(Ord(DeviceName[1]), Num);
  180.         GetName := Num+' Block Unit'+Plural[Ord(DeviceName[1]) <> 1];
  181.       end;
  182.   end;
  183.  
  184.   procedure RawReport;
  185.     {-Raw, unsorted device report}
  186.   var
  187.     D : Word;
  188.   begin
  189.     WriteLn;
  190.     WriteLn(' Starting      Next             Strategy   Interrupt   Device');
  191.     WriteLn(' Address     Hdr Addr   Attr   Entry Pnt   Entry Pnt   Name');
  192.     WriteLn('---------   ---------   ----   ---------   ---------   --------');
  193.  
  194.     for D := 1 to DeviceCount do
  195.       with Devices[D], Header do
  196.         WriteLn(HexPtr(StartAddr), '   ',
  197.                 HexW(NextHeaderSegment), ':', HexW(NextHeaderOffset), '   ',
  198.                 HexW(Attributes), '   ',
  199.                 HexW(DeviceSegment), ':', HexW(StrategyEntPt), '   ',
  200.                 HexW(DeviceSegment), ':', HexW(InterruptEntPt), '   ',
  201.                 GetName(Header));
  202.   end;
  203.  
  204.   function GetCommandPtr(DosPtr : DosRecPtr) : Pointer;
  205.     {-Get the address of COMMAND.COM}
  206.   type
  207.     McbRec =
  208.       record
  209.         ID : Char;
  210.         PSPSeg : Word;
  211.         Len : Word;
  212.       end;
  213.   var
  214.     McbPtr : ^McbRec;
  215.   begin
  216.     McbPtr := Ptr(DosPtr^.McbSeg, 0);
  217.     McbPtr := Ptr(OS(McbPtr).S+McbPtr^.Len+1, 0);
  218.     GetCommandPtr := Ptr(McbPtr^.PSPSeg, 0);
  219.   end;
  220.  
  221.   procedure WriteDevice(StartAddr : Pointer;
  222.                         Name : String;
  223.                         Start, Stop : LongInt;
  224.                         ShowVecs : Boolean);
  225.     {-Write data for one device}
  226.   var
  227.     Size : LongInt;
  228.     VecAddr : LongInt;
  229.     Vec : Byte;
  230.     Cnt : Byte;
  231.     BPtr : ^Byte;
  232.   begin
  233.     Size := Stop-Start;
  234.     ShowVecs := ShowVecs and (Size <> 0);
  235.  
  236.     Write(HexPtr(StartAddr), '   ');
  237.     if Size <> 0 then
  238.       Write(Size:6)
  239.     else
  240.       Write('     -');
  241.     if ShowVecs then
  242.       while Length(Name) < 14 do
  243.         Name := Name+' ';
  244.     Write('   ', Name);
  245.  
  246.     if ShowVecs then begin
  247.       Cnt := 0;
  248.       for Vec := 0 to $80 {!!} do begin
  249.         VecAddr := PhysAddr(Pointer(MemL[0:4*Vec]));
  250.         if (VecAddr >= Start) and (VecAddr < Stop) then
  251.           {Points to this memory block}
  252.           if Byte(Pointer(VecAddr)^) <> $CF then begin
  253.             {Doesn't point to IRET}
  254.             if Cnt >= 12 then begin
  255.               WriteLn;
  256.               Write('                                   ');
  257.               Cnt := 0;
  258.             end;
  259.             inc(Cnt);
  260.             Write(' ', HexB(Vec));
  261.           end;
  262.       end;
  263.     end;
  264.     WriteLn;
  265.   end;
  266.  
  267.   procedure SortedReport;
  268.     {-Sorted report better for user consumption}
  269.   const
  270.     NulDevice : array[1..8] of Char = 'NUL     ';
  271.   var
  272.     D : Word;
  273.     DosCode : Pointer;
  274.     CommandPtr : Pointer;
  275.     DosPtr : DosRecPtr;
  276.     DosBuffers : SftRecPtr;
  277.     Start : LongInt;
  278.     Stop : LongInt;
  279.     FoundNul : Boolean;
  280.   begin
  281.     {Pointer to DOS variables}
  282.     DosPtr := Ptr(OS(DosList).S, OS(DosList).O-2);
  283.  
  284.     {Get the address of the lowest DOS code}
  285.     DosCode := Ptr(OS(Devices[1].StartAddr).S, 0);
  286.  
  287.     {Get the address of the start of DOS's file tables}
  288.     DosBuffers := DosPtr^.FirstSFT^.Next;
  289.  
  290.     {Get pointer to command.com}
  291.     CommandPtr := GetCommandPtr(DosPtr);
  292.  
  293.     WriteLn;
  294.     WriteLn(' Address     Bytes   Name           Hooked vectors');
  295.     WriteLn('---------   ------   -------------- --------------');
  296.     {        ssss:oooo   ssssss   nnnnnnnn       xx xx xx xx xx}
  297.  
  298.     {Display the devices}
  299.     FoundNul := False;
  300.     for D := 1 to DeviceCount-1 do begin
  301.       if FoundNul then begin
  302.         Start := PhysAddr(Devices[D].StartAddr);
  303.         Stop := PhysAddr(Devices[D+1].StartAddr);
  304.       end else if GetName(Devices[D].Header) = NulDevice then begin
  305.         FoundNul := True;
  306.         Start := PhysAddr(DosCode);
  307.         Stop := PhysAddr(Devices[D+1].StartAddr);
  308.       end else begin
  309.         Start := 0;
  310.         Stop := 0;
  311.       end;
  312.       {Protect against devices patched in after DOS}
  313.       if Stop > PhysAddr(DosBuffers) then begin
  314.         WriteLn('Detected device drivers patched in after CONFIG.SYS');
  315.         Exit;
  316.       end;
  317.       with Devices[D] do
  318.         WriteDevice(StartAddr, GetName(Header), Start, Stop, True);
  319.     end;
  320.  
  321.     {Last device}
  322.     with Devices[DeviceCount] do begin
  323.       Start := PhysAddr(StartAddr);
  324.       Stop := PhysAddr(DosBuffers);
  325.       WriteDevice(StartAddr, GetName(Header), Start, Stop, True);
  326.     end;
  327.  
  328.     {DOS buffers}
  329.     Start := PhysAddr(DosBuffers);
  330.     Stop := PhysAddr(CommandPtr);
  331.     WriteDevice(DosBuffers, 'DOS buffers', Start, Stop, False);
  332.   end;
  333.  
  334. begin
  335.   WriteLn('DEVICE ', Version, ', Copyright 1991 TurboPower Software');
  336.  
  337.   GetOptions;
  338.  
  339.   {Find the start of the device driver chain via the NUL device}
  340.   FillChar(DeviceControlBlock, SizeOf(DeviceControlBlock), 0);
  341.   with DeviceControlBlock do begin
  342.     Filename := 'NUL     ';
  343.     Extension := '   ';
  344.     asm
  345.       mov ax,$0F00
  346.       mov dx,offset devicecontrolblock
  347.       int $21
  348.       mov NulStatus,al
  349.     end;
  350.     if NulStatus <> 0 then
  351.       Abort('Error opening the NUL device');
  352.     if Hi(DosVersion) > 2 then begin
  353.       {DOS 3.0 or later}
  354.       DeviceSegment := 0;
  355.       DeviceOffset := FindNulDevice(DeviceSegment);
  356.     end else begin
  357.       {DOS 2.x}
  358.       DeviceOffset := Word(Pointer(@Other[1])^);
  359.       DeviceSegment := Word(Pointer(@Other[3])^);
  360.     end;
  361.     DevicePtr := Ptr(DeviceSegment, DeviceOffset);
  362.   end;
  363.  
  364.   {Scan the chain, building an array}
  365.   DeviceCount := 0;
  366.   while OS(DevicePtr).O <> $FFFF do begin
  367.     if DeviceCount < MaxDevices then begin
  368.       Inc(DeviceCount);
  369.       with Devices[DeviceCount] do begin
  370.         StartAddr := Pointer(DevicePtr);
  371.         Header := DevicePtr^;
  372.       end;
  373.     end;
  374.     with DevicePtr^ do
  375.       DevicePtr := Ptr(NextHeaderSegment, NextHeaderOffset);
  376.   end;
  377.  
  378.   if RawMode then
  379.     RawReport
  380.   else begin
  381.     {Sort the array in order of starting address}
  382.     Sort(1, DeviceCount);
  383.     SortedReport;
  384.   end;
  385. end.
  386.