/* Machine or compiler-dependent portions of kernel
 * Turbo-C version for PC
 * Copyright 1991 Phil Karn, KA9Q
 */
#include "global.h"
#include "commands.h"
#include "proc.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: ksubr.c,v 1.31 2001/05/14 19:08:40 brian Exp $";
#endif

void proc_launch (void);
extern int valid_proc (struct proc *pp);
static int stkutil (struct proc *pp);
static void pproc(struct proc *pp);



#ifdef USE_SETSTACK
/*
 * This needs to be extern because it is referenced by setstack()
 */
void *newstackptr;
void setstack(void);
#endif



void
kinit()
{
	/* Initialize signal queue */
	Ksig.wp = Ksig.rp = Ksig.entry;
}



int
dokstat (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	tprintf ("\nksigs %lu queued %lu hiwat %u woken %lu nops %lu dups %lu lost %lu\n",
		Ksig.ksigs, Ksig.ksigsqueued, Ksig.maxentries, Ksig.ksigwakes,
		Ksig.ksignops, Ksig.duksigs, Ksig.lostsigs);
	tprintf ("kwaits %lu nops %lu from int %lu\n",
		Ksig.kwaits, Ksig.kwaitnops, Ksig.kwaitints);
#ifdef UNIX
#ifdef ALPHATEST
	tprintf ("kresumes %lu, allowresumes %d daemon/server restarts %lu\n",
		Ksig.kresumes, RESTART_COUNT, Ksig.krestarts);
#endif
	tprintf ("kfreesegvs %lu\n", Ksig.kfreesegvs);
#endif
	tputs ("\n");
	Ksig.maxentries = 0;
	return 0;
}



/* 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 OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
register struct proc *pp;
int i;

	tprintf("Uptime %s\n", tformat (secclock()));

	(void) dokstat (0, NULLCHARP, NULL);
	tputs ("\nPID      stksize  maxstk   event    fl  in  out  name\n");

	for (pp = Susptab; pp != NULLPROC; pp = pp->next)
		pproc (pp);

	for (i = 0; i < PHASH; i++)
		for (pp = Waittab[i]; pp != NULLPROC; pp = pp->next)
			pproc (pp);

	for (pp = Rdytab; pp != NULLPROC; pp = pp->next)
		pproc (pp);

	if (Curproc != NULL)
		pproc (Curproc);

	return 0;
}



static void
pproc (struct proc *pp)
{
	if (valid_proc (pp))	{
		tprintf ("%8.8lx %8.8lx %8.8lx %8.8lx %c%c%c ", (uint32) pp,
        	        (int32) pp->stksize, (int32) stkutil(pp),
                	(uint32) pp->event,
	                pp->i_state ? 'I' : ' ',
			(pp->state & WAITING) ? 'W' : ' ',
			(pp->state & SUSPEND) ? 'S' : ' ');
		tprintf ((pp->input != -1) ? "%3d " : "--- ", pp->input);
		tprintf ((pp->output != -1) ? "%3d " : "--- ", pp->output);
		tprintf (" %s\n", pp->name);
	} else
		tprintf ("-------- -------- -------- --------     --- --- (Invalid process entry skipped)\n");
}



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

	if (pp->stack == (int16 *) 0LU)
		return (0);
#ifdef UNIX
	if (pp->stack == (int16 *) STACKBASE)
		return ((int) pp->stksize);
#endif
	i = pp->stksize;
	for (sp = pp->stack; *sp == STACKPAT && sp < pp->stack + pp->stksize; sp++)
		i--;
	return ((int)i);
}



void
proc_launch (void)
{
	(Curproc->func)(Curproc->iarg, Curproc->parg1, Curproc->parg2);

	/* Clean up, just in case the function returns. */
	killself();
	return;
}



/* Machine-dependent initialization of a task */
void
psetup (struct proc *pp)	/* Pointer to task structure */
{
#ifndef USE_SETSTACK	/* not the UNIX SETSTACK version */
char *cp;

	(void) setjmp (pp->env);

	cp = (char *)pp->stack;
	cp += (pp->stksize * sizeof (int16));

#ifdef MSDOS	/* this conditional section is SOLELY to silent MSDOS warnings */
	TNOS_SP(pp) = (long) cp;
	TNOS_PC(pp) = (long) proc_launch;
#else
	TNOS_SP(pp) = (int) cp;
	TNOS_PC(pp) = (int) proc_launch;
#endif
#ifdef TNOS_EBP
	TNOS_EBP(pp) = 0;		/* Anchor stack traces */
#endif
#ifdef TNOS_BP
	TNOS_BP(pp) = cp;
#endif

#else	/* USE_SETSTACK */
	/*
	 * setstack() is a routine borrowed from WAMPES.  It's actually
	 * an assembly language routine inside setsp.c.  TNOS needs to
	 * return the proc pointer, so I have to play a little game here.
	 * I do a setjmp() call, establishing this piece of code as the
	 * process context.  Note that we won't really be on the new
	 * process stack when setjmp() returns the first time.  The
	 * actual switchover doesn't happen until the process gets scheduled
	 * and the setjmp() returns for the second time.
	 */
	if (setjmp (pp->env))	{
		newstackptr = Curproc->stack + Curproc->stksize;
		setstack();
		proc_launch();

		/* Clean up, just in case the function returns. */
		return;
	}

#endif	/* !USE_SETSTACK */
}



unsigned
phash (volatile void *event)
{
register unsigned x;

	/* Fold the two halves of the pointer */
	x = (unsigned) event;

	/* If PHASH is a power of two, this will simply mask off the
	 * higher order bits
	 */
	return x % PHASH;
}
