[Back to HARDWARE SWAG index] [Back to Main SWAG index] [Original]

    {
    I've posted a working util that lists the Device Drivers that are resident in
    memory.  It uses the header record to point to the next driver in the chain
    and "walks" the memory chain until an offset end flag is reached.  Hope you
    enjoy it and that it isn't too sloppy.  At the end, I have a question that
    needs to be answered if you're interested....
    }

    program DevList;

    { this program walks the device driver memory chain. Each device
      driver points to the next until the ENDFLAG is reached.  I use
      the popular undocumented DOS function $52 to jump to the DOS
      "List of Lists" then $22 bytes beyond that, the first device in
      the chain (NUL) can be found.

      Thanks to Ralf Brown and his valuable MS DOS Interrupts List,
      to Timo Salmi, and to the person(?) who wrote the cool
      hex-to-string conversion functions that I use all the time.
    }

    {$M 8192,0,0}

    uses
      DOS;

    type
      pstrg = string[9];                { pointer conversion format }

      Array8C = array [1..8] of char; { for device and file names }

      DevRec = record
        NextDev_ofs  : word; {pointer to next device header, offset value}
        NextDev_seg  : word; {pointer to next device header, segment value}
        Attributes   : word; {Attributes: block or char, IOCTL, etc.}
        Strategy     : word; {pointer to device strategy routine, offset}
        Interrupt    : word; {pointer to device interrupt routine, offset}
        NameDev      : Array8C; {Name if char, or units if block}
      end;
      DevPtr = ^DevRec;

      DevFileRec = record
        FileName : Array8C;
      end;
      DevFilePtr = ^DevFileRec;

    const
      LOL_HEADDEV_NUL  = $22; { offset from "List of Lists"
                                to NUL device header }
      FNAME            = $8;
      ENDFLAG          = $FFFF;
      STDDEVS : array [1..12] of Array8C =
        ('NUL     ', 'CON     ', 'AUX     ', 'PRN     ',
         'CLOCK$  ', 'COM1    ', 'COM2    ', 'COM3    ',
         'COM4    ', 'LPT1    ', 'LPT2    ', 'LPT3    ');

    var
      r       : registers;
      i,        { index }
      Adjust  : byte;
      Header  : DevPtr;
      DevFile : DevFilePtr;
      Valid,
      Done    : boolean;

    function BinW(Decimal : word) : string;
    const
      BINDIGIT : array [0..1] of char = '01';
    var
      i     : byte;
      Binar : string;
    begin
      fillchar (binar, sizeof(Binar), ' ');
      Binar [0] := chr(16);
      for i := 0 to 15 do
        Binar[16-i] := BINDIGIT[(Decimal shr i) and 1];
      BinW := Binar;
    end;

    function HexN (b : byte) : char;        { convert nibble to char }
    begin
      b := b and 15;                   { forces to only 4 bits }
      if b > 9 then
         inc(b,7);                    { adjust for hex digits };
      HexN := chr(b+48);              { convert to character }
    end;

    function HexB(b : byte) : string;
    begin
      HexB := HexN (b shr 4) + HexN (b);  { assemble the nibbles }
    end;

    function HexW(w : word) : string;
    begin
    {$R-}
      hexw := HexB(w shr 8) + HexB(w);  { assemble the bytes }
    {$R+}
    end;

    function HexL(l : longint) : string;
    begin
      HexL := HexW(l shr 16) + HexW(l); { assemble the words }
    end;

    function XP(p : pointer) : pstrg;         { display pointer P }
    begin
      XP := HexW(seg(p^)) + ':' + HexW(ofs(p^));
    end;

    begin
      assign(output, '');
      rewrite(output);     { allow command line redirection }
      writeln('Device':0, 'Address':12, 'Strat':10, 'Intrpt':8,
              'Attrib':10, 'File Name':23);
      for i := 1 to 69 do
        write('-');
      writeln;

      with r do
      begin
        es := 0;
        bx := 0;
        ah := $52;
        { this is an undocumented DOS function call:
          Get pointer to DOS "List of Lists" }
        msdos (r);
        { es and bx now have values }
        if (es = 0) and (bx = 0) then
          halt(0);

        Header := ptr(es, bx + LOL_HEADDEV_NUL); { we get NUL dev from this }
      end; {with}

      Done := FALSE; { dummy variable to keep the repeat loop going,
                        otherwise would have to duplicate the output
                        routines one more time for the final device. }
      repeat
        with Header^ do
        begin
          Adjust := 0;
          { adjust keeps display columns aligned, bit 15 set is a Character
            device, if clear it is a Block device and 1st byte is # of block
            devs supported}

          if boolean ((Attributes shr 15) and 1) = TRUE then
            write (NameDev)
          else
          begin
            write ('BLKdev=', byte (NameDev[1]));
            Adjust := byte (NameDev[1]) div 10;
          end;

          write(XP(Header) : 12 - Adjust);
          write(HexW(Strategy) : 7);
          write(HexW(Interrupt) : 7);
          write(HexW(Attributes) : 7, '=');
          write(BinW(Attributes));

          { this next section I can't find documented anywhere, but I observed it
            and decided to include it anyway, with MSDOS v5.0, others are unknown.
            The file name's extension isn't saved and doesn't matter, either. }

          if ofs(Header^) < FNAME then
          { "borrow" from the segment and give it to the offset }
            DevFile := ptr(seg(Header^) - $1, ofs(Header^) + $10 - FNAME)
          else
            DevFile := ptr(seg(Header^), ofs(Header^) - FNAME);

          Valid := TRUE;
          for i := 1 to 12 do
            if DevFile^.FileName = STDDEVS[i] then
              Valid := FALSE;

          if Valid then
            for i := 1 to 8 do
              if not (DevFile^.Filename[i] in [' '..'z']) then
                Valid := FALSE;

          if {still} Valid then
            write ('  ', DevFile^.FileName);

          writeln;
          if NextDev_ofs = ENDFLAG then
            exit; { end of the device chain }

          Header := ptr(NextDev_seg, NextDev_ofs);

        end; {with}
      until Done;
    end.
    {
    The question: I have seen utils that do this actually give the size of
    the driver in memory.  MSD and PMap both do this.  Does anybody know
    how I can determine the size of the driver in memory?
    }

[Back to HARDWARE SWAG index] [Back to Main SWAG index] [Original]