program seektest;
{----------------------------------------------------------------------------
 |  Program SEEKTEST.PAS                                                    |
 |                                                                          |
 |  This program demonstrates the use of TPHRT in timing seek performance   |
 |  of a PC based hard disk drive.  The method used will determine the total|
 |  seek time of the device which includes actual disk seek, controller     |
 |  overhead, and ROM BIOS overhead.  This is a "real world" measurement    |
 |  of disk performance under actual usage conditions.                      |
 |                                                                          |
 |  This program uses both inline generic timers and BIOS interrupt         |
 |  timers to measure disk performance.                                     |
 |                                                                          |
 |  (c)1989 Ryle Design, P.O. Box 22, Mt. Pleasant, Michigan 48804          |
 |                                                                          |
 |  V3.00 Shareware Evaluation Version                                      |
 ----------------------------------------------------------------------------}
uses
    dos, crt, tphrt;

var
    regs    : registers;
    indx    : integer;
    numdisk : integer;
    atom    : byte;
    keyin   : char;
    dodad   : byte;


procedure disk_err(istat : integer);
{----------------------------------------------------------------------------
 |  This procedure outputs a description of an INT $13 error status, and    |
 |  halts program execution.                                                |
 |                                                                          |
 |  Globals referenced: none                                                |
 |                                                                          |
 |  Arguments: (integer) istat - status returned from INT $13 in AH if      |
 |                               carry flag set.                            |
 |                                                                          |
 |  Returns  : void                                                         |
 ----------------------------------------------------------------------------}
begin
    if (istat <> 0) then
    begin
        case istat of
            $01 : writeln('Disk error: Invalid command');
            $02 : writeln('Disk error: Address mark not found');
            $03 : writeln('Disk error: Disk is write-protected');
            $04 : writeln('Disk error: Requested sector not found');
            $05 : writeln('Disk error: Reset failed');
            $06 : writeln('Disk error: Floppy disk removed');
            $07 : writeln('Disk error: Bad parameter table');
            $08 : writeln('Disk error: DMA overrun');
            $09 : writeln('Disk error: DMA crossed 64KB boundary');
            $0A : writeln('Disk error: Bad sector flag set');
            $0B : writeln('Disk error: Bad track flag set');
            $0C : writeln('Disk error: Requested media type not found');
            $0D : writeln('Disk error: Invalid number of sectors on format');
            $0E : writeln('Disk error: Control data address mark detected');
            $0F : writeln('Disk error: DMA arbitration level out of range');
            $10 : writeln('Disk error: Uncorrectable CRC or ECC data error');
            $11 : writeln('Disk warning: ECC corrected data error');
            $20 : writeln('Disk error: Controller failed');
            $40 : writeln('Disk error: Seek failed');
            $80 : writeln('Disk error: Disk has timed out');
            $AA : writeln('Disk error: Drive not ready');
            $BB : writeln('Disk error: Error is undefined');
            $CC : writeln('Disk error: Write fault');
            $E0 : writeln('Disk error: Status register error');
            $FF : writeln('Disk error: Sense operation failed');
        else
            writeln('Unknown INT 13 return status ',istat);
        end;

        halt;
    end;
end; { disk_err }


procedure test_disk(which_disk : byte);
{----------------------------------------------------------------------------
 |  This procedure, which contains the actual disk test routines, does the  |
 |  following:                                                              |
 |      1. Seeks the test disk to track 0.                                  |
 |      2. Times 100 calls to seek to track 0.  Since the heads are already |
 |         on track 0, they will not move, and a estimate of the software   |
 |         overhead for each seek call can be made.                         |
 |      3. Times single track seeks to all cylinders (0-1,1-2,2-3,3-4,etc). |
 |         This provides a measurement of single track seek time.           |
 |      4. Seeks from track 0 to all tracks (0-1,0-2,0-3,0-4,etc).  This    |
 |         provides average seek time for the entire disk.                  |
 |      5. The results are reported.                                        |
 |                                                                          |
 |  TP intr() is used to call the ROM BIOS.  There is some software         |
 |  overhead incurred using this method.                                    |
 |                                                                          |
 |  Globals referenced: regs                                                |
 |                                                                          |
 |  Arguments: (char) which_disk - physical disk # - add to $80 & call      |
 |                                                                          |
 |  Returns  : void                                                         |
 ----------------------------------------------------------------------------}
var
    maxhead,maxcyl,indx                     : integer;
    seek1,seek2,seek3,hits1,hits2,hits3     : longint;

begin

    regs.dl := $80 + which_disk;                                  { get disk config }
    regs.ah := $08;
    intr($13,regs);
    if ( (regs.flags and Fcarry) <> 0) then disk_err(regs.ah);

    maxhead := regs.dh;                                     { move bits to get }
    maxcyl := ((regs.cl and $C0) shl 2) + regs.ch;          { heads & tracks   }

    writeln;
    writeln('Physical drive ',which_disk,' shows ',maxcyl+1,' cylinders, ',maxhead+1,' heads');
    writeln;

    writeln('Starting single track seek test ...');

    regs.ah := $0C;                                         { seek command                        }
    regs.ch := $00;                                         { track 0                             }
    regs.cl := $01;                                         { XTs need sector bit set, or no seek }
    regs.dh := 0;                                           { head 0                              }
    regs.dl := $80 + which_disk;                                  { disk #                              }

    intr($13,regs);                                         { seek to track 0 }
    if ( (regs.flags and Fcarry) <> 0) then disk_err(regs.ah);

    for indx := 1 to 100 do                                 { seek to 0 100 times to get ave overhead }
    begin
        regs.ah := $0C;                                     { seek command                        }
        regs.ch := $00;                                     { track 0                             }
        regs.cl := $01;                                     { XTs need sector bit set, or no seek }
        regs.dh := 0;                                       { head 0                              }
        regs.dl := $80 + which_disk;                              { disk #                              }

        t_entry(3);
        intr($13,regs);
        t_exit(3);
    end;

    for indx := 1 to maxcyl do                              { from zero, single track seek to end of disk }
    begin
        regs.ah := $0C;                                     { seek command                         }
        regs.ch := indx and $00FF;                          { mask track bits and stuff in cl & ch }
        regs.cl := ((indx and $0300) shr 2) + 1;            { cl sector bit 1 for XTs              }
        regs.dh := 0;                                       { head 0                               }
        regs.dl := $80 + which_disk;                              { disk #                               }

        t_entry(1);
        intr($13,regs);                                     { seek }
        t_exit(1);

		if ( (regs.flags and Fcarry) <> 0) then disk_err(regs.ah);

        if ((indx mod 100) = 0) then write(indx,' ');       { echo to user our progress }
    end;

    writeln;
    writeln;
    writeln('Starting full disk seek test ...');

    regs.ah := $0C;
    regs.ch := $00;                                         { back to track 0 for next test }
    regs.cl := $01;                                         { sector bit for XTs            }
    regs.dh := 0;
    regs.dl := $80 + which_disk;
    intr($13,regs);                                         { seek }

    if ( (regs.flags and Fcarry) <> 0) then disk_err(regs.ah);

    for indx := 1 to maxcyl do                              { from track 0, seek to all tracks }
    begin
        regs.ah := $0C;
        regs.ch := indx and $00FF;                          { mask tracks bits and stuff in cl & ch }
        regs.cl := ((indx and $0300) shr 2) + 1;            { cl sector bit 1 for XTs               }
        regs.dh := 0;
        regs.dl := $80 + which_disk;

        t_entry(2);
        intr($13,regs);                                     { seek }
        t_exit(2);

        if ( (regs.flags and Fcarry) <> 0) then disk_err(regs.ah);

        if ((indx mod 100) = 0) then write(indx,' ');       { echo to user our progress }

        regs.ah := $0C;
        regs.ch := $00;                                     { go back to track 0 for next seek }
        regs.cl := $01;
        regs.dh := 0;
        regs.dl := $80 + which_disk;
        intr($13,regs);

        if ( (regs.flags and Fcarry) <> 0) then disk_err(regs.ah);

    end;

    t_ask_timer(1,hits1,seek1);                             { query timers }
    t_ask_timer(2,hits2,seek2);
    t_ask_timer(3,hits3,seek3);

    writeln;
    writeln;
    writeln('Test of physical disk ',which_disk,' with source timers complete.');
    writeln('Average single track seek ............. ',((seek1/hits1)/1000.0):7:3,' milliseconds');
    writeln('Average seek to all tracks ............ ',((seek2/hits2)/1000.0):7:3,' milliseconds');
    writeln('Estimated software overhead per seek .. ',((seek3/hits3)/1000.0):7:3,' milliseconds');

    t_reset(1);                                             { reset all timers }
    t_reset(2);
    t_reset(3);

end; { test_disk }


procedure test_disk_bios(which_disk : byte);
{----------------------------------------------------------------------------
 |  This procedure is identical to test_disk but utilizes BIOS interrupt    |
 |  timing to time the disk activity.  This is an excellant tutorial on the |
 |  t_bios timer functions.                                                 |
 |                                                                          |
 |  TP intr() is used to call the ROM BIOS.  There is some software         |
 |  overhead incurred using this method.                                    |
 |                                                                          |
 |  Globals referenced: regs                                                |
 |                                                                          |
 |  Arguments: (char) which_disk - physical disk # - add to $80 & call      |
 |                                                                          |
 |  Returns  : void                                                         |
 ----------------------------------------------------------------------------}
var
    maxhead,maxcyl,indx                     : integer;
    seek1,seek2,seek3,hits1,hits2,hits3     : longint;

begin

    t_bios_start(DISK);

    regs.dl := $80 + which_disk;                                  { get disk config }
    regs.ah := $08;
    intr($13,regs);
    if ( (regs.flags and Fcarry) <> 0) then disk_err(regs.ah);

    maxhead := regs.dh;                                     { move bits to get }
    maxcyl := ((regs.cl and $C0) shl 2) + regs.ch;          { heads & tracks   }

    writeln;
    writeln('Physical drive ',which_disk,' shows ',maxcyl+1,' cylinders, ',maxhead+1,' heads');
    writeln;

    writeln('Starting single track seek test ...');

    regs.ah := $0C;                                         { seek command                        }
    regs.ch := $00;                                         { track 0                             }
    regs.cl := $01;                                         { XTs need sector bit set, or no seek }
    regs.dh := 0;                                           { head 0                              }
    regs.dl := $80 + which_disk;                                  { disk #                              }

    intr($13,regs);                                         { seek to track 0 }
    if ( (regs.flags and Fcarry) <> 0) then
    begin
        t_bios_stop;
        disk_err(regs.ah);
    end;

    t_bios_reset(DISK,$0C);                                 { zero the seek function timer }

    for indx := 1 to 100 do                                 { seek to 0 100 times to get ave overhead }
    begin
        regs.ah := $0C;                                     { seek command                        }
        regs.ch := $00;                                     { track 0                             }
        regs.cl := $01;                                     { XTs need sector bit set, or no seek }
        regs.dh := 0;                                       { head 0                              }
        regs.dl := $80 + which_disk;                              { disk #                              }

        intr($13,regs);
    end;

    t_bios_ask(DISK,$0C,hits3,seek3);
    t_bios_reset(DISK,$0C);

    for indx := 1 to maxcyl do                              { from zero, single track seek to end of disk }
    begin
        regs.ah := $0C;                                     { seek command                         }
        regs.ch := indx and $00FF;                          { mask track bits and stuff in cl & ch }
        regs.cl := ((indx and $0300) shr 2) + 1;            { cl sector bit 1 for XTs              }
        regs.dh := 0;                                       { head 0                               }
        regs.dl := $80 + which_disk;                              { disk #                               }

        intr($13,regs);                                     { seek }

        if ( (regs.flags and Fcarry) <> 0) then
        begin
            t_bios_stop;
            disk_err(regs.ah);
        end;
    
        if ((indx mod 100) = 0) then write(indx,' ');       { echo to user our progress }
    end;

    t_bios_ask(DISK,$0C,hits1,seek1);

    writeln;
    writeln;
    writeln('Starting full disk seek test ...');

    regs.ah := $0C;
    regs.ch := $00;                                         { back to track 0 for next test }
    regs.cl := $01;                                         { sector bit for XTs            }
    regs.dh := 0;
    regs.dl := $80 + which_disk;
    intr($13,regs);                                         { seek }


    if ( (regs.flags and Fcarry) <> 0) then
    begin
        t_bios_stop;
        disk_err(regs.ah);
    end;    

    t_bios_reset(DISK,$0C);

    for indx := 1 to maxcyl do                              { from track 0, seek to all tracks }
    begin
        regs.ah := $0C;
        regs.ch := indx and $00FF;                          { mask tracks bits and stuff in cl & ch }
        regs.cl := ((indx and $0300) shr 2) + 1;            { cl sector bit 1 for XTs               }
        regs.dh := 0;
        regs.dl := $80 + which_disk;

        intr($13,regs);                                     { seek }

        if ( (regs.flags and Fcarry) <> 0) then
        begin
            t_bios_stop;
            disk_err(regs.ah);
        end;    

        if ((indx mod 100) = 0) then write(indx,' ');       { echo to user our progress }

        regs.ah := $0C;
        regs.ch := $00;                                     { go back to track 0 for next seek }
        regs.cl := $01;
        regs.dh := 0;
        regs.dl := $80 + which_disk;
        intr($13,regs);

        if ( (regs.flags and Fcarry) <> 0) then
        begin
            t_bios_stop;
            disk_err(regs.ah);
        end;    

    end;

    t_bios_ask(DISK,$0C,hits2,seek2);

    writeln;
    writeln;
    writeln('Test of physical disk ',which_disk,' with BIOS timers complete.');
    writeln('Average single track seek ............. ',((seek1/hits1)/1000.0):7:3,' milliseconds');
    writeln('Average seek to all tracks ............ ',((seek2/hits2)/1000.0):7:3,' milliseconds');
    writeln('Estimated software overhead per seek .. ',((seek3/hits3)/1000.0):7:3,' milliseconds');

    t_bios_stop;

end; { test_disk_bios }



begin

    t_start;                                                { start TPHRT }

    writeln('SeekTest  TPHRT V3 Demonstration Series');
    writeln;
    write('Checking equipment ... ');

    regs.ah := $08;
    regs.dl := $80;
    intr($13,regs);                                         { get available physical disks }

    if ( (regs.flags and Fcarry) <> 0) then
    begin
        writeln('There are no hard disks on this system!');
        writeln('SeekTest complete');
        halt;
    end;

    numdisk := regs.dl;                                     { DL has total disks on controller }
    writeln(numdisk,' physical hard disk(s) found');
    writeln;
    writeln('*** WARNING -- Do not proceed unless the test disk is backed up!');     { A word of advice ... }
    repeat
        writeln;
        for indx := 0 to (numdisk-1) do writeln(indx,' ... Test disk ',indx);
        writeln(numdisk,' ... Exit SeekTest');
        repeat
            write('Select disk or exit (0-',numdisk,') >> ');
            readln(atom);
        until ( (atom >= 0) and (atom <= numdisk) );

        if (atom = numdisk) then
        begin
            t_stop;                                         { shut down TPHRT before exit }
            writeln('SeekTest complete.');
            halt;
        end;

        repeat
            writeln;
            writeln('Test with source or BIOS timers?');
            writeln('0 .... Source timers');
            writeln('1 .... BIOS timers');
            write('Source or BIOS timers (0-1) >> ');
            readln(dodad);
        until ( (dodad >= 0) and (dodad <= 1) );
        
        if (dodad = 0) then
            test_disk(atom)
        else
            test_disk_bios(atom);

    until (atom = numdisk);

end.  { seektest }
