/* L_QDOS loads and runs QDOS */

/* uses a custom built Cstartup.o */

#include "/inc/stdio.h"
#include "/inc/exec.h"
#include "/inc/dos.h"
#include "/inc/intuition.h"
#include "/inc/workbench.h"

extern struct WBStartup *WBenchMsg;

long IconBase;

extern int sub(),dummy();

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

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

struct memrange
{
    long lomem;
    long himem;
};

extern struct memrange toclear[];

shutdown(s) char *s;
{
    if(stdout==0)
      openWB("CON:0/12/320/72/Message from L_QDOS...");
    printf("%ls\n",s);
    if(WBenchMsg!=0)
      Delay(500);
    exit(0);
}

usage()
{    shutdown
    (
"usage:\tL_QDOS [QDOS_file]\n\t[-s<ssp>]\n\t[-m<lomem><+len|-himem>]\n\t[-c<lomem><+len|-himem>]\n\t[-r[himem] rom1 rom2..]\n"
    );
}

warning(s) char *s;
{
    if(stdout==0)
      openWB("CON:0/12/320/72/Messsage from L_QDOS...");
    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 'C':
        if(ccount<8)
          while(*p!=0)
          { p=getnum(p,&num);
            switch(*p)
            {
            case '+':
              cflag=1;
              toclear[ccount].lomem=num;
              break;
            case '-':
              cflag=2;
              toclear[ccount].lomem=num;
              break;
            case 0:
              switch(cflag)
              {
              case 1:
                toclear[ccount].himem=toclear[ccount].lomem+num;
                break;
              case 2:
                toclear[ccount].himem=num;
                break;
              default:
                usage();
              }
              toclear[ccount].lomem=(toclear[ccount].lomem+1)&(-2);
              toclear[ccount].himem=(toclear[ccount].himem)&(-2);
              ccount++;
              break;
            default:
              usage();
            }
            if(*p!=0)
              p++;
          }
        else
          shutdown("too many memory ranges to clear");
        break;
      case 'S':
        if(*p!=0)
        { p=getnum(p,&num);
          if(num>=0x28480)
          { sflag=1;
            ql_ssp=num&(-2);
            ql_var=ql_ssp&(-0x8000);
            if(ql_ssp-ql_var<0x480)
              ql_ssp=ql_var+0x480;
          }
        }
        break;
      case 'R':
        rcount=1;
        if(*p!=0)
        { p=getnum(p,&num);
          rflag=1;
          rom_himem=num&(-0x100);
        }
        break;
      case 'M':
        while(*p!=0)
        { p=getnum(p,&num);
          switch(*p)
          {
          case '+':
            mflag=1;
            ql_lomem=num;
            break;
          case '-':
            mflag=2;
            ql_lomem=num;
            break;
          case 0:
            switch(mflag)
            {
            case 1:
              ql_himem=ql_lomem+num;
              break;
            case 2:
              ql_himem=num;
              break;
            default:
              usage();
            }
            ql_lomem=(ql_lomem+0x1FF)&(-200);
            ql_himem=ql_himem&(-200);
            break;
          default:
            usage();
          }
          if(*p!=0)
            p++;
        }
        break;
      default:
        usage();
      }
    }
}

int
findrom(rom) struct rominfo *rom;
{
    long f,len;
    char temp[80];
    f=fopen(rom->name,"r");
    if(f==0)
    { strcpy(temp,rom->name);
      strcat(temp," not found");
      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;
    struct WBStartup *m;
    struct WBArg *ap;
    struct DiskObject *icon;
    long olddir;
    long romlen,len,rom_dst,sub_len,sub_dst;
    char *p,*qp,*rp,*cp,**av;
    struct rominfo rom[8];
    struct
    { long day;
      long min;
      long tick;
    } date;
    long qldate;
    if(argc==0)
    {
/*
      openWB("CON:0/12/320/132/L_QDOS");
*/
      if((IconBase=OpenLibrary("icon.library",0))==0)
        exit(0);
      m=WBenchMsg;
      n=m->sm_NumArgs;
      if(n>1)
        n=1;
      else
        n=0;
      ap=m->sm_ArgList+n;
/*    printf ("icon=%ls.info\n",ap->wa_Name); */
      olddir=CurrentDir(ap->wa_Lock);
      icon=GetDiskObject(ap->wa_Name);
      if(icon!=0)
      {
        argc=1;
        argv[1]=argv[0];
        if(icon->do_ToolTypes!=0)
          for(j=0;(cp=icon->do_ToolTypes[j])!=0;j++)
          {
/*          printf("%ls\n",cp); */
            p=argv[argc];
            strcpy(p,cp);
            argv[++argc]=p+strlen(p)+1;
          }
        FreeDiskObject(icon);
      }
      CurrentDir(olddir);
      CloseLibrary(IconBase);
    }
    mflag=sflag=cflag=ccount=rflag=rcount=0;
    ql_lomem=rom_lomem=0;
    ql_himem=rom_himem=0x80000;
    ql_ssp=0x28480;
    ql_var=0x28000;
    rom[0].name=0;
    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==0)
        { if(rom[0].name!=0)
            usage();
          rom[0].name=argv[i];
        }
        else
        { if(rcount<8)
          { rom[rcount].name=argv[i];
            rcount++;
          }
          else
            shutdown("too many roms");
        }
      }
    }
    if(rom[0].name==0)
      rom[0].name="QDOS";
    romlen=0;
    for(i=0;i==0 || i<rcount;i++)
    { len=findrom(&rom[i]);
      if((len==0)&&(i==0))
        shutdown("sorry but I can't load QDOS");
/*
      printf("%ls\n",rom[i].name);
*/
      romlen+=len;
    }
/*
    printf("total space req=$%lx/%ld\n",romlen,romlen);
*/
    rp=0;
    if(romlen!=0)
    {
      p=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;
        }
      }
    }
/*
    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);
/*
    printf("days=%ld mins=%ld ticks=%ld\n",date.day,date.min,date.tick);
    printf("qldate=$%08lx\n",qldate);
*/

    rom_dst=rom_himem-(romlen-rom[0].rlen);
/*
    printf("rom_dst=$%lx rom_himem=$%lx\n",rom_dst,rom_himem);
    printf("vars=$%lx ssp=$%lx\n\n",ql_var,ql_ssp);
*/

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

    if((ql_lomem<=rom_dst)&&(ql_himem>=rom_dst))
      ql_himem=rom_dst&(-0x200);
    else
      if((ql_lomem<=rom_himem)&&(ql_himem>=rom_himem))
        ql_lomem=rom_himem;

    if((ql_lomem<=ql_ssp)&&(ql_himem>=ql_ssp))
      ql_lomem=(ql_ssp+0x1FF)&(-0x200);
    else
      if((ql_lomem<=ql_var)&&(ql_himem>=ql_var))
        ql_himem=ql_var;
/*
    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=dummy-sub;
    sub_dst=ql_himem-sub_len;
check_dst:
    if((qp<=sub_dst)&&(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;
    }
/*
    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;
        }
/*
      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(WBenchMsg!=0)
      Delay(1000);
    exit(0);
*/

    Delay(150);

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

