[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]