 /*
  * UAE - The Un*x Amiga Emulator
  *
  * exec.library emulation
  *
  * Copyright 1996 Bernd Schmidt
  */

#undef USE_EXECLIB

#include "sysconfig.h"
#include "sysdeps.h"

#include <assert.h>
#include <signal.h>
#ifdef USE_EXECLIB
#include <setjmp.h>
#endif

#include "config.h"
#include "options.h"
#include "memory.h"
#include "custom.h"
#include "newcpu.h"
#include "xwin.h"
#include "autoconf.h"
#include "osemu.h"

static CPTR sysbase;

/*
 * Exec list management
 */

void EXEC_NewList(CPTR list)
{
    put_long(list, list + 4);
    put_long(list + 4, 0);
    put_long(list + 8, list);
}

void EXEC_Insert(CPTR list, CPTR node, CPTR pred)
{
    CPTR succ;
    if (pred == 0)
	pred = list;
    put_long(node, succ = get_long(pred));
    put_long(pred, node);
    put_long(succ + 4, node);
    put_long(node + 4, pred);
}

void EXEC_Enqueue(CPTR list, CPTR node)
{
    int pri = (BYTE)get_byte (node + 9);
    CPTR lnode = get_long(list); /* lh_Head */
    CPTR prev = 0, next;
    
    for (;(next = get_long(lnode)) != 0 && (BYTE)get_byte(lnode + 9) >= pri;) {
	prev = lnode;
	lnode = next;
    }
    EXEC_Insert(list, node, prev);
}

void EXEC_Remove(CPTR node)
{
    CPTR pred = get_long(node + 4), succ = get_long(node);
    put_long(pred, succ);
    put_long(succ + 4, pred);
}

ULONG EXEC_RemHead(CPTR list)
{
    CPTR head = get_long(list);
    if (get_long(head) == 0)
	return 0;
    EXEC_Remove(head);
    return head;
}

ULONG EXEC_RemTail(CPTR list)
{
    CPTR tail = get_long(list + 8);
    if (get_long(tail + 4) == 0)
	return 0;
    
    EXEC_Remove(tail);
    return tail;
}

void EXEC_AddTail(CPTR list, CPTR node) 
{
    EXEC_Insert(list, node, get_long(list + 8)); 
}

CPTR EXEC_FindName(CPTR start, char *name)
{
    CPTR node = start;
    if (name == NULL)
	return 0;
    
    for (;;) {
	CPTR nnp;
	char *nn;

	node = get_long(start);

	if (node == 0)
	    break;
	nnp = get_long(node + 10);
	if (nnp != 0 && (nn = get_real_address(nnp)) != NULL)
	    if (strcmp(nn, name) == 0)
		return node;
	start = node;
    }
    return 0;
}

static ULONG execl_Enqueue(void) { EXEC_Enqueue(regs.a[0], regs.a[1]); return 0; }
static ULONG execl_Insert(void) { EXEC_Insert(regs.a[0], regs.a[1], regs.a[2]); return 0; }
static ULONG execl_AddHead(void) { EXEC_Insert(regs.a[0], regs.a[1], 0); return 0; }
static ULONG execl_AddTail(void) { EXEC_Insert(regs.a[0], regs.a[1], get_long(regs.a[0] + 8)); return 0; }
static ULONG execl_Remove(void) { EXEC_Remove(regs.a[1]); return 0; }
static ULONG execl_RemHead(void) { return EXEC_RemHead(regs.a[0]); }
static ULONG execl_RemTail(void) { return EXEC_RemTail(regs.a[0]); }
static ULONG execl_FindName(void) { CPTR n = EXEC_FindName(regs.a[0], get_real_address(regs.a[1])); ZFLG = n == 0; return n; }

/*
 * Miscellaneous
 */

void EXEC_InitStruct(CPTR inittable, CPTR memory, unsigned long size)
{
    CPTR dptr = memory;
    UBYTE *mem = get_real_address(memory);
    if (memory == 0 || mem == NULL || !valid_address(memory, size)) {
	fprintf(stderr, "Foo\n");
	return;
    }
    memset(mem, 0, size);
    fprintf(stderr, "InitStruct\n");
    for (;;) {
	UBYTE command;
	int count, desttype, srcloc;
	ULONG offset;

	/* Read commands from even bytes */
	inittable = (inittable + 1) & ~1;

	command = get_byte(inittable);
	if (command == 0)
	    break;
	desttype = (command >> 6) & 3;
	srcloc = (command >> 4) & 3;
	count = (command & 15) + 1;

	if (desttype == 3) {
	    dptr = memory + (get_long(inittable) & 0xFFFFFF);
	    inittable += 4;
	} else if (desttype == 2) {
	    dptr = memory + get_byte(inittable+1), inittable += 2;
	} else 
	    inittable++;

	if (srcloc != 0)
	    inittable = (inittable + 1) & ~1;

	if (desttype != 1) {
	    for(; count > 0; count--) {
		switch (srcloc) {
		 case 0: put_long(dptr, get_long(inittable)); dptr += 4; inittable += 4; break;
		 case 1: put_word(dptr, get_word(inittable)); dptr += 2; inittable += 2; break;
		 case 2: put_byte(dptr, get_byte(inittable)); dptr += 1; inittable += 1; break;
		}
	    }
	} else {
	    ULONG data;
	    switch (srcloc) {
	     case 0: data = get_byte(inittable); inittable += 1; break;
	     case 1: data = get_word(inittable); inittable += 2; break;
	     case 2: data = get_long(inittable); inittable += 4; break;
	     default: data = 0; /* Alert */ break;
	    }
	    for(; count > 0; count--) {
		switch (srcloc) {
		 case 0: put_long(dptr, data); dptr += 4; break;
		 case 1: put_word(dptr, data); dptr += 2; break;
		 case 2: put_byte(dptr, data); dptr += 1; break;
		}
	    }
	}
    }
}
/* The size parameter sometimes seems to contain garbage in the top 16 bit */
static ULONG execl_InitStruct(void) { EXEC_InitStruct(regs.a[1], regs.a[2], regs.d[0] & 0xFFFF); return 0; }

/*
 * Interrupts
 * This is all very confusing. As I see things, the IntVects field of the
 * ExecBase is set up as follows:
 *   One interrupt may either be assigned an IntServer, or an IntHandler.
 *   PORTS, COPER, VERTB EXTER and NMI are set up as Servers (at least that's
 *   what the AutoDocs say), the others as IntHandlers.
 *   An IntServer field has a pointer to an Exec list in the iv_Data field,
 *   the list contains all the server Interrupt nodes in a nice chain. The
 *   iv_Node field is zero. For an IntHandler, the iv_Node field contains a
 *   pointer to the node that was passed in SetIntVector.
 */

CPTR EXEC_SetIntVector(int number, CPTR interrupt)
{
    CPTR oldvec = get_long(sysbase + 84 + 12*number + 8);
    if (interrupt == 0) {
	put_long(sysbase + 84 + 12*number, 0);
	put_long(sysbase + 84 + 12*number + 4, 0);
	put_long(sysbase + 84 + 12*number + 8, 0);
    } else {
	put_long(sysbase + 84 + 12*number, get_long(interrupt + 14));
	put_long(sysbase + 84 + 12*number + 4, get_long(interrupt + 18));
	put_long(sysbase + 84 + 12*number + 8, interrupt);
    }
    return oldvec;
}

void EXEC_RemIntServer(ULONG nr, CPTR interrupt)
{
    CPTR list = get_long(sysbase + 84 + nr*12);
    
    EXEC_Remove(interrupt);
    /* Disable interrupt if necessary */
    if (get_long(get_long(list)) == 0)
	put_word(0xDFF09A, 1 << nr);
}

void EXEC_AddIntServer(ULONG nr, CPTR interrupt)
{
    CPTR list = get_long(sysbase + 84 + nr*12);
    int was_empty;

    /* Disable interrupt if necessary */
    was_empty = get_long(get_long(list)) == 0;
    
    EXEC_Enqueue(list, interrupt);
    if (was_empty)
	put_word(0xDFF09A, 0x8000 | (1 << nr));
    
}

static ULONG execl_SetIntVector(void) { return EXEC_SetIntVector(regs.d[0], regs.a[1]); }
static ULONG execl_AddIntServer(void) { EXEC_AddIntServer(regs.d[0] & 15, regs.a[1]); return 0; }
static ULONG execl_RemIntServer(void) { EXEC_RemIntServer(regs.d[0] & 15, regs.a[1]); return 0; }

/*
 * Memory functions
 */

#define MEMF_PUBLIC 1
#define MEMF_CHIP 2
#define MEMF_FAST 4
#define MEMF_LOCAL 256
#define MEMF_24BITDMA 512
#define MEMF_CLEAR (1<<16)
#define MEMF_LARGEST (1<<17)
#define MEMF_REVERSE (1<<18)
#define MEMF_TOTAL (1<<19)

CPTR EXEC_Allocate(CPTR memheader, unsigned long size)
{
    CPTR chunk, chunkpp;
    unsigned long avail;
    
    size = (size + 7) & ~7;
    
    if (size == 0 || size > get_long(memheader + 28))
	return 0;
	
    for (chunk = get_long(chunkpp = memheader + 16); chunk != 0; chunkpp = chunk, chunk = get_long(chunk))
	if ((avail = get_long(chunk + 4)) >= size) {
	    CPTR nextchunk = get_long(chunk);
	    if (avail-size == 0)
		put_long(chunkpp, nextchunk);
	    else {
		put_long(chunkpp, chunk + size);
		put_long(chunk + size, nextchunk);
		put_long(chunk + size + 4, avail - size);
	    }
	    put_long(memheader + 28, get_long(memheader + 28) - size);
	    return chunk;
	}
    return 0;
}

void EXEC_Deallocate(CPTR memheader, CPTR addr, unsigned long size)
{
    CPTR chunk, chunkpp;
    CPTR areaend;

    if (addr == 0)
	return;

    size = (size + 7) & ~7;
    areaend = addr + size;
    
    put_long(memheader + 28, get_long(memheader + 28) + size);
    
    for (chunk = get_long(chunkpp = memheader + 16); chunk != 0; chunkpp = chunk, chunk = get_long(chunk)) {
	if (chunk + get_long(chunk + 4) == addr) {
	    /* Merge with this and go ahead so we can also merge with the next */
	    put_long(chunkpp, get_long(chunk));
	    addr = chunk; size += get_long(chunk + 4);
	    chunk = chunkpp;
	    continue;
	}
	if (areaend == chunk) {
	    /* Merge with previous */
	    put_long(chunkpp, addr);
	    put_long(addr, get_long(chunk));
	    put_long(addr + 4, get_long(chunk + 4) + size);
	    return;
	}
	if (chunk > addr)
	    break;
    }
    put_long(chunkpp, addr);
    put_long(addr, chunk);
    put_long(addr + 4, size);
}

CPTR EXEC_AllocMem(unsigned long size, ULONG requirements)
{
    CPTR nextmh,  memheader = get_long(sysbase + 322);
    ULONG attrs = requirements & (MEMF_PUBLIC | MEMF_CHIP | MEMF_FAST);
    CPTR result = 0;

    size = (size + 7) & ~7;

    for (;(nextmh = get_long(memheader)) != 0; memheader = nextmh) {
	if ((get_word(memheader + 14) & attrs) != attrs)
	    continue;
	result = EXEC_Allocate(memheader, size);
	if (result != 0)
	    break;
    }
    if (result) {
	if (requirements & MEMF_CLEAR)
	    memset(get_real_address(result), 0, size);
    }
    return result;
}

CPTR EXEC_AllocEntry(CPTR ml)
{
    CPTR newml = EXEC_AllocMem(16 + 8*get_word(ml + 14), MEMF_PUBLIC|MEMF_CLEAR);
    int i;

    if (newml == 0)
	return 0x80000000|MEMF_PUBLIC;
    for (i = 0; i < get_word(ml + 14); i++) {
	ULONG reqs = get_long(ml + 16 + 8*i);
	ULONG addr = EXEC_AllocMem(get_long(ml + 16 + 8*i + 4), reqs);
	if (addr == 0)
	    /* Thank god for SetPatch */
	    return 0x80000000 | reqs;
	put_long(newml + 16 + 8*i, addr);
	put_long(newml + 16 + 8*i + 4, reqs);
    }
    return newml;
}

void EXEC_FreeMem(CPTR addr, unsigned long size)
{
    CPTR nextmh,  memheader = get_long(sysbase + 322);

    size = (size + 7) & ~7;
    if (addr == 0)
	return;

    for (;(nextmh = get_long(memheader)) != 0; memheader = nextmh) {
	if (get_long(memheader + 20) <= addr
	    && get_long(memheader + 24) > addr) {
	    EXEC_Deallocate(memheader, addr, size);
	    return;
	}
    }
}

unsigned long EXEC_AvailMem(ULONG requirements)
{
    ULONG attrs = requirements & (MEMF_PUBLIC | MEMF_CHIP | MEMF_FAST);
    CPTR nextmh, memheader = get_long(sysbase + 322);
    CPTR result = 0;

    for (;(nextmh = get_long(memheader)) != 0; memheader = nextmh) {
	if ((get_word(memheader + 14) & attrs) != attrs)
	    continue;
	if (attrs & MEMF_LARGEST) {
	    unsigned long maxsize = 0;
	    CPTR chunk;
	    for (chunk = get_long(memheader + 16); chunk != 0; chunk = get_long(chunk))
		if (get_long(chunk+4) > maxsize)
		    maxsize = get_long(chunk + 4);
	    result += maxsize;
	} else if (attrs & MEMF_TOTAL) {
	    result += get_long(memheader + 24) - get_long(memheader + 20);
	} else result += get_long(memheader + 28);
    }
    return result;
}

void EXEC_AddMemList(unsigned long size, ULONG attrs, int pri, CPTR base,
		     CPTR name)
{
    put_byte(base + 8, 0);
    put_byte(base + 9, pri);
    put_long(base + 10, name);
    put_word(base + 14, attrs);
    put_long(base + 16, base + 32);
    put_long(base + 20, base + 32);
    put_long(base + 36, size - 32);
    put_long(base + 28, size - 32);
    put_long(base + 24, base + size);
    EXEC_Enqueue(sysbase + 322, base);
}

static ULONG execl_AddMemList(void)
{
    EXEC_AddMemList(regs.d[0], regs.d[1], (LONG)regs.d[2], regs.a[0], regs.a[1]);
    return 0;
}

static ULONG execl_CopyMem(void)
{
    UBYTE *src = get_real_address(regs.a[0]);
    UBYTE *dst = get_real_address(regs.a[1]);

    if (src != NULL && dst != NULL
	&& valid_address(regs.a[0], regs.d[0])
	&& valid_address(regs.a[1], regs.d[0]))
	memcpy(dst, src, regs.d[0]);
    else
	fprintf(stderr, "CopyMem with bogus parameters\n");
    return 0;
}

static ULONG execl_AllocMem(void) { return EXEC_AllocMem(regs.d[0], regs.d[1]); }
static ULONG execl_AllocEntry(void) { return EXEC_AllocEntry(regs.a[0]); }
static ULONG execl_Allocate(void) { return EXEC_Allocate(regs.a[0], regs.d[0]); }
static ULONG execl_FreeMem(void) { EXEC_FreeMem(regs.a[1], regs.d[0]); return 0; }
static ULONG execl_AvailMem(void) { return EXEC_AvailMem(regs.d[1]); }
static ULONG execl_Deallocate(void) { EXEC_Deallocate(regs.a[0], regs.a[1], regs.d[0]); return 0; }

/*
 * Scheduling
 */

#define TS_RUN 2
#define TS_READY 3
#define TS_WAIT 4
#define TS_EXCEPT 5

struct NewTask {
    struct NewTask *next, *prev;
    CPTR exectask;
#ifdef USE_EXECLIB
    jmp_buf j;
#endif
    void *stack;
    struct regstruct regs;
    struct flag_struct flags;
};

static struct NewTask idle_task;
static int intr_count, need_resched;

static struct NewTask *find_newtask(CPTR task)
{
    /* Ugh, this is ugly */
    struct NewTask *t = &idle_task;
    do {
	if (t->exectask == task)
	    return t;
	t = t->next;
    } while (t != &idle_task);
    fprintf(stderr, "Uh oh. Task list corrupt\n");
}

static void schedule(void)
{
#ifdef USE_EXECLIB
    CPTR readylist = sysbase + 406;
    CPTR readytask = get_long(readylist);
    CPTR runtask = get_long(sysbase + 276);
    struct NewTask *runtask_nt, *readytask_nt;
    unsigned long oldflags;

    int idis;
    
    need_resched = 0;
    /* Any ready tasks? */
    if (get_long(readytask) == 0)
	return;
    
    /* If the running task is going to sleep, don't check priorities*/
    if (get_byte(runtask + 15) != TS_WAIT) {
	/* Has the running task a higher priority than any ready tasks? */
	if ((BYTE)get_byte(runtask + 9) > (BYTE)get_byte(readytask + 9))
	return;
	/* We should only preempt a task of the same priority if the quantum
	 * has expired. Hmmm... */
    }
    if (get_byte(runtask + 15) == TS_RUN)
	put_byte(runtask + 15, TS_READY);
    if (get_byte(runtask + 15) == TS_WAIT)
	EXEC_Enqueue(sysbase + 420, runtask);
    else
	EXEC_Enqueue(readylist, runtask);
    put_byte(readytask + 15, TS_RUN);
    EXEC_Remove(readytask);
    
    readytask_nt = find_newtask(readytask);
    runtask_nt = find_newtask(runtask);
    

    oldflags = regs.spcflags;
    runtask_nt->regs = regs;
    runtask_nt->flags = regflags;
    regs = readytask_nt->regs;
    regflags = readytask_nt->flags;
    regs.spcflags = (regs.spcflags & ~PRESERVED_FLAGS) | (oldflags & PRESERVED_FLAGS);
    
    put_byte(runtask + 16, get_byte(sysbase + 294));
    put_byte(runtask + 17, get_byte(sysbase + 295));
    put_byte(sysbase + 294, idis = get_byte(readytask + 16));
    put_byte(sysbase + 295, get_byte(readytask + 17));
    put_long(sysbase + 276, readytask);
    /* Set INTENA according to new interrupt enable status */
    if (idis == 0xFF)
	put_word(0xDFF09A, 0xC000);
    else
	put_word(0xDFF09A, 0x4000);
    /* Whee... */
    fprintf(stderr, "Task switch: new task %s\n", get_real_address(get_long(readytask + 10)));
    if (setjmp(runtask_nt->j) == 0)
	longjmp(readytask_nt->j, 1);
    regs.spcflags &= ~SPCFLAG_BRK;
#endif
}

static __inline__ void maybe_schedule(void)
{
    /* @@@ Should we avoid a schedule when supervisor bit is set?
     * Definitely not if the running task is the idle task */
    if (intr_count == 0 && get_byte(sysbase + 294) == 0xFF && get_byte(sysbase + 295) == 0xFF && need_resched)
	schedule();
}

/*
 * Signals
 */

int EXEC_AllocSignal(int signum)
{
    CPTR thistask = get_long(sysbase + 276);
    ULONG sigalloc = get_long(thistask + 18);
    if (signum == -1)
	for (signum = 0; signum < 32; signum++)
	    if ((sigalloc & (1 << signum)) == 0)
		break;
    
    if ((sigalloc & (1 << signum)) == 0) {
	put_long(thistask + 18, sigalloc | (1 << signum));
    } else
	signum = -1;

    return signum;
}

void EXEC_FreeSignal(int signum)
{
    CPTR thistask = get_long(sysbase + 276);
    if (signum != -1)
	return;
    put_long(thistask + 18, get_long(thistask + 18) & ~(1 << signum));
}

ULONG EXEC_SetSignal(ULONG newsig, ULONG exec_sigmask)
{
    CPTR task = get_long(sysbase + 276);
    ULONG signals = get_long(task + 26);
    /* @@@ check SigExcept here */
    put_long(task + 26, (signals & ~exec_sigmask) | (newsig & exec_sigmask));
    return signals;
}

void EXEC_Signal(CPTR task, ULONG exec_sigmask)
{
    ULONG exec_sigs = get_long(task + 26);
    ULONG sigwait = get_long(task + 22);
    put_long(task + 26, exec_sigs | exec_sigmask);
    if (get_byte(task + 15) == TS_WAIT && (sigwait & exec_sigmask) != 0) {
	EXEC_Remove(task);
	EXEC_Enqueue(sysbase + 406, task);
	need_resched = 1;
	maybe_schedule();
    }
}

ULONG EXEC_Wait(ULONG exec_sigmask)
{
    CPTR task = get_long(sysbase + 276);
    ULONG exec_sigs = get_long(task + 26);
    if ((exec_sigs & exec_sigmask) != 0) {
	put_long(task + 26, exec_sigs & ~exec_sigmask);
	return exec_sigs & exec_sigmask;
    }
    if (get_byte(sysbase + 294) != 0xFF || get_byte(sysbase + 295) != 0xFF)
    {
	fprintf(stderr, "Arrgh, task Wait()ing when it shouldn't\n");
    }
    if (intr_count || regs.s) {
	fprintf(stderr, "Arrgh, task Wait()ing when it _really_ shouldn't\n");
    }

    put_byte(task + 15, TS_WAIT);
    put_long(task + 22, exec_sigmask);
    schedule();
    exec_sigs = get_long(task + 26);
    if ((exec_sigs & exec_sigmask) != 0) {
	put_long(task + 26, exec_sigs & ~exec_sigmask);
	return exec_sigs & exec_sigmask;
    } 
    fprintf(stderr, "Bug: task woke up without signals\n");
    return 0;
}

static ULONG execl_SetSignal(void) { return EXEC_SetSignal(regs.d[0], regs.d[1]); }
static ULONG execl_Signal(void) { EXEC_Signal(regs.a[1], regs.d[0]); return 0; }
static ULONG execl_Wait(void) { return EXEC_Wait(regs.d[0]); }
static ULONG execl_AllocSignal(void) { return EXEC_AllocSignal((BYTE)regs.d[0]); }
static ULONG execl_FreeSignal(void) { EXEC_FreeSignal((BYTE)regs.d[0]); return 0; }

/*
 * Semaphores
 */

void EXEC_InitSemaphore(CPTR exec_sigsem)
{
    EXEC_NewList(exec_sigsem + 16);
    put_word(exec_sigsem + 14, 0);
    put_word(exec_sigsem + 44, 0xFFFF);
}

ULONG EXEC_AttemptSemaphore(CPTR exec_sigsem)
{
    CPTR task = get_long(sysbase + 276);
    UWORD count = get_word(exec_sigsem + 44);
    if (count == 0xFFFF) {
	/* Semaphore free -> lock it */
	put_word(exec_sigsem + 44, 0);
	put_long(exec_sigsem + 40, task);
	put_word(exec_sigsem + 14, 1);
	return 1;
    }
    if (get_long(exec_sigsem + 40) == task) {
	/* Locked by same task -> Increment NestCount */
	put_word(exec_sigsem + 14, get_word(exec_sigsem + 14) + 1);
	return 1;
    }
    return 0;
}

void EXEC_ObtainSemaphore(CPTR exec_sigsem)
{
    CPTR task = get_long(sysbase + 276);
    UWORD count = get_word(exec_sigsem + 44);
    if (count == 0xFFFF) {
	/* Semaphore free -> lock it */
	put_word(exec_sigsem + 44, 0);
	put_long(exec_sigsem + 40, task);
	put_word(exec_sigsem + 14, 1);
	return;
    }
    if (get_long(exec_sigsem + 40) == task) {
	/* Locked by same task -> Increment NestCount */
	put_word(exec_sigsem + 14, get_word(exec_sigsem + 14) + 1);
	return;
    }
    /* Need to wait. */
    /* @@@ None of the predefined signals in exec/tasks.h seem to be used
     * for this. We use 0x8000, which appears not to be used otherwise */
    put_word(exec_sigsem + 44, count + 1);
    EXEC_AddTail(exec_sigsem + 16, task);
    if (EXEC_Wait(0x8000) != 0x8000)
	fprintf(stderr, "BUG\n");

    /* Now it's mine, all mine! */
    put_long(exec_sigsem + 40, task);
    put_word(exec_sigsem + 14, 1);
}

void EXEC_ReleaseSemaphore(CPTR exec_sigsem)
{
    UWORD nest = get_word(exec_sigsem + 14);
    put_word(exec_sigsem + 14, nest - 1);
    if (nest == 1) {
	/* We're losing it. Signal the first task on the wait queue
	 * (that one will in turn wake the next, etc.) */
	CPTR task = EXEC_RemHead(exec_sigsem + 16);
	UWORD count = get_word(exec_sigsem + 44);
	put_word(exec_sigsem + 44, count - 1);
	if (task) {
	    EXEC_Signal(task, 0x8000);
	}
    }
}

static ULONG execl_ObtainSemaphore(void) { EXEC_ObtainSemaphore(regs.a[0]); return 0; }
static ULONG execl_ReleaseSemaphore(void) { EXEC_ReleaseSemaphore(regs.a[0]); return 0; }
static ULONG execl_FindSemaphore(void) { return EXEC_FindName(sysbase + 532, get_real_address(regs.a[1])); }
static ULONG execl_InitSemaphore(void) { EXEC_InitSemaphore(regs.a[0]); return 0; }
static ULONG execl_AttemptSemaphore(void) { return EXEC_AttemptSemaphore(regs.a[0]); }
static ULONG execl_AddSemaphore(void) { put_byte(regs.a[1] + 8, 15); EXEC_Enqueue(sysbase + 532, regs.a[1]); return 0; }

/*
 * Messages and ports
 */

CPTR EXEC_GetMsg(CPTR port)
{
    return EXEC_RemHead(port + 20);
}

/* This is shared between PutMsg and ReplyMsg */
static void EXEC_doputmsg(CPTR port, CPTR msg)
{
    UBYTE flags = get_byte (port + 14);
    EXEC_AddTail(port + 20, msg);
    if (flags == 0)
	EXEC_Signal(get_long(port + 16), 1 << get_byte(port + 15));
    else if (flags == 1)
	fprintf(stderr, "PA_SOFTINT unsupported\n");
    
}

void EXEC_PutMsg(CPTR port, CPTR msg)
{
    put_byte(msg + 8, 5);
    EXEC_doputmsg(port, msg);
}

void EXEC_ReplyMsg(CPTR msg)
{    
    put_byte(msg + 8, 7);
    EXEC_doputmsg(get_long(msg + 14), msg);
}

CPTR EXEC_WaitPort(CPTR port)
{
    for (;;) {
	CPTR msg = get_long(port + 20);
	if (get_long(msg) != 0)
	    return msg;
	if (get_byte(port + 14) != 0)
	    fprintf(stderr, "Don't know how to WaitPort()\n");
	EXEC_Wait(1 << get_byte(port + 15));
    }
}

static ULONG execl_GetMsg(void) { return EXEC_GetMsg(regs.a[0]); }
static ULONG execl_PutMsg(void) { EXEC_PutMsg(regs.a[0], regs.a[1]); return 0; }
static ULONG execl_ReplyMsg(void) { EXEC_ReplyMsg(regs.a[1]); return 0; }
static ULONG execl_WaitPort(void) { return EXEC_WaitPort(regs.a[0]); }
static ULONG execl_FindPort(void) { return EXEC_FindName(sysbase + 392, get_real_address(regs.a[1])); }
static ULONG execl_AddPort(void) { put_byte(regs.a[1] + 8, 4); EXEC_Enqueue(sysbase + 392, regs.a[1]); return 0; }

/*
 * I/O
 */

LONG EXEC_WaitIO(CPTR ioreq)
{
    /* The device is supposed to clear the IO_QUICK bit if it's going to
     * reply to the command */
    if ((get_byte(ioreq + 30) & 1) == 1)
	return (LONG)(BYTE)get_byte(ioreq + 31);

    for (;;) {
	if (get_byte(ioreq + 8) == 7) /* NT_REPLYMSG? */
	    break;
	/* We'll just hope that this a) has a ReplyPort and b) that port
	 * is of type PA_SIGNAL 
	 * Don't WaitPort() here: WaitPort() returns a message, and we
	 * aren't sure it's for us. */
	
	EXEC_Wait(1 << get_byte(get_long(ioreq + 14) + 15));
    }
    EXEC_Remove(ioreq);
    return (LONG)(BYTE)get_byte(ioreq + 31);
}

static void EXEC_BeginIO(CPTR ioreq)
{
    CPTR dev = get_long(ioreq + 20);
    regs.a[6] = dev;
    regs.a[1] = ioreq;
    Call68k(dev - 30, 1);
}

void EXEC_DoIO(CPTR ioreq)
{
    put_byte(ioreq + 30, 1);
    EXEC_BeginIO(ioreq);
    EXEC_WaitIO(ioreq);
}
    
void EXEC_SendIO(CPTR ioreq)
{
    put_byte(ioreq + 30, 0);
    EXEC_BeginIO(ioreq);
}

CPTR EXEC_CheckIO(CPTR ioreq)
{
    if ((get_byte(ioreq + 30) & 1) == 1)
	return ioreq;
    if (get_byte(ioreq + 8) == 7)
	return ioreq;
    return 0;
}

static ULONG execl_WaitIO(void) { return EXEC_WaitIO(regs.a[1]); }
static ULONG execl_DoIO(void) { EXEC_DoIO(regs.a[1]); return 0;  }
static ULONG execl_SendIO(void) { EXEC_SendIO(regs.a[1]); return 0;  }
static ULONG execl_CheckIO(void) { return EXEC_CheckIO(regs.a[1]); }

/*
 * Libraries
 */

void EXEC_SumLibrary(CPTR lib)
{
    CPTR start = lib - get_word(lib + 16);
    int len = get_word(lib + 16) + get_word(lib + 18);
    ULONG sum = 0;
    UWORD flags = get_word(lib + 14);
    
    if (flags & 1) /* SUMMING? */
	return;
    if (!(flags & 4)) /* SUMUSED? */
	return;
    put_word(lib + 14, flags & ~2); /* mark as not changed */
    /* Pretend the checksum is OK, I don't care enough */
}

CPTR EXEC_SetFunction(CPTR lib, int funcOffset, CPTR function)
{
    CPTR oldfunc = get_long(lib + funcOffset + 2);
    put_word(lib + funcOffset, 0x4EF9);
    put_long(lib + funcOffset + 2, function);
    put_word(lib + 14, get_word(lib + 14) | 2);
    EXEC_SumLibrary(lib);
    return oldfunc;
}

void EXEC_AddLibrary(CPTR lib)
{
    EXEC_Enqueue(sysbase + 378, lib);
    put_word(lib + 14, 2); /* mark as changed */
    EXEC_SumLibrary(lib);
}

CPTR EXEC_OpenLibrary(char *name, int version)
{
    CPTR lib = EXEC_FindName(sysbase + 378, name);
    if (lib != 0) {
	regs.a[6] = lib;
	regs.d[0] = version;
	lib = Call68k(lib - 6, 1);
    }
    return lib;
}

CPTR EXEC_OpenDevice(char *name, ULONG unit, CPTR ioRequest, ULONG flags)
{
    CPTR dev = EXEC_FindName(sysbase + 350, name);
    CPTR replyport = get_long(ioRequest + 14);
    put_long(ioRequest + 20, dev);
    put_byte(ioRequest + 31, 0);
    put_byte(ioRequest + 30, flags); /* is this right? */
    if (replyport == 0)
	fprintf(stderr, "ioRequest has no ReplyPort\n");
    else {
	CPTR rtask = get_long(replyport + 16);
	if (rtask == 0)
	    fprintf(stderr, "replyTask == 0\n");
	else if (rtask != get_long(sysbase + 276))
	    fprintf(stderr, "replyTask != current\n");
/*	put_long(replyport + 16, get_long(sysbase + 276));*/
    }
    if (dev != 0) {
	regs.a[6] = dev;
	regs.a[1] = ioRequest;
	regs.d[0] = unit;
	regs.d[1] = flags;
	dev = Call68k(dev - 6, 1);
    }
    return (LONG)(BYTE)get_byte(ioRequest + 31);
}

void EXEC_AddDevice(CPTR lib)
{
    EXEC_Enqueue(sysbase + 350, lib);
}

void EXEC_AddResource(CPTR lib)
{
    EXEC_Enqueue(sysbase + 336, lib);
}

CPTR EXEC_OpenResource(char *name)
{
    CPTR lib = EXEC_FindName(sysbase + 336, name);
    return lib;
}

void EXEC_MakeFunctions(CPTR target, CPTR funcarray, CPTR funcdispb)
{
    if (funcdispb != 0) {
	WORD tmp;
	for (;;) {
	    tmp = get_word(funcarray); funcarray += 2;
	    if (tmp == -1)
		break;
	    target -= 6;
	    put_word(target, 0x4EF9); /* JMP.L */
	    put_long(target + 2, funcdispb + tmp);
	}
    } else {
	CPTR tmp;
	for (;;) {
	    tmp = get_long(funcarray); funcarray += 4;
	    if (tmp == (CPTR)-1)
		break;
	    target -= 6;
	    put_word(target, 0x4EF9); /* JMP.L */
	    put_long(target + 2, tmp);
	}
    }
}

/* This doesn't work yet */
CPTR EXEC_MakeLibrary(CPTR vectors, CPTR structure, CPTR init,
		      unsigned long dsize, ULONG seglist_b)
{
    CPTR seglist = seglist_b << 2;
    int vec_cnt = 0;
    unsigned long negsize;
    CPTR base, fdispb = 0, funcarray = vectors;
    ULONG retval = 0;

    fprintf(stderr, "MakeLibrary\n");
    if (get_word (vectors) == (UWORD)-1) {
	int offs = 2;
	fdispb = vectors;
	funcarray += 2;
	while (get_word (vectors+offs) != (UWORD)-1) {
	    offs += 2;
	    vec_cnt++;
	}
    } else {
	int offs = 0;
	while (get_long (vectors+offs) != (LONG)-1) {
	    offs += 4; 
	    vec_cnt++;
	}
    }
    negsize = (vec_cnt * 6 + 3) & ~3;
    dsize = (dsize + 3) & ~3;

    base = EXEC_AllocMem(negsize + dsize, MEMF_PUBLIC|MEMF_CLEAR);
    if (base == 0) /* Uh oh */
	return 0;
    EXEC_MakeFunctions(base + negsize, funcarray, fdispb);
    base += negsize;
    put_word(base + 16, negsize);
    put_word(base + 18, dsize);
    if (structure != 0) {
	/* Size is set to 0 so we won't clear it again */
	EXEC_InitStruct(structure, base, 0);
    }
    if (init != 0) {
	regs.a[6] = sysbase;
	regs.a[0] = seglist_b; /* BCPL seglist here? */
	regs.d[0] = base;
	/* call it */
	retval = Call68k(init, 0);
    } else
	retval = base;
    
    return retval;
}

CPTR EXEC_InitResident(CPTR resident, ULONG segList)
{
    UBYTE flags = get_byte(resident + 10);
    
    if (flags & 0x80) {
	CPTR autoinit = get_long(resident + 22);
	return EXEC_MakeLibrary(get_long(autoinit + 4), get_long(autoinit + 8),
				get_long(autoinit + 12), get_long(autoinit), segList >> 4);
    }
    
    regs.d[0] = 0; regs.a[0] = segList; regs.a[6] = sysbase;
    return Call68k(get_long(resident + 22), 0);
}

static ULONG execl_MakeFunctions(void) { EXEC_MakeFunctions(regs.a[0], regs.a[1], regs.a[2]); return 0; }
static ULONG execl_SumLibrary(void) { EXEC_SumLibrary(regs.a[1]); return 0; }
static ULONG execl_SetFunction(void) { return EXEC_SetFunction(regs.a[1], (WORD)regs.a[0], regs.d[0]); }
static ULONG execl_AddLibrary(void) { EXEC_AddLibrary(regs.a[1]); return 0; }
static ULONG execl_AddDevice(void) { EXEC_AddDevice(regs.a[1]); return 0; }
static ULONG execl_AddResource(void) { EXEC_AddResource(regs.a[1]); return 0; }
static ULONG execl_OpenLibrary(void) { return EXEC_OpenLibrary(get_real_address(regs.a[1]), regs.d[0]); }
static ULONG execl_OpenDevice(void) { return EXEC_OpenDevice(get_real_address(regs.a[0]), regs.d[0], regs.a[1], regs.d[1]); }
static ULONG execl_OpenResource(void) { return EXEC_OpenResource(get_real_address(regs.a[1])); }
static ULONG execl_MakeLibrary(void) { return EXEC_MakeLibrary(regs.a[0], regs.a[1], regs.a[2], regs.d[0], regs.d[1]); }

/*
 * Tasks
 */

static void task_startup(void)
{
    m68k_setpc(regs.pc);
    Call68k_retaddr(regs.pc, 0, get_long(regs.a[7] - 4));
    fprintf(stderr, "Task fell through at the end\n");
}

CPTR EXEC_AddTask(CPTR task, CPTR initPC, CPTR finalPC)
{
#ifdef USE_EXECLIB
    struct NewTask *nt = (struct NewTask *)malloc(sizeof (struct NewTask));
    nt->exectask = task;
    nt->prev = &idle_task;
    nt->next = idle_task.next;
    idle_task.next->prev = nt;
    idle_task.next = nt;
    memset(&nt->regs, 0, sizeof (nt->regs));
    nt->regs.pc = initPC;
    nt->regs.a[7] = get_long(task + 54);

    if (finalPC == 0)
	finalPC = 0xF0FFF0; /* standard "return from 68k mode" calltrap */
    put_byte(task + 16, 0xFF);
    put_byte(task + 17, 0xFF);
    put_long(nt->regs.a[7] - 4, finalPC);
    nt->stack = malloc(65536); /* @@@ make this smaller after checking that
				* no parts of the emulator need much stack
				* space */
    nt->j[0].__sp = nt->stack + 65536;
    nt->j[0].__pc = &task_startup;
    put_byte(task + 15, TS_READY);
    put_long(task + 18,0x0000FFFF); /* all tasks have the lower 16 signals allocated */
    EXEC_Enqueue(sysbase + 406, task);
    need_resched = 1;
    maybe_schedule();
    return task;
#endif
}

CPTR EXEC_FindTask(char *name)
{
    char *n;
    CPTR v;

    if (name == NULL || ((n = get_real_address(get_long(get_long(sysbase + 276) + 10))) != NULL
			 && strcmp(n, name) == 0))
	return get_long(sysbase + 276);
    
    v = EXEC_FindName(sysbase + 406, name); /* ready tasks */
    if (v != 0)
	return v;
    return EXEC_FindName(sysbase + 420, name); /* waiting tasks */
}

static ULONG execl_FindTask(void) { return EXEC_FindTask(regs.a[1] == 0 ? NULL : get_real_address(regs.a[1])); }
static ULONG execl_AddTask(void) { return EXEC_AddTask(regs.a[1], regs.a[2], regs.a[3]); }

ULONG EXEC_Forbid(void)
{
    int count = (BYTE)get_byte(sysbase + 295);
    count++;
    put_byte(sysbase + 295, count);
    return 0; 
}
ULONG EXEC_Permit(void) 
{
    int count = (BYTE)get_byte(sysbase + 295);
    if (count == -1)
	fprintf(stderr, "Too many Permit() calls\n");
    else
	count--;
    put_byte(sysbase + 295, count);    
    maybe_schedule();
    return 0;
}

ULONG EXEC_Disable(void) 
{
    int count = (BYTE)get_byte(sysbase + 294);
    count++;
    put_byte(sysbase + 294, count);
    put_word(0xDFF09A, 0x4000);
    return 0;
}

ULONG EXEC_Enable(void)
{
    int count = (BYTE)get_byte(sysbase + 294);
    if (count == -1)
	fprintf(stderr, "Too many Enable() calls\n");
    else
	count--;
    if (count == -1)
	put_word(0xDFF09A, 0xC000);
    put_byte(sysbase + 294, count);
    maybe_schedule();
    return 0;
}
/*
 *  Initialization
 */
static ULONG execlib_init(void)
{
    sysbase = get_long(4);
    
    /* CopyMem and CopyMemQuick */
    libemu_InstallFunction(execl_CopyMem, sysbase, -630);
    libemu_InstallFunction(execl_CopyMem, sysbase, -624);
    libemu_InstallFunction(execl_Allocate, sysbase, -186);
    libemu_InstallFunction(execl_AllocMem, sysbase, -198);
    libemu_InstallFunction(execl_Deallocate, sysbase, -192);
    libemu_InstallFunction(execl_FreeMem, sysbase, -210);
    libemu_InstallFunction(execl_AvailMem, sysbase, -216);
    
    /* List management */
    libemu_InstallFunction(execl_Insert, sysbase, -234);
    libemu_InstallFunction(execl_AddHead, sysbase, -240);
    libemu_InstallFunction(execl_AddTail, sysbase, -246);
    libemu_InstallFunction(execl_Enqueue, sysbase, -270);
    libemu_InstallFunction(execl_RemHead, sysbase, -258);
    libemu_InstallFunction(execl_RemTail, sysbase, -264);
    libemu_InstallFunction(execl_Remove, sysbase, -252);
    libemu_InstallFunction(execl_FindName, sysbase, -276);
    
    /* Signals */
    libemu_InstallFunction(execl_AllocSignal, sysbase, -330);
    libemu_InstallFunction(execl_FreeSignal, sysbase, -336);

    /* Semaphores */
    libemu_InstallFunction(execl_FindSemaphore, sysbase, -594);
    libemu_InstallFunction(execl_InitSemaphore, sysbase, -558);
    libemu_InstallFunction(execl_AddSemaphore, sysbase, -600);

    /* Messages/Ports */
    libemu_InstallFunction(execl_FindPort, sysbase, -390);
    libemu_InstallFunction(execl_GetMsg, sysbase, -372);

    /* Libraries */
    libemu_InstallFunction(execl_MakeFunctions, sysbase, -90);
    libemu_InstallFunction(execl_SumLibrary, sysbase, -426);
    libemu_InstallFunction(execl_SetFunction, sysbase, -420);
    
    /* Tasks */
    libemu_InstallFunction(execl_FindTask, sysbase, -294);

    /* Interrupts */
    libemu_InstallFunction(execl_SetIntVector, sysbase, -162);
    libemu_InstallFunction(execl_AddIntServer, sysbase, -168);
    libemu_InstallFunction(execl_RemIntServer, sysbase, -174);
    
    /* Miscellaneous */
    libemu_InstallFunction(execl_InitStruct, sysbase, -78);

    return 0;
}

static ULONG execlib_init2(void)
{
    sysbase = get_long(4);
    
    libemu_InstallFunction(execl_AllocEntry, sysbase, -222);
    
    libemu_InstallFunction(execl_AddLibrary, sysbase, -396);
    libemu_InstallFunction(execl_AddDevice, sysbase, -432);
    libemu_InstallFunction(execl_AddResource, sysbase, -486);
    libemu_InstallFunction(execl_OpenLibrary, sysbase, -552);
    libemu_InstallFunction(execl_OpenDevice, sysbase, -444);
    libemu_InstallFunction(execl_OpenResource, sysbase, -498);
    libemu_InstallFunction(execl_MakeLibrary, sysbase, -84);

    libemu_InstallFunctionFlags(EXEC_Disable, sysbase, -120, TRAPFLAG_NORETVAL);
    libemu_InstallFunctionFlags(EXEC_Enable, sysbase, -126, TRAPFLAG_NORETVAL);
    libemu_InstallFunctionFlags(EXEC_Permit, sysbase, -138, TRAPFLAG_NORETVAL);
    libemu_InstallFunctionFlags(EXEC_Forbid, sysbase, -132, TRAPFLAG_NORETVAL);
    
    libemu_InstallFunction(execl_AddTask, sysbase, -282);

    libemu_InstallFunction(execl_SetSignal, sysbase, -306);
    libemu_InstallFunction(execl_Signal, sysbase, -324);
    libemu_InstallFunction(execl_Wait, sysbase, -318);

    libemu_InstallFunction(execl_PutMsg, sysbase, -366);
    libemu_InstallFunction(execl_ReplyMsg, sysbase, -378);
    libemu_InstallFunction(execl_WaitPort, sysbase, -384);

    libemu_InstallFunction(execl_WaitIO, sysbase, -474);
    libemu_InstallFunction(execl_DoIO, sysbase, -456);
    libemu_InstallFunction(execl_SendIO, sysbase, -462);
    libemu_InstallFunction(execl_CheckIO, sysbase, -468);
    
    libemu_InstallFunctionFlags(execl_ObtainSemaphore, sysbase, -564, TRAPFLAG_NORETVAL);
    libemu_InstallFunctionFlags(execl_ReleaseSemaphore, sysbase, -570, TRAPFLAG_NORETVAL);
    libemu_InstallFunction(execl_AttemptSemaphore, sysbase, -576);

    return 0;
}

/*
 * These functions are one day going to bootstrap the emulated Exec
 */

#define NR_EXEC_FUNCTIONS 105 /* V34 (1.3) maximum */

static ULONG EXEC_ENOSYS(void)
{
    fprintf(stderr, "Not a system call\n");
    return 0;
}

static ULONG EXEC_StandardIntVec(void)
{
    int num = regs.a[1];
    /* Call the IntHandlers */
    return 0;
}

static ULONG EXEC_IntTrap(void)
{
    UWORD intsreq = get_word(0xDFF01E) & get_word(0xDFF01C);
    int i;
    intr_count++;
    
    for (i = 13; i >= 0; i--) {
	if ((intsreq & (1 << i)) != 0) {
	/* Is this a server chain? */
	    if (i == 3 || i == 4 || i == 5 || i == 13 || i == 15) {
		CPTR slist = get_long(sysbase + 84 + 12*i);
		CPTR snode = get_long(slist);
		for (;;) {
		    if (get_long(snode) == 0)
			break;
		    regs.a[0] = 0xDFF000;
		    regs.a[5] = get_long(snode + 18);
		    regs.a[1] = get_long(snode + 14);
		    Call68k(regs.a[5], 0);
		    if (ZFLG == 0)
			break;
		    snode = get_long(snode);
		}
		put_word(0xDFF09C, 1 << i);
	    } else {
		CPTR intnode = get_long(sysbase + 84 + 12*i + 8);
		regs.d[1] = intsreq;
		regs.a[1] = get_long(intnode + 14);
		regs.a[0] = 0xDFF000;
		regs.a[5] = get_long(intnode + 18);
		regs.a[6] = sysbase;
		Call68k(regs.a[5], 0);
	    }
	    break;
	}
    }
    if (i < 0)
	fprintf(stderr, "So what interrupt am I supposed to serve?\n");
    intr_count--;
    maybe_schedule();
}

void execlib_sysinit(void)
{
    CPTR tmp, tmp2;
    CPTR enosys = deftrap(EXEC_ENOSYS);
    CPTR intvec = deftrap(EXEC_StandardIntVec);
    CPTR inttrap = deftrap2(EXEC_IntTrap, TRAPFLAG_NORETVAL);
    CPTR sysstack;
    int i;

    tmp = here();
    calltrap2(enosys); dw(RTS);
    
    /* why 0x400? */
    sysbase = 0x400 + NR_EXEC_FUNCTIONS*6;
    memset(get_real_address(sysbase), 0, 558);
    put_long(4, sysbase);

    /* Build vectors. We initialize SetFunction so we can call execlib_Init() */
    tmp2 = here();
    calltrap(deftrap(execl_SetFunction));
    dw(RTS);
    for (i = 0; i < NR_EXEC_FUNCTIONS; i++) {
	put_word(sysbase - 6*(i+1), 0x4EF9);
	put_long(sysbase - 6*(i+1) + 2, i == 69 ? tmp2 : tmp);
    }

    /* Build syslists */
    EXEC_NewList(sysbase + 322); /* MemList */
    EXEC_NewList(sysbase + 336); /* ResourceList */
    EXEC_NewList(sysbase + 350); /* ResourceList */
    EXEC_NewList(sysbase + 364); /* List */
    EXEC_NewList(sysbase + 378); /* LibList */
    EXEC_NewList(sysbase + 392); /* PortList */
    EXEC_NewList(sysbase + 406); /* TaskReady */
    EXEC_NewList(sysbase + 420); /* TaskWait */
    EXEC_NewList(sysbase + 434); /* SoftInts[5] */
    EXEC_NewList(sysbase + 450); 
    EXEC_NewList(sysbase + 466); 
    EXEC_NewList(sysbase + 482); 
    EXEC_NewList(sysbase + 498); 
    EXEC_NewList(sysbase + 532); /* SemaphoreList */

    tmp = sysbase + 558; /* sizeof struct V34 execbase */
    /* We put the supervisor stack at the end, the user stack for our new
     * task at the end - 0x800, and build the ResModules array at the bottom
     * of the stack */
    sysstack = chipmem_size - 0x2000;
    
    /* Don't mess with Fast or Bogo for now, just set up Chip */
    EXEC_AddMemList(sysstack - tmp, MEMF_CHIP|MEMF_PUBLIC, -10, tmp, 
		    ds("Chip Memory"));

    /* Set up the CPU */
    regs.usp = chipmem_size - 0x800;
    regs.isp = chipmem_size; /* What about MSP? */
    regs.a[7] = regs.usp;
    regs.s = 0; regs.m = 0;
    regs.vbr = 0;
    regs.t0 = regs.t1 = 0;
    regs.intmask = 0;
    put_word(0xDFF09A, 0x7FFF);
    put_word(0xDFF096, 0x7FFF);
    put_word(0xDFF09C, 0x7FFF);
    put_word(0xDFF09A, 0xC000);
    put_word(0xDFF096, 0xE100);
    /* Initialize our supported functions */
    execlib_init();
    execlib_init2();
    
    /* Initialize interrupts */
    intr_count = 0; 
    put_byte(sysbase + 294, 0xFF);
    put_byte(sysbase + 295, 0xFF);

    tmp = here();
    calltrap2(inttrap);
    dw(RTE);
    for (i = 0; i < 8; i++)
	put_long((24+i)*4, tmp);
    
    tmp = here();
    calltrap2(intvec); dw(RTS);
    for (i = 0; i < 16; i++) {
	tmp2 = 0;
	/* Is this a server chain? */
	if (i == 3 || i == 4 || i == 5 || i == 13 || i == 15)
	    EXEC_NewList(tmp2 = EXEC_AllocMem(14, MEMF_PUBLIC));
	put_long(sysbase + 84 + i*12, tmp2);
	put_long(sysbase + 84 + i*12 + 4, tmp);
	put_long(sysbase + 84 + i*12 + 8, 0);
    }
    put_word(0xDFF09A, 0xAFC3); /* Enable everything but the server chains */
    /* Set up the init task.  We don't really set it up properly. */
    tmp = EXEC_AllocMem(92, MEMF_CLEAR);
    put_long(sysbase + 276, tmp);
    put_byte(tmp + 8, 1);
    put_byte(tmp + 9, -127);
    put_long(tmp + 10, ds("init"));
    put_byte(tmp + 16, 0xFF);
    put_byte(tmp + 17, 0xFF);
    put_long(tmp + 54, chipmem_size - 0x804);
    put_long(tmp + 58, chipmem_size - 0x1800);
    put_long(tmp + 62, chipmem_size - 0x800);
    put_byte(tmp + 15, TS_RUN);
    put_long(tmp + 18,0x0000FFFF);
    EXEC_NewList(tmp + 74);
    idle_task.next = idle_task.prev = &idle_task;
    idle_task.exectask = tmp;
    idle_task.stack = NULL; /* we have a stack for this one already */

    /* Set up an idle task */
    tmp2 = here();
    dw (0x4E72); dw(0x2000); dw (0x4eF9); dl (tmp2);

    tmp = EXEC_AllocMem(92, MEMF_CLEAR);
    put_byte(tmp + 8, 1);
    put_byte(tmp + 9, -128);
    put_long(tmp + 10, ds("idle"));
    put_long(tmp + 58, chipmem_size - 0x2000);
    put_long(tmp + 62, chipmem_size - 0x1800);
    put_long(tmp + 54, chipmem_size - 0x1804);
    EXEC_NewList(tmp + 74);
    EXEC_AddTask(tmp,  tmp2, 0); /* This won't start executing yet */
    find_newtask(tmp)->regs.s = 1;


    /* Grep Kickstart for resident modules */
    regs.a[7] -= 4;
    put_long(regs.a[7], 0);
    tmp2 = regs.a[7];
    for (tmp = 0xF80000; tmp < 0xFFFFFF; tmp += 2) {
	if (get_word(tmp) == 0x4AFC && get_long(tmp + 2) == tmp) {
	    regs.a[7] -= 4; put_long(regs.a[7], tmp);
	}
    }
    put_long(sysbase + 300, regs.a[7]);
    /* Bubble me do */
    for (tmp = regs.a[7]; tmp < tmp2; tmp += 4) {
	CPTR tmp3;
	for (tmp3 = tmp2 - 4; tmp3 > tmp; tmp3 -= 4) {
	    if ((BYTE)get_byte(get_long(tmp3) + 13) > (BYTE)get_byte(get_long(tmp3 - 4) + 13)) {
		CPTR tmp4 = get_long(tmp3);
		put_long(tmp3, get_long(tmp3 - 4));
		put_long(tmp3 - 4, tmp4);
	    }
	}
    }
    
    /* Initialize them and watch everything blow up. */
    for (tmp = regs.a[7]; (tmp2 = get_long(tmp)) != 0; tmp += 4) {
	UBYTE flags = get_byte(tmp2 + 10);
	if (!(flags & 1)) /* RTF_COLDSTART? */
	    continue;
	if (strncmp("alert", get_real_address(get_long(tmp2 + 18)), 5) == 0)
	    /*continue*/;
	fprintf(stderr, "Initializing %s\n", get_real_address(get_long(tmp2 + 18)));
	EXEC_InitResident(tmp2, /* ? */ 0);
    }
    /* Idle task */
    for(;;) {
	Call68k(0xF0FFF0 - 4, 0);
	schedule();
    }
}

/* 
 *  Install the gfx-library-replacement 
 */
void execlib_install(void)
{
    ULONG begin, end, resname, resid;
    int i;
    
    if (!use_gfxlib)
	return;
    
    resname = ds("UAEexeclib.resource");
    resid = ds("UAE execlib 0.1");

    begin = here();
    dw(0x4AFC);             /* RTC_MATCHWORD */
    dl(begin);              /* our start address */
    dl(0);                  /* Continue scan here */
    dw(0x0101);             /* RTF_COLDSTART; Version 1 */
    dw(0x0805);             /* NT_RESOURCE; pri 5 */
    dl(resname);            /* name */
    dl(resid);              /* ID */
    dl(here() + 4);         /* Init area: directly after this */

    calltrap(deftrap(execlib_init)); dw(RTS);

    end = here();
    org(begin + 6);
    dl(end);

    org(end);

    intr_count = 1; /* So we don't try to schedule if we don't emulate
		     * all of Exec */
}
