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