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