;/*
A68k sub.asm
dcc -o L_QDOS L_QDOS.c -l hardware SUB.o
quit
*/
/* L_QDOS loads and runs QDOS */

#include <exec/exec.h>
#include <exec/libraries.h>
#include <dos/dos.h>
#include <intuition/intuition.h>
#include <workbench/workbench.h>
#include <stdio.h>
#include <hardware/cia.h>

#define MAXROMS 16

extern struct ExecBase *SysBase;
extern __far struct CIA ciaa, ciab;

typedef struct List      LIST;
typedef struct Node      NODE;
typedef struct MemHeader MEMHEADER;
typedef struct MemChunk  MEMCHUNK;

extern int sub(),dummy();
extern int subtoclear();

long ql_lomem,ql_himem,ql_ssp,ql_var,rom_lomem,rom_himem;
int sflag,mflag,cflag,ccount,rflag,rcount,dflag;
long qldate;

struct
{
  long day;
  long min;
  long tick;
} date;

struct rominfo
{
  char *name;
  VOID *fp;
  long len;
  long rlen;
};

struct memrange
{
  long lomem;
  long himem;
};

struct memrange *toclear;
struct rominfo rom[MAXROMS];

typedef struct CommandLineInterface CLI;
typedef struct Process PROCESS;

CLI     *cli;
PROCESS *proc;

extern int  _start_();
extern int  _realstart_();

extern long _Detached_,   /* are we detached?   */
            _WorkBench_,  /* are we on the WB?  */
            _CLI_,        /* are we on the CLI? */
            _Process_;    /* are we a process?  */
#if 1
/* how to change the default console (if it's used) */
char _DefaultConsole[] =
       "CON:0/12/320/72/L_QDOS 2.02, loading QDOS...";
#endif

char VERSTAG[] = "\0$VER: L_QDOS 2.02";

shutdown(s) char *s;
{
  printf("%ls\n",s);
  if(_WorkBench_ || _Detached_)
  {
    Delay(500);
  }
  exit(0);
}

usage()
{
  shutdown("L_QDOS v2.02\n\n"
           "usage:\tL_QDOS [-q<QDOS rom>]\n"
                 "\t       [-p<MAIN rom>]\n"
                 "\t       [-m<lomem><+len|-himem>]\n"
                 "\t       [<OTHER roms>...]\n");
}

warning(s) char *s;
{
  printf("%ls\n",s);
}

char *
getnum(p,np) char *p; long *np;
{ long n,h;
  char c;
  c=*p++;
  n=0;
  if(c!='$')
  {
    while(isdigit(c))
    {
      h=c-48;
      n=n*10+h;
      c=*p++;
    }
  }
  else
  {
    c=*p++;
    while(isxdigit(c))
    {
      h=(c<58?c-48:(c<96?c-55:c-87));
      n=n*16+h;
      c=*p++;
    }
  }
  *np=n;
  return --p;
}

doflags(p) char *p;
{
  long num;
  while(*p!=0)
  { switch(toupper(*p++))
    {
    case 'D':
      dflag=1;
      break;
    case 'Q':
      rom[0].name=p;
      while (*p!=0)
        p++;
      break;
    case 'P':
      rom[1].name=p;
      while (*p!=0)
        p++;
      break;
    case 'M':
      while(*p!=0)
      { p=getnum(p,&num);
        switch(*p)
        {
        case '+':
          mflag=1;
          ql_var=num;
          break;
        case '-':
          mflag=2;
          ql_var=num;
          break;
        case 0:
          switch(mflag)
          {
          case 1:
            ql_himem=ql_var+num;
            break;
          case 2:
            ql_himem=num;
            break;
          default:
            usage();
          }
          break;
        default:
          usage();
        }
        if(*p!=0)
          p++;
      }
      break;
    default:
      usage();
    }
  }
}

int
findrom(rom) struct rominfo *rom;
{
  long len;
  VOID *f;
  char temp[80];
  f = fopen(rom->name,"r");
  if(f == 0)
  { strcpy(temp,rom->name);
    strcat(temp," not available");
    warning(temp);
    len=0;
  }
  else
  { fseek(f,0,2);
    len=ftell(f);
    fseek(f,0,0);
  }
  rom->fp = f;
  rom->len=len;
  len+=255;
  len&=-256;
  rom->rlen=len;
  return len;
}

main(argc,argv)
int argc;
char **argv;
{
  int i,j,n;
  long olddir;
  long romlen,len,rom_dst,sub_len,sub_dst;
  char *p,*qp,*rp,*cp,**av;

  MEMHEADER *mh;
  long mh_lower, mh_upper;

  _DetachFromCLI();      /* Does just that. (Or nothing if */
                         /* run from the WorkBench).       */

  _RunOnWorkBench();     /* Indicates this program should  */
                         /* continue and will run properly */
                         /* if started from the WorkBench. */
                         /* Well, you say it will.         */

  _ToolTypeArgs();       /* Means move tooltype array from */
                         /* .info file to argc,argv.       */

  _OpenDefaultConsole(); /* Need this to use printf's etc  */
                         /* when starting from WorkBench   */
                         /* or if you've detached.         */
                         /* (You won't need it if you're   */
                         /* only using intuition stuff).   */
                         /* Does nothing if you're running */
                         /* on the CLI/Shell.              */

  toclear=(struct memrange *)subtoclear;

  mflag=sflag=cflag=ccount=rflag=dflag=0;
  rcount=2;
  ql_lomem=rom_lomem=0;
  ql_himem=rom_himem=0x10000000;
  ql_ssp=0x28480;
  ql_var=0x28000;

  for(i=1;i<argc;i++)
  {
/*  printf("arg %ld = %ls\n",i,argv[i]); */
    if(argv[i][0]=='?')
      usage();
    if(argv[i][0]=='-')
      doflags(argv[i]+1);
    else
    {
      if(rcount<MAXROMS)
      { rom[rcount].name=argv[i];
        rcount++;
      }
      else
        shutdown("too many roms");
    }
  }
  if(dflag!=0)
    warning("...now in debug mode");

  if(dflag!=0)
    printf("to clear structure=$%lx $%lx\n\n",(long)toclear,(long)subtoclear);

  ccount=0;
  for (mh  = (MEMHEADER *)GetHead((LIST *)&SysBase->MemList);
       mh != 0;
       mh  = (MEMHEADER *)GetSucc((NODE *)mh))
  {
    mh_lower=mh->mh_Lower;
    mh_upper=mh->mh_Upper;
    if (((long)mh+sizeof(MEMHEADER))=mh_lower)
      mh_lower=(long)mh;

    if (ccount!=0)
      for (i=0;i<ccount;i++)
        if (mh_lower<toclear[i].lomem)
        {
          n=mh_lower;mh_lower=toclear[i].lomem;toclear[i].lomem=n;
          n=mh_upper;mh_upper=toclear[i].himem;toclear[i].himem=n;
        }

    toclear[ccount].lomem=mh_lower;
    toclear[ccount].himem=mh_upper;
    ccount++;

  }

  i=0;
  while ((i<ccount)&(ql_var>toclear[i].himem))
    i++;

  if (i<ccount)
    if (ql_var<toclear[i].lomem)
      ql_var=toclear[i].lomem;

  i=ccount-1;
  while ((i>=0)&(ql_himem<toclear[i].lomem))
    i--;

  if (i>=0)
    if (ql_himem>toclear[i].himem)
      ql_himem=toclear[i].himem;

  if (ql_himem>0x1000000)
    if (ql_var<toclear[ccount-1].lomem)
      ql_var=toclear[ccount-1].lomem;

  if(dflag!=0)
  {
    for (i=0;i<ccount;i++)
      printf("$%08lX $%08lX \n",toclear[i].lomem,toclear[i].himem);
  }

  if(rom[0].name==0)
    rom[0].name="QLboot:ROMs/SYS_cde";
  if(rom[1].name==0)
    rom[1].name="QLboot:ROMs/MAIN_cde";
  romlen=0;
  for(i=0;i==0 || i<rcount;i++)
  { len=findrom(&rom[i]);
    if((len==0)&&(i<2))
      shutdown("sorry but I can't load QDOS");
    if(dflag!=0)
      printf("%ls\n",rom[i].name);

    romlen+=len;
  }

  if(dflag!=0)
    printf("total space req=$%lx/%ld\n",romlen,romlen);

  rp=0;
  if(romlen!=0)
  {
    p = (char *)malloc(romlen);
    if(p==0)
      shutdown("no memory");
    qp=p;
    for(i=0;i<rcount;i++)
    { if(i==1)
        rp=p;
      if(rom[i].len!=0)
      { fread(p,rom[i].len,1,rom[i].fp);
        p+=rom[i].rlen;
      }
    }
  }
  if(dflag!=0)
  { printf("QDOS and ROMS source=$%lx\n",qp);
    printf("QDOS len=$%lx\n",rom[0].rlen);
    printf("ROMs len=$%lx\n\n",romlen-rom[0].rlen);
  }

  DateStamp(&date);
  qldate=date.day+365*17+4;
  qldate=qldate*(24*60*60/8);
  qldate<<=3;
  qldate+=(date.min*60);
  qldate+=(date.tick/50);
  if(dflag!=0)
  { printf("days=%ld mins=%ld ticks=%ld\n",date.day,date.min,date.tick);
    printf("qldate=$%08lx\n",qldate);
  }

  ql_var=(ql_var+0x7FFF)&(-0x8000);
  ql_himem=ql_himem&(-0x200);

  rom_himem=ql_himem;
  rom_dst=rom_himem-(romlen-rom[0].rlen);
  ql_himem=rom_dst&(-0x200);

  if(dflag!=0)
    printf("rom_dst=$%lx rom_himem=$%lx\n",rom_dst,rom_himem);

  if(ql_var<0x28000)
    ql_var=0x28000;

  ql_ssp=ql_var+0x480;
  ql_lomem=ql_ssp;

  if(dflag!=0)
    printf("lomem=$%lx himem=$%lx\n\n",ql_lomem,ql_himem);


  if(ql_lomem>=ql_himem)
    shutdown("bad value for memory range\n...might be out of memory\n ");


  sub_len=(long)dummy-(long)sub;
  sub_dst=ql_himem-sub_len;
check_dst:
  if(((long)qp <= sub_dst) && ((long)qp + romlen > sub_dst))
    sub_dst=qp-sub_len;
  if(((long)sub<=sub_dst)&&((long)sub+sub_len>sub_dst))
  { sub_dst=(long)sub-sub_len;
    goto check_dst;
  }
  if(dflag!=0)
    printf("sub src=$%lx sub dst=$%lx sub len=$%lx\n\n"
           ,(long)sub,sub_dst,sub_len);

  i=0;
  while(i<ccount)
  { if(toclear[i].lomem<rom[0].rlen)
    { if(toclear[i].himem>rom[0].rlen)
        toclear[i].lomem=rom[0].rlen;
      else
        goto removerange;
    }

    if(toclear[i].lomem<sub_dst)
    { if(toclear[i].himem>sub_dst)
      { if(toclear[i].himem>ql_himem)
        { if(ccount<8)
          { toclear[ccount].lomem=ql_himem;
            toclear[ccount].himem=toclear[i].himem;
            ccount++;
          }
          else
            shutdown("range too complex");
        }
        toclear[i].himem=sub_dst;
      }
    }
    else
      if(toclear[i].lomem<ql_himem)
      { if(toclear[i].himem>ql_himem)
          toclear[i].lomem=ql_himem;
        else
          goto removerange;
      }

    if(toclear[i].lomem<rom_dst)
    { if(toclear[i].himem>rom_dst)
      { if(toclear[i].himem>rom_himem)
        { if(ccount<8)
          { toclear[ccount].lomem=rom_himem;
            toclear[ccount].himem=toclear[i].himem;
            ccount++;
          }
          else
            shutdown("range too complex");
        }
        toclear[i].himem=rom_dst;
      }
    }
    else
      if(toclear[i].lomem<rom_himem)
      { if(toclear[i].himem>rom_himem)
          toclear[i].lomem=rom_himem;
        else
          goto removerange;
      }
    if(dflag!=0)
      printf("range: lomem=$%lx himem=%lx\n"
             ,toclear[i].lomem,toclear[i].himem);

    i++;
    goto endwhile;

    removerange:
    ccount--;
    toclear[i].lomem=toclear[ccount].lomem;
    toclear[i].himem=toclear[ccount].himem;
    endwhile:
  }
  toclear[ccount].lomem=0;
  toclear[ccount].himem=0;

  if(dflag!=0)
  { printf("vars=$%lx ssp=$%lx\n\n",ql_var,ql_ssp);
    shutdown("quitting L_QDOS...\n");
  }

  Forbid();
  Disable();
  OwnBlitter();
  while(WaitBlit());

  if(((struct Library *)SysBase)->lib_Version>=37)
   CacheControl(0,CACRF_EnableI|CACRF_EnableD|CACRF_IBE|CACRF_DBE);

/* deselect all drives                       */
  ciab.ciaprb|=CIAF_DSKSEL0|CIAF_DSKSEL1|CIAF_DSKSEL2|CIAF_DSKSEL3;
/* motor signal (off)                        */
  ciab.ciaprb|=CIAF_DSKMOTOR;
/* select all drives (thus, switch them off) */
  ciab.ciaprb&=~(CIAF_DSKSEL0|CIAF_DSKSEL1|CIAF_DSKSEL2|CIAF_DSKSEL3);
/* deselect drives again (well, why not?)    */
  ciab.ciaprb|=CIAF_DSKSEL0|CIAF_DSKSEL1|CIAF_DSKSEL2|CIAF_DSKSEL3;

  sub(sub_dst,ql_lomem,ql_himem,rom_dst,qp,rp,
      rom[0].rlen,romlen-rom[0].rlen,rom[1].rlen,ql_ssp,qldate);
}

