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