/*
    (C) 1995-96 AROS - The Amiga Replacement OS
    $Id: init.c,v 1.31 1997/02/03 18:35:33 digulla Exp $

    Desc: startup code for AROS (main())
    Lang: english
*/
#define AROS_ALMOST_COMPATIBLE
#include <aros/system.h>
#include <stdlib.h>
#include <signal.h>
#ifndef _AMIGA
#define timeval     linux_timeval
#include <sys/time.h>
#undef timeval
#else
#include <sys/time.h>
#endif
#include <unistd.h>
#include <stdio.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/devices.h>
#include <proto/aros.h>
#include <proto/exec.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <proto/dos.h>
#include <utility/tagitem.h>
#include <aros/rt.h>
#include <aros/arosbase.h>
#include <hardware/intbits.h>
#include "memory.h"
#include <aros/machine.h>
#include <aros/asmcall.h>
#undef kprintf

extern void *ExecFunctions[];
extern const struct Resident Utility_resident;
extern const struct Resident Dos_resident;
extern const struct Resident Graphics_resident;
extern const struct Resident Intuition_resident;
extern const struct Resident emul_handler_resident;
extern const struct Resident Console_resident;
extern void InitCore(void);

#define MEMSIZE 1024*1024*3
/*#define STATIC_MEMORY */ /* So that gdb can disassemble it */
static struct MemHeader mh;
#ifdef STATIC_MEMORY
static UBYTE memory[MEMSIZE+MEMCHUNK_TOTAL];
#else
UBYTE * memory;
#endif

#define NUMVECT 131

struct ExecBase *SysBase;
struct DosLibrary *DOSBase;

static int returncode=20;
static struct AROSBase AROSBase;

extern struct Task * inputDevice;
extern void debugmem (void);

static void idleTask (void)
{
    while (1)
    {
	if (inputDevice)
	    Signal (inputDevice, SIGBREAKF_CTRL_F);
    }
}

int submain(int argc,char *argv[]);

static int gargc;
static char **gargv;

static void boot(void)
{
    returncode=submain(gargc,gargv);
    RemTask(NULL);
}

AROS_UFH2 (void, IntServer,
    AROS_UFHA (struct List      *,intList,A1),
    AROS_UFHA (struct ExecBase  *,SysBase,A6)
)
{
    struct Interrupt * irq;

    ForeachNode (intList, irq)
    {
	if (AROS_UFC2 (int, irq->is_Code,
		AROS_UFCA (APTR,              irq->is_Data, A1),
		AROS_UFCA (struct ExecBase *, SysBase,      A6)
	))
	    break;
    }
}

AROS_UFH2(int, Dispatcher,
    AROS_UFHA (APTR,              is_Data, A1),
    AROS_UFHA (struct ExecBase *, SysBase, A6)
)
{
    Disable();
    /* Check if a task switch is necessary */
    if(SysBase->TaskReady.lh_Head->ln_Succ!=NULL&&
       SysBase->ThisTask->tc_Node.ln_Pri<=
	((struct Task *)SysBase->TaskReady.lh_Head)->tc_Node.ln_Pri)
    {
	/* Check if it is possible */
	if(SysBase->TDNestCnt<0)
	{
	    if(SysBase->ThisTask->tc_State==TS_RUN)
	    {
		SysBase->ThisTask->tc_State=TS_READY;
		Enqueue(&SysBase->TaskReady,&SysBase->ThisTask->tc_Node);
		SysBase->AttnResched|=0x8000;
	    }
	}else
	    SysBase->AttnResched|=0x80;
    }
    Enable();
    /* Wasn't explicitly for me */
    return 0;
}

static APTR allocmem(ULONG size)
{
    UBYTE *ret;

    size = (size + MEMCHUNK_TOTAL-1) & ~(MEMCHUNK_TOTAL-1);

    ret = (UBYTE *)mh.mh_First;

    mh.mh_First = (struct MemChunk *)(ret + size);
    mh.mh_First->mc_Next = NULL;
    mh.mh_Free =
	mh.mh_First->mc_Bytes = ((struct MemChunk *)ret)->mc_Bytes - size;

    return ret;
}

void _aros_not_implemented (void)
{
    fprintf (stderr, "This function is not implemented\n");
}

void aros_print_not_implemented (char * name)
{
    fprintf (stderr, "The function %s is not implemented\n", name);
}

int main(int argc,char *argv[])
{
    ULONG * space;

    /* Put arguments into globals */
    gargc=argc;
    gargv=argv;

    /* Leave a space of 4096 bytes before the memory */
    space = malloc (4096);
#ifndef STATIC_MEMORY
    memory = malloc (MEMSIZE+MEMCHUNK_TOTAL);
#endif

    { /* erase space */
	int size = 4096/sizeof(ULONG);

	while (--size)
	    *space ++ = 0xDEADBEEF;
    }

    /*
	Prepare first MemHeader. I cannot use exec functions
	here because exec is not yet up.
    */
    mh.mh_Node.ln_Name = "chip memory"; /* Amiga has always chip, but maybe no fast */
    mh.mh_Node.ln_Pri  = 0;
    mh.mh_Attributes   = MEMF_CHIP|MEMF_PUBLIC; /* Public to my emulation */
    mh.mh_First = (struct MemChunk *)
		(((IPTR)memory + MEMCHUNK_TOTAL-1) & ~(MEMCHUNK_TOTAL-1));
    mh.mh_First->mc_Next  = NULL;
    mh.mh_First->mc_Bytes = MEMSIZE;

    mh.mh_Lower = mh.mh_First;
    mh.mh_Upper = (UBYTE *)mh.mh_Lower+MEMSIZE;
    mh.mh_Free	= MEMSIZE;

    /* The following allocations cannot and must not fail. */
    {
	/* Prepare exec.library */
	ULONG neg, i;

	neg = AROS_ALIGN(LIB_VECTSIZE*NUMVECT);

	SysBase = (struct ExecBase *)
		((UBYTE *)allocmem (neg + sizeof (struct ExecBase)) + neg);

	for (i=1; i<=NUMVECT; i++)
	{
	    __AROS_INITVEC (SysBase, i);
	    __AROS_SETVECADDR (SysBase, i, ExecFunctions[i-1]);
	}

	SysBase->LibNode.lib_Node.ln_Name = "exec.library";
	SysBase->LibNode.lib_Version	  = 41;
	SysBase->LibNode.lib_Revision	  = 10;

	SysBase->DebugData = &AROSBase;

	AROSBase.kprintf = (void *)kprintf;

	NEWLIST (&SysBase->MemList);
	AddHead (&SysBase->MemList, &mh.mh_Node);
	NEWLIST (&SysBase->ResourceList);
	NEWLIST (&SysBase->DeviceList);
	NEWLIST (&SysBase->IntrList);
	NEWLIST (&SysBase->LibList);
	NEWLIST (&SysBase->PortList);
	NEWLIST (&SysBase->TaskReady);
	NEWLIST (&SysBase->TaskWait);

	for (i=0; i<5; i++)
	{
	    NEWLIST (&SysBase->SoftInts[i].sh_List);
	}

	for (i=0; i<16; i++)
	{
	    SysBase->IntVects[i].iv_Code = NULL;
	    SysBase->IntVects[i].iv_Data = NULL;
	    SysBase->IntVects[i].iv_Node = NULL;
	}

	NEWLIST (&SysBase->SemaphoreList);

	/* There are no memhandlers yet.
	 * (not even the library flushing one which is part of ram/dos.library) */
	NEWLIST ((struct List *)&SysBase->ex_MemHandlers);

	SysBase->IDNestCnt = 0;
	SysBase->TDNestCnt = 0;
	SysBase->AttnResched = 0;
    }

    {
	/* Add boot task */
	struct Task    * t;
	struct MemList * ml;

	ml=(struct MemList *)AllocMem(sizeof(struct MemList),MEMF_PUBLIC|MEMF_CLEAR);
	t =(struct Task *)   AllocMem(sizeof(struct Task),   MEMF_PUBLIC|MEMF_CLEAR);
	ml->ml_NumEntries     =1;
	ml->ml_ME[0].me_Addr  =t;
	ml->ml_ME[0].me_Length=sizeof(struct Task);

	NEWLIST(&t->tc_MemEntry);
	AddHead(&t->tc_MemEntry,&ml->ml_Node);
	t->tc_Node.ln_Name="Boot task";
	t->tc_Node.ln_Pri=0;
	t->tc_State=TS_RUN;
	t->tc_SigAlloc=0xffff;
	t->tc_SPLower = NULL;	    /* This is the system's stack */
	t->tc_SPUpper = (APTR)~0UL; /* all available addresses are ok */
	SysBase->ThisTask=t;
    }
    {
	/* Add idle task */
	struct Task *t;
	struct MemList *ml;
	UBYTE *s;

	/* Allocate one header (incl. the first entry) and one additional
	    entry */
	ml=(struct MemList *)AllocMem(sizeof(struct MemList)+sizeof(struct MemEntry),
				      MEMF_PUBLIC|MEMF_CLEAR);
	t =(struct Task *)   AllocMem(sizeof(struct Task),    MEMF_PUBLIC|MEMF_CLEAR);
	s =(UBYTE *)         AllocMem(AROS_STACKSIZE,         MEMF_PUBLIC|MEMF_CLEAR);
	ml->ml_NumEntries     =2;
	ml->ml_ME[0].me_Addr  =t;
	ml->ml_ME[0].me_Length=sizeof(struct Task);
	ml->ml_ME[1].me_Addr  =s;
	ml->ml_ME[1].me_Length=AROS_STACKSIZE;

	NEWLIST(&t->tc_MemEntry);
	AddHead(&t->tc_MemEntry,&ml->ml_Node);
	t->tc_SPLower=s;
	t->tc_SPUpper=s+AROS_STACKSIZE;
	t->tc_Node.ln_Name="Idle task";
	t->tc_Node.ln_Pri=-128;
	AddTask(t,&idleTask,NULL);
    }
    /* Install all interrupt servers */
    {
	int i;
	for(i=0;i<16;i++)
	    if((1<<i)&(INTF_PORTS|INTF_COPER|INTF_VERTB|INTF_EXTER|INTF_SETCLR))
	    {
		struct Interrupt *is;
		struct SoftIntList *sil;
		is=AllocMem(sizeof(struct Interrupt),MEMF_PUBLIC);
		sil=AllocMem(sizeof(struct SoftIntList),MEMF_PUBLIC);
		is->is_Code=&IntServer;
		is->is_Data=sil;
		NEWLIST((struct List *)sil);
		SetIntVector(i,is);
	    }
    }
    InitCore();
    /* Install the Dispatcher */
    {
	struct Interrupt *is;
	is=(struct Interrupt *)AllocMem(sizeof(struct Interrupt),MEMF_PUBLIC);
	is->is_Code=(void (*)())&Dispatcher;
	AddIntServer(INTB_VERTB,is);
    }
    Enable();
    Permit();

    debugmem ();

    (void) InitResident((struct Resident *)&Utility_resident,0);

    DOSBase = (struct DosLibrary *)InitResident((struct Resident *)&Dos_resident,0);

    (void) InitResident((struct Resident *)&Graphics_resident,0);
    (void) InitResident((struct Resident *)&Intuition_resident,0);

    {
	struct consolebase
	{
	    struct Device device;
	};

	struct consolebase *conbase;

	conbase=(struct consolebase *)InitResident((struct Resident *)&Console_resident,0);
	/* AddDevice (&conbase->device); */
    }

    DOSBase = (struct DosLibrary *)OpenLibrary (DOSNAME, 39);

    if (!DOSBase)
    {
	fprintf (stderr, "Cannot open dos.library");
	exit (10);
    }

    {
	struct emulbase
	{
	    struct Device eb_device;
	    struct Unit *eb_stdin;
	    struct Unit *eb_stdout;
	    struct Unit *eb_stderr;
	};

	struct emulbase *emulbase;

	struct TagItem fhtags[]=
	{ { TAG_END, 0 } };

	struct FileHandle *fh_stdin=(struct FileHandle *)AllocDosObject(DOS_FILEHANDLE,fhtags);
	struct FileHandle *fh_stdout=(struct FileHandle *)AllocDosObject(DOS_FILEHANDLE,fhtags);

	struct TagItem bootprocess[]=
	{
	    { NP_Entry,     (IPTR)boot },
	    { NP_Input,     (IPTR)MKBADDR(fh_stdin) },
	    { NP_Output,    (IPTR)MKBADDR(fh_stdout) },
	    { NP_Name,	    (IPTR)"Boot process" },
	    { NP_Cli,	    1 },
	    { TAG_END, }
	};

	emulbase = (struct emulbase *) InitResident (
	    (struct Resident *)&emul_handler_resident,
	    0
	);
	/* AddDevice (&emulbase->eb_device); */

	AssignLock ("C",    Lock ("SYS:c",    SHARED_LOCK));
	AssignLock ("S",    Lock ("SYS:s",    SHARED_LOCK));
	AssignLock ("Libs", Lock ("SYS:libs", SHARED_LOCK));
	AssignLock ("Devs", Lock ("SYS:devs", SHARED_LOCK));

	fh_stdin->fh_Device  =&emulbase->eb_device;
	fh_stdin->fh_Unit    =emulbase->eb_stdin;
	fh_stdout->fh_Device =&emulbase->eb_device;
	fh_stdout->fh_Unit   =emulbase->eb_stdout;

	/* AROSBase.StdOut = MKBADDR(fh_stdout); */
	AROSBase.StdOut = stderr;

	CreateNewProc (bootprocess);
    }

    RemTask(NULL); /* get rid of Boot task */

    Switch (); /* Rescedule */

    /* Should never get here... */
    return 21;
}
