/* Machine or compiler-dependent portions of kernel
 * Turbo-C version for PC
 *
 * Copyright 1991 Phil Karn, KA9Q
 */
#include <stdio.h>
#include <dos.h>
#include "global.h"
#include "proc.h"
#include "pc.h"
#include "commands.h"

static char *Taskers[] = {
	"",
	"DoubleDos",
	"DesqView",
	"Windows",
};


static oldNull;

/* Template for contents of jmp_buf in Turbo C */
struct env {
	unsigned	sp;
	unsigned	ss;
	unsigned	flag;
	unsigned	cs;
	unsigned	ip;
	unsigned	bp;
	unsigned	di;
	unsigned	es;
	unsigned	si;
	unsigned	ds;
};

static int chkintstk __ARGS((void));
static int stkutil __ARGS((struct proc *pp));

void
kinit()
{
	int i;

	/* Initialize interrupt stack for high-water-mark checking */
	for(i=0;i<512;i++)
		Intstk[i] = STACKPAT;

	/* Remember location 0 pattern to detect null pointer derefs */
	oldNull = *(unsigned short *)NULL;

}
/* 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(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct proc *pp;
	register struct env *ep;
	int i;

	tprintf("Uptime %s Stack %x max intstk %u",tformat(secclock()),
	 getss(),chkintstk());
	if(Mtasker != 0){
		tprintf(" Running under %s",Taskers[Mtasker]);
	}
	tprintf("\n");

	tprintf("PID       SP        stksize   maxstk    event     fl  in  out  name\n");

	for(pp = Susptab;pp != NULLPROC;pp = pp->next){
		ep = (struct env *)&pp->env;
		if(tprintf("%-10lx%-10lx%-10u%-10u%-10lx%c%c%c %3d %3d  %s\n",
		 ptol(pp),
		 ptol(MK_FP(ep->ss,ep->sp)),
		 pp->stksize,
		 stkutil(pp),
		 ptol(pp->event),
		 pp->i_state ? 'I' : ' ',
		 (pp->state & WAITING) ? 'W' : ' ',
		 (pp->state & SUSPEND) ? 'S' : ' ',
		 pp->input, pp->output,
		 pp->name) == EOF)
			return 0;
	}
	for(i=0;i<PHASH;i++){
		for(pp = Waittab[i];pp != NULLPROC;pp = pp->next){
			ep = (struct env *)&pp->env;
			if(tprintf("%-10lx%-10lx%-10u%-10u%-10lx%c%c%c %2d %2d  %s\n",
			 ptol(pp),ptol(MK_FP(ep->ss,ep->sp)),pp->stksize,stkutil(pp),
			 ptol(pp->event),
			 pp->i_state ? 'I' : ' ',
			 (pp->state & WAITING) ? 'W' : ' ',
			 (pp->state & SUSPEND) ? 'S' : ' ',
			 pp->input,pp->output,
			 pp->name) == EOF)
				return 0;
		}
	}
	for(pp = Rdytab;pp != NULLPROC;pp = pp->next){
		ep = (struct env *)&pp->env;
		if(tprintf("%-10lx%-10lx%-10u%-10u          %c%c%c %2d %2d  %s\n",
		 ptol(pp),ptol(MK_FP(ep->ss,ep->sp)),pp->stksize,stkutil(pp),
		 pp->i_state ? 'I' : ' ',
		 (pp->state & WAITING) ? 'W' : ' ',
		 (pp->state & SUSPEND) ? 'S' : ' ',
		 pp->input,pp->output,
		 pp->name) == EOF)
			return 0;
	}
	if(Curproc != NULLPROC){
		ep = (struct env *)&Curproc->env;
		tprintf("%-10lx%-10lx%-10u%-10u          %c   %2d %2d  %s\n",
		 ptol(Curproc),ptol(MK_FP(ep->ss,ep->sp)),Curproc->stksize,
		 stkutil(Curproc),
		 Curproc->i_state ? 'I' : ' ',
		 Curproc->input,Curproc->output,
		 Curproc->name);
	}
	return 0;
}
static int
stkutil(pp)
struct proc *pp;
{
	unsigned i;
	register int16 *sp;

	i = pp->stksize;
	for(sp = pp->stack;*sp == STACKPAT && sp < pp->stack + pp->stksize;sp++)
		i--;
	return i;
}
/* Return number of used words in interrupt stack. Note hardwired value
 * for stack size; this is also found in the various .asm files
 */
static int
chkintstk()
{
	register int i;
	register int16 *cp;

	for(i=512,cp = Intstk; i != 0 && *cp == STACKPAT; cp++)
		i--;
	return i;
}

/* Verify that stack pointer for current process is within legal limits;
 * also check that no one has dereferenced a null pointer
 */
void
chkstk()
{
	int16 *sbase;
	int16 *stop;
	int16 *sp;

	sp = MK_FP(_SS,_SP);
	if(_SS == _DS){
		/* Probably in interrupt context */
		return;
	}
	sbase = Curproc->stack;
	if(sbase == NULL)
		return;	/* Main task -- too hard to check */

	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",
		ptol(sp),ptol(sbase),ptol(stop));
		fflush(stdout);
		killself();
	}
	if(*(unsigned short *)NULL != oldNull){
		printf("WARNING: Location 0 smashed, process %s\n",Curproc->name);
		*(unsigned short *)NULL = oldNull;
		fflush(stdout);
	}
}
/* 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 int *stktop;
	register struct env *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 = (int *)(pp->stack + pp->stksize);
#ifdef	LARGEDATA
	*--stktop = FP_SEG(parg2);
#endif
	*--stktop = FP_OFF(parg2);
#ifdef	LARGEDATA
	*--stktop = FP_SEG(parg1);
#endif
	*--stktop = FP_OFF(parg1);
	*--stktop = iarg;
		
	/* Now push the entry address of killself(), simulating the call to
	 * the user function.
	 */
#ifdef	LARGECODE
	*--stktop = FP_SEG(killself);
#endif
	*--stktop = FP_OFF(killself);

	/* Set up task environment. Note that for Turbo-C, the setjmp
	 * sets the interrupt enable flag in the environment so that
	 * interrupts will be enabled when the task runs for the first time.
	 * Note that this requires newproc() to be called with interrupts
	 * enabled!
	 */
	setjmp(pp->env);
	ep = (struct env *)&pp->env;
	ep->ss = FP_SEG(stktop);
	ep->sp = FP_OFF(stktop);
	ep->cs = FP_SEG(pc);	/* Doesn't hurt in small model */
	ep->ip = FP_OFF(pc);
	/* Task initially runs with interrupts on */
	pp->i_state = 1;
}
unsigned
phash(event)
void *event;
{
	register unsigned x;

	/* Fold the two halves of the pointer */
	x = FP_SEG(event) ^ FP_OFF(event);

	/* If PHASH is a power of two, this will simply mask off the
	 * higher order bits
	 */
	return x % PHASH;
}
