/* vb2091 0.2b (c) in 1994 by Volker Barthelmann */

#define __USE_SYSBASE 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <exec/semaphores.h>
#include <devices/trackdisk.h>
#include <devices/timer.h>
#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <dos/dostags.h>

#ifdef __SASC
#include <proto/exec.h>
#include <proto/dos.h>
#endif

/*#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>*/

#define MAXUNIT 8

char zahl[]="0123456789";
struct IOStdReq *IO[MAXUNIT]={0,0,0,0,0,0,0,0};
struct SignalSemaphore *ss;

#define DEV_BEGINIO (-30)

#define min(a,b)    ((a) < (b) ? (a) : (b))

#define STAY   1
#define BROKEN 2
#define SINGLEF 4
#define NOCACHE 8
#define NOWRITE 16

#ifdef __SASC
void __asm (*oldbeginio)(register __a1 char *req,register __a6 char *dev);
#else
void            (*oldbeginio)(/*register char *req asm("a1"),
                              register char *dev asm("a6")*/)=0;
#endif


void    *Unit[MAXUNIT]={0,0,0,0,0,0,0,0};

int SIZE=262144,msize,flags=0,UnitNr=0,unitmask=0;
int MAXN=16,MIN1BUF=131072,MIN2BUF=65536;
char *buffer;

#ifdef __SASC
void __saveds __asm mybeginio(register __a1 struct IOStdReq *req,register __a6 struct Device *dev)
{
#else
void mybeginio()
{
    register struct IOStdReq *req asm ("a1");
    register struct Device *dev asm ("a6");
#endif
    struct IOStdReq *mreq;
    struct Device *mdev;
    struct MsgPort *port;
    int actual,data,offset,length,lactual,ldata,flag,msize,un=-1;

    mdev=dev;mreq=req;
    ObtainSemaphore(ss);

    for(flag=0;flag<MAXUNIT;flag++) if(req->io_Unit==Unit[flag]) un=flag;
    if(un==-1) goto old;

    if((req->io_Command & 0xFF) == CMD_READ){
        if((flags & BROKEN)==0 && (long) req->io_Data<16777216) goto old;
        port=(struct MsgPort *)CreatePort(NULL,0);
        IO[un]->io_Message.mn_ReplyPort=port;
        actual=0;
        length=mreq->io_Length;
/*        if(length>=65536) msize=min(length/4,SIZE/2); else msize=min(length,SIZE/2);*/
        if(length>MIN2BUF*MAXN){msize=min((length+MAXN-1)/MAXN,SIZE/2);}
         else{if(length<MIN1BUF) msize=min(length,SIZE/2); else msize=min(MIN2BUF,SIZE/2);}
        if(msize>=512) msize=msize&0xfffffe00;
        offset=mreq->io_Offset;
        data=(int) mreq->io_Data;
        lactual=flag=0;ldata=data;
        if(flags&NOCACHE) CacheControl(0,CACRF_EnableD);
        do{
            IO[un]->io_Offset=offset;
            if(flags&SINGLEF){
                IO[un]->io_Length=min(SIZE,length);
                IO[un]->io_Data=buffer;
            }else{
                IO[un]->io_Length=min(msize,length);
                if(flag==0) IO[un]->io_Data=buffer; else
                  IO[un]->io_Data=buffer+SIZE/2;
            }
            IO[un]->io_Command=CMD_READ;
            req=IO[un];dev=IO[un]->io_Device;
#ifdef __SASC
            oldbeginio((char *) req,(char *) dev);
#else
            oldbeginio();
#endif
            if((flags&SINGLEF)==0){
                if(flag!=0) CopyMem(buffer,(APTR) ldata,lactual);
                else CopyMem(buffer+SIZE/2,(APTR) ldata,lactual);
            }
            WaitIO((struct IORequest *) IO[un]);
            actual+=IO[un]->io_Actual;lactual=IO[un]->io_Actual;
            ldata=data;data+=IO[un]->io_Actual;
            offset+=IO[un]->io_Actual;
            length-=IO[un]->io_Actual;
            flag=1-flag;
            if(flags&SINGLEF) CopyMem(buffer,(APTR) ldata,lactual);

        }while(length>0 && IO[un]->io_Actual>=IO[un]->io_Length);
        if((flags&SINGLEF)==0){
            if(flag!=0) CopyMem(buffer,(APTR) ldata,lactual);
            else CopyMem(buffer+SIZE/2,(APTR) ldata,lactual);
        }
        if(flags&NOCACHE) CacheControl(0xffffffff,CACRF_EnableD);
        mreq->io_Actual=actual;
        mreq->io_Error=IO[un]->io_Error;
        DeletePort(port);
        if((mreq->io_Flags & IOF_QUICK) ==0)
            ReplyMsg(&mreq->io_Message);
        ReleaseSemaphore(ss);
        return;
    }
    if((req->io_Command & 0xFF) == CMD_WRITE){
        if(flags&NOWRITE) goto old;
        if((flags & BROKEN)==0 && (long) req->io_Data<16777216) goto old;
        port=(struct MsgPort *)CreatePort(NULL,0);
        IO[un]->io_Message.mn_ReplyPort=port;
        actual=0;
        length=mreq->io_Length;
        offset=mreq->io_Offset;
        data=(int) mreq->io_Data;
        if(flags&NOCACHE) CacheControl(0,CACRF_EnableD);
        do{
            if(SIZE>=length) IO[un]->io_Length=length; else
              IO[un]->io_Length=SIZE;
            CopyMem((APTR) data,buffer,IO[un]->io_Length);
            IO[un]->io_Offset=offset;
            IO[un]->io_Data=buffer;
            IO[un]->io_Command=CMD_WRITE;
            req=IO[un];dev=IO[un]->io_Device;
#ifdef __SASC
            oldbeginio((char *) req,(char *) dev);
#else
            oldbeginio();
#endif
            WaitIO((struct IORequest *) IO[un]);
            actual+=IO[un]->io_Actual;
            data+=IO[un]->io_Actual;
            offset+=IO[un]->io_Actual;
            length-=IO[un]->io_Actual;
        }while(length>0 && IO[un]->io_Actual>=IO[un]->io_Length);
        if(flags&NOCACHE) CacheControl(0xffffffff,CACRF_EnableD);
        mreq->io_Actual=actual;
        mreq->io_Error=IO[un]->io_Error;
        DeletePort(port);
        if((mreq->io_Flags & IOF_QUICK) ==0)
            ReplyMsg(&mreq->io_Message);
        ReleaseSemaphore(ss);
        return;
    }
old:
#ifdef __SASC
    oldbeginio((char *) req,(char *) dev);
#else
    oldbeginio();
#endif
    ReleaseSemaphore(ss);

}


int
main(int argc, char *argv[])
{
    long            tmp,op;
    int             devopen,i;
    struct MsgPort *iosink[MAXUNIT]={0,0,0,0,0,0,0,0};
    char *Device="2nd.scsi.device",*p;

    tmp = 0;
/*    IO = NULL;
    iosink = NULL; */
    devopen = 0;
    Device = "2nd.scsi.device";
    op=Output();
    for(i=1; i<argc; i++){
        /*printf("Arg: %d  %s: ",i,argv[i]);*/
        if(!strcmp("BUFSIZE",argv[i])){
            /*printf("Buffersize\n");*/SIZE=atoi(argv[++i])*1024;continue;}
        if(!strcmp("MIN1BUF",argv[i])){
            MIN1BUF=atoi(argv[++i])*1024;continue;}
        if(!strcmp("MIN2BUF",argv[i])){
            MIN2BUF=atoi(argv[++i])*1024;continue;}
        if(!strcmp("MAXN",argv[i])){
            MAXN=atoi(argv[++i]);continue;}
        if(!strcmp("DEVICE",argv[i])){
            /*printf("Device\n");*/ Device=argv[++i];continue;}
        if(!strcmp("UNIT",argv[i])){
            p=argv[++i];
            while(*p!=0){
                if(*p<'0'||*p>'7') {Write(op,"You can only use unit 0..7!!\n",29L);exit(0);}
                unitmask|=1<<(*p-'0');
                p++;
            }
            continue;}
        if(!strcmp("BROKEN",argv[i])){
            flags|=BROKEN;continue;}
        if(!strcmp("SINGLE",argv[i])){
            flags|=SINGLEF;continue;}
        if(!strcmp("NOCACHE",argv[i])){
            flags|=NOCACHE;continue;}
        if(!strcmp("NOWRITE",argv[i])){
            flags|=NOWRITE;continue;}
        Write(op,"Wrong argument: ",16L);
        Write(op,argv[i],strlen(argv[i]));
        Write(op,"\nPLEASE READ THE MANUAL !!\n",27L);
        exit(0);
    }

    Write(op,"vb2091 V0.2b (c) in 1994 by Volker Barthelmann\n",47L);
    /*printf("Flags: %d  SIZE: %d  Device: %s\n",flags,SIZE,Device);*/
    /*printf("unitmask: %d\n",unitmask);*/
    for(i=0;i<MAXUNIT;i++){
        if((unitmask&(1<<i))==0) continue;
        iosink[i] = (struct MsgPort *)CreatePort(NULL, 0);
        IO[i] = (struct IOStdReq *) CreateExtIO(iosink[i], sizeof (*IO[0]));
        tmp = OpenDevice(Device, i, (struct IORequest *) IO[i], 0);

        if (tmp) {
            Write(op,"Unable to open ",15L);
            Write(op,Device,strlen(Device));
            Write(op," Unit ",6L);Write(op,&(zahl[i]),1L);
            Write(op,"\n",1L);
/*            if(IO[i])DeleteExtIO((struct IORequest *) IO[i]);
            if(iosink[i]) DeletePort(iosink[i]);  */
            IO[i]=0;
            continue;
        } else{
            Write(op,"Opened ",7L);
            Write(op,Device,strlen(Device));
            Write(op," Unit ",6L);Write(op,&(zahl[i]),1L);
            Write(op,"\n",1L);

            devopen = 1;
            /*printf("Opened %s unit %d\n",Device,i);*/

            SumLibrary((struct Library *) IO[i]->io_Device);
            if(oldbeginio==NULL) oldbeginio = (void (* )()) SetFunction((struct Library *) IO[i]->io_Device, DEV_BEGINIO, (APTR) &mybeginio);
            Unit[i]=IO[i]->io_Unit;
        }
    }
    ss = (struct SignalSemaphore *)AllocMem(sizeof (struct SignalSemaphore), MEMF_PUBLIC);
    if (!ss)
        goto quit;
    InitSemaphore(ss);

    if(flags&BROKEN)
        buffer=(char *)AllocMem(SIZE,MEMF_24BITDMA|MEMF_FAST|MEMF_PUBLIC);
    else
        buffer=(char *)AllocMem(SIZE,MEMF_24BITDMA|MEMF_PUBLIC);

    if(buffer==0) {Write(op,"Cannot allocate buffer!!\n",25L);goto quit;}


    if(devopen) Wait(SIGBREAKF_CTRL_C);

quit:

    ObtainSemaphore(ss);
    for(i=MAXUNIT-1;i>=0;i--){
        if(IO[i]) /*printf("Restoring Unit %d\n",i);*/
        if(IO[i]) SetFunction((struct Library *) IO[i]->io_Device, DEV_BEGINIO, (APTR) oldbeginio);
    }
    ReleaseSemaphore(ss);


    if(buffer)
        FreeMem(buffer, SIZE);
    if (ss)
        FreeMem(ss, sizeof (struct SignalSemaphore));
    for(i=0;i<MAXUNIT;i++){
        if (devopen) {
            if(IO[i]) CloseDevice((struct IORequest *) IO[i]);
        }
        if (IO[i])
            DeleteExtIO((struct IORequest *) IO[i]);
        if (iosink[i])
            DeletePort(iosink[i]);
    }

    return 0;
}

