/* vb2091 V1.3 (14.9.97)

    (c) in 1994 by Volker Barthelmann
    modified 1996-97 by Andreas R. Kleinert,
                        Andreas_Kleinert@t-online.de

   USAGE AT YOUR OWN RISK. NOBODY CAN BE HELD RESPONSIBLE FOR ANY DAMAGES.
*/

#include "compiler.h"


#define AUTHORS "vb2091 V1.3 (c) in 1994 by Volker Barthelmann.\n"   \
                "Changes by Andreas R. Kleinert in 1996-97\n"
#define RISK    "USAGE AT YOUR OWN RISK.\n"                          \
                "NOBODY CAN BE HELD RESPONSIBLE FOR ANY DAMAGES.\n"


#ifdef __SASC
#define __USE_SYSBASE
#endif

#ifndef __inline
#define __inline inline
#endif

#ifndef inline
#define inline
#endif


#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>
#include <proto/utility.h>
#include <clib/alib_protos.h>
#else
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#endif /* __SASC */



#define DEV_BEGINIO (-30)

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


#define RESERVED (1<<0)
#define BROKEN   (1<<1)
#define SINGLEF  (1<<2)
#define NOCACHE  (1<<3)
#define NOWRITE  (1<<4)
#define NOASYNCH (1<<5)


#define MAXUNIT 8

#ifdef __SASC

void __asm (*oldbeginio)(register __a1 struct IOStdReq *req, register __a6 struct Device *dev);

__aligned struct IOStdReq *IO  [MAXUNIT] = { 0, 0, 0, 0, 0, 0, 0, 0};
__aligned void            *Unit[MAXUNIT] = { 0, 0, 0, 0, 0, 0, 0, 0};

char              __aligned zahl[]        = "0123456789";

void __inline __regargs copymem(UBYTE *s, UBYTE *d, ULONG b);
void __inline __regargs copymem(UBYTE *s, UBYTE *d, ULONG b)
{
 while(b--) *d++ = *s++;
}

void __inline __regargs copymemquick(ULONG *s, ULONG *d, ULONG b);
void __inline __regargs copymemquick(ULONG *s, ULONG *d, ULONG b)
{
 while(b) { *d++ = *s++; b -= 4; }
}

void __inline __regargs FastCopy(APTR s, APTR d, ULONG b);
void __inline __regargs FastCopy(APTR s, APTR d, ULONG b)
{
 /* -----------------------------------------------------

    #define one out of:  OS_COPY_PLAIN,  OS_COPY_OPT
                         OWN_COPY_PLAIN, OWN_COPY_OPT
                         OWN_COPY_SPECIAL

    ----------------------------------------------------- */
#define OWN_COPY_SPECIAL



 /* >>--------------------------------------------------- */
#ifdef OS_COPY_PLAIN

 CopyMem(s, d, b);

#endif

 /* >>--------------------------------------------------- */
#ifdef OS_COPY_OPT

      if((ULONG)b & 3) CopyMem(      s, d, b);
 else if((ULONG)s & 3) CopyMem(      s, d, b);
 else if((ULONG)d & 3) CopyMem(      s, d, b);
 else                  CopyMemQuick( s, d, b);

#endif

 /* >>--------------------------------------------------- */
#ifdef OWN_COPY_PLAIN

 copymem(s, d, b);

#endif

 /* >>--------------------------------------------------- */
#ifdef OWN_COPY_OPT

      if((ULONG)b & 3) copymem(      s, d, b);
 else if((ULONG)s & 3) copymem(      s, d, b);
 else if((ULONG)d & 3) copymem(      s, d, b);
 else                  copymemquick( s, d, b);

 #endif


 /* >>------------ FASTEST in comparison ---------------- */
 /* >>             on 4000/040 with 16 MB FAST            */
#ifdef OWN_COPY_SPECIAL

      if((ULONG)b & 3) CopyMem(      s, d, b);
 else if((ULONG)s & 3) CopyMem(      s, d, b);
 else if((ULONG)d & 3) CopyMem(      s, d, b);
 else                  copymemquick( s, d, b);

#endif
}

#else /* __SASC */

#ifdef __VBCC

void (*oldbeginio)(__reg("a1") struct IOStdReq *, __reg("a6") struct Device *);

#else

void            (*oldbeginio)( register __a1 struct IOStdReq *req_a1 GNUCREG(a1), register __a6 struct Device *dev GNUCREG(a6) );

#endif /* __VBCC */

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

char             zahl[]        = "0123456789";

#define FastCopy CopyMem

#endif /* __SASC */


ULONG flags      = 0,           UnitNr   = 0,      unitmask = 0;
ULONG SIZE       = 262144,      DIVSIZE2 = 131072, msize;
ULONG MAXN       = 16,          MIN1BUF  = 131072, MIN2BUF  = 65536;
ULONG MULMIN2BUF = 16 * 131072;

struct SignalSemaphore *ss      = NULL;
UBYTE                  *buffer  = NULL;
UBYTE                  *buffer2 = NULL;


#ifdef __SASC
void __saveds __asm mybeginio(register __a1 struct IOStdReq *req_a1,register __a6 struct Device *dev_a6)
{
 struct IOStdReq *req = req_a1;
 struct Device   *dev = dev_a6;
 ULONG flag;
#else

#ifdef __VBCC

void mybeginio(__reg("a1") struct IOStdReq *req,__reg("a6") struct Device *dev)
{
 ULONG flag;
 geta4();

#else

ULONG __saveds ASM mybeginio( register __a1 struct IOStdReq *req_a1 GNUCREG(a1), register __a6 struct Device *dev GNUCREG(a6) )
{
 struct IOStdReq *req = req_a1;
 struct Device   *dev = dev_a6;
 ULONG flag;

#endif /* __VBCC */

#endif



 ObtainSemaphore(ss);

 for(flag = 0; flag < MAXUNIT; flag++)
  {
   if(req->io_Unit == Unit[flag])
    {
     struct IOStdReq *mreq;
     struct MsgPort  *port;
     ULONG  actual,data,offset,length,lactual,un;

     mreq = req;
     un   = flag;

     if((req->io_Command & 0xFF) == CMD_READ)
      {
       ULONG ldata, msize;


       if(!(flags & BROKEN))
         if((ULONG) req->io_Data < 16777216) goto old;


       port = (struct MsgPort *) CreatePort(NULL,0);
       if(!port) goto old;


       IO[un]->io_Message.mn_ReplyPort = port;

       actual = 0;
       length = mreq->io_Length;

       if(length < MIN1BUF)
        {
                                 msize = min( length,              DIVSIZE2);
        }else
        {
         if(length > MULMIN2BUF) msize = min((length+MAXN-1)/MAXN, DIVSIZE2);
          else                   msize = min( MIN2BUF,             DIVSIZE2);
        }

       if(msize >= 512) msize = msize & 0xfffffe00;

       offset  =         mreq->io_Offset;
       data    = (ULONG) mreq->io_Data;
       lactual =         (flag = 0);
       ldata   =         data;

       req = IO[un];
       dev = IO[un]->io_Device;

       if(flags & NOCACHE) CacheControl(0, CACRF_EnableD);
       if(flags & SINGLEF)
        {
         do
          {
           req->io_Command = CMD_READ;
           req->io_Offset  = offset;
           req->io_Length  = min(SIZE, length);
           req->io_Data    = buffer;

#ifdef __SASC
            oldbeginio(req, dev);
#else
#ifdef __VBCC
            oldbeginio(req, dev);
#else
            oldbeginio(req, dev);
#endif /* __VBCC */
#endif

           WaitIO((struct IORequest *) req);

           lactual  = req->io_Actual;
           actual  += lactual;

           ldata    = data;
           data    += lactual;
           offset  += lactual;
           length  -= lactual;

           flag     = 1-flag; /* toggle 0/1 */

           FastCopy(buffer,(APTR) ldata,lactual);

          }while( (length) && (req->io_Actual >= req->io_Length) );
        }else
        {
         do
          {
           req->io_Command = CMD_READ;
           req->io_Offset  = offset;
           req->io_Length  = min(msize, length);

           if(!flag) req->io_Data = buffer;
            else     req->io_Data = buffer2;

#ifdef __SASC
           oldbeginio(req, dev);
#else
#ifdef __VBCC
           oldbeginio(req, dev);
#else
           oldbeginio(req, dev);
#endif /* __VBCC */
#endif

           if(flag) FastCopy(buffer,  (APTR) ldata, lactual);
            else    FastCopy(buffer2, (APTR) ldata, lactual);

           WaitIO((struct IORequest *) req);

           lactual  = req->io_Actual;
           actual  += lactual;

           ldata    = data;
           data    += lactual;
           offset  += lactual;
           length  -= lactual;

           flag     = 1-flag; /* toggle 0/1 */

          }while( (length) && (req->io_Actual >= req->io_Length) );

         if(flag) FastCopy(buffer,  (APTR) ldata, lactual);
          else    FastCopy(buffer2, (APTR) ldata, lactual);

        }

       if(flags & NOCACHE) CacheControl(0xffffffff, CACRF_EnableD);

       mreq->io_Actual = actual;
       mreq->io_Error  = req->io_Error;

       if(!(mreq->io_Flags & IOF_QUICK)) ReplyMsg(&mreq->io_Message);

       DeletePort(port);

       ReleaseSemaphore(ss);

       return;
      }

     if((req->io_Command & 0xFF) == CMD_WRITE)
      {
       if(flags & NOWRITE) goto old;

       if(!(flags & BROKEN))
         if((ULONG) req->io_Data<16777216) goto old;


       port = (struct MsgPort *) CreatePort(NULL,0);
       if(!port) goto old;

       IO[un]->io_Message.mn_ReplyPort = port;

       actual =         0;
       length =         mreq->io_Length;
       offset =         mreq->io_Offset;
       data   = (ULONG) mreq->io_Data;

       req = IO[un];
       dev = IO[un]->io_Device;

       if(flags & NOCACHE) CacheControl(0,CACRF_EnableD);

       do
        {
         if(SIZE >= length) req->io_Length = length;
          else              req->io_Length = SIZE;

         FastCopy((APTR) data, buffer, req->io_Length);

         req->io_Offset  = offset;
         req->io_Data    = buffer;
         req->io_Command = CMD_WRITE;

#ifdef __SASC
         oldbeginio(req, dev);
#else
#ifdef __VBCC
         oldbeginio(req, dev);
#else
         oldbeginio(req, dev);
#endif /* __VBCC */
#endif

         WaitIO((struct IORequest *) req);

         actual  += (lactual = req->io_Actual);
         data    +=  lactual;
         offset  +=  lactual;
         length  -=  lactual;

        }while( (length) && (req->io_Actual >= req->io_Length) );

       if(flags & NOCACHE) CacheControl(0xffffffff, CACRF_EnableD);

       mreq->io_Actual = actual;
       mreq->io_Error  = req->io_Error;

       if(!(mreq->io_Flags & IOF_QUICK)) ReplyMsg(&mreq->io_Message);

       DeletePort(port);

       ReleaseSemaphore(ss);

       return;
      }

     break;
    }
  }

old:
#ifdef __SASC
 oldbeginio(req, dev);
#else
#ifdef __VBCC
 oldbeginio(req, dev);
#else
 oldbeginio(req, dev);
#endif /* __VBCC */
#endif

 if(flags & NOASYNCH)
  {
   ULONG sig = 1<<req->io_Message.mn_ReplyPort->mp_SigBit;

   Wait(sig);
   SetSignal(sig, sig); /* again */
  }

 ReleaseSemaphore(ss);
}


#ifdef __SASC

void __regargs __chkabort(void) { } /* don't break us ! */
void __regargs _CXBRK(void)     { }

void __inline StdioWrite(BPTR out, char *txt);
void __inline StdioWrite(BPTR out, char *txt)
{
 if(out) Write(out, txt, strlen(txt));
}

#else

#ifdef __VBCC

void StdioWrite(BPTR out, char *txt);
void StdioWrite(BPTR out, char *txt)
{
 if(out) Write(out, txt, strlen(txt));
}

#else

void inline StdioWrite(BPTR out, char *txt);
void inline StdioWrite(BPTR out, char *txt)
{
 if(out) Write(out, txt, strlen(txt));
}

#endif /* __VBCC */

#endif


char version[] = "\0$VER: vb2091 1.3 (14.9.97)";

int main(long argc, char *argv[])
{
 ULONG           tmp, op, devopen, i, j, ret = 0;
 ULONG           opened[8], patched[8];
 struct MsgPort *iosink[MAXUNIT] = {0,0,0,0,0,0,0,0};
 char           *Device, *p;
 UBYTE           port_name[256];


 tmp     = 0;
 devopen = 0;
 Device  = "2nd.scsi.device";

 op = Output();

 for(i=1; i<argc; i++)
  {
   if(!strcmp("BUFSIZE",argv[i]))
    {
               SIZE = atol(argv[++i])*1024;
     if(!SIZE) SIZE = 262144;

     DIVSIZE2 = SIZE >> 1;

     continue;
    }

   if(!strcmp("MIN1BUF",argv[i]))
    {
                  MIN1BUF = atol(argv[++i])*1024;
     if(!MIN1BUF) MIN1BUF = 131072;

     continue;
    }

   if(!strcmp("MIN2BUF",argv[i]))
    {
                  MIN2BUF = atol(argv[++i])*1024;
     if(!MIN2BUF) MIN2BUF = 65536;

     continue;
    }

   if(!strcmp("MAXN",argv[i]))
    {
               MAXN = atol(argv[++i]);
     if(!MAXN) MAXN = 16;

     continue;
    }

   if(!strcmp("DEVICE",argv[i]))
    {
     Device = argv[++i];

     continue;
    }

   if(!strcmp("UNIT",argv[i]))
    {
     p = argv[++i];

     while(*p!=0)
      {
       if(*p<'0'||*p>'7')
        {
         StdioWrite(op, "You can only use unit 0..7!!\n");
         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;
    }

   if(!strcmp("NOASYNCH",argv[i]))
    {
     flags |= NOASYNCH;

     continue;
    }

   StdioWrite(op,"Wrong argument: ");
   StdioWrite(op,argv[i]);
   StdioWrite(op,"\nPLEASE READ THE MANUAL !!\n");

   exit(0);
  }

 strcpy(port_name, "vb2091_");
 strcat(port_name, Device);

 if(FindPort(port_name))
  {
   StdioWrite(op, "vb2091 already running for this device !\n");
   exit(0);
  }

 MULMIN2BUF = MIN2BUF * MAXN;

 StdioWrite(op, AUTHORS);
 StdioWrite(op, RISK);

 for(i=0;i<MAXUNIT;i++)
  {
   opened[i]  = FALSE;
   patched[i] = FALSE;

   if(!(unitmask&(1<<i))) continue;

   iosink[i] = (struct MsgPort *)CreatePort(NULL, 0);
   if(iosink[i])
    {
     IO[i] = (struct IOStdReq *) CreateExtIO(iosink[i], sizeof(struct IOStdReq));
     if(IO[i]) tmp = OpenDevice(Device, i, (struct IORequest *) IO[i], 0);
    }

   if(tmp || (!IO[i]) || (!iosink[i]))
    {
     UBYTE digit[2];

     StdioWrite(op,"Unable to open ");
     StdioWrite(op, Device);
     StdioWrite(op," Unit ");

     digit[0] = zahl[i];
     digit[1] = (UBYTE) 0;
     StdioWrite(op, digit);
     StdioWrite(op,"\n");

     IO[i] = 0;

     continue;
    }else
    {
     UBYTE digit[2];

     StdioWrite(op, "Opened ");
     StdioWrite(op, Device);
     StdioWrite(op, " Unit ");

     digit[0] = zahl[i];
     digit[1] = (UBYTE) 0;
     StdioWrite(op, digit);
     StdioWrite(op,"\n");

     devopen    = 1;
     opened[i]  = TRUE;
     patched[i] = TRUE;

     Forbid();

     SumLibrary((struct Library *) IO[i]->io_Device);
#ifdef __SASC
     if(oldbeginio==NULL) oldbeginio = (void (* __asm )(register __a1 struct IOStdReq *req,
                                                        register __a6 struct Device   *dev)) SetFunction((struct Library *) IO[i]->io_Device, DEV_BEGINIO, (APTR) &mybeginio);
#else
     if(oldbeginio==NULL) oldbeginio = (void (* )()) SetFunction((struct Library *) IO[i]->io_Device, DEV_BEGINIO, (APTR) &mybeginio);
#endif /* __SASC */
     Unit[i]=IO[i]->io_Unit;

     Permit();
    }
  }

 ss = (struct SignalSemaphore *)AllocVec(sizeof (struct SignalSemaphore), MEMF_PUBLIC);
 if(ss)
  {
   InitSemaphore(ss);

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

   if(buffer)
    {
     buffer2 = buffer + DIVSIZE2;

     if(devopen)
      {
       struct MsgPort *inst;

       inst = (struct MsgPort *)CreatePort(port_name, 0);

       Wait(SIGBREAKF_CTRL_C);

       if(inst) DeletePort(inst);
      }

     ObtainSemaphore(ss);

     for(i=0; i<MAXUNIT; i++)
      {
       j = MAXUNIT-i-1;

       if(IO[j])
        {
         if(patched[j])
          {
           Forbid();
           SetFunction((struct Library *) IO[j]->io_Device, DEV_BEGINIO, (APTR) oldbeginio);
           Permit();
          }

         if(opened[j])  CloseDevice((struct IORequest *) IO[j]);
                        DeleteExtIO((struct IORequest *) IO[j]);
        }

       if(iosink[j]) DeletePort(iosink[j]);
      }

     ReleaseSemaphore(ss);

     FreeVec(buffer);

    }else
    {
     StdioWrite(op,"Cannot allocate buffer!!\n");
     ret = 20;
    }

   FreeVec(ss);

  }else
  {
   StdioWrite(op,"Cannot allocate semaphore!!\n");
   ret = 20;
  }

 exit(ret);
}
