home *** CD-ROM | disk | FTP | other *** search
/ World of Shareware - Software Farm 2 / wosw_2.zip / wosw_2 / PASCAL / TSRUTILS.ZIP / DEVICE.PAS < prev    next >
Pascal/Delphi Source File  |  1989-05-04  |  12KB  |  458 lines

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