;/*
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;
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.04, loading QDOS...";
#endif

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

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

usage()
{
  shutdown("L_QDOS v2.04\n\n"
			  "usage:\tL_QDOS [-r]\n"
					  "\t       [-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 'R':
		rflag=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 rom_tot,rom_len,len,rom_dst,sub_len,sub_dst;
  char *p,*qp,*cp,**av;
  long GfxBase;

  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=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";
  rom_tot=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);

	 rom_tot+=len;
  }

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

  if(rom_tot!=0)
  {
	 p = (char *)malloc(rom_tot);
	 if(p==0)
		shutdown("no memory");
	 qp=p;
	 for(i=0;i<rcount;i++)
	 { if(rom[i].len!=0)
		{ fread(p,rom[i].len,1,rom[i].fp);
		  p+=rom[i].rlen;
		}
	 }
  }

  rom_len=rom_tot-rom[0].rlen-rom[1].rlen;

  if(dflag!=0)
  { printf("QDOS and ROMS source=$%lx\n",qp);
	 printf("QDOS len=$%lx\n",rom[0].rlen);
	 printf("MAIN len=$%lx\n",rom[1].rlen);
	 printf("ROMs len=$%lx\n\n",rom_len);
  }

  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_dst=(ql_himem-rom_len)&(-0x200);
  sub_len=(long)dummy-(long)sub;
  sub_dst=((ql_himem-rom_tot)&(-0x200))-sub_len;

  ql_himem=rom_dst;
  if(rflag!=0)
	 if(ql_himem>0x200000)
		ql_himem=ql_himem-rom[0].rlen+0x600;

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

  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 ");

check_dst:
  if(((long)qp <= sub_dst) && ((long)qp + rom_tot > 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<0x1F400)
	 { if(toclear[i].himem>0x1F400)
		{ if(toclear[i].himem>(0x1F400+rom[1].rlen))
		  { if(ccount<8)
			 { toclear[ccount].lomem=0x1F400+rom[1].rlen;
				toclear[ccount].himem=toclear[i].himem;
				ccount++;
			 }
			 else
				shutdown("range too complex");
			 }

		  toclear[i].himem=0x1F400;
		}
	 }

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

		  toclear[i].himem=sub_dst;
		}
	 }

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

		  toclear[i].himem=rom_dst;
		}
	 }

	 if(toclear[i].lomem<ql_himem)
	 { if(toclear[i].himem>ql_himem)
		  toclear[i].himem=ql_himem;
	 }
	 else
		goto removerange;

	 if(toclear[i].lomem==toclear[i].himem)
		goto removerange;

	 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)
  { i=0;
	 while(i<ccount)
	 { printf("range: lomem=$%lx himem=%lx\n"
				 ,toclear[i].lomem,toclear[i].himem);
		i++;
	 }
  }

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

  GfxBase=0;
  if (!(GfxBase =
		OpenLibrary("graphics.library",0)))
	 goto die;

  LoadView(NULL);

  if (GfxBase)
	 CloseLibrary(GfxBase);

die:
  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,
		rom[0].rlen,rom_len,rom[1].rlen,ql_ssp,qldate);
}

