/*

	ZKick V2.30 -- Copyright (C) 1990 by Daniel Zenchelsky

		This program may be freely copied, as long as all copyright
		notices are left intact and unchanged.

*/

#include <stdio.h>

#include <libraries/configvars.h>
#include <libraries/expansion.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/tasks.h>
#include <exec/execbase.h>

#define BUFSIZE 	(64*1024)
#define KICKSIZE 	(512*1024)
#define NUMBUFS 	(KICKSIZE/BUFSIZE)

#define STARTKICK	(void *)0x200000
#define ENDKICK	(void *)0x27FFFF

#define STACK_SIZE 1024

#define BOGUSMMU	0xffffffffL

#define IFDEBUG if (DEBUG) {
#define ENDIFDEBUG }

#define USAGE "Usage: ZKick [-debug] [-die] [-mmu] [-noreset] [-nochecksum] [-print] KickFile\n"

/* For the Assembly routines to use: */

long NumBufs=NUMBUFS;
long BufSize=BUFSIZE;
long StartKick=STARTKICK;
long EndKick=ENDKICK;

APTR stack = NULL;
struct Task *tc = NULL;
char *taskname = "Zkick Task";

struct ExpansionBase *ExpansionBase=NULL;
struct ExecBase *ExecBase=NULL;

void main();
void cleanup();
void DoAddCD();
void GetMem();
void KickLoad();
void StartTask();
extern void KickCopy();
extern void MakeRomTag();
extern long GetCPUType();
extern long GetMMUType();
extern long connum;
extern struct ConfigDev config[8];
extern long memnum;
extern long memory[8][2];
extern long Survive;
extern long MMU;
extern long NOMMU;
extern long NORESET;
extern long NOCHECKSUM;
extern long DEBUG;
extern long PRINT;
long CPU;

int CXBRK(void) { return(0); }  

void *MemArray[NUMBUFS];

void main(argc,argv)
int argc;
char *argv[];
{
	struct ConfigDev *MyConfigDev;
	char *kickfile;

	printf("ZKick V2.30 Copyright (C) 1990 by Daniel Zenchelsky\n");
	printf("---------------------------------------------------\n");
	printf("      This program may be freely copied, as long\n");
	printf("      as all copyright notices are left intact.\n\n");
	connum=0;	

	argv++;
	argc--;

	Survive=1;
	NOMMU=1;

	if (argc<1)
	{
		printf(USAGE);
		cleanup();
	}

	while(argc>1)
	{
		if (argv[0][0]!='-')
		{
			printf(USAGE);
			cleanup();
		}
		if (strcmp("-die",argv[0])==0)
		{
IFDEBUG
			printf("-die\n");
ENDIFDEBUG
			Survive=0;
		}
		if (strcmp("-mmu",argv[0])==0)
		{
IFDEBUG
			printf("-nommu\n");
ENDIFDEBUG
			NOMMU=0;
		}
		if (strcmp("-noreset",argv[0])==0)
		{
IFDEBUG
			printf("-noreset\n");
ENDIFDEBUG
			NORESET++;
		}
		if (strcmp("-nochecksum",argv[0])==0)
		{
IFDEBUG
			printf("-nochecksum\n");
ENDIFDEBUG
			NOCHECKSUM++;
		}
		if (strcmp("-debug",argv[0])==0)
		{
			printf("-debug\n");
			DEBUG++;
		}
		if (strcmp("-print",argv[0])==0)
		{
IFDEBUG
			printf("-print\n");
ENDIFDEBUG
			PRINT++;
		}

		argv++;
		argc--;
	}

	kickfile=argv[0];

	ExpansionBase= (struct ExpansionBase *)OpenLibrary( EXPANSIONNAME,0);
	if(ExpansionBase==NULL)
	{
		printf("Error opening expansion library!!\n");
		cleanup();
	}

	ExecBase= (struct ExecBase *)OpenLibrary("exec.library",0);
	if(ExecBase==NULL)
	{
		printf("Error opening Exec library!!\n");
		cleanup();
	}

	if((stack = (APTR) AllocMem(STACK_SIZE, MEMF_CHIP | MEMF_CLEAR)) == NULL)
	{
     	printf("Not enough memory for task stack\n");
		cleanup();
	}

	if ((tc = (struct Task *)
        AllocMem(sizeof(struct Task),MEMF_CHIP | MEMF_CLEAR | MEMF_PUBLIC)) == NULL)
	{
		printf("Not enough memory for task structure\n");
		cleanup();
	}

	if (CheckForMem()==-1)
	{
		printf("You must have a 512k or greater memory board at $200000.\n");
		cleanup();
	}

	MyConfigDev=NULL;

	MyConfigDev=FindConfigDev(NULL,-1,-1);
	
	if(MyConfigDev==NULL) printf("No configured devices found\n");
		else DoAddCD(MyConfigDev);

	while((MyConfigDev=FindConfigDev(MyConfigDev,-1,-1))!=NULL)
	{
		DoAddCD(MyConfigDev);
	}

	printf("Found %d expansion devices, and %d memory boards.\n",connum,memnum);

	if (NOMMU==0)
	{

		CPU=GetCPUType();
		MMU=GetMMUType();

		if (MMU==BOGUSMMU) MMU=0;

IFDEBUG
		printf("CPU: %d, MMU: %d\n",CPU,MMU);
ENDIFDEBUG
	}
	
	GetMem();
	KickLoad(kickfile);
	MakeRomTag();
	printf("Rebooting in 5 seconds...\n");
	Delay(50*5);
	StartTask();

	Wait(0L);		/* Hang around forever (not very long, actually.) */
}

void cleanup()
{
	if(stack) FreeMem((void *)stack,(ULONG)STACK_SIZE);
	if(ExpansionBase) CloseLibrary(ExpansionBase);
	if(ExecBase) CloseLibrary(ExecBase);
	exit(0);
}

void DoAddCD(dev)
struct ConfigDev *dev;
{
	if ((dev->cd_Rom.er_Type & ERTF_MEMLIST) == 0)
	{
		if (connum<8)
		{
	
			config[connum]=*dev;
			config[connum].cd_Rom.er_Reserved0c=0;
			config[connum].cd_Rom.er_Reserved0d=0;
			config[connum].cd_Rom.er_Reserved0e=0;
			config[connum].cd_Rom.er_Reserved0f=0;
			config[connum].cd_Flags |= CDF_CONFIGME;
			config[connum].cd_Driver=NULL;

			connum++;

IFDEBUG
		printf("Adding board at $%x to list.\n",(long)(dev->cd_BoardAddr));
ENDIFDEBUG
		}
			else
		{
			printf("Too many expansion boards!\n");
			cleanup();
		}
	}
		else
	{

		if (memnum<8)
		{

			memory[memnum][0]=(long)dev->cd_BoardAddr;
			memory[memnum][1]=(long)dev->cd_BoardSize;

			if (memory[memnum][0]==0x200000)
			{
				memory[memnum][0]+=0x080000;
				memory[memnum][1]-=0x080000;
			}

			if (memory[memnum][1]>0)
			{

IFDEBUG
				printf("Adding memory at $%x, size $%x to list.\n",
					memory[memnum][0],memory[memnum][1]);
ENDIFDEBUG
			}

			memnum++;
		}
			else
		{
			printf("Too many memory boards!\n");
			cleanup();
		}
	}
}

/* Allocate NUMBUFS (8) buffers of size BUFSIZE (64k) _NOT_ within the final
   KickStart address region.  
*/

void GetMem()
{
	int BufNum,BadNum=0,x;
	void *MemBad[NUMBUFS];

	for (BufNum=0;BufNum<NUMBUFS;BufNum++)
	{
		MemArray[BufNum]=(void *)AllocMem(BUFSIZE,0);
		while (MemArray[BufNum]!=(void *)NULL && MemArray[BufNum]>=STARTKICK && MemArray[BufNum]<=ENDKICK)
		{
			MemBad[BadNum++]=MemArray[BufNum];	
IFDEBUG
			printf("* UNUSED * Memory allocated at location $%x\n",(long)MemBad[BadNum-1]);
ENDIFDEBUG
			MemArray[BufNum]=(void *)AllocMem(BUFSIZE,0);
		}
		if (MemArray[BufNum]==(void *)NULL)
		{
			if (BufNum>0)
				for (x=0;x<BufNum-1;x++) FreeMem(MemArray[x],BUFSIZE);
			if (BadNum>0)
				for (x=0;x<BadNum-1;x++) FreeMem(MemBad[x],BUFSIZE);
			printf("Couldn't allocate memory.\n");
			cleanup();
		}
IFDEBUG
		printf("Memory allocated at location $%x\n",(long)MemArray[BufNum]);
ENDIFDEBUG
	}

/* Free memory that was allocated inside KickStart area */

	if (BadNum>0)
		for (x=0;x<BadNum-1;x++) FreeMem(MemBad[x],BUFSIZE);

}

void KickLoad(filename)
char filename[];
{
	int BufNum;
	int file;

	file=open(filename,0);

	if (file==-1)
	{
		printf("Error opening %s\n",filename);
		for (BufNum=0;BufNum<NUMBUFS;BufNum++) FreeMem(MemArray[BufNum],(ULONG)BUFSIZE);
		cleanup();
	}

	lseek(file,8,0);

	for (BufNum=0;BufNum<NUMBUFS;BufNum++)
	{
IFDEBUG
		printf("Reading %d\n",BufNum*BUFSIZE);
ENDIFDEBUG
		if (read(file,MemArray[BufNum],BUFSIZE)!=BUFSIZE)
		{
			printf("Error reading %s\n",filename);
			for (BufNum=0;BufNum<NUMBUFS;BufNum++) FreeMem(MemArray[BufNum],(ULONG)BUFSIZE);
			cleanup();
		}
	}
}

void StartTask()
{
	/* Initialize necessary fields, others were cleared by MEMF_CLEAR */
	tc->tc_Node.ln_Type = NT_TASK;
	tc->tc_Node.ln_Name = taskname;
	tc->tc_SPLower = (APTR)stack;
	tc->tc_SPUpper = (APTR)(STACK_SIZE + (ULONG)stack);
	tc->tc_SPReg   = tc->tc_SPUpper;

	AddTask(tc, KickCopy, 0L);
}

#define FUDGEFACTOR 64	/* Fudge factor for MemHeader */

CheckForMem()
{
   struct MemHeader  *mem;
   BOOL flag=FALSE;

   Forbid();
   for (mem = (struct MemHeader *)ExecBase->MemList.lh_Head;
        mem->mh_Node.ln_Succ;
        mem = (struct MemHeader *)mem->mh_Node.ln_Succ)
	{
		if (((void *) (mem->mh_Lower - FUDGEFACTOR) <= STARTKICK) && ((void *) mem->mh_Upper >= ENDKICK))
			flag=TRUE;
	}
   Permit();

   if (flag==TRUE) return(0);
	else return(-1);
}

