[Back to DRIVES SWAG index] [Back to Main SWAG index] [Original]
{
> Has anyone any ideas how to interrogate an IDE drive to get its
> setup parameters, ie. number of cylinders, heads and sectors, as
> some of the more recent BIOS's seem to be able to do?
The code below identifies some of the params. Actually, it identifies
most of what you'd ever need.
Theres only one catch. The main program checks the letter given on the
command line using a function:
DriveValid(Drive: Char; var Drv: Byte): Boolean; assembler;
This returns the numeric equivalent of the Drive character into Drv and
returns true/false if the Drive is Valid. The trick is the validity check
is done by DOS. Meaning that on my machine, my second drive, a HPFS drive
won't show up unless I stick something like Drive=3 (Second HDD) and
disable the DriveValid check. I hope this helps.
}
(*******************************************************************
idediag
shows characteristics of IDE hard disks.
Public Domain by Paolo Bevilacqua, Rome.
Rewritten from C to Turbo Pascal 7.0 by Ivan Peev, Sofia.
You can add more disk type to the idetypes[]
table, and distribuite freely.
********************************************************************)
const
{ read/write }
HDC_DATA = $01F0;
HDC_ERROR = $01F1;
HDC_SECCOU = $01F2;
HDC_SECNUM = $01F3;
HDC_CYLLOW = $01F4;
HDC_CYLHIGH = $01F5;
HDC_SDH = $01F6;
{ read }
HDC_STATUS : Word = $01F7;
HDC_ALTSTA = $03F6;
{ write }
HDC_COMMAND = $01F7;
HDC_FIXED = $03F6;
{ commands }
HDC_COMMAND_RESTORE = $10;
HDC_COMMAND_SEEK = $70;
HDC_COMMAND_READ = $20;
HDC_COMMAND_WRITE = $30;
HDC_COMMAND_FORMAT = $50;
HDC_COMMAND_READVER = $90;
HDC_COMMAND_DIAG = $90;
HDC_COMMAND_SETPAR = $91;
HDC_COMMAND_WRSTACK = $E8;
HDC_COMMAND_RDSTACK = $E4;
HDC_COMMAND_READPAR = $EC;
HDC_COMMAND_POWER = $E0;
HDC_FIXED_IRQ = $02;
HDC_FIXED_RESET = $04;
HDC_STATUS_ERROR = $01;
HDC_STATUS_INDEX = $02;
HDC_STATUS_ECC = $04;
HDC_STATUS_DRQ = $08;
HDC_STATUS_COMPLETE = $10;
HDC_STATUS_WRFAULT = $20;
HDC_STATUS_READY = $40;
HDC_STATUS_BUSY = $80;
type
TIdeTypes = record
Cylinders,
Heads,
Sectors: Word;
Name: String[38];
end;
PIdeInfo = ^TIdeInfo;
TIdeInfo = record
genconf,
fixcyls,
remcyls,
heads,
bytetrack, { bytes per track }
bytesector, { bytes per sector }
sectors, { sectors per track }
byteisg, { bytes intesector gap }
byteplo, { bytes in sync }
worduniq: Word; { words unique status }
serial: array[1..20] of Char;
contype, { controller type }
bufsiz, { buffer size in 512 byte blocks }
byteecc: Word; { ECC bytes trasferred in read/write long }
firmware: array[1..8] of Char; { firmware revision }
model: array[1..40] of Char; { model ID }
secsint, { number of sectors transferred per
interrupt } dblword, { double word transfer flag }
writepro: Word; { write protect }
end;
const
IdesInDataBase = 17;
IdeTypes: array[1..IdesInDataBase] of TIdeTypes =
((Cylinders:667; Heads:4; Sectors:33; Name:'Fujitsu M2611T (42.9 MB)'),
(Cylinders:667; Heads:8; Sectors:33; Name:'Fujitsu M2612T (85.9 MB)'),
(Cylinders:667; Heads:12; Sectors:33; Name:'Fujitsu M2613T (128.9 MB)'),
(Cylinders:667; Heads:16; Sectors:33; Name:'Fujitsu M2614T (171.9 MB)'),
(Cylinders:782; Heads:2; Sectors:27; Name:'Western Digital WD93024-A (20.6 MB)'),
(Cylinders:782; Heads:4; Sectors:27; Name:'Western Digital WD93044-A (41.2 MB)'),
(Cylinders:845; Heads:3; Sectors:35; Name:'Toshiba MK232FC (45.4 MB'),
(Cylinders:845; Heads:7; Sectors:35; Name:'Toshiba MK234FC (106 MB'),
(Cylinders:965; Heads:5; Sectors:17; Name:'Quantum ProDrive 40AT (40 MB)'),
(Cylinders:965; Heads:10; Sectors:17; Name:'Quantum ProDrive 80AT (80 MB)'),
(Cylinders:1050; Heads:2; Sectors:40; Name:'Teac SD-340 (41 MB)'),
(Cylinders:776; Heads:8; Sectors:33; Name:'Conner CP-3104 (100 MB)'),
(Cylinders:745; Heads:4; Sectors:28; Name:'Priam 3804M (40.7 MB)'),
(Cylinders:980; Heads:10; Sectors:17; Name:'Western Digitial Caviar AC280 (81 MB)'),
(Cylinders:560; Heads:6; Sectors:26; Name:'Seagate ST157A (42 MB)'),
(Cylinders:732; Heads:8; Sectors:35; Name:'ALPS ELECTRIC Co.,LTD. DR311C (102 MB)'),
(Cylinders:0; Heads:0; Sectors:0; Name:''));
type
parray = ^tarray;
tarray = array[1..256] of Word;
var
secbuf: parray;
drive: Byte;
drv: String[1];
procedure printinfo;
var
id: TIdeInfo;
capacity: Word;
types: String;
i: Integer;
function zo(const value: Byte): String;
begin
if Boolean(value) then
zo := ''
else
zo := 'not';
end;
function ToStr(value: LongInt): String;
var
S: String;
begin
Str(value, S);
ToStr := S;
end;
function ConvertHex(Value: Word): String;
const
hexTable: array[0..15] of Char = '0123456789ABCDEF';
begin
ConvertHex := hexTable[Hi(Value) shr 4] + hexTable[Hi(Value) and $f] +
hexTable[Lo(Value) shr 4] + hexTable[Lo(Value) and $f];
end;
procedure SwapBytes(var Source, Dest; Len: Byte); assembler;
asm
push ds
lds si, Source
les di, Dest
mov cl, len
xor ch, ch
@1: mov ax, ds:[si]
xchg ah, al
mov es:[di], ax
inc si
inc si
inc di
inc di
loop @1
pop ds
end;
begin
id := PIdeInfo(secbuf)^;
{ get disk type by characteristics }
i := 1;
while IdeTypes[i].Cylinders <> 0 do
Begin
if (IdeTypes[i].cylinders = id.fixcyls) and
(IdeTypes[i].heads = id.heads) and
(IdeTypes[i].sectors = id.sectors) then
Begin
types := IdeTypes[i].name;
break;
end;
inc(i);
end;
{ unknown disk }
if (IdeTypes[i].cylinders = 0) then
Begin
types := 'Unknown ';
{ calculate capacity in MB }
capacity := (LongInt(id.fixcyls) * id.heads * id.sectors) div 2048;
types := types + ToStr(capacity);
types := types + ' Mbytes';
end;
{ swap bytes in ASCII fields except for WD disks }
if (i <> 4) and (i <> 5) then
Begin
SwapBytes(id.serial, id.serial, 10);
SwapBytes(id.firmware, id.firmware, 4);
SwapBytes(id.model, id.model, 20);
end;
WriteLn('Informations for drive ', drive-2, ', ', types);
WriteLn('Drive ID ', ConvertHex(id.genconf));
WriteLn(id.fixcyls, ' fixed cylinders, ', id.remcyls, ' removables');
WriteLn(id.heads, ' heads, ', id.sectors, ' sectors');
WriteLn('Serial number: ', id.serial);
WriteLn('Controller firmware: ', id.firmware);
WriteLn('Controller model: ', id.model);
WriteLn(id.bytetrack, ' bytes per track, ', id.bytesector, ' per sector');
WriteLn(id.byteisg, ' bytes of intersector gap, ', id.byteplo, ' of sync');
WriteLn('Controller type ', id.contype, ', buffer ', id.bufsiz div 2, 'KBytes');
WriteLn(id.byteecc, ' bytes of ECC, ', id.secsint, ' sector(s) transferred per interrupt');
WriteLn('Double word transfer ', zo(id.dblword), ' allowed, ', zo(id.writepro), 'write protected.');
end;
procedure readsect; assembler;
asm
{ poll DRQ }
@1: mov dx, HDC_STATUS
in al, dx
and al, HDC_STATUS_BUSY
or al, al
jne @1
{ read up sector }
mov cx, 256
mov dx, HDC_DATA
les di, secbuf
@2: in ax, dx
mov es:[di], ax
inc di
inc di
loop @2
end;
function DriveValid(Drive: Char; var Drv: Byte): Boolean; assembler;
asm
mov ah, 19h { Save the current drive in BL }
int 21h
mov bl, al
mov dl, Drive { Select the given drive }
sub dl, 'A'
les di, DRV
mov es:[di], dl
mov ah, 0Eh
int 21h
mov ah, 19h { Retrieve what DOS thinks is current }
int 21h
mov cx, 0 { Assume false }
cmp al, dl { Is the current drive the given drive? }
jne @1
mov cx, 1 { It is, so the drive is valid }
mov dl, bl { Restore the old drive }
mov ah, 0eh
int 21h
@1: xchg ax, cx { Put the return value into AX }
end;
function CurDisk: Byte; assembler;
{ Returns current drive }
asm
mov ah, 19h
int 21h
end;
begin
if ParamCount > 0 then
Begin
drv := ParamStr(1);
drv[1] := UpCase(drv[1]);
if not DriveValid(drv[1], Drive) or not (drv[1] in ['C'..'Z']) then
Begin
WriteLn('There isn''t such drive or drive invalid!');
Halt(1);
end;
end
else
drive := CurDisk;
{ disable interrupt from drive }
Port[HDC_FIXED] := HDC_FIXED_IRQ;
{ set up task file parameter }
Port[HDC_SDH] := $A0 + (drive shl 4);
{ issue read parameters }
Port[HDC_COMMAND] := HDC_COMMAND_READPAR;
GetMem(secbuf, SizeOf(secbuf));
{ read up sector }
readsect;
{ print out info }
printinfo;
FreeMem(secbuf, SizeOf(secbuf));
end.
[Back to DRIVES SWAG index] [Back to Main SWAG index] [Original]