#include <stdio.h>	/***/
#ifndef AZTEC_C
#include <dos.h>
#endif
#include <setjmp.h>
/* Machine or compiler-dependent portions of kernel - Amiga Lattice C version */

#include <exec/tasks.h>
#ifdef LATTICE
#include <proto/exec.h>
#endif

#include "global.h"
#include "proc.h"
#include "commands.h"

static unsigned short oldNull;
static unsigned short *oldNullp;
static int stkutil __ARGS((struct proc *pp));

void
kinit(void)
{
	/* Remember location 0 pattern to detect null pointer derefs */
	oldNullp = NULL;
	oldNull = *oldNullp;

}


/* Print process table info
 * Since things can change while ps is running, the ready proceses are
 * displayed last. This is because an interrupt can make a process ready,
 * but a ready process won't spontaneously become unready. Therefore a
 * process that changes during ps may show up twice, but this is better
 * than not having it showing up at all.
 */
int
ps(int argc, char *argv[], void *p)
{
	register struct proc *pp;
	register struct JMP_BUF *ep;
	unsigned short i;

	tprintf("PID       SP        stksize   maxstk    event     fl  in out name\n");

	for(pp = Susptab;pp != NULLPROC;pp = pp->next){
		ep = (struct JMP_BUF *)&pp->env;
		if(tprintf("%-10lx%-10lx%-10u%-10u%-10lx%c%c%c %2d %2d  %s\n",
		 pp,				/* process */
		 ep->jmp_a7,			/* stack pointer */
		 pp->stksize,			/* stack size */
		 stkutil(pp),			/* maxstk */
		 pp->event,			/* event */
		 pp->i_state ? 'I' : ' ',	/* flags */
		 (pp->state & WAITING) ? 'W' : ' ',
		 (pp->state & SUSPEND) ? 'S' : ' ',
		 pp->input, pp->output,		/* input, output sockets */
		 pp->name) == EOF)		/* name */
			return 0;
	}

	for(i=0;i<PHASH;i++){
		for(pp = Waittab[i];pp != NULLPROC;pp = pp->next){
			ep = (struct JMP_BUF *)&pp->env;
			if(tprintf("%-10lx%-10lx%-10u%-10u%-10lx%c%c%c %2d %2d  %s\n",
			 pp,			/* process */
			 ep->jmp_a7,			/* stack pointer */
			 pp->stksize,			/* stack size */
			 stkutil(pp),			/* maxstk */
			 pp->event,			/* event */
			 pp->i_state ? 'I' : ' ',	/* flags */
			 (pp->state & WAITING) ? 'W' : ' ',
			 (pp->state & SUSPEND) ? 'S' : ' ',
			 pp->input, pp->output,
			 pp->name) == EOF)		/* name */
				return 0;
		}
	}

	for(pp = Rdytab;pp != NULLPROC;pp = pp->next){
		ep = (struct JMP_BUF *)&pp->env;
		if(tprintf("%-10lx%-10lx%-10u%-10u%-10lx%c%c%c %2d %2d  %s\n",
		 pp,			/* process */
		 ep->jmp_a7,			/* stack pointer */
		 pp->stksize,			/* stack size */
		 stkutil(pp),			/* maxstk */
		 pp->event,			/* event */
		 pp->i_state ? 'I' : ' ',	/* flags */
		 (pp->state & WAITING) ? 'W' : ' ',
		 (pp->state & SUSPEND) ? 'S' : ' ',
		 pp->input, pp->output,
		 pp->name) == EOF)		/* name */
			return 0;
	}

	if(Curproc != NULLPROC){
		pp = Curproc;
		if(tprintf("%-10lx%-10lx%-10u%-10u%-10lx%c%c%c %2d %2d  %s\n",
		 pp,			/* process */
		 ep->jmp_a7,			/* stack pointer */
		 pp->stksize,			/* stack size */
		 stkutil(pp),			/* maxstk */
		 pp->event,			/* event */
		 pp->i_state ? 'I' : ' ',	/* flags */
		 (pp->state & WAITING) ? 'W' : ' ',
		 (pp->state & SUSPEND) ? 'S' : ' ',
		 pp->input, pp->output,
		 pp->name) == EOF)		/* name */
			return 0;
	}
	return 0;
}

static int
stkutil(pp)
struct proc *pp;
{
	unsigned int i;
	register int16 *sp;

	i = pp->stksize;
	for(sp = pp->stack;*sp == STACKPAT && sp < pp->stack + pp->stksize;sp++)
		i--;
	return (int) i;
}

void *tmp_reg;

/* Verify that stack pointer for current process is within legal limits;
 * also check that no one has dereferenced a null pointer
 */
void
chkstk(void)
{
	int16 *sbase;
	int16 *stop;
	int16 *sp = (int16 *) 0;  /* get current stack pointer */
					  /* this is Lattice C 5.0 specific */
#asm
	move.l a7,_tmp_reg
#endasm
	sp=(int16 *)tmp_reg;

	sbase = Curproc->stack;
	stop = sbase + Curproc->stksize;
	if(sp < sbase || sp >= stop){
		printf("Stack violation, process %s\n",Curproc->name);
		printf("SP = %lx, legal stack range [%lx,%lx)\n",
			sp, sbase, stop);
		fflush(stdout);
		killself();
	}
	if(*oldNullp != oldNull){
		printf("WARNING: Location 0 smashed, process %s\n",Curproc->name);
		*oldNullp = oldNull;
		fflush(stdout);
	}
}

/* Machine-dependent initialization of the initial task */
void
init_psetup(pp)
	struct proc *pp;
{
	/*
	 *  The base of the stack that's allocated to this program by the
	 *  Amiga operating system has been left for us in "base" by the
	 *  C startup routine.  
	 */
	extern int16 *_stkbase;
	register int i;

	pp->stack = _stkbase;
#asm
	move.l a7,_tmp_reg
#endasm

	pp->stksize = (int16 *)tmp_reg - pp->stack;
	/*
	 *  Have to be careful here to only clear as much of the stack as we're 
	 *  not currently using.
	 */

	for (i=0; i<(pp->stksize/2); i++)
		pp->stack[i] = STACKPAT;

	pp->stksize += 50;	/* slop.. there is a way to find out what this
				   really is. */
	setjmp(pp->env);
	pp->i_state = 1;
}


/* Machine-dependent initialization of a task */
void
psetup(pp,iarg,parg1,parg2,pc)
struct proc *pp;	/* Pointer to task structure */
int iarg;		/* Generic integer arg */
void *parg1;		/* Generic pointer arg #1 */
void *parg2;		/* Generic pointer arg #2 */
void (*pc)();		/* Initial execution address */
{
	register long *stktop;
	register struct JMP_BUF *ep;

	/* Set up stack to make it appear as if the user's function was called
	 * by killself() with the specified arguments. When the user returns,
	 * killself() automatically cleans up.
	 *
	 * First, push args on stack in reverse order, simulating what C
	 * does just before it calls a function.
	 */
	stktop = (long *)(pp->stack + pp->stksize);

	/* NOTE:  this only works when compiled with 32 bit integers */
	*--stktop = (long) parg2;
	*--stktop = (long) parg1;
	*--stktop = (long) iarg;
		
	/* Now push the entry address of killself(), simulating the call to
	 * the user function.
	 */
	*--stktop = (long) killself;

	*--stktop = 0;		/* fodder for longjmp() */

	setjmp(pp->env);
	ep = (struct JMP_BUF *)&pp->env;

	ep->jmp_a7 = (long) stktop;
	ep->jmpret = (long) pc;
#asm
	move.l a4,_tmp_reg
#endasm
	ep->jmp_a4 = (long) tmp_reg;/* set-up base relative addressing
					   environment pointer */
	ep->jmp_d1 = (long) iarg;
	ep->jmp_a1 = (long) parg1;
	ep->jmp_a2 = (long) parg2;
	ep->jmp_a3 = (long) pp;

	/* Task initially runs with interrupts on */
	pp->i_state = 1;
}

unsigned int
phash(event)
void *event;
{
	return (unsigned int)event % PHASH;
}
