/* Machine or compiler-dependent portions of kernel
 * Turbo-C version for PC
 * Copyright 1991 Phil Karn, KA9Q
 */
#include <stdio.h>
#ifdef MSDOS
#include <dos.h>
#endif
#include "global.h"
#include "config.h"
#include "proc.h"
#ifndef UNIX
#include "pc.h"
#endif
#include "commands.h"
#include "socket.h"

#if !defined(TNOS_68K) && !defined(UNIX)
static char *Taskers[] = {
	"",
	"DoubleDos",
	"DesqView",
	"Windows",
        "DOS 5/6",
        "OS/2 DOS",
        "DPMI"
};

#endif
#ifdef MSDOS
/* 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;
};

#endif
#ifdef TNOS_68K
/* Template for contents of jmp_buf for 68302 */
struct env {
	unsigned	_pc;
	unsigned	_d0;
	unsigned	_d1;
	unsigned	_d2;
	unsigned	_d3;
	unsigned	_d4;
	unsigned	_d5;
	unsigned	_d6;
	unsigned	_d7;
	unsigned	_a0;
	unsigned	_a1;
	unsigned	_a2;
	unsigned	_a3;
	unsigned	_a4;
	unsigned	_a5;
	unsigned	_a6;
	unsigned	_a7;
	unsigned	_xtra;
};

#endif

#ifdef UNIX
/*
 * There are several different ways to implement jmp_buf's.  We use macros to
 * extract the fields.  Note that these must expand into lvalues, because we
 * load them during process initialization.  Current code only uses _SP and
 * _PC; this may change.
 *
 * I should fold the DOS code into this, but then I need to worry about
 * segments.  No thanks.
 */

#ifdef M_UNIX
/*
 * I am unsure of these; the i386 contents are not documented in 3.2.2.  I
 * guessed at them by comparing "info regs" with the contents of a jmp_buf
 * immediately after a setjmp().
 */
#define _PC(p) (p->env[5])
#define _SP(p) (p->env[4])
#define _BP(p) (p->env[3])
#endif

#ifdef linux
#define _PC(p) (p->env->__pc)
#define _SP(p) (p->env->__sp)
#define _BP(p) (p->env->__bp)
#endif

#endif

#ifndef UNIX
static unsigned short oldNull;
static int chkintstk __ARGS((void));
#endif
int stkutil __ARGS((struct proc *pp));

void
kinit()
{
#if !defined(UNIX) && !defined(TNOS_68K)
	int i;

	/* Initialize interrupt stack for high-water-mark checking */
	for(i=0;i<512;i++)
		Intstk[i] = STACKPAT;
#endif

#ifndef UNIX
	/* Remember location 0 pattern to detect null pointer derefs */
	oldNull = *(unsigned short *)NULL;
#endif
}
/* 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;
#ifndef UNIX
	register struct env *ep;
#endif
	int i;

#if !defined(UNIX) && !defined(TNOS_68K)

	tprintf("Uptime %s Stack %x max intstk %u",tformat(secclock()),
		getss(),chkintstk());
	if(Mtasker != 0)
		tprintf(" Running under %s",Taskers[Mtasker]);
#else
#ifdef UNIX
	tprintf("Uptime %s",tformat(secclock()));
#endif
#ifdef TNOS_68K
	tprintf("Uptime %s Stack %x",tformat(secclock()),stacksiz());
#endif
#endif
	tprintf("\n");

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

	for (pp = Susptab; pp != NULLPROC; pp = pp->next)
	{
		if (tprintf("%8.8lx %8.8lx %8.8lx %8.8lx %8.8lx %c%c%c %3d \
%3d  %s\n",
			    FP_SEG(pp), FP_SEG(_SP(pp)), (int32) pp->stksize,
			    (int32) stkutil(pp), FP_SEG(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)
		{
			if (tprintf("%8.8lx %8.8lx %8.8lx %8.8lx %8.8lx \
%c%c%c %3d %3d  %s\n",
				    FP_SEG(pp), FP_SEG(_SP(pp)),
				    (int32) pp->stksize, (int32) stkutil(pp),
				    FP_SEG(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)
	{
		if (tprintf("%8.8lx %8.8lx %8.8lx %8.8lx %8.8lx \
%c%c%c %3d %3d  %s\n",
			    FP_SEG(pp), FP_SEG(_SP(pp)), (int32) pp->stksize,
			    (int32) stkutil(pp), FP_SEG(pp->event),
			    pp->i_state ? 'I' : ' ',
			    (pp->state & WAITING) ? 'W' : ' ',
			    (pp->state & SUSPEND) ? 'S' : ' ',
			    pp->input, pp->output, pp->name) == EOF)
			return 0;
	}
	if( Curproc != NULLPROC)
	{
		if (tprintf("%8.8lx %8.8lx %8.8lx %8.8lx %8.8lx \
%c%c%c %3d %3d  %s\n",
			    FP_SEG(Curproc), FP_SEG(_SP(Curproc)),
			    (int32) Curproc->stksize, (int32) stkutil(Curproc),
			    FP_SEG(Curproc->event),
			    Curproc->i_state ? 'I' : ' ',
			    (Curproc->state & WAITING) ? 'W' : ' ',
			    (Curproc->state & SUSPEND) ? 'S' : ' ',
			    Curproc->input, Curproc->output,
			    Curproc->name) == EOF)
			return 0;
	}
#else
#ifndef TNOS_68K
    tprintf("PID  SP        stksize   maxstk    event     fl  in  out  name\n");
#else
    tprintf("PID      SP       stksize  maxstk   event    fl  in  out  name\n");
#endif
	for(pp = Susptab;pp != NULLPROC;pp = pp->next){
#ifndef TNOS_68K
		ep = (struct env *)&pp->env;
        if(tprintf("%4.4x %-10lx%-10u%-10u%-10lx%c%c%c %3d %3d  %s\n",
#else
		ep = (struct env *)pp->env;
        if(tprintf("%-8.8x %-9lx%-9u%-9u%-9lx%c%c%c %3d %3d  %s\n",
#endif
         FP_SEG(pp),
#ifndef TNOS_68K
		 ptol(MK_FP(ep->ss,ep->sp)),
#else
		 ptol(ep->_a7),
#endif
		 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){
#ifndef TNOS_68K
			ep = (struct env *)&pp->env;
        if(tprintf("%4.4x %-10lx%-10u%-10u%-10lx%c%c%c %3d %3d  %s\n",
#else
		ep = (struct env *)pp->env;
        if(tprintf("%-8.8x %-9lx%-9u%-9u%-9lx%c%c%c %3d %3d  %s\n",
#endif
             FP_SEG(pp),
#ifndef TNOS_68K
			 ptol(MK_FP(ep->ss,ep->sp)),
#else
			 ptol(ep->_a7),
#endif
			 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){
#ifndef TNOS_68K
		ep = (struct env *)&pp->env;
        if(tprintf("%4.4x %-10lx%-10u%-10u          %c%c%c %3d %3d  %s\n",
#else
		ep = (struct env *)pp->env;
        if(tprintf("%-8.8x %-9lx%-9u%-9u         %c%c%c %3d %3d  %s\n",
#endif
         FP_SEG(pp),
#ifndef TNOS_68K
		 ptol(MK_FP(ep->ss,ep->sp)),
#else
		 ptol(ep->_a7),
#endif
		 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){
#ifndef TNOS_68K
		ep = (struct env *)&Curproc->env;
        tprintf("%4.4x %-10lx%-10u%-10u          %c   %3d %3d  %s\n",
#else
		ep = (struct env *)pp->env;
        tprintf("%-8.8x %-9lx%-9u%-9u         %c   %3d %3d  %s\n",
#endif
         FP_SEG(Curproc),
#ifndef TNOS_68K
		 ptol(MK_FP(ep->ss,ep->sp)),
#else
		 ptol(ep->_a7),
#endif
		 Curproc->stksize,
		 stkutil(Curproc),
		 Curproc->i_state ? 'I' : ' ',
		 Curproc->input,Curproc->output,
		 Curproc->name);
	}
#endif
	return 0;
}
int
stkutil(pp)
struct proc *pp;
{
	unsigned i;
	register int16 *sp;

#ifdef UNIX
#ifdef linux
	if (FP_SEG(pp->stack) == 0xC0000000)
#else
	if (FP_SEG(pp->stack) == 0x80000000)
#endif
		return pp->stksize;
#endif
	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
 */
#if !defined(TNOS_68K) && !defined(UNIX)
static int
chkintstk()
{
	register int i;
	register int16 *cp;

	for(i=512,cp = Intstk; i != 0 && *cp == STACKPAT; cp++)
		i--;
	return i;
}
#endif

/* Verify that stack pointer for current process is within legal limits;
 * also check that no one has dereferenced a null pointer
 */
#ifdef UNIX
static void
#ifdef PROTOTYPES
__chkstk_internal(int16 spp)
#else
__chkstk_internal(spp)
    int16 spp;
#endif
{
	int16 *sp, *sbase, *stop;

	sp = &spp;		/* close enough for government work */
	sbase = Curproc->stack;
	if(sbase == NULL)
		return;	/* Main task -- too hard to check */
	stop = sbase + Curproc->stksize;
	if(sp < sbase || sp >= stop){
		tprintf("Stack violation, process %s\n",Curproc->name);
		tprintf("SP = %lx, legal stack range [%lx,%lx)\n",
			ptol(sp),ptol(sbase),ptol(stop));
		tflush();
		rflush();
		killself();
	}
}	
#endif

void
chkstk()
{
#ifdef UNIX
	__chkstk_internal(0);	/* must have an argument to take address of */
#else
	int16 *sbase;
	int16 *stop;
	int16 *sp;
#ifdef MULTITASK
	extern int Nokeys;		/* indicates we are shelled out [pc.c]*/
#endif

#ifndef TNOS_68K
	sp = MK_FP(_SS,_SP);
	if(_SS == _DS){
		/* Probably in interrupt context */
		return;
	}
#else
	sp = getstack();
#endif
	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){
#ifdef MULTITASK
        if(!Nokeys)     /* don't complain if we are shelled out */
#endif
			printf("WARNING: Location 0 smashed, process %s\n",Curproc->name);
        *(unsigned short *)NULL = oldNull;
		fflush(stdout);
	}
#endif
}
/* 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)__ARGS((int,void*,void*));           /* Initial execution address */
{
	register int16 *stktop; /* KF8NH was "int", this breaks 32-bit code */
#ifndef UNIX
	register struct env *ep;
#endif

	/* 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 = (int16 *)(pp->stack + pp->stksize); /* KF8NH as above */
#ifdef UNIX
#ifdef LITTLE_ENDIAN
	*--stktop = (((unsigned long) parg2 & 0xFFFF0000) >> 16) & 0x0000FFFF;
	*--stktop = ((unsigned long) parg2 & 0x0000FFFF);
	*--stktop = (((unsigned long) parg1 & 0xFFFF0000) >> 16) & 0x0000FFFF;
	*--stktop = ((unsigned long) parg1 & 0x0000FFFF);
	*--stktop = (((unsigned long) iarg & 0xFFFF0000) >> 16) & 0x0000FFFF;
	*--stktop = ((unsigned long) iarg & 0x0000FFFF);
	*--stktop = (((unsigned long) killself & 0xFFFF0000) >> 16) & 0x0000FFFF;
	*--stktop = ((unsigned long) killself & 0x0000FFFF);
#else
	*--stktop = ((unsigned long) parg2 & 0x0000FFFF);
	*--stktop = (((unsigned long) parg2 & 0xFFFF0000) >> 16) & 0x0000FFFF;
	*--stktop = ((unsigned long) parg1 & 0x0000FFFF);
	*--stktop = (((unsigned long) parg1 & 0xFFFF0000) >> 16) & 0x0000FFFF;
	*--stktop = ((unsigned long) iarg & 0x0000FFFF);
	*--stktop = (((unsigned long) iarg & 0xFFFF0000) >> 16) & 0x0000FFFF;
	*--stktop = ((unsigned long) killself & 0x0000FFFF);
	*--stktop = (((unsigned long) killself & 0xFFFF0000) >> 16) & 0x0000FFFF;
#endif
#else
#ifdef	LARGEDATA
	*--stktop = FP_SEG(parg2);
#endif
	*--stktop = FP_OFF(parg2);
#ifndef TNOS_68K
#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
#endif
	*--stktop = FP_OFF(killself);
#endif

	/* 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);
#ifdef UNIX
	_SP(pp) = stktop;
	_BP(pp) = stktop;
	_PC(pp) = pc;
#else
#ifndef TNOS_68K
	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);
#else
	ep = (struct env *)pp->env;
	ep->_d1 = parg1;
	ep->_d0 = iarg;
	ep->_a7 = stktop;
	ep->_pc = pc;
#endif
#endif
	/* Task initially runs with interrupts on */
	pp->i_state = 1;
}
unsigned
#ifdef PROTOTYPES
phash(volatile void *event)
#else
phash(event)
volatile void *event;
#endif
{
	register unsigned x;

	/* Fold the two halves of the pointer */
#ifdef UNIX
	x = (unsigned) event;
#else
#ifndef TNOS_68K
	x = FP_SEG(event) ^ FP_OFF(event);
#else
	x = hiword(((long)event)) ^ loword(((long)event));
#endif
#endif

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