#include <stdio.h>

#pragma pack(2)
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <exec/memory.h>
#include <exec/tasks.h>
#include <exec/libraries.h>
#include <exec/execbase.h>
#pragma pack()

#include "arun.h"


void unsupp(char *X)
{
    panic("unsupported function at offset -0x%x in %s",
	  (&X)[-2] - (&X)[-1] + 6,
	  ((struct Library *)(&X)[-2])->lib_Node.ln_Name);
}


#define NFUNCS 400
struct execlib {
    struct func vectors[NFUNCS];
    struct ExecBase lib;
} execlib;
struct ExecBase *const ExecBase = &execlib.lib;



extern void SSysBase_OpenLibrary(void);
extern void SSysBase_OldOpenLibrary(void);
extern void SSysBase_CloseLibrary(void);
extern void SSysBase_AllocMem(void);
extern void SSysBase_FreeMem(void);
extern void SSysBase_FindTask(void);
extern void SSysBase_SetSignal(void);
extern void SSysBase_WaitPort(void);
extern void SSysBase_Forbid(void);
extern void SSysBase_Permit(void);
static struct libinit { void (*func)(); int offset; } libinit[] =
{
    { SSysBase_OpenLibrary, -0x228, },
    { SSysBase_OldOpenLibrary, -0x198, },
    { SSysBase_CloseLibrary, -0x19e, },
    { SSysBase_AllocMem, -0xc6, },
    { SSysBase_FreeMem, -0xd2, },
    { SSysBase_FindTask, -0x126, },
    { SSysBase_SetSignal, -0x132, },
    { SSysBase_WaitPort, -0x180, },
    { SSysBase_Forbid, -0x84, },
    { SSysBase_Permit, -0x8a, },
    { 0, 0, },
};


void *exec_init(void)
{
    struct libinit *p2;
    struct func *p;
    int i;

    ExecBase->LibNode.lib_Node.ln_Name = "exec.library";

    /* First, fill all vectors with "unsupp" calls */
    p = (struct func *)ExecBase;
    for ( i=0 ; i<NFUNCS ; ++i )
    {
	--p;
	p->opcode = 0x4eb9;	/* jsr N.l */
	p->addr = unsupp;
    }

    /* Now, fill in the supported vectors with the real jumps */
    for ( p2=libinit ; p2->func ; ++p2 )
    {
	p = (struct func *)((char *)ExecBase + p2->offset);
	p->opcode = 0x4ef9;	/* jmp N.l */
	p->addr = p2->func;
    }

    verbose("ExecBase = 0x%x", ExecBase);
    return ExecBase;
}


static struct Library *ManxEnviron(void)
{
    extern char **environ;
    char **p, *x;
    int n;
#pragma pack(2)
    static struct { struct Library lib; char *env; } *ep;
#pragma pack()

    if (ep)
	return (struct Library *)ep;

    verbose("Building Manx environment");
    ep = Malloc(sizeof *ep);

    n = 1;
    for ( p=environ ; *p ; ++p )
	n += strlen(*p) + 1;

    x = ep->env = Malloc(n);
    for ( p=environ ; *p ; ++p )
    {
	strcpy(x, *p);
	x += strlen(x) + 1;
    }
    *x++ = 0;

    return (struct Library *)ep;
}


struct Library *SysBase_OpenLibrary(char *libname, unsigned version)
{
    verbose("OpenLibrary(%s, %d)", libname, version);

    if (!strcmp(libname, "dos.library"))
	return (struct Library *)DOSBase;

    if (!strcmp(libname, "environment"))
	return ManxEnviron();

    return 0;
}
struct Library *SysBase_OldOpenLibrary(char *libname, unsigned version)
{
    return SysBase_OpenLibrary(libname, 0);
}
void SysBase_CloseLibrary(struct Library *lib)
{
    verbose("CloseLibrary(0x%x)");
}

void *SysBase_AllocMem(ULONG nbytes, ULONG flags)
{
    extern void *malloc(unsigned nbytes);
    void *p;

    if (flags&MEMF_CHIP)
    {
	warning("AllocMem(0x%x, MEMF_CHIP)", nbytes);
	return 0;
    }
    verbose("AllocMem(0x%x, 0x%x)", nbytes, flags);
    p = malloc(nbytes);
    if (p && (flags&MEMF_CLEAR))
	memset(p, 0, nbytes);
    return p;
}
void SysBase_FreeMem(void *p, unsigned nbytes)
{
    extern void free(void *p);
    verbose("FreeMem(0x%x, 0x%x)", p, nbytes);
    free(p);
}

struct Task *SysBase_FindTask(UBYTE *name)
{
    verbose("FindTask(%s)", name?(char *)name:"0");
    if (!name || !strcmp(name, ExecBase->ThisTask->tc_Node.ln_Name))
	return ExecBase->ThisTask;
    return 0;
}

ULONG SysBase_SetSignal(ULONG new, ULONG mask)
{
    ULONG old = ExecBase->ThisTask->tc_SigRecvd;
    verbose("SetSignal(%08x, %08x)", new, mask);
    if (mask)
	ExecBase->ThisTask->tc_SigRecvd =
	    (ExecBase->ThisTask->tc_SigRecvd & ~mask) |
	    (new & mask);
    return old;
}

struct Message *SysBase_WaitPort(struct MsgPort *port)
{
    struct Message *msg;
    verbose("WaitPort(0x%x)", port);
    if (!(msg=(struct Message *)port->mp_MsgList.lh_Head))
	panic("waiting forever");
    return msg;
}

void SysBase_Forbid(void)
{
}

void SysBase_Permit(void)
{
}
