
/* I owe many many thanks to the writer of DiskVerify for this wonderful
   BCPL compatible device list munging code (I never would have figured
   this >>2BADPTR() stuff out)

   I owe many many thanks to MANX, who make the only compiler for the masses
   who only have a 68000 and a meg of ram...   Lettuce is rabbit cr*p!

    This is freely distributable, assuming that you don't make more than $5
    for it, and that you don't attempt to distribute modified versions
    without asking me first and giving me a copy...  And that you leave this
    notice here...  AND and that you distribute the ENTIRE archive,
    including, yes, THE SOURCE.  There is nothing worse than a programmer
    to have to send a letter to somebody way off in CA to convince them to
    send the source that has been stripped from their copy of something...

    TO send me your contributions, put them in a crate and send to

        David Donley (Author of USEFUL programs like IBM and Bounce)
        1708 Harkness
        Manhattan Beach, CA  90266

    To compile this, type into your CLI:

cc DSpeed
ln DSpeed -lc
*/

#include <exec/memory.h>
#include <exec/devices.h>
#include <exec/io.h>
#include <libraries/dosextens.h>
#include <libraries/filehandler.h>
#include <devices/trackdisk.h>
#include <stdio.h>

extern struct DosLibrary *DOSBase;              /* DOS Library base pointer */

struct MsgPort *read_port;
struct IOStdReq *read_req;

char *device_name;

unsigned long unit;                                     /* unit number */
unsigned long numsects;                         /* number of sectors/track */
unsigned long numheads;                         /* number of heads/cylinder */
unsigned long locyl;                            /* low cylinder number */
unsigned long hicyl;                            /* high cylinder number */
unsigned long sectsize;

unsigned long CPUTest,CPUTemp;

unsigned long bytespercyl;
BYTE *diskbuffer;
ULONG minmicros=0;

extern struct MsgPort *CreatePort();
extern struct IOStdReq *CreateStdIO();
void *AllocMem();
struct Task *HogTask,*CreateTask();

main(argc, argv)
int argc;
char *argv[];
{
    USHORT cylinders,head;
    ULONG temp,i,secs,micros,bps;
    UBYTE *base=0xbfe001;
    void HTask();

    if(!(read_port=CreatePort(0L,0L)))
        quit(20);
    if(!(read_req=CreateStdIO(read_port)))
        quit(20);
    if(argc<2)
    {
        printf("Format: DSpeed2 <DRIVE> [<CYLS>]\n");
        quit(20);
    }
    if(argv[1][strlen(argv[1])-1]==':')
        argv[1][strlen(argv[1])-1]=0;
    init_device(argv[1]);

    if(OpenDevice(device_name,unit,read_req,0L))
    {
        printf("Error opening device %s [%lu]\n",device_name,unit);
        quit(20);
    }
    bytespercyl=sectsize*numsects*numheads;
    if(!(diskbuffer=(BYTE *)AllocMem(bytespercyl,MEMF_CHIP)))
    {
        printf("Can't get disk cylinder buffer!");
        CloseDevice(read_req);
        quit(20);
    }
    if(argc>2)
        cylinders=atol(argv[2]);
    else
        cylinders=1;
    if(cylinders<1) cylinders=1;

    base[0xa00]=0;
    base[0x900]=0;
    base[0x800]=0;

    printf(" Buffer Size: %lu (%luK)\nTotal Length: %lu (%luK)\n\n Testing Reads...",bytespercyl,bytespercyl/1024,bytespercyl*cylinders,(bytespercyl*cylinders)/1024);
    fflush(stdout);
    SeekCyl(locyl);
    if(!(HogTask=CreateTask("DiskHog2.Task",-5L,HTask,2000L)))
    {
        printf("Sorry, can't start CPU test task\n");
        quit(20);
    }
    temp=(base[0xa00]*65535)+(base[0x900]*256)+base[0x800];
    for(i=0;i<cylinders;i++)
    {
        ReadCyl(locyl);
        if(read_req->io_Error)
        {
            printf("\b\b\b - Error during test\n");
            goto end;
        }
        Chk_Abort();
    }
    micros=(base[0xa00]*65535)+(base[0x900]*256)+base[0x800];
    CPUTemp=CPUTest;
    if(micros-temp)
    {
        bps=(bytespercyl*cylinders*60)/(micros-temp);
        printf("\r Reads: %lu (%luK)  CPU: %lu%%  Acuracy: 1 in %lu, +-%lu bytes\n",bps,bps/1024,((CPUTemp/(micros-temp))*100L)/3141L,micros-temp,bps/(micros-temp));
        minmicros=micros-temp;
    }
    else
        printf("\b\b\b - Divide/0 error in time calculation- Try more cyls\n");
    printf("Testing Writes...");
    fflush(stdout);
    CPUTest=0;
    temp=(base[0xa00]*65535)+(base[0x900]*256)+base[0x800];
    for(i=0;i<cylinders;i++)
    {
        WriteCyl(locyl);
        if(read_req->io_Error)
        {
            printf("\b\b\b - Error during test\n");
            goto end;
        }
        Chk_Abort();
    }
    micros=(base[0xa00]*65535)+(base[0x900]*256)+base[0x800];
    CPUTemp=CPUTest;
    if(micros-temp)
    {
        bps=((bytespercyl*cylinders*60)/(micros-temp));
        printf("\rWrites: %lu (%luK)  CPU: %lu%%  Acuracy: 1 in %lu, +-%lu bytes\n",bps,bps/1024,((CPUTemp/(micros-temp))*100L)/3141L,micros-temp,bps/(micros-temp));
        if(micros-temp<minmicros) minmicros=micros-temp;
    }
    else
        printf("\b\b\b - Divide/0 error in time calculation\n");
    if(!minmicros)
        printf("WARNING: Test results unusable- Try again with at least 100 cyls\n");
    else
        if(minmicros<60)
            printf("WARNING: Test results unusable- Try again with at least %lu cyls\n",((60/minmicros)*2)>cylinders?((60/minmicros)*2):cylinders*2);
end:
    quit(0);
}

ReadCyl(cyl)
ULONG cyl;
{
    read_req->io_Length=bytespercyl;
    read_req->io_Data=(APTR)diskbuffer;
    read_req->io_Command=CMD_READ;
    read_req->io_Offset=cyl*bytespercyl;
    DoIO(read_req);
}

WriteCyl(cyl)
ULONG cyl;
{
    read_req->io_Length=bytespercyl;
    read_req->io_Data=(APTR)diskbuffer;
    read_req->io_Command=CMD_WRITE;
    read_req->io_Offset=cyl*bytespercyl;
    DoIO(read_req);
}

SeekCyl(cyl)
ULONG cyl;
{
    read_req->io_Length=sectsize;
    read_req->io_Data=(APTR)diskbuffer;
    read_req->io_Command=CMD_READ;
    read_req->io_Offset=cyl*bytespercyl;
    DoIO(read_req);
}

MotorOff()
{
    read_req->io_Length=0;
    read_req->io_Command=TD_MOTOR;
    DoIO(read_req);
}

quit(return_code)
{
    if(HogTask) DeleteTask(HogTask);
    if(read_req)
    {
        if(read_req->io_Device)
        {
            MotorOff();
            CloseDevice(read_req);
        }
        DeleteStdIO(read_req);
    }
    if(diskbuffer) FreeMem(diskbuffer,bytespercyl);
    if(read_port) DeletePort(read_port);
    exit(return_code);
}

Chk_Abort()
{
    if(SetSignal(0,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
    {
        MotorOff();
        printf("\n");
        quit(0);
    }
}

init_device(name)
register char *name;
{
    struct RootNode *root_node;
    struct DosInfo *info_node;
    register struct DeviceNode *device_node;
    struct FileSysStartupMsg *startup_msg;
    unsigned long *envptr;

    Forbid();
    root_node=(struct RootNode *)DOSBase->dl_Root;
    info_node=(struct DosInfo *)BADDR(root_node->rn_Info);
    device_node=(struct DeviceNode *)BADDR(info_node->di_DevInfo);

    while(device_node)
    {
        if(device_node->dn_Type==DLT_DEVICE)
        {
            if(!match(name,BADDR(device_node->dn_Name)+1))
                break;
        }
        device_node=(struct DeviceNode *)BADDR(device_node->dn_Next);
    }
    if(!device_node)
    {
        Permit();
        printf("Device %s: not found\n",name);
        quit(20);
    }
    startup_msg=(struct FileSysStartupMsg *)BADDR(device_node->dn_Startup);
    unit=startup_msg->fssm_Unit;          /* unit number */
    device_name=(char *)BADDR(startup_msg->fssm_Device)+1;
    envptr=(unsigned long *)BADDR(startup_msg->fssm_Environ);
    if(!envptr || envptr==-1 || envptr[DE_TABLESIZE]<DE_UPPERCYL)
    {
        Permit();
        printf("Device %s: is not a real, honest-to-goodness disk device\n",name);
        quit(20);
    }
    locyl=envptr[DE_LOWCYL];
    hicyl=envptr[DE_UPPERCYL];
    numheads=envptr[DE_NUMHEADS];
    numsects=envptr[DE_BLKSPERTRACK];
    sectsize=envptr[DE_SIZEBLOCK]*4;
    Permit();
}

match(s1,s2)
register char *s1,*s2;
{
        register char c;
        while((c=toupper(*s1++))==toupper(*s2++))
            if(!c) return(0);
        return(1);
}

void HTask()
{
    geta4();
    while(1) CPUTest++;
}
