Contributor: WILLIAM PLANKE { 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? }