File:  [UNIX Amiga Emulator] / uae / src / execlib.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 16:38:55 2018 UTC (8 years, 2 months ago) by root
Branches: MAIN, BSchmidt
CVS tags: uae068, HEAD
uae-0.6.8

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

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

#include <assert.h>
#include <signal.h>
#include <ctype.h>

#include "config.h"
#include "options.h"
#include "machdep/exectasks.h"
#include "machdep/m68k.h"
#include "memory.h"
#include "custom.h"
#include "readcpu.h"
#include "newcpu.h"
#include "xwin.h"
#include "autoconf.h"
#include "osemu.h"
#include "execlib.h"

#ifdef USE_EXECLIB

CPTR EXEC_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, next;

    if (name == NULL)
	return 0;
    
    for (node = get_long(start);(next = get_long(node)) != 0; node = next) {
	CPTR nnp;
	char *nn;

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

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

/*
 * Miscellaneous
 */

void EXEC_InitStruct(CPTR inittable, CPTR memory, unsigned long size)
{
    CPTR dptr = memory;
    UBYTE *mem = (UBYTE *)raddr(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(m68k_areg(regs, 1), m68k_areg(regs, 2), m68k_dreg(regs, 0) & 0xFFFF); return 0; }

static ULONG execl_GetCC(void)
{
    MakeSR();
    m68k_dreg(regs, 0) = regs.sr & 0xFF;
    return 0; /* ignored */
}

/*
 * 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(EXEC_sysbase + 84 + 12*number + 8);
    if (interrupt == 0) {
	put_long(EXEC_sysbase + 84 + 12*number, 0);
	put_long(EXEC_sysbase + 84 + 12*number + 4, 0);
	put_long(EXEC_sysbase + 84 + 12*number + 8, 0);
    } else {
	put_long(EXEC_sysbase + 84 + 12*number, get_long(interrupt + 14));
	put_long(EXEC_sysbase + 84 + 12*number + 4, get_long(interrupt + 18));
	put_long(EXEC_sysbase + 84 + 12*number + 8, interrupt);
    }
    return oldvec;
}

void EXEC_RemIntServer(ULONG nr, CPTR interrupt)
{
    CPTR list = get_long(EXEC_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(EXEC_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(m68k_dreg(regs, 0), m68k_areg(regs, 1)); }
static ULONG execl_AddIntServer(void) { EXEC_AddIntServer(m68k_dreg(regs, 0) & 15, m68k_areg(regs, 1)); return 0; }
static ULONG execl_RemIntServer(void) { EXEC_RemIntServer(m68k_dreg(regs, 0) & 15, m68k_areg(regs, 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(EXEC_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(raddr(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(EXEC_sysbase + 322);

    size = (size + 7) & ~7;
    if (addr == 0)
	return;
    if (size == 0) {
	fprintf(stderr, "warning: Freemem(..., 0)\n");
	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(EXEC_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)
{
    if (size == 0xFFFFFFFF) {
	fprintf(stderr, "Weird AddMemList call\n");
	return;
    }
	
    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(EXEC_sysbase + 322, base);
}

static ULONG execl_AddMemList(void)
{
    EXEC_AddMemList(m68k_dreg(regs, 0), m68k_dreg(regs, 1), (LONG)m68k_dreg(regs, 2), m68k_areg(regs, 0), m68k_areg(regs, 1));
    return 0;
}

static ULONG execl_CopyMem(void)
{
    UBYTE *src = (UBYTE *)raddr(m68k_areg(regs, 0));
    UBYTE *dst = (UBYTE *)raddr(m68k_areg(regs, 1));

    if (src != NULL && dst != NULL
	&& valid_address(m68k_areg(regs, 0), m68k_dreg(regs, 0))
	&& valid_address(m68k_areg(regs, 1), m68k_dreg(regs, 0)))
	memcpy(dst, src, m68k_dreg(regs, 0));
    else {
	/* Ho hum. This happens when copying expansion card data. We
	 * better do something sensible in this case */
	unsigned long int i;
	for(i = 0; i < m68k_dreg(regs, 0); i++)
	    put_byte(m68k_areg(regs, 1) + i, get_byte(m68k_areg(regs, 0) + i));
    }
    return 0;
}

static ULONG execl_AllocMem(void) { return EXEC_AllocMem(m68k_dreg(regs, 0), m68k_dreg(regs, 1)); }
static ULONG execl_AllocEntry(void) { return EXEC_AllocEntry(m68k_areg(regs, 0)); }
static ULONG execl_Allocate(void) { return EXEC_Allocate(m68k_areg(regs, 0), m68k_dreg(regs, 0)); }
static ULONG execl_FreeMem(void) { EXEC_FreeMem(m68k_areg(regs, 1), m68k_dreg(regs, 0)); return 0; }
static ULONG execl_AvailMem(void) { return EXEC_AvailMem(m68k_dreg(regs, 1)); }
static ULONG execl_Deallocate(void) { EXEC_Deallocate(m68k_areg(regs, 0), m68k_areg(regs, 1), m68k_dreg(regs, 0)); return 0; }

/*
 * Scheduling
 */

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

struct NewTask {
    struct NewTask *next, *prev;
    CPTR exectask;
    struct switch_struct sws;
    void *stack;
    struct regstruct regs;
    struct flag_struct flags;
};

static struct NewTask idle_task;
static int intr_count, need_resched, quantum_elapsed;
static struct NewTask *task_to_kill;

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");
    return 0;
}

void EXEC_QuantumElapsed(void)
{
    need_resched = 1;
    quantum_elapsed = 1;
}

static int trace_schedules = 0;

static void schedule(void)
{
    CPTR readylist = EXEC_sysbase + 406;
    CPTR readytask = get_long(readylist);
    CPTR runtask = get_long(EXEC_sysbase + 276);
    struct NewTask *runtask_nt, *readytask_nt;
    unsigned long oldflags;
    UBYTE oldstate;
    int idis;
    
    TIMER_block();
    
    need_resched = 0;
    /* Any ready tasks? */
    if (get_long(readytask) == 0)
	goto dont_schedule;
    
    /* If the running task is going to sleep, don't check priorities */
    oldstate = get_byte(runtask + 15);
    if (oldstate != TS_WAIT && oldstate != TS_REMOVED) {
	/* Has the running task a higher priority than any ready tasks? */
	if ((BYTE)get_byte(runtask + 9) > (BYTE)get_byte(readytask + 9))
	    goto dont_schedule;
	if ((BYTE)get_byte(runtask + 9) == (BYTE)get_byte(readytask + 9)
	    && !quantum_elapsed)
	    goto dont_schedule;
	quantum_elapsed = 0;
    }
    if (oldstate == TS_RUN)
	put_byte(runtask + 15, oldstate = TS_READY);
    if (oldstate == TS_WAIT)
	EXEC_Enqueue(EXEC_sysbase + 420, runtask);
    else 
	/* Removed tasks end up here also, so we can safely Remove() them
	 * always. */
	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);
    regs.isp = runtask_nt->regs.isp;
    regs.msp = runtask_nt->regs.msp;
    put_byte(runtask + 16, get_byte(EXEC_sysbase + 294));
    put_byte(runtask + 17, get_byte(EXEC_sysbase + 295));
    put_byte(EXEC_sysbase + 294, idis = get_byte(readytask + 16));
    put_byte(EXEC_sysbase + 295, get_byte(readytask + 17));
    put_long(EXEC_sysbase + 276, readytask);
    /* Set INTENA according to new interrupt enable status */
    if (idis == 0xFF)
	put_word(0xDFF09A, 0xC000);
    else
	put_word(0xDFF09A, 0x4000);

    if (trace_schedules)
	fprintf(stderr, "Task switch: new task %s\n", raddr(get_long(readytask + 10)));

    EXEC_SWITCH_TASKS(runtask_nt, readytask_nt);

    regs.spcflags &= ~SPCFLAG_BRK;
    
    dont_schedule:
    
    TIMER_unblock();

    /* Now we know we are running in a live task, so we can safely remove
     * a corpse if necessary */
    if (task_to_kill != NULL) {
	struct NewTask *nt = task_to_kill;
	task_to_kill = NULL;
	EXEC_Remove(nt->exectask);
	EXEC_FreeMem(nt->exectask, 92);
	free(nt->stack);
	nt->prev->next = nt->next;
	nt->next->prev = nt->prev;
	free(nt);
    }
}

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(EXEC_sysbase + 294) == 0xFF && get_byte(EXEC_sysbase + 295) == 0xFF && need_resched)
	schedule();
}

/*
 * Signals
 */

int EXEC_AllocSignal(int signum)
{
    CPTR thistask = get_long(EXEC_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(EXEC_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(EXEC_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);
    
    if (task == 0) {
/*	fprintf(stderr, "Signalling NULL task. Bad\n");*/
	return;
    }
    put_long(task + 26, exec_sigs | exec_sigmask);
    if (get_byte(task + 15) == TS_WAIT && (sigwait & exec_sigmask) != 0) {
	EXEC_Remove(task);
	put_byte(task + 15, TS_READY);
	EXEC_Enqueue(EXEC_sysbase + 406, task);
	need_resched = 1;
	maybe_schedule();
    }
}

ULONG EXEC_Wait(ULONG exec_sigmask)
{
    CPTR task = get_long(EXEC_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(EXEC_sysbase + 294) != 0xFF || get_byte(EXEC_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(m68k_dreg(regs, 0), m68k_dreg(regs, 1)); }
static ULONG execl_Signal(void) { EXEC_Signal(m68k_areg(regs, 1), m68k_dreg(regs, 0)); return 0; }
static ULONG execl_Wait(void) { return EXEC_Wait(m68k_dreg(regs, 0)); }
static ULONG execl_AllocSignal(void) { return EXEC_AllocSignal((BYTE)m68k_dreg(regs, 0)); }
static ULONG execl_FreeSignal(void) { EXEC_FreeSignal((BYTE)m68k_dreg(regs, 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(EXEC_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(EXEC_sysbase + 276);
    UWORD count = get_word(exec_sigsem + 44);
    CPTR n;

    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);
    n = EXEC_AllocMem(12, 0); /* Ugh... is there a cleaner way? */
    put_long(n + 8, task);
    EXEC_AddTail(exec_sigsem + 16, n);
    if (EXEC_Wait(0x8000) != 0x8000)
	fprintf(stderr, "BUG\n");
    EXEC_FreeMem(n, 12);
    /* 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 = get_long(EXEC_RemHead(exec_sigsem + 16) + 8);
	UWORD count = get_word(exec_sigsem + 44);
	put_word(exec_sigsem + 44, count - 1);
	if (task) {
	    EXEC_Signal(task, 0x8000);
	}
    }
}

/* Assumption: Semaphores on the list are only handled through these two
 * functions. I don't see how it could work reliably otherwise. But I don't
 * see either why these functions ought to be used: You might as well have
 * one semaphore instead of a list of them.
 * For safety, these functions try hard to function even in cases where they
 * might otherwise deadlock or break.
 * @@@ what should happen if the task already owns semaphores on the list?
 */

void EXEC_ObtainSemaphoreList(CPTR l)
{
    CPTR task = get_long(EXEC_sysbase + 276);
    CPTR n;

    int retry;
    EXEC_Forbid();
    do {
	retry = 0;
	/* See if any semaphores on the list are locked. If so, go to
	 * sleep */
	n = get_long(l);
	while(get_long(n) != 0) {
	    if (get_word(n + 44) != 0xFFFF && get_long(n + 40) != task) {
		/* Semaphore is locked. Obtain it the normal way */
		EXEC_Permit();
		EXEC_ObtainSemaphore(n);
		EXEC_ReleaseSemaphore(n);
		EXEC_Forbid();
		retry = 1;
		break;
	    }
	    n = get_long(n);
	}
    } while (retry);
    /* All semaphores on the list are unlocked. */
    for (n = get_long(l); get_long(n) != 0; n = get_long(n)) {
	put_word(n + 44, get_word(n + 44) + 1);
	put_long(n + 40, task);
	put_word(n + 14, get_word(n + 14) + 1);
    }
    EXEC_Permit();
}

void EXEC_ReleaseSemaphoreList(CPTR l)
{
    CPTR n;

    EXEC_Forbid();
    for (n = get_long(l); get_long(n) != 0; n = get_long(n)) {
	EXEC_ReleaseSemaphore(n);
    }
    EXEC_Permit();
}

static ULONG execl_ObtainSemaphore(void) { EXEC_ObtainSemaphore(m68k_areg(regs, 0)); return 0; }
static ULONG execl_ReleaseSemaphore(void) { EXEC_ReleaseSemaphore(m68k_areg(regs, 0)); return 0; }
static ULONG execl_ObtainSemaphoreList(void) { EXEC_ObtainSemaphoreList(m68k_areg(regs, 0)); return 0; }
static ULONG execl_ReleaseSemaphoreList(void) { EXEC_ReleaseSemaphoreList(m68k_areg(regs, 0)); return 0; }
static ULONG execl_FindSemaphore(void) { return EXEC_FindName(EXEC_sysbase + 532, raddr(m68k_areg(regs, 1))); }
static ULONG execl_InitSemaphore(void) { EXEC_InitSemaphore(m68k_areg(regs, 0)); return 0; }
static ULONG execl_AttemptSemaphore(void) { return EXEC_AttemptSemaphore(m68k_areg(regs, 0)); }
static ULONG execl_AddSemaphore(void) { put_byte(m68k_areg(regs, 1) + 8, NT_SIGNALSEM); EXEC_Enqueue(EXEC_sysbase + 532, m68k_areg(regs, 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, NT_MESSAGE);
    EXEC_doputmsg(port, msg);
}

void EXEC_ReplyMsg(CPTR msg)
{
    CPTR p = get_long(msg + 14);
    if (p == 0) {
	put_byte(msg + 8, NT_FREEMSG);
    } else {
	put_byte(msg + 8, NT_REPLYMSG);
	EXEC_doputmsg(p, 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));
    }
}

void EXEC_AddPort(CPTR port)
{
    put_byte(port + 8, NT_MSGPORT);
    EXEC_Enqueue(EXEC_sysbase + 392, port);
    EXEC_NewList(port + 20);
    /* should we put the task into the appropriate place? */
}

void EXEC_RemPort(CPTR port)
{
    EXEC_Remove(port);
}

static ULONG execl_GetMsg(void) { return EXEC_GetMsg(m68k_areg(regs, 0)); }
static ULONG execl_PutMsg(void) { EXEC_PutMsg(m68k_areg(regs, 0), m68k_areg(regs, 1)); return 0; }
static ULONG execl_ReplyMsg(void) { EXEC_ReplyMsg(m68k_areg(regs, 1)); return 0; }
static ULONG execl_WaitPort(void) { return EXEC_WaitPort(m68k_areg(regs, 0)); }
static ULONG execl_FindPort(void) { return EXEC_FindName(EXEC_sysbase + 392, raddr(m68k_areg(regs, 1))); }
static ULONG execl_AddPort(void) { EXEC_AddPort(m68k_areg(regs, 1)); return 0; }
static ULONG execl_RemPort(void) { EXEC_RemPort(m68k_areg(regs, 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) == 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);
    m68k_areg(regs, 6) = dev;
    m68k_areg(regs, 1) = ioreq;
    Call68k(dev - 30, 1);
    m68k_areg(regs, 6) = EXEC_sysbase;
}

LONG EXEC_DoIO(CPTR ioreq)
{
    put_byte(ioreq + 30, 1);
    EXEC_BeginIO(ioreq);
    return 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) == NT_REPLYMSG)
	return ioreq;
    return 0;
}

void EXEC_AbortIO(CPTR ioRequest)
{
    CPTR dev = get_long(ioRequest + 20);
    m68k_areg(regs, 6) = dev;
    m68k_areg(regs, 1) = ioRequest;
    /* The example code returns a value here. What should we do with it? */
    Call68k(dev - 36, 1);
    m68k_areg(regs, 6) = EXEC_sysbase;
}

static ULONG execl_WaitIO(void) { return EXEC_WaitIO(m68k_areg(regs, 1)); }
static ULONG execl_DoIO(void) { return EXEC_DoIO(m68k_areg(regs, 1)); }
static ULONG execl_SendIO(void) { EXEC_SendIO(m68k_areg(regs, 1)); return 0; }
static ULONG execl_CheckIO(void) { return EXEC_CheckIO(m68k_areg(regs, 1)); }
static ULONG execl_AbortIO(void) { EXEC_AbortIO(m68k_areg(regs, 1)); return 0; }

/*
 * 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(EXEC_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(EXEC_sysbase + 378, name);
    
    if (lib != 0 && get_word(lib + 20) < version)
	return 0;
    
    if (lib != 0) {
	CPTR lib2;
	m68k_areg(regs, 6) = lib;
	m68k_dreg(regs, 0) = version;
	lib2 = Call68k(lib - 6, 1);
	if (lib2 == 0)
	    lib = 0;
	else if (lib2 != lib)
	    fprintf(stderr, "OpenLibrary: weirdness\n");
	m68k_areg(regs, 6) = EXEC_sysbase;
    }
    return lib;
}

CPTR EXEC_OpenDevice(char *name, ULONG unit, CPTR ioRequest, ULONG flags)
{
    CPTR dev = EXEC_FindName(EXEC_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");
	    put_long(replyport + 16, get_long(EXEC_sysbase + 276));
	}
	else if (rtask != get_long(EXEC_sysbase + 276))
	    fprintf(stderr, "replyTask != current\n");
/*	put_long(replyport + 16, get_long(EXEC_sysbase + 276));*/
    }
    if (dev != 0) {
	m68k_areg(regs, 6) = dev;
	m68k_areg(regs, 1) = ioRequest;
	m68k_dreg(regs, 0) = unit;
	m68k_dreg(regs, 1) = flags;
	EXEC_Forbid();
	dev = Call68k(dev - 6, 1);
	EXEC_Permit();
	m68k_areg(regs, 6) = EXEC_sysbase;
    }
    return (LONG)(BYTE)get_byte(ioRequest + 31);
}

/*
 * There seems to be some magic involved here. These functions are documented
 * as returning void. However, if we leave D0 unmodified by giving
 * TRAPFLAG_NORETVAL, there are bogus FreeMem calls; these go away if we
 * return 0 in D0 as the old code did. I guess these are really supposed to
 * return the value that was given to them by the library's Close routine
 * (for the DOS CloseDevice()/CloseLibrary() calls, probably)
 */

ULONG EXEC_CloseDevice(CPTR ioRequest)
{
    CPTR dev = get_long(ioRequest + 20);
    ULONG retval;
    
    m68k_areg(regs, 6) = dev;
    m68k_areg(regs, 1) = ioRequest;
    EXEC_Forbid();
    retval = Call68k(dev - 12, 1);
    EXEC_Permit();
    return retval;
}

ULONG EXEC_CloseLibrary(CPTR lib)
{
    ULONG retval;

    m68k_areg(regs, 6) = lib;
    EXEC_Forbid();
    retval = Call68k(lib - 12, 1);
    EXEC_Permit();
    return retval;
}

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

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

CPTR EXEC_OpenResource(char *name)
{
    CPTR lib = EXEC_FindName(EXEC_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);
	}
    }
}

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;

    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) != (ULONG)-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) {
	m68k_areg(regs, 6) = EXEC_sysbase;
	m68k_areg(regs, 0) = seglist; /* BCPL seglist here? */
	m68k_dreg(regs, 0) = base;
	/* call it */
	retval = Call68k(init, 0);
    } else
	retval = base;
    
    fprintf(stderr, "MakeLibrary: %s\n", raddr(get_long(base+10)));
    return retval;
}

CPTR EXEC_InitResident(CPTR resident, ULONG segList)
{
    UBYTE flags = get_byte(resident + 10);

    if (flags & 0x80) {
	CPTR autoinit = get_long(resident + 22);
	CPTR lib;
	lib = EXEC_MakeLibrary(get_long(autoinit + 4), get_long(autoinit + 8),
			       get_long(autoinit + 12), get_long(autoinit), 
			       segList >> 2);

	/* Is this right? Some libraries never get added if we don't do
	 * this. */
	if (get_byte(resident + 12) == NT_LIBRARY)
	    EXEC_AddLibrary(lib);
	else if (get_byte(resident + 12) == NT_DEVICE)
	    EXEC_AddDevice(lib);
	else if (get_byte(resident + 12) == NT_RESOURCE)
	    EXEC_AddResource(lib);
	return lib;

    }

    m68k_dreg(regs, 0) = 0;
    m68k_areg(regs, 0) = segList;
    m68k_areg(regs, 6) = EXEC_sysbase;
    return Call68k(get_long(resident + 22), 0);
}

CPTR EXEC_FindResident(char *name)
{
    CPTR rmods = get_long(EXEC_sysbase + 300);
    CPTR tmp2;

    if (name == NULL)
	return 0;

    for (; (tmp2 = get_long(rmods)) != 0; rmods += 4) {
	char *name2 = raddr(get_long(tmp2 + 14));
	if (strcmp(name, name2) == 0)
	    return tmp2;
    }
    return 0;
}

static ULONG execl_MakeFunctions(void) { EXEC_MakeFunctions(m68k_areg(regs, 0), m68k_areg(regs, 1), m68k_areg(regs, 2)); return 0; }
static ULONG execl_SumLibrary(void) { EXEC_SumLibrary(m68k_areg(regs, 1)); return 0; }
static ULONG execl_SetFunction(void) { return EXEC_SetFunction(m68k_areg(regs, 1), (WORD)m68k_areg(regs, 0), m68k_dreg(regs, 0)); }
static ULONG execl_AddLibrary(void) { EXEC_AddLibrary(m68k_areg(regs, 1)); return 0; }
static ULONG execl_AddDevice(void) { EXEC_AddDevice(m68k_areg(regs, 1)); return 0; }
static ULONG execl_AddResource(void) { EXEC_AddResource(m68k_areg(regs, 1)); return 0; }
static ULONG execl_OpenLibrary(void) { return EXEC_OpenLibrary(raddr(m68k_areg(regs, 1)), m68k_dreg(regs, 0)); }
static ULONG execl_OldOpenLibrary(void) { return EXEC_OpenLibrary(raddr(m68k_areg(regs, 1)), 0); }
static ULONG execl_CloseLibrary(void) { return EXEC_CloseLibrary(m68k_areg(regs, 1)); }
static ULONG execl_OpenDevice(void) { return EXEC_OpenDevice(raddr(m68k_areg(regs, 0)), m68k_dreg(regs, 0), m68k_areg(regs, 1), m68k_dreg(regs, 1)); }
static ULONG execl_CloseDevice(void) { return EXEC_CloseDevice(m68k_areg(regs, 1)); }
static ULONG execl_OpenResource(void) { return EXEC_OpenResource(raddr(m68k_areg(regs, 1))); }
static ULONG execl_MakeLibrary(void) { return EXEC_MakeLibrary(m68k_areg(regs, 0), m68k_areg(regs, 1), m68k_areg(regs, 2), m68k_dreg(regs, 0), m68k_dreg(regs, 1)); }
static ULONG execl_FindResident(void) { return EXEC_FindResident(raddr(m68k_areg(regs, 1))); }
static ULONG execl_InitResident(void) { return EXEC_InitResident(m68k_areg(regs, 1), m68k_dreg(regs, 1)); }

/*
 * Tasks
 */

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

void EXEC_RemTask(CPTR task)
{
    if (task == 0)
	task = get_long(EXEC_sysbase + 276);
    /* We can't easily delete it here while we use it's stack. Let schedule()
      * do it. */
    put_byte(task + 15, TS_REMOVED);
    task_to_kill = find_newtask(task);
    schedule();
}

CPTR EXEC_AddTask(CPTR task, CPTR initPC, CPTR finalPC)
{
    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;
    m68k_areg(nt->regs, 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(m68k_areg(nt->regs, 7) - 4, finalPC);
    nt->stack = malloc(65536); /* @@@ make this smaller after checking that
				* no parts of the emulator need much stack
				* space */    
    EXEC_SETUP_SWS(nt);
    
    put_byte(task + 15, TS_READY);
    put_long(task + 18,0x0000FFFF); /* all tasks have the lower 16 signals allocated */
    EXEC_Enqueue(EXEC_sysbase + 406, task);
    need_resched = 1;
    fprintf(stderr, "AddTask: %s\n", raddr(get_long(task + 10)));
    maybe_schedule();
    return task;
}

int EXEC_SetTaskPri(CPTR task, int pri)
{
    int oldpri = get_byte(task + 9);

    if (task == get_long(EXEC_sysbase + 276)) {
	put_byte(task + 9, pri);
	return oldpri;
    }
    if (get_byte(task + 15) == TS_WAIT) {
	EXEC_Remove(task);
	put_byte(task + 9, pri);
	EXEC_Enqueue(EXEC_sysbase + 420, task);
	return oldpri;
    }
    EXEC_Remove(task);
    put_byte(task + 9, pri);
    EXEC_Enqueue(EXEC_sysbase + 406, task);
    schedule();
    return oldpri;
}

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

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

static ULONG execl_FindTask(void) { return EXEC_FindTask(raddr(m68k_areg(regs, 1))); }
static ULONG execl_AddTask(void) { return EXEC_AddTask(m68k_areg(regs, 1), m68k_areg(regs, 2), m68k_areg(regs, 3)); }
static ULONG execl_RemTask(void) { EXEC_RemTask(m68k_areg(regs, 1)); return 0; }
static ULONG execl_SetTaskPri(void) { return EXEC_SetTaskPri(m68k_areg(regs, 1), (BYTE)m68k_dreg(regs, 0)); }

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

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

ULONG EXEC_Enable(void)
{
    int count = (BYTE)get_byte(EXEC_sysbase + 294);
    if (count == -1)
	fprintf(stderr, "Too many Enable() calls\n");
    else
	count--;
    if (count == -1)
	put_word(0xDFF09A, 0xC000);
    put_byte(EXEC_sysbase + 294, count);
    maybe_schedule();
    return 0;
}

/*
 * Annoyances...
 */

enum rdf_state { rdf_flags, rdf_width, rdf_limit, rdf_type };

CPTR EXEC_RawDoFormat(UBYTE *fstr, CPTR data, CPTR pcp, ULONG pcd)
{
    if (fstr == NULL)
	return data;
    for (;;) {
	UBYTE c = *fstr++;

	if (c == 0)
	    break;

	if (c == '%') {
	    enum rdf_state rdfs = rdf_flags;
	    int ljust = 0, fill0 = 0, islong = 0;
	    int w = 0, lim = 0, havelim = 0;

	    for (;;) {
		c = *fstr++;
		
		if (c == 0)
		    break;
		
		if (rdfs == rdf_flags) {
		    rdfs = rdf_width;
		    if (c == '-') {
			ljust = 1;
			continue;
		    }
		}
		if (rdfs == rdf_width && c == '.') {
		    rdfs = rdf_limit;
		    continue;
		} else if (rdfs == rdf_width && isdigit(c)) {
		    w = w*10 + c - '0';
		    if (w == 0)
			fill0 = 1;
		    continue;
		} else if (rdfs == rdf_limit && isdigit(c)) {
		    lim = lim*10 + c - '0';
		    havelim = 1;
		    continue;
		} else if ((rdfs == rdf_limit || rdfs == rdf_width) && c == 'l') {
		    islong = 1;
		    rdfs = rdf_type;
		    continue;
		}
		switch (c) {
		    CPTR tmp;
		    ULONG tmp1;
		    unsigned int tmpl;
		    char tmps1[20], tmps2[20], *tmpsp;

		 case 'b':
		    tmp = get_long(data); 
		    data += 4;
		    tmpl = get_byte(tmp++);
		    for(; tmpl > 0; tmpl--) {
			m68k_dreg(regs, 0) = get_byte(tmp++);
			m68k_areg(regs, 3) = pcd;
			Call68k(pcp, 0);
		    }
		    break;

		 case 'd':
		 case 'u':
		 case 'x':
		    if (islong) {
			tmp1 = get_long(data); data += 4;
		    } else {
			tmp1 = get_word(data); data += 2;
			if (c == 'd')
			    tmp1 = (LONG)(WORD)tmp1;
		    }
		    tmpsp = tmps1;
		    *tmpsp++ = '%';
		    if (fill0)
			*tmpsp++ = '0';
		    if (w != 0)
			sprintf(tmpsp, "%d", w);
		    tmpsp += strlen(tmpsp);
		    *tmpsp++ = c;
		    *tmpsp++ = 0;
		    sprintf(tmps2, tmps1, tmp1);
		    for (tmpsp = tmps2; ;) {
			m68k_dreg(regs, 0) = *tmpsp++;
			if (m68k_dreg(regs, 0) == 0)
			    break;
			m68k_areg(regs, 3) = pcd;
			Call68k(pcp, 0);			
		    }
		    break;

		 case 's':
		    tmp = get_long(data); 
		    data += 4;
		    for(;!havelim || lim-- > 0;) {
			m68k_dreg(regs, 0) = get_byte(tmp++);
			if (m68k_dreg(regs, 0) == 0)
			    break;
			m68k_areg(regs, 3) = pcd;
			Call68k(pcp, 0);
		    }
		    break;

		 case 'c':
		    if (islong) {
			m68k_dreg(regs, 0) = get_long(data); data += 4;
		    } else {
			m68k_dreg(regs, 0) = get_word(data); data += 2;
		    }
		    m68k_areg(regs, 3) = pcd;
		    Call68k(pcp, 0);
		}
		break;
	    }

	} else {
	    m68k_dreg(regs, 0) = c;
	    m68k_areg(regs, 3) = pcd;
	    Call68k(pcp, 0);
	}
    }
    return data;
}

static ULONG execl_RawDoFormat(void) 
{
    return EXEC_RawDoFormat((UBYTE *)raddr(m68k_areg(regs, 0)), m68k_areg(regs, 1), m68k_areg(regs, 2), m68k_areg(regs, 3)); 
}

/*
 *  Initialization
 */
static ULONG execlib_init(void)
{
    EXEC_sysbase = get_long(4);
#if 1
    /* CopyMem and CopyMemQuick */
    libemu_InstallFunctionFlags(execl_CopyMem, EXEC_sysbase, -630, 0, "CopyMem");
    libemu_InstallFunctionFlags(execl_CopyMem, EXEC_sysbase, -624, 0, "CopyMem");
    libemu_InstallFunctionFlags(execl_Allocate, EXEC_sysbase, -186, 0, "Allocate");
    libemu_InstallFunctionFlags(execl_AllocMem, EXEC_sysbase, -198, 0, "AllocMem");
    libemu_InstallFunctionFlags(execl_Deallocate, EXEC_sysbase, -192, 0, "Deallocate");
    libemu_InstallFunctionFlags(execl_FreeMem, EXEC_sysbase, -210, 0, "FreeMem");
    libemu_InstallFunctionFlags(execl_AvailMem, EXEC_sysbase, -216, 0, "AvailMem");
    
    /* List management */
    libemu_InstallFunctionFlags(execl_Insert, EXEC_sysbase, -234, TRAPFLAG_NORETVAL, "Insert");
    libemu_InstallFunctionFlags(execl_AddHead, EXEC_sysbase, -240, TRAPFLAG_NORETVAL, "AddHead");
    libemu_InstallFunctionFlags(execl_AddTail, EXEC_sysbase, -246, TRAPFLAG_NORETVAL, "AddTail");
    libemu_InstallFunctionFlags(execl_Enqueue, EXEC_sysbase, -270, TRAPFLAG_NORETVAL, "Enqueue");
    libemu_InstallFunctionFlags(execl_RemHead, EXEC_sysbase, -258, 0, "RemHead");
    libemu_InstallFunctionFlags(execl_RemTail, EXEC_sysbase, -264, 0, "RemTail");
    libemu_InstallFunctionFlags(execl_Remove, EXEC_sysbase, -252, TRAPFLAG_NORETVAL, "Remove");
    libemu_InstallFunctionFlags(execl_FindName, EXEC_sysbase, -276, 0, "FindName");
    
    /* Signals */
    libemu_InstallFunctionFlags(execl_AllocSignal, EXEC_sysbase, -330, 0, "AllocSignal");
    libemu_InstallFunctionFlags(execl_FreeSignal, EXEC_sysbase, -336, 0, "FreeSignal");

    /* Semaphores */
    libemu_InstallFunctionFlags(execl_FindSemaphore, EXEC_sysbase, -594, 0, "FindSemaphore");
    libemu_InstallFunctionFlags(execl_InitSemaphore, EXEC_sysbase, -558, 0, "InitSemaphore");
    libemu_InstallFunctionFlags(execl_AddSemaphore, EXEC_sysbase, -600, TRAPFLAG_NORETVAL, "AddSemaphore");

    /* Messages/Ports */
    libemu_InstallFunctionFlags(execl_FindPort, EXEC_sysbase, -390, 0, "FindPort");
    libemu_InstallFunctionFlags(execl_GetMsg, EXEC_sysbase, -372, 0, "GetMsg");

    /* Libraries */
    libemu_InstallFunctionFlags(execl_MakeFunctions, EXEC_sysbase, -90, 0, "MakeFunctions");
    libemu_InstallFunctionFlags(execl_SumLibrary, EXEC_sysbase, -426, TRAPFLAG_NORETVAL, "SumLibrary");
    libemu_InstallFunctionFlags(execl_SetFunction, EXEC_sysbase, -420, 0, "SetFunction");
    
    /* Tasks */
    libemu_InstallFunctionFlags(execl_FindTask, EXEC_sysbase, -294, 0, "FindTask");

    /* Interrupts */
    libemu_InstallFunctionFlags(execl_SetIntVector, EXEC_sysbase, -162, 0, "SetIntVector");
    libemu_InstallFunctionFlags(execl_AddIntServer, EXEC_sysbase, -168, TRAPFLAG_NORETVAL, "AddIntServer");
    libemu_InstallFunctionFlags(execl_RemIntServer, EXEC_sysbase, -174, 0, "RemIntServer");
    
    /* Miscellaneous */
    libemu_InstallFunctionFlags(execl_InitStruct, EXEC_sysbase, -78, 0, "InitStruct");
    libemu_InstallFunctionFlags(execl_GetCC, EXEC_sysbase, -528, TRAPFLAG_NORETVAL|TRAPFLAG_NOREGSAVE, "InitStruct");
#elif 0
    libemu_InstallFunctionFlags(NULL, EXEC_sysbase, -564, TRAPFLAG_NORETVAL, "ObtainSemaphore");
    libemu_InstallFunctionFlags(NULL, EXEC_sysbase, -570, TRAPFLAG_NORETVAL, "ReleaseSemaphore");
    libemu_InstallFunctionFlags(NULL, EXEC_sysbase, -576, 0, "AttemptSemaphore");
    libemu_InstallFunctionFlags(NULL, EXEC_sysbase, -582, TRAPFLAG_NORETVAL, "ObtainSemaphoreList");
    libemu_InstallFunctionFlags(NULL, EXEC_sysbase, -588, TRAPFLAG_NORETVAL, "ReleaseSemaphoreList");
#endif
    return 0;
}

static ULONG execl_Open(void) { return EXEC_sysbase; }

static ULONG execlib_init2(void)
{
    EXEC_sysbase = get_long(4);
    
    libemu_InstallFunctionFlags(execl_Open, EXEC_sysbase, -6, 0, "Open");
    libemu_InstallFunctionFlags(execl_AllocEntry, EXEC_sysbase, -222, 0, "AllocEntry");
    libemu_InstallFunctionFlags(execl_AddMemList, EXEC_sysbase, -618, 0, "AddMemList");

    libemu_InstallFunctionFlags(execl_AddLibrary, EXEC_sysbase, -396, TRAPFLAG_NORETVAL, "AddLibrary");
    libemu_InstallFunctionFlags(execl_AddDevice, EXEC_sysbase, -432, TRAPFLAG_NORETVAL, "AddDevice");
    libemu_InstallFunctionFlags(execl_AddResource, EXEC_sysbase, -486, TRAPFLAG_NORETVAL, "AddResource");
    libemu_InstallFunctionFlags(execl_OpenLibrary, EXEC_sysbase, -552, 0, "OpenLibrary");
    libemu_InstallFunctionFlags(execl_OldOpenLibrary, EXEC_sysbase, -408, 0, "OldOpenLibrary");
    libemu_InstallFunctionFlags(execl_OpenDevice, EXEC_sysbase, -444, 0, "OpenDevice");
    libemu_InstallFunctionFlags(execl_CloseDevice, EXEC_sysbase, -450, 0, "CloseDevice");
    libemu_InstallFunctionFlags(execl_OpenResource, EXEC_sysbase, -498, 0, "OpenResource");
    libemu_InstallFunctionFlags(execl_MakeLibrary, EXEC_sysbase, -84, 0, "MakeLibrary");
    libemu_InstallFunctionFlags(execl_CloseLibrary, EXEC_sysbase, -414, 0, "CloseLibrary");

    libemu_InstallFunctionFlags(execl_RawDoFormat, EXEC_sysbase, -522, 0, "RawDoFmt");
    libemu_InstallFunctionFlags(execl_InitResident, EXEC_sysbase, -102, 0, "InitResident");

    libemu_InstallFunctionFlags(EXEC_Disable, EXEC_sysbase, -120, TRAPFLAG_NORETVAL, "");
    libemu_InstallFunctionFlags(EXEC_Enable, EXEC_sysbase, -126, TRAPFLAG_NORETVAL, "");
    libemu_InstallFunctionFlags(EXEC_Permit, EXEC_sysbase, -138, TRAPFLAG_NORETVAL, "Permit");
    libemu_InstallFunctionFlags(EXEC_Forbid, EXEC_sysbase, -132, TRAPFLAG_NORETVAL, "Forbid");

    libemu_InstallFunctionFlags(execl_AddTask, EXEC_sysbase, -282, 0, "AddTask");
    libemu_InstallFunctionFlags(execl_RemTask, EXEC_sysbase, -288, TRAPFLAG_NORETVAL, "RemTask");
    libemu_InstallFunctionFlags(execl_SetTaskPri, EXEC_sysbase, -300, 0, "SetTaskPri");

    libemu_InstallFunctionFlags(execl_SetSignal, EXEC_sysbase, -306, 0, "SetSignal");
    libemu_InstallFunctionFlags(execl_Signal, EXEC_sysbase, -324, TRAPFLAG_NORETVAL, "Signal");
    libemu_InstallFunctionFlags(execl_Wait, EXEC_sysbase, -318, 0, "Wait");

    libemu_InstallFunctionFlags(execl_PutMsg, EXEC_sysbase, -366, 0, "PutMsg");
    libemu_InstallFunctionFlags(execl_ReplyMsg, EXEC_sysbase, -378, 0, "ReplyMsg");
    libemu_InstallFunctionFlags(execl_WaitPort, EXEC_sysbase, -384, 0, "WaitPort");
    libemu_InstallFunctionFlags(execl_AddPort, EXEC_sysbase, -354, TRAPFLAG_NORETVAL, "AddPort");
    libemu_InstallFunctionFlags(execl_RemPort, EXEC_sysbase, -360, TRAPFLAG_NORETVAL, "RemPort");

    libemu_InstallFunctionFlags(execl_WaitIO, EXEC_sysbase, -474, 0, "WaitIO");
    libemu_InstallFunctionFlags(execl_DoIO, EXEC_sysbase, -456, 0, "DoIO");
    libemu_InstallFunctionFlags(execl_SendIO, EXEC_sysbase, -462, TRAPFLAG_NORETVAL, "SendIO");
    libemu_InstallFunctionFlags(execl_CheckIO, EXEC_sysbase, -468, 0, "CheckIO");
    libemu_InstallFunctionFlags(execl_AbortIO, EXEC_sysbase, -468, 0, "AbortIO");

    libemu_InstallFunctionFlags(execl_ObtainSemaphore, EXEC_sysbase, -564, TRAPFLAG_NORETVAL, "ObtainSemaphore");
    libemu_InstallFunctionFlags(execl_ReleaseSemaphore, EXEC_sysbase, -570, TRAPFLAG_NORETVAL, "ReleaseSemaphore");
    libemu_InstallFunctionFlags(execl_AttemptSemaphore, EXEC_sysbase, -576, 0, "AttemptSemaphore");
    libemu_InstallFunctionFlags(execl_ObtainSemaphoreList, EXEC_sysbase, -582, TRAPFLAG_NORETVAL, "ObtainSemaphoreList");
    libemu_InstallFunctionFlags(execl_ReleaseSemaphoreList, EXEC_sysbase, -588, TRAPFLAG_NORETVAL, "ReleaseSemaphoreList");

    libemu_InstallFunctionFlags(execl_FindResident, EXEC_sysbase, -96, 0, "FindResident");
    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 = m68k_areg(regs, 1);
    /* Call the IntHandlers */
    return 0;
}

static int trace_ints = 0;

static ULONG EXEC_IntTrap(void)
{
    struct regstruct rtmp = regs;
    struct flag_struct ftmp = regflags;
    
    UWORD intsreq = get_word(0xDFF01E) & get_word(0xDFF01C);
    int i, do_timer = 0;
    intr_count++;

    if ((intena & 0x4000) == 0)
	fprintf(stderr, "Interrupt came in with interrupts off (%x, %x)!\n", get_byte(EXEC_sysbase + 294), get_byte(EXEC_sysbase + 295));
    
    TIMER_block();
    
    if (regs.spcflags & SPCFLAG_TIMER) {
	regs.spcflags &= ~SPCFLAG_TIMER;
	do_timer = 1;
    }
    
    TIMER_unblock();

    if (do_timer)
	TIMER_Interrupt();
    
    for (i = 13; i >= 0; i--) {
	if ((intsreq & (1 << i)) != 0) {
	    if (trace_ints)
		fprintf(stderr, "INT: %d\n", i);
	    /* Is this a server chain? */
	    if (i == 3 || i == 4 || i == 5 || i == 13 || i == 15) {
		CPTR slist = get_long(EXEC_sysbase + 84 + 12*i);
		CPTR snode = get_long(slist);
		for (;;) {
		    if (get_long(snode) == 0)
			break;
		    m68k_areg(regs, 0) = 0xDFF000;
		    m68k_areg(regs, 5) = get_long(snode + 18);
		    m68k_areg(regs, 1) = get_long(snode + 14);
		    Call68k(m68k_areg(regs, 5), 0);
		    if (ZFLG == 0)
			break;
		    snode = get_long(snode);
		}
		put_word(0xDFF09C, 1 << i);
	    } else {
		CPTR intnode = get_long(EXEC_sysbase + 84 + 12*i + 8);
		/* Blitter interrupts happen before vector is set up :-/ */
		if (intnode != 0) {
		    m68k_dreg(regs, 1) = intsreq;
		    m68k_areg(regs, 1) = get_long(intnode + 14);
		    m68k_areg(regs, 0) = 0xDFF000;
		    m68k_areg(regs, 5) = get_long(intnode + 18);
		    m68k_areg(regs, 6) = EXEC_sysbase;
		    Call68k(m68k_areg(regs, 5), 0);
		}
	    }
	    break;
	}
    }
    if (i < 0)
	fprintf(stderr, "So what interrupt am I supposed to serve?\n");
    intr_count--;
    
    /* This usually is a great indicator that the ExecBase pointer at
     * address 4 has been messed with... */
    if ((intena & 0x4000) == 0)
	fprintf(stderr, "Interrupt left interrupts off (%x, %x)!\n", get_byte(EXEC_sysbase + 294), get_byte(EXEC_sysbase + 295));
    regflags = ftmp;
    regs = rtmp;
    /* Perform a RTE */
    {
	CPTR sra = m68k_areg(regs, 7);
	regs.sr = get_word(sra);
	m68k_setpc_rte(get_long(sra+2));
	m68k_areg(regs, 7) = sra + 6;
	MakeFromSR();
    }

    maybe_schedule();
    return 0; /* ignored */
}

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

    tmp = here();
    calltrap2(enosys); dw(RTS);
    
    /* why 0x400? */
    EXEC_sysbase = 0x400 + NR_EXEC_FUNCTIONS*6;
    memset(raddr(EXEC_sysbase), 0, 558);
    put_long(4, EXEC_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(EXEC_sysbase - 6*(i+1), 0x4EF9);
	put_long(EXEC_sysbase - 6*(i+1) + 2, i == 69 ? tmp2 : tmp);
    }

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

    put_long(EXEC_sysbase + 10, ds("exec.library"));
    put_word(EXEC_sysbase + 20, 34); put_word(EXEC_sysbase + 22, 0);
    put_byte(EXEC_sysbase + 8, NT_LIBRARY);
    put_byte(EXEC_sysbase + 9, 0);
    
    put_long(EXEC_sysbase + 42, 0);
    put_long(EXEC_sysbase + 46, 0);
    put_long(EXEC_sysbase + 50, 0);
    put_long(EXEC_sysbase + 62, chipmem_size);
    put_long(EXEC_sysbase + 514, 0xFFFFFFFF);
    put_long(EXEC_sysbase + 518, 0); /* seems to contain random values */
    put_long(EXEC_sysbase + 522, 0);
    put_long(EXEC_sysbase + 526, 0);
    put_word(EXEC_sysbase + 530, 0x3232);
    put_long(EXEC_sysbase + 546, 0);
    put_long(EXEC_sysbase + 550, 0);
    put_long(EXEC_sysbase + 554, 0);
    EXEC_Enqueue(EXEC_sysbase + 378, EXEC_sysbase);
    
    tmp = EXEC_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? */
    m68k_areg(regs, 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, 0x8200);
    /* Initialize our supported functions */
    execlib_init();
    execlib_init2();
    
    /* Initialize interrupts */
    intr_count = 0; 
    put_byte(EXEC_sysbase + 294, 0xFF);
    put_byte(EXEC_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(EXEC_sysbase + 84 + i*12, tmp2);
	put_long(EXEC_sysbase + 84 + i*12 + 4, tmp);
	put_long(EXEC_sysbase + 84 + i*12 + 8, 0);
    }
    put_word(0xDFF09A, 0xAFC3); /* Enable everything but the server chains */
    
    task_to_kill = NULL;
    /* Set up the init task.  We don't really set it up properly. */
    tmp = EXEC_AllocMem(92, MEMF_CLEAR);
    put_long(EXEC_sysbase + 276, tmp);
    put_byte(tmp + 8, NT_TASK);
    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 (0xFF0D); dw (0x4eF9); dl (tmp2);

    tmp = EXEC_AllocMem(92, MEMF_CLEAR);
    put_byte(tmp + 8, NT_TASK);
    put_byte(tmp + 9, -128);
    put_long(tmp + 10, ds("idle"));
    put_byte(tmp + 16, 0xFF);
    put_byte(tmp + 17, 0xFF);
    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 */
    m68k_areg(regs, 7) -= 4;
    put_long(m68k_areg(regs, 7), 0);
    tmp2 = m68k_areg(regs, 7);
    for (tmp = 0xF80000; tmp < 0xFFFFFF; tmp += 2) {
	if (get_word(tmp) == 0x4AFC && get_long(tmp + 2) == tmp) {
	    m68k_areg(regs, 7) -= 4; put_long(m68k_areg(regs, 7), tmp);
	}
    }
    put_long(EXEC_sysbase + 300, m68k_areg(regs, 7));
    /* Bubble me do */
    for (tmp = m68k_areg(regs, 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);
	    }
	}
    }
    
    need_resched = quantum_elapsed = 0;

    /* Initialize them and watch everything blow up. */
    for (tmp = m68k_areg(regs, 7); (tmp2 = get_long(tmp)) != 0; tmp += 4) {
	UBYTE flags = get_byte(tmp2 + 10);
	if (!(flags & 1)) /* RTF_COLDSTART? */
	    continue;
	fprintf(stderr, "Initializing %s\n", raddr(get_long(tmp2 + 18)));
/*	if (strcmp("timer.device", raddr(get_long(tmp2 + 14))) == 0) {
	    timerdev_init();
	    continue;
	}*/
	m68k_areg(regs, 6) = EXEC_sysbase;
	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 */
}
#else
ULONG EXEC_Forbid(void) { return 0; }
ULONG EXEC_Permit(void) { return 0; }
ULONG EXEC_Enable(void) { return 0; }
ULONG EXEC_Disable(void) { return 0; }
void execlib_install (void) { abort (); }
#endif

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.