/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd and Vincent Hayward.	    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

/*=~=~=~=~ McGill-Computer-Vision-and-Robotics-Laboratory--C-Module ~=~=~=~=
 *		
 * MODULE NAME:	kernel.c
 *
 * DESCRIPTION:	Support functions for the LSI11 microkernel.
 *
 *		The main feature of this file is a system call table that
 *		emulates a couple of UNIX v7 system calls. The reason this was
 *		done was that some of the stdio routines in the Xcompiler
 *		library use read, write, and ioctl, and it was easier to
 *		write this emulation table than to to rewrite the calls in
 *		stdio library. Other system calls in microkernel do
 *		NOT go through the system call table (mainly for speed).
 *
 *		Of course, the microkernel does really have an I/O system to
 *		support these calls, so what is does is simply map file
 *		descriptors 0 through 2 (stdin, stdout, and stderr) onto the
 *		console serial line.
 * 
 *		The other main routine in this file is the one to set up
 *		an interrupt vector handler for an I/O device.
 *
 * AUTHOR/DATE:	Vincent Hayward and John Lloyd (1983 through 1988)
 *
 *~=~=~=~=~=~= McGill-Computer-Vision-and-Robotics-Laboratory ~=~=~=~=~=~=*/

#include "cdefs.h"
#include "sys.h"

#if 0
/* Trap handler to simulate Unix kernel traps */

#define DLV     ((struct dlvp *)0177560)
#define READY   0200
#define ENXIO   6

struct dlvp {
	int     dl_rcsr;
	int     dl_rbuf;
	int     dl_xcsr;
	int     dl_xbuf;
};

struct  sys {
	int     s_args;
	int     (*s_fun)();
};

extern  zexit(),zread(),zwrite(),nosys(),z_ioctl();

/*
 * This is the system call dispatch table, used to transfer
 * to the appropriate routine for processing a system call.
 * Each row contains the number of arguments expected
 * and a pointer to the routine.
 */
struct sys sysent[] = {

	0, nosys,                      /*  0 = indir */
	0, zexit,                      /*  1 = exit */
	0, nosys,                      /*  2 = fork */
	2, zread,                      /*  3 = read */
	2, zwrite,                     /*  4 = write */
	2, nosys,                      /*  5 = open */
	0, nosys,                      /*  6 = close */
	0, nosys,                      /*  7 = wait */
	2, nosys,                      /*  8 = creat */
	2, nosys,                      /*  9 = link */
	1, nosys,                      /* 10 = unlink */
	2, nosys,                      /* 11 = exec */
	1, nosys,                      /* 12 = chdir */
	0, nosys,                      /* 13 = time */
	3, nosys,                      /* 14 = mknod */
	2, nosys,                      /* 15 = chmod */
	2, nosys,                      /* 16 = chown */
	1, nosys,                      /* 17 = break */
	2, nosys,                      /* 18 = stat */
	2, nosys,                      /* 19 = seek */
	0, nosys,                      /* 20 = getpid */
	3, nosys,                      /* 21 = mount */
	1, nosys,                      /* 22 = umount */
	0, nosys,                      /* 23 = setuid */
	0, nosys,                      /* 24 = getuid */
	0, nosys,                      /* 25 = stime */
	3, nosys,                      /* 26 = ptrace */
	0, nosys,                      /* 27 = alarm */
	1, nosys,                      /* 28 = fstat */
	0, nosys,                      /* 29 = pause */
	1, nosys,                      /* 30 = smdate; inoperative */
	1, nosys,                      /* 31 = stty */
	1, nosys,                      /* 32 = gtty */
	2, nosys,                      /* 33 = access */
	0, nosys,                      /* 34 = nice */
	0, nosys,                      /* 35 = sleep */
	0, nosys,                      /* 36 = sync */
	1, nosys,                      /* 37 = kill */
	0, nosys,                      /* 38 = switch */
	0, nosys,                      /* 39 = x */
	0, nosys,                      /* 40 = tell */
	0, nosys,                      /* 41 = dup */
	0, nosys,                      /* 42 = pipe */
	1, nosys,                      /* 43 = times */
	4, nosys,                      /* 44 = prof */
	0, nosys,                      /* 45 = tiu */
	0, nosys,                      /* 46 = setgid (nop) */
	0, nosys,                      /* 47 = getgid (nop) */
	2, nosys,                      /* 48 = sig */
	0, nosys,                      /* 49 = geteuid (16 bit effective uid) */
	0, nosys,                      /* 50 = clrtty (clear controlling tty) */
	1, nosys,                      /* 51 = nicer (nice on another job) */
	0, nosys,                      /* 52 = wakeup (for debugging) */
	0, nosys,                      /* 53 = x */
	2, z_ioctl,                    /* 54 = ioctl (dev, flags, arg) */
	0, nosys,                      /* 55 = x */
	0, nosys,                      /* 56 = x */
	0, nosys,                      /* 57 = x */
	0, nosys,                      /* 58 = x */
	0, nosys,                      /* 59 = x */
	0, nosys,                      /* 60 = x */
	0, nosys,                      /* 61 = x */
	0, nosys,                      /* 62 = x */
	0, nosys                       /* 63 = x */
};

int     u_arg[4];
int     u_error;
int     r0,r1;

#define CBREAK	0x01
#define RAW	0x02
#define NOECHO	0x04

#define KBCSR	(*(unsigned short *) 0177560)   /* Port 3, or console ... */
#define KBBUF		  (*(char *) 0177562)
#define PRCSR	(*(unsigned short *) 0177564)
#define PRBUF		  (*(char *) 0177566)

#define S_READY		0200			/* Serial register ready  */
#define S_IEA		0100			/* Enable interrupts      */

static ioFlags = 0;
	
PUTC(c)
{
	/* puts a character onto the console serial line, using busy 
	   waiting */

	while (!(PRCSR & S_READY))
		;
	PRBUF = c;
}

GETC()
{
	/* gets a character from the console serial line, using busy 
	   waitoing */

	while (!(KBCSR & S_READY))
	   ;
	return(KBBUF & 0177);
}

TESTC()
{	
	/* tests to see if there is a character waiting to be read from
	   the console serial line */

	return (KBCSR & S_READY);
}

z_ioctl ()
{
	/* just a futz to allow programs to set a simulated CBREAK and noecho
	   mode on I/O from the console serial line */

	PUTC ('$');

	if (r0 < 0 || r0 > 2)
	 { return;
	 }

	ioFlags = u_arg[0];
}

zwrite()
{
	/* writes a buffer of characters to the console serial line.

	   Carriage returns a placed in from of new line characters
	   unless RAW mode has been set. */

	char *cp,c;
	int n;
	if (znxio())return;
	cp = (char *) u_arg[0];
	r0 = n = u_arg[1];
	while (n--) {
		c = *cp++;
		if (c == '\n' && !(ioFlags & RAW))
	         { PUTC('\r');
		 }
		PUTC(c);
	}
}

zread()
{
	/* reads a buffer of characters from the console serial line.

	   The read returns whenever either the buffer fills up, or
	   a new line is read. The routine will also return after the
	   first character is read if CBREAK mode has been set.

	   The routine echos characters, unless RAW mode has been
	   set. 

	   The routine converts '\r' into '\n', unless RAW mode has been
	   set.

	   The routine honors character deletion using backspace or delete
	   unless RAW or CBREAK mode has been set.
	 */

	char *cp,*start,c;
	int n,nr;

	if (znxio()) return;
	cp = start = (char *) u_arg[0];
	n = u_arg[1];
	nr = 0;
        c = '\0';

	while (n > nr && c != '\n') 
	 { c = GETC();
	   switch (c)
	    { case '\r':
	      case '\n':
		   if (ioFlags & RAW)
		    { *cp++ = c;
		      if (!(ioFlags & NOECHO))
		       { PUTC (c);
		       }
		    }
		   else
		    { *cp++ = c = '\n';
		      if (!(ioFlags & NOECHO))
		       { PUTC('\r'), PUTC('\n');
		       }
		    }
		   nr++;
		   break;
	      case 8:		/* backspace */
	      case 127:		/* delete */

		   if (ioFlags & (RAW | CBREAK))
		    { *cp++ = c;
		      nr++;
		    }
		   else
		    { if (nr > 0)
		       { if (!(ioFlags & NOECHO))
		          { PUTC( 8 ), PUTC(' '), PUTC( 8 );
		          }
		         nr--; cp--;
		       }
		    }
		   break;
	      default:
		   if (!(ioFlags & NOECHO))
		    { PUTC(c);
		    }
		   *cp++ = c;
		   nr++;
		   break;
	    }
	   if (ioFlags & CBREAK)
	    { break;
	    }
	 }
	r0 = nr;
}

znxio()
{
	/* Routine called if read or write is called with file descriptors
	   that are not 0, 1, or 2. */

	if (u_arg[1] <= 0 || r0 < 0 || r0 > 2) {
		u_error = ENXIO;
		return(1);
	}
	return(0);
}

nosys()
{
	u_error = ENXIO;
}

#endif

_trap(cr1,cr0,pc,ps)
int cr1, cr0;
int *pc;
int ps;
{
	/* Basic routine for processing system calls */

	printf ("System call trap. Shouldn't happen; something's wrong\n");
	zexit (034);
}

#define CBREAK	0x01
#define RAW	0x02
#define NOECHO	0x04

#define KBCSR	(*(unsigned short *) 0177560)   /* Port 3, or console ... */
#define KBBUF		  (*(char *) 0177562)
#define PRCSR	(*(unsigned short *) 0177564)
#define PRBUF		  (*(char *) 0177566)

#define S_READY		0200			/* Serial register ready  */
#define S_IEA		0100			/* Enable interrupts      */

static ioFlags = 0;
	
PUTC(c)
{
	/* puts a character onto the console serial line, using busy 
	   waiting */

	while (!(PRCSR & S_READY))
		;
	PRBUF = c;
}

GETC()
{
	/* gets a character from the console serial line, using busy 
	   waitoing */

	while (!(KBCSR & S_READY))
	   ;
	return(KBBUF & 0177);
}

TESTC()
{	
	/* tests to see if there is a character waiting to be read from
	   the console serial line */

	return (KBCSR & S_READY);
}

ioctl (fd, cmd, arg)
int fd, cmd;
char *arg;
{
	/* just a futz to allow programs to set a simulated CBREAK and noecho
	   mode on I/O from the console serial line */

	PUTC ('$');

	if (fd < 0 || fd > 2)
	 { return;
	 }
	ioFlags = cmd;
	return (1);
}

write(fd, buf, cnt)
int fd;
char *buf;
int cnt;
{
	/* writes a buffer of characters to the console serial line.

	   Carriage returns a placed in from of new line characters
	   unless RAW mode has been set. */

	char *cp,c;
	int n;
	if ((fd != 1 && fd != 2) || cnt<0)
	 { return (-1);
	 }
	cp = buf;
	n = cnt;
	while (n--) {
		c = *cp++;
		if (c == '\n' && !(ioFlags & RAW))
	         { PUTC('\r');
		 }
		PUTC(c);
	}
	return (cnt);
}

read(fd, buf, cnt)
int fd;
char *buf;
int cnt;
{
	/* reads a buffer of characters from the console serial line.

	   The read returns whenever either the buffer fills up, or
	   a new line is read. The routine will also return after the
	   first character is read if CBREAK mode has been set.

	   The routine echos characters, unless RAW mode has been
	   set. 

	   The routine converts '\r' into '\n', unless RAW mode has been
	   set.

	   The routine honors character deletion using backspace or delete
	   unless RAW or CBREAK mode has been set.
	 */

	char *cp,*start,c;
	int n,nr;

	if (fd != 0 || cnt<0)
	 { return (-1);
	 }
	cp = start = buf;
	n = cnt;
	nr = 0;
        c = '\0';

	while (n > nr && c != '\n') 
	 { c = GETC();
	   switch (c)
	    { case '\r':
	      case '\n':
		   if (ioFlags & RAW)
		    { *cp++ = c;
		      if (!(ioFlags & NOECHO))
		       { PUTC (c);
		       }
		    }
		   else
		    { *cp++ = c = '\n';
		      if (!(ioFlags & NOECHO))
		       { PUTC('\r'), PUTC('\n');
		       }
		    }
		   nr++;
		   break;
	      case 8:		/* backspace */
	      case 127:		/* delete */

		   if (ioFlags & (RAW | CBREAK))
		    { *cp++ = c;
		      nr++;
		    }
		   else
		    { if (nr > 0)
		       { if (!(ioFlags & NOECHO))
		          { PUTC( 8 ), PUTC(' '), PUTC( 8 );
		          }
		         nr--; cp--;
		       }
		    }
		   break;
	      default:
		   if (!(ioFlags & NOECHO))
		    { PUTC(c);
		    }
		   *cp++ = c;
		   nr++;
		   break;
	    }
	   if (ioFlags & CBREAK)
	    { break;
	    }
	 }
	return(nr);
}

sbrk(s)
int s;
{
	return (getmem(s));

}

extern VECTOR *dsptabbeg;
extern VECTOR *dsptabend;

extern unsigned short dispfast;		/* entry point for fast dispatcher */
extern unsigned short dispsave;		/* entry point for reg dispatcher */
VECTOR *freevecs = NULL;
VECTOR *dsptabbeg;
VECTOR *dsptabend;

VECTOR *vectorAlloc(vecnum, vdesc)
register unsigned *vecnum;
VECT_DESC *vdesc;
{
	/* Routine to attach a handler routine to an I/O device vector.
	   The vector number is given by 'vecnum'; this is the actual
	   memory location of the vector. Other information about the
	   handler to be set up is contained in 'vdesc'. */	   

	register VECTOR *v;	/* handler block */
	int s;

	/* check for impossible vector number */

	if (vecnum < (unsigned *)0104 || vecnum >= (unsigned *)dsptabbeg)
	 { return (NULL);
	 }

	/* We don't actually put the address of the handler in the vector
	   location. Instead, we put the address of a special handling
	   block set up for that vector. The handling block contains code
	   that sets up the function call (complete with argument) and
	   then calls the function using either 'dispsave' (which saves
	   all registers), or 'dispfast' (which does not dave any registers).
	   'dispsave' and 'dispfast' are defined in locore.c.
	*/

	s = spl7();	
	if (vecnum[0] ==(unsigned)&vecnum[1])
	 { if (freevecs)
	    { v = freevecs;		/* get a previously freed block */
	      freevecs = v->next;
	    }
	   else
	    { v = dsptabend;		/* allocate new handler block */
	      dsptabend++;
	    }
	 }
	else
	 { v = (VECTOR*)vecnum[0];	/* reuse old handler block */
	 }

	vecnum[0] = (unsigned)v;	/* set up actual vector with block */
	vecnum[1] = 0340;		/* and max priority for delivery */

	/* now put the code into the handler block */

	v->buf[0] = 012746;	/* code to push the function */
	v->buf[1] = (unsigned)vdesc->fxn;
	v->buf[2] = 012746;	/* code to push the argument */
	v->buf[3] = vdesc->arg;
	v->buf[4] = 0137;	/* code to jmp to dispatcher */
	if (vdesc->saveregs)
	 { v->buf[5] = (unsigned)&dispsave;
	 }
	else
	 { v->buf[5] = (unsigned)&dispfast;
	 }

	splx(s);
	return (v);
}
