[comp.os.minix] kernel/tty.c part 1 of 2

ast@cs.vu.nl (Andy Tanenbaum) (06/28/88)

/* This file contains the terminal driver, both for the IBM console and regular
 * ASCII terminals.  It is split into two sections, a device-independent part
 * and a device-dependent part.  The device-independent part accepts
 * characters to be printed from programs and queues them in a standard way
 * for device-dependent output.  It also accepts input and queues it for
 * programs. This file contains 2 main entry points: tty_task() and keyboard().
 * When a key is struck on a terminal, an interrupt to an assembly language
 * routine is generated.  This routine saves the machine state and registers
 * and calls keyboard(), which enters the character in an internal table, and
 * then sends a message to the terminal task.  The main program of the terminal
 * task is tty_task(). It accepts not only messages about typed input, but
 * also requests to read and write from terminals, etc. 
 *
 * The device-dependent part interfaces with the IBM console and ASCII
 * terminals.  The IBM keyboard is unusual in that keystrokes yield key numbers
 * rather than ASCII codes, and furthermore, an interrupt is generated when a
 * key is depressed and again when it is released.  The IBM display is memory
 * mapped, so outputting characters such as line feed, backspace and bell are
 * tricky.
 *
 * The valid messages and their parameters are:
 *
 *   TTY_CHAR_INT: a character has been typed on a terminal (input interrupt)
 *   TTY_O_DONE:   a character has been output (output completed interrupt)
 *   TTY_READ:     a process wants to read from a terminal
 *   TTY_WRITE:    a process wants to write on a terminal
 *   TTY_IOCTL:    a process wants to change a terminal's parameters
 *   TTY_SETPGRP:  indicate a change in a control terminal
 *   CANCEL:       terminate a previous incomplete system call immediately
 *
 *    m_type      TTY_LINE   PROC_NR    COUNT   TTY_SPEK  TTY_FLAGS  ADDRESS
 * ---------------------------------------------------------------------------
 * | TTY_CHAR_INT|         |         |         |         |         |array ptr|
 * |-------------+---------+---------+---------+---------+---------+---------|
 * | TTY_O_DONE  |minor dev|         |         |         |         |         |
 * |-------------+---------+---------+---------+---------+---------+---------|
 * | TTY_READ    |minor dev| proc nr |  count  |         |         | buf ptr |
 * |-------------+---------+---------+---------+---------+---------+---------|
 * | TTY_WRITE   |minor dev| proc nr |  count  |         |         | buf ptr |
 * |-------------+---------+---------+---------+---------+---------+---------|
 * | TTY_IOCTL   |minor dev| proc nr |func code|erase etc|  flags  |         |
 * |-------------+---------+---------+---------+---------+---------+---------|
 * | TTY_SETPGRP |minor dev| proc nr |         |         |         |         |
 * |-------------+---------+---------+---------+---------+---------+---------
 * | CANCEL      |minor dev| proc nr |         |         |         |         |
 * ---------------------------------------------------------------------------
 */

#include "../h/const.h"
#include "../h/type.h"
#include "../h/callnr.h"
#include "../h/com.h"
#include "../h/error.h"
#include "../h/sgtty.h"
#include "../h/signal.h"
#include "const.h"
#include "type.h"
#include "glo.h"
#include "proc.h"
/*------------------------------------------------------------------------*/
/* DEBUG*/
#define LOGSIZE 32
int log[LOGSIZE], logpt = 0;
int ct_rs232, ct_rd, ct_wt, ct_wb, ct_ser, ct_o_done, ct_out_char;
int ct_ser1, ct_ser2;

#define Count(x) x++; if (rs_struct[0].rs_base > 0x3F8){ printf("Count %d hit\n",x); prlog(); while(1);}

/* DEBUG*/
/*------------------------------------------------------------------------*/


#define NR_CONS            1	/* how many consoles can system handle */
#define	NR_RS_LINES	   1	/* how many rs232 terminals can system handle*/
#define TTY_IN_BYTES     200	/* input queue size */
#define TTY_RAM_WORDS    320	/* ram buffer size */
#define TTY_BUF_SIZE     256	/* unit for copying to/from queues */
#define TAB_SIZE           8	/* distance between tabs */
#define TAB_MASK          07	/* mask for tty_column when tabbing */
#define MAX_OVERRUN      100	/* size of overrun input buffer */
#define MAX_ESC_PARMS      2	/* number of escape sequence parameters allowed */

#define ERASE_CHAR      '\b'	/* default erase character */
#define KILL_CHAR        '@'	/* default kill character */
#define INTR_CHAR (char)0177	/* default interrupt character */
#define QUIT_CHAR (char) 034	/* default quit character */
#define XOFF_CHAR (char) 023	/* default x-off character (CTRL-S) */
#define XON_CHAR  (char) 021	/* default x-on character (CTRL-Q) */
#define EOT_CHAR  (char) 004	/* CTRL-D */
#define MARKER    (char) 000	/* non-escaped CTRL-D stored as MARKER */
#define AT_SIGN         0220	/* code to yield for CTRL-@ */
#define SCODE1            71	/* scan code for Home on numeric pad */
#define SCODE2            81	/* scan code for PgDn on numeric pad */
#define DEL_CODE   (char) 83	/* DEL for use in CTRL-ALT-DEL reboot */
#define ESC       (char) 033	/* escape */
#define BRACKET          '['	/* Part of the ESC [ letter escape seq */

#define F1                59	/* scan code for function key F1 */
#define F2                60	/* scan code for function key F2 */
#define F3                61	/* scan code for function key F3 */
#define F4                62	/* scan code for function key F4 */
#define F5                63	/* scan code for function key F5 */
#define F9                67	/* scan code for function key F9 */
#define F10               68	/* scan code for function key F10 */
#define TOP_ROW           14	/* codes below this are shifted if CTRL */

PRIVATE struct tty_struct {
  /* Input queue.  Typed characters are stored here until read by a program. */
  char tty_inqueue[TTY_IN_BYTES];    /* array used to store the characters */
  char *tty_inhead;		/* pointer to place where next char goes */
  char *tty_intail;		/* pointer to next char to be given to prog */
  int tty_incount;		/* # chars in tty_inqueue */
  int tty_lfct;			/* # line feeds in tty_inqueue */

  /* Output section. */
  int tty_ramqueue[TTY_RAM_WORDS];	/* buffer for video RAM */
  int tty_rwords;		/* number of WORDS (not bytes) in outqueue */
  int tty_org;			/* location in RAM where 6845 base points */
  int tty_vid;			/* current position of cursor in video RAM */
  char tty_esc_state;		/* 0=normal, 1=ESC, 2=ESC[ */
  char tty_esc_intro;		/* Distinguishing character following ESC */
  int tty_esc_parmv[MAX_ESC_PARMS];	/* list of escape parameters */
  int *tty_esc_parmp;		/* pointer to current escape parameter */
  int tty_attribute;		/* current attribute byte << 8 */
  int (*tty_devstart)();	/* routine to start actual device output */

  /* Terminal parameters and status. */
  int tty_mode;			/* terminal mode set by IOCTL */
  int tty_speed;		/* terminal speed set by IOCTL */
  int tty_column;		/* current column number (0-origin) */
  int tty_row;			/* current row (0 at top of screen) */
  char tty_busy;		/* 1 when output in progress, else 0 */
  char tty_escaped;		/* 1 when '\' just seen, else 0 */
  char tty_inhibited;		/* 1 when CTRL-S just seen (stops output) */
  char tty_makebreak;		/* 1 for terminals that interrupt twice/key */
  char tty_waiting;		/* 1 when output process waiting for reply */

  /* User settable characters: erase, kill, interrupt, quit, x-on; x-off. */
  char tty_erase;		/* char used to erase 1 char (init ^H) */
  char tty_kill;		/* char used to erase a line (init @) */
  char tty_intr;		/* char used to send SIGINT  (init DEL) */
  char tty_quit;		/* char used for core dump   (init CTRL-\) */
  char tty_xon;			/* char used to start output (init CTRL-Q)*/
  char tty_xoff;		/* char used to stop output  (init CTRL-S) */
  char tty_eof;			/* char used to stop output  (init CTRL-D) */

  /* Information about incomplete I/O requests is stored here. */
  char tty_incaller;		/* process that made the call (usually FS) */
  char tty_inproc;		/* process that wants to read from tty */
  char *tty_in_vir;		/* virtual address where data is to go */
  int tty_inleft;		/* how many chars are still needed */
  char tty_otcaller;		/* process that made the call (usually FS) */
  char tty_outproc;		/* process that wants to write to tty */
  char *tty_out_vir;		/* virtual address where data comes from */
  phys_bytes tty_phys;		/* physical address where data comes from */
  int tty_outleft;		/* # chars yet to be copied to tty_outqueue */
  int tty_cum;			/* # chars copied to tty_outqueue so far */
  int tty_pgrp;			/* slot number of controlling process */

  /* Miscellaneous. */
  int tty_ioport;		/* I/O port number for this terminal */
} tty_struct[NR_CONS+NR_RS_LINES];

/* Values for the fields. */
#define NOT_ESCAPED        0	/* previous character on this line not '\' */
#define ESCAPED            1	/* previous character on this line was '\' */
#define RUNNING            0	/* no CRTL-S has been typed to stop the tty */
#define STOPPED            1	/* CTRL-S has been typed to stop the tty */
#define INACTIVE           0	/* the tty is not printing */
#define BUSY               1	/* the tty is printing */
#define ONE_INT            0	/* regular terminals interrupt once per char */
#define TWO_INTS           1	/* IBM console interrupts two times per char */
#define NOT_WAITING        0	/* no output process is hanging */
#define WAITING            1	/* an output process is waiting for a reply */

PRIVATE char tty_driver_buf[2*MAX_OVERRUN+2]; /* driver collects chars here */
PRIVATE char tty_copy_buf[2*MAX_OVERRUN];  /* copy buf used to avoid races */
PRIVATE char tty_buf[TTY_BUF_SIZE];	/* scratch buffer to/from user space */
PRIVATE int shift1, shift2, capslock, numlock;	/* keep track of shift keys */
PRIVATE int control, alt;	/* keep track of key statii */
PRIVATE caps_off = 1;		/* 1 = normal position, 0 = depressed */
PRIVATE num_off = 1;		/* 1 = normal position, 0 = depressed */
PRIVATE softscroll = 0;		/* 1 = software scrolling, 0 = hardware */
PUBLIC int color;		/* 1 if console is color, 0 if it is mono */
PUBLIC scan_code;		/* scan code for '=' saved by bootstrap */


/* Scan codes to ASCII for unshifted keys */
PRIVATE char unsh[] = {
 0,033,'1','2','3','4','5','6',        '7','8','9','0','-','=','\b','\t',
 'q','w','e','r','t','y','u','i',      'o','p','[',']',015,0202,'a','s',
 'd','f','g','h','j','k','l',';',      047,0140,0200,0134,'z','x','c','v',
 'b','n','m',',','.','/',0201,'*',     0203,' ',0204,0241,0242,0243,0244,0245,
 0246,0247,0250,0251,0252,0205,0210,0267,  0270,0271,0211,0264,0265,0266,0214
,0261,0262,0263,'0',0177
};

/* Scan codes to ASCII for shifted keys */
PRIVATE char sh[] = {
 0,033,'!','@','#','$','%','^',        '&','*','(',')','_','+','\b','\t',
 'Q','W','E','R','T','Y','U','I',      'O','P','{','}',015,0202,'A','S',
 'D','F','G','H','J','K','L',':',      042,'~',0200,'|','Z','X','C','V',
 'B','N','M','<','>','?',0201,'*',    0203,' ',0204,0221,0222,0223,0224,0225,
 0226,0227,0230,0231,0232,0204,0213,'7',  '8','9',0211,'4','5','6',0214,'1',
 '2','3','0','.'
};


/* Scan codes to ASCII for Olivetti M24 for unshifted keys. */
PRIVATE char unm24[] = {
 0,033,'1','2','3','4','5','6',        '7','8','9','0','-','^','\b','\t',
 'q','w','e','r','t','y','u','i',      'o','p','@','[','\r',0202,'a','s',
 'd','f','g','h','j','k','l',';',      ':',']',0200,'\\','z','x','c','v',
 'b','n','m',',','.','/',0201,'*',     0203,' ',0204,0241,0242,0243,0244,0245,
 0246,0247,0250,0251,0252,023,0210,0267,0270,0271,0211,0264,0265,0266,0214,0261,
0262,0263,'0','.',' ',014,0212,'\r',   0264,0262,0266,0270,032,0213,' ','/',
 0253,0254,0255,0256,0257,0215,0216,0217
};

/* Scan codes to ASCII for Olivetti M24 for shifted keys. */
PRIVATE char m24[] = {
 0,033,'!','"','#','$','%','&',        047,'(',')','_','=','~','\b','\t',
 'Q','W','E','R' ,'T','Y','U','I',     'O','P',0140,'{','\r',0202,'A','S',
 'D','F','G','H','J','K','L','+',      '*','}',0200,'|','Z','X','C','V',
 'B','N','M','<','>','?',0201,'*',     0203,' ',0204,0221,0222,0223,0224,0225,
 0226,0227,0230,0231,0232,0270,023,'7', '8','9',0211,'4','5','6',0214,'1',
 '2','3',0207,0177,0271,014,0272,'\r', '\b','\n','\f',036,032,0273,0274,'/',
 0233,0234,0235,0236,0237,0275,0276,0277
};

char scode_map[] = {'H', 'A', 'V', 'S', 'D', 'G', 'C', 'T', 'Y', 'B', 'U'};

/*===========================================================================*
 *				tty_task				     *
 *===========================================================================*/
PUBLIC tty_task()
{
/* Main routine of the terminal task. */

  message tty_mess;		/* buffer for all incoming messages */
  register struct tty_struct *tp;

  tty_init();			/* initialize */
  init_rs232();

  while (TRUE) {
	receive(ANY, &tty_mess);
	tp = &tty_struct[tty_mess.TTY_LINE];
	switch(tty_mess.m_type) {
	    case TTY_CHAR_INT:	do_charint(&tty_mess);		break;
	    case TTY_READ:	do_read(tp, &tty_mess);		break;
	    case TTY_WRITE:	do_write(tp, &tty_mess);	break;
	    case TTY_IOCTL:	do_ioctl(tp, &tty_mess);	break;
	    case TTY_SETPGRP:   do_setpgrp(tp, &tty_mess);	break;
	    case CANCEL   :	do_cancel(tp, &tty_mess);	break;
	    case TTY_O_DONE:	do_tty_o_done(tp, &tty_mess);	break;
	    default:		tty_reply(TASK_REPLY, tty_mess.m_source, 
					tty_mess.PROC_NR, EINVAL, 0L, 0L);
	}
  }
}


/*===========================================================================*
 *				do_charint				     *
 *===========================================================================*/
PRIVATE do_charint(m_ptr)
message *m_ptr;			/* message containing pointer to char(s) */
{
/* A character has been typed.  If a character is typed and the tty task is
 * not able to service it immediately, the character is accumulated within
 * the tty driver.  Thus multiple chars may be accumulated.  A single message
 * to the tty task may have to process several characters.
 */

  int m, n, count, replyee, caller;
  char *ptr, *copy_ptr, ch;
  struct tty_struct *tp;

  lock();			/* prevent races by disabling interrupts */
  ptr = m_ptr->ADDRESS;		/* pointer to accumulated char array */
  copy_ptr = tty_copy_buf;	/* ptr to shadow array where chars copied */
  n = *ptr;			/* how many chars have been accumulated */
  count = n;			/* save the character count */
  n = n + n;			/* each char occupies 2 bytes */
  ptr += 2;			/* skip count field at start of array */
  while (n-- > 0)
	*copy_ptr++ = *ptr++;	/* copy the array to safety */
  ptr = m_ptr->ADDRESS;
  *ptr = 0;			/* accumulation count set to 0 */
  unlock();			/* re-enable interrupts */

  /* Loop on the accumulated characters, processing each in turn. */
  copy_ptr = tty_copy_buf;
  while (count-- > 0) {
	ch = *copy_ptr++;	/* get the character typed */
	n = *copy_ptr++;	/* get the line number it came in on */
	in_char(n, ch);		/* queue the char and echo it */

	/* See if a previously blocked reader can now be satisfied. */
	tp = &tty_struct[n];	/* pointer to struct for this character */
	if (tp->tty_inleft > 0 ) {	/* does anybody want input? */
		m = tp->tty_mode & (CBREAK | RAW);
		if (tp->tty_lfct > 0 || (m != 0 && tp->tty_incount > 0)) {
			m = rd_chars(tp);

			/* Tell hanging reader that chars have arrived. */
			replyee = (int) tp->tty_incaller;
			caller = (int) tp->tty_inproc;
			tty_reply(REVIVE, replyee, caller, m, 0L, 0L);
		}
	}
  }
}


/*===========================================================================*
 *				in_char					     *
 *===========================================================================*/
PRIVATE in_char(line, ch)
int line;			/* line number on which char arrived */
char ch;			/* scan code for character that arrived */
{
/* A character has just been typed in.  Process, save, and echo it. */

  register struct tty_struct *tp;
  int mode, sig, scode;
  char make_break();

  scode = ch;			/* save the scan code */
  tp = &tty_struct[line];	/* set 'tp' to point to proper struct */

  /* Function keys are temporarily being used for debug dumps. */
  if (line == 0 && ch >= F1 && ch <= F10) {	/* Check for function keys */
	func_key(ch);		/* process function key */
	return;
  }
  if (tp->tty_incount >= TTY_IN_BYTES) return;	/* no room, discard char */
  mode = tp->tty_mode & (RAW | CBREAK);
  if (tp->tty_makebreak == TWO_INTS)
	ch = make_break(ch);	/* console give 2 ints/ch */
  else
	if (mode != RAW) ch &= 0177;	/* 7-bit chars except in raw mode */
  if (ch == 0) return;

  /* Processing for COOKED and CBREAK mode contains special checks. */
  if (mode == COOKED || mode == CBREAK) {
	/* Handle erase, kill and escape processing. */
	if (mode == COOKED) {
		/* First erase processing (rub out of last character). */
		if (ch == tp->tty_erase && tp->tty_escaped == NOT_ESCAPED) {
			if (chuck(tp) != -1) {	/* remove last char entered */
				echo(tp, '\b');	/* remove it from the screen */
				echo(tp, ' ');
				echo(tp, '\b');
			}
			return;
		}

		/* Now do kill processing (remove current line). */
		if (ch == tp->tty_kill && tp->tty_escaped == NOT_ESCAPED) {
			while( chuck(tp) == OK) /* keep looping */ ;
			echo(tp, tp->tty_kill);
			echo (tp, '\n');
			return;
		}

		/* Handle EOT and the escape symbol (backslash). */
		if (tp->tty_escaped == NOT_ESCAPED) {
			/* Normal case: previous char was not backslash. */
			if (ch == '\\') {
				/* An escaped symbol has just been typed. */
				tp->tty_escaped = ESCAPED;
				echo(tp, ch);
				return;	/* do not store the '\' */
			}
			/* CTRL-D means end-of-file, unless it is escaped. It
			 * is stored in the text as MARKER, and counts as a
			 * line feed in terms of knowing whether a full line
			 * has been typed already.
			 */
			if (ch == tp->tty_eof) ch = MARKER;
		} else {
			/* Previous character was backslash. */
			tp->tty_escaped = NOT_ESCAPED;	/* turn escaping off */
			if (ch != tp->tty_erase && ch != tp->tty_kill &&
						   ch != tp->tty_eof) {
				/* Store the escape previously skipped over */
				*tp->tty_inhead++ = '\\';
				tp->tty_incount++;
				if (tp->tty_inhead ==
						&tp->tty_inqueue[TTY_IN_BYTES])
					tp->tty_inhead = tp->tty_inqueue;
			}
		}
	}
	/* Both COOKED and CBREAK modes come here; first map CR to LF. */
	if (ch == '\r' && (tp->tty_mode & CRMOD)) ch = '\n';

	/* Check for interrupt and quit characters. */
	if (ch == tp->tty_intr || ch == tp->tty_quit) {
		sig = (ch == tp->tty_intr ? SIGINT : SIGQUIT);
		sigchar(tp, sig);
		return;
	}

	/* Check for and process CTRL-S (terminal stop). */
	if (ch == tp->tty_xoff) {
		tp->tty_inhibited = STOPPED;
		return;
	}

	/* Check for and process CTRL-Q (terminal start). */
	if (tp->tty_inhibited == STOPPED) {
		tp->tty_inhibited = RUNNING;
		(*tp->tty_devstart)(tp);	/* resume output */
		return;
	}
  }

  /* All 3 modes come here. */
  if (ch == '\n' || ch == MARKER) tp->tty_lfct++;	/* count line feeds */

  /* The numeric pad generates ASCII escape sequences: ESC [ letter */
  if (line == 0 &&scode >= SCODE1 && scode <= SCODE2 && 
		shift1 == 0 && shift2 == 0 && numlock == 0) {
	/* This key is to generate a three-character escape sequence. */
	*tp->tty_inhead++ = ESC; /* put ESC in the input queue */
	if (tp->tty_inhead == &tp->tty_inqueue[TTY_IN_BYTES])
		tp->tty_inhead = tp->tty_inqueue;      /* handle wraparound */
	tp->tty_incount++;
	echo(tp, 'E');
	*tp->tty_inhead++ = BRACKET; /* put ESC in the input queue */
	if (tp->tty_inhead == &tp->tty_inqueue[TTY_IN_BYTES])
		tp->tty_inhead = tp->tty_inqueue;      /* handle wraparound */
	tp->tty_incount++;
	echo(tp, BRACKET);
	ch = scode_map[scode-SCODE1];	/* generate the letter */
  }

  *tp->tty_inhead++ = ch;	/* save the character in the input queue */
  if (tp->tty_inhead == &tp->tty_inqueue[TTY_IN_BYTES])
	tp->tty_inhead = tp->tty_inqueue;	/* handle wraparound */
  tp->tty_incount++;
  echo(tp, ch);
}


#ifdef i8088
/*===========================================================================*
 *				make_break				     *
 *===========================================================================*/
PRIVATE char make_break(ch)
char ch;			/* scan code of key just struck or released */
{
/* This routine can handle keyboards that interrupt only on key depression,
 * as well as keyboards that interrupt on key depression and key release.
 * For efficiency, the interrupt routine filters out most key releases.
 */

  int c, make, code;


  c = ch & 0177;		/* high-order bit set on key release */
  make = (ch & 0200 ? 0 : 1);	/* 1 when key depressed, 0 when key released */
  if (olivetti == FALSE) {
	/* Standard IBM keyboard. */
	code = (shift1 || shift2 ? sh[c] : unsh[c]);
	if (control && c < TOP_ROW) code = sh[c];	/* CTRL-(top row) */
	if (c > 70 && numlock) 		/* numlock depressed */
		code = (shift1 || shift2 ? unsh[c] : sh[c]);
  } else {
	/* (Olivetti M24 or AT&T 6300) with Olivetti-style keyboard. */
	code = (shift1 || shift2 ? m24[c] : unm24[c]);
	if (control && c < TOP_ROW) code = sh[c];	/* CTRL-(top row) */
	if (c > 70 && numlock) 		/* numlock depressed */
		code = (shift1 || shift2 ? unm24[c] : m24[c]);
  }
  code &= BYTE;
  if (code < 0200 || code >= 0206) {
	/* Ordinary key, i.e. not shift, control, alt, etc. */
	if (capslock)
		if (code >= 'A' && code <= 'Z')
			code += 'a' - 'A';
		else if (code >= 'a' && code <= 'z')
			code -= 'a' - 'A';
	if (alt) code |= 0200;	/* alt key ORs 0200 into code */
	if (control) code &= 037;
	if (code == 0) code = AT_SIGN;	/* @ is 0100, so CTRL-@ = 0 */
	if (make == 0) code = 0;	/* key release */
	return(code);
  }

  /* Table entries 0200 - 0206 denote special actions. */
  switch(code - 0200) {
    case 0:	shift1 = make;		break;	/* shift key on left */
    case 1:	shift2 = make;		break;	/* shift key on right */
    case 2:	control = make;		break;	/* control */
    case 3:	alt = make;		break;	/* alt key */
    case 4:	if (make && caps_off) {
			capslock = 1 - capslock;
			set_leds();
		}
		caps_off = 1 - make;
		break;	/* caps lock */
    case 5:	if (make && num_off) {
			numlock  = 1 - numlock;
			set_leds();
		}
		num_off = 1 - make;
		break;	/* num lock */
  }
  return(0);
}
#endif


/*===========================================================================*
 *				echo					     *
 *===========================================================================*/
PRIVATE echo(tp, c)
register struct tty_struct *tp;	/* terminal on which to echo */
register char c;		/* character to echo */
{
/* Echo a character on the terminal. */

  if ( (tp->tty_mode & ECHO) == 0) return;	/* if no echoing, don't echo */
  if (c != MARKER) {
	if (tp - tty_struct < NR_CONS)
		out_char(tp, c);	/* echo to console */
	else
		rs_out_char(tp, c);	/* echo to RS232 line */
  }
  flush(tp);			/* force character out onto the screen */
}


/*===========================================================================*
 *				chuck					     *
 *===========================================================================*/
PRIVATE int chuck(tp)
register struct tty_struct *tp;	/* from which tty should chars be removed */
{
/* Delete one character from the input queue.  Used for erase and kill. */

  char *prev;

  /* If input queue is empty, don't delete anything. */
  if (tp->tty_incount == 0) return(-1);

  /* Don't delete '\n' or '\r'. */
  prev = (tp->tty_inhead != tp->tty_inqueue ? tp->tty_inhead - 1 :
					     &tp->tty_inqueue[TTY_IN_BYTES-1]);
  if (*prev == '\n' || *prev == '\r') return(-1);
  tp->tty_inhead = prev;
  tp->tty_incount--;
  return(OK);			/* char erasure was possible */
}


/*===========================================================================*
 *				do_read					     *
 *===========================================================================*/
PRIVATE do_read(tp, m_ptr)
register struct tty_struct *tp;	/* pointer to tty struct */
message *m_ptr;			/* pointer to message sent to the task */
{
/* A process wants to read from a terminal. */

  int code, caller;


  if (tp->tty_inleft > 0) {	/* if someone else is hanging, give up */
	tty_reply(TASK_REPLY,m_ptr->m_source,m_ptr->PROC_NR, E_TRY_AGAIN,0L,0L);
	return;
  }

  /* Copy information from the message to the tty struct. */
  tp->tty_incaller = m_ptr->m_source;
  tp->tty_inproc = m_ptr->PROC_NR;
  tp->tty_in_vir = m_ptr->ADDRESS;
  tp->tty_inleft = m_ptr->COUNT;

  /* Try to get chars.  This call either gets enough, or gets nothing. */
  code = rd_chars(tp);

  caller = (int) tp->tty_inproc;
  tty_reply(TASK_REPLY, m_ptr->m_source, caller, code, 0L, 0L);
}


/*===========================================================================*
 *				rd_chars				     *
 *===========================================================================*/
PRIVATE int rd_chars(tp)
register struct tty_struct *tp;	/* pointer to terminal to read from */
{
/* A process wants to read from a terminal.  First check if enough data is
 * available. If so, pass it to the user.  If not, send FS a message telling
 * it to suspend the user.  When enough data arrives later, the tty driver
 * copies it to the user space directly and notifies FS with a message.
 */

  int cooked, ct, user_ct, buf_ct, cum, enough, eot_seen;
  vir_bytes in_vir, left;
  phys_bytes user_phys, tty_phys;
  char ch, *tty_ptr;
  struct proc *rp;
  extern phys_bytes umap();

  cooked = ( (tp->tty_mode & (RAW | CBREAK)) ? 0 : 1);	/* 1 iff COOKED mode */
  if (tp->tty_incount == 0 || (cooked && tp->tty_lfct == 0)) return(SUSPEND);
  rp = proc_addr(tp->tty_inproc);
  in_vir = (vir_bytes) tp-> tty_in_vir;
  left = (vir_bytes) tp->tty_inleft;
  if ( (user_phys = umap(rp, D, in_vir, left)) == 0) return(E_BAD_ADDR);
  tty_phys = umap(proc_addr(TTY), D, (vir_bytes) tty_buf, TTY_BUF_SIZE);
  cum = 0;
  enough = 0;
  eot_seen = 0;

  /* The outer loop iterates on buffers, one buffer load per iteration. */
  while (tp->tty_inleft > 0) {
	buf_ct = MIN(tp->tty_inleft, tp->tty_incount);
	buf_ct = MIN(buf_ct, TTY_BUF_SIZE);
	ct = 0;
	tty_ptr = tty_buf;

	/* The inner loop fills one buffer. */
	while(buf_ct-- > 0) {
		ch = *tp->tty_intail++;
		if (tp->tty_intail == &tp->tty_inqueue[TTY_IN_BYTES])
			tp->tty_intail = tp->tty_inqueue;
		*tty_ptr++ = ch;
		ct++;
		if (ch == '\n' || ch == MARKER) {
			tp->tty_lfct--;
			if (cooked && ch == MARKER) eot_seen++;
			enough++;	/* exit loop */
			if (cooked) break;	/* only provide 1 line */
		}
	}

	/* Copy one buffer to user space.  Be careful about CTRL-D.  In cooked
	 * mode it is not transmitted to user programs, and is not counted as
	 * a character as far as the count goes, but it does occupy space in 
	 * the driver's tables and must be counted there.
	 */
	user_ct = (eot_seen ? ct - 1 : ct);	/* bytes to copy to user */
	phys_copy(tty_phys, user_phys, (phys_bytes) user_ct);
	user_phys += user_ct;
	cum += user_ct;
	tp->tty_inleft -= ct;
	tp->tty_incount -= ct;
	if (tp->tty_incount == 0 || enough) break;
  }

  tp->tty_inleft = 0;
  return(cum);
}


/*===========================================================================*
 *				finish					     *
 *===========================================================================*/
PRIVATE finish(tp, code)
register struct tty_struct *tp;	/* pointer to tty struct */
int code;			/* reply code */
{
/* A command has terminated (possibly due to DEL).  Tell caller. */

  int replyee, caller;

  tp->tty_rwords = 0;
  tp->tty_outleft = 0;
  if (tp->tty_waiting == NOT_WAITING) return;
  replyee = (int) tp->tty_otcaller;
  caller = (int) tp->tty_outproc;
  tty_reply(TASK_REPLY, replyee, caller, code, 0L, 0L);
  tp->tty_waiting = NOT_WAITING;
}


/*===========================================================================*
 *				do_write				     *
 *===========================================================================*/
PRIVATE do_write(tp, m_ptr)
register struct tty_struct *tp;	/* pointer to tty struct */
message *m_ptr;			/* pointer to message sent to the task */
{
/* A process wants to write on a terminal. */

  vir_bytes out_vir, out_left;
  struct proc *rp;
  extern phys_bytes umap();
  int caller,replyee;

  /* Copy message parameters to the tty structure. */
  tp->tty_otcaller = m_ptr->m_source;
  tp->tty_outproc = m_ptr->PROC_NR;
  tp->tty_out_vir = m_ptr->ADDRESS;
  tp->tty_outleft = m_ptr->COUNT;
  tp->tty_waiting = WAITING;
  tp->tty_cum = 0;

  /* Compute the physical address where the data is in user space. */
  rp = proc_addr(tp->tty_outproc);
  out_vir = (vir_bytes) tp->tty_out_vir;
  out_left = (vir_bytes) tp->tty_outleft;
  if ( (tp->tty_phys = umap(rp, D, out_vir, out_left)) == 0) {
	/* Buffer address provided by user is outside its address space. */
	tp->tty_cum = E_BAD_ADDR;
	tp->tty_outleft = 0;
  }

  /* Copy characters from the user process to the terminal. */
  (*tp->tty_devstart)(tp);	/* copy data to queue and start I/O */

  /* If output is for a bitmapped terminal as the IBM-PC console, the output-
   * routine will return at once so there is no need to suspend the caller,
   * on ascii terminals however, the call is suspended and later revived by
   * tty_o_done.
   */
  if (m_ptr->TTY_LINE != 0) {
	caller = (int) tp->tty_outproc;
	replyee = (int) tp->tty_otcaller;
	tty_reply(TASK_REPLY, replyee, caller, SUSPEND, 0L, 0L);
  }
}


/*===========================================================================*
 *				do_ioctl				     *
 *===========================================================================*/
PRIVATE do_ioctl(tp, m_ptr)
register struct tty_struct *tp;	/* pointer to tty_struct */
message *m_ptr;			/* pointer to message sent to task */
{
/* Perform IOCTL on this terminal. */

  long flags, erki, erase, kill, intr, quit, xon, xoff, eof;
  int r;

  r = OK;
  flags = 0;
  erki = 0;
  switch(m_ptr->TTY_REQUEST) {
     case TIOCSETP:
	/* Set erase, kill, and flags. */
	tp->tty_erase = (char) ((m_ptr->TTY_SPEK >> 8) & BYTE);	/* erase  */
	tp->tty_kill  = (char) ((m_ptr->TTY_SPEK >> 0) & BYTE);	/* kill  */
	tp->tty_mode  = (int) m_ptr->TTY_FLAGS;	/* mode word */
	if (m_ptr->TTY_SPEED != 0) tp->tty_speed = m_ptr->TTY_SPEED & BYTE;
	if (tp-tty_struct >= NR_CONS)
		set_uart(tp - tty_struct, tp->tty_mode, tp->tty_speed);
	break;

     case TIOCSETC:
	/* Set intr, quit, xon, xoff, eof (brk not used). */
	tp->tty_intr = (char) ((m_ptr->TTY_SPEK >> 24) & BYTE);	/* interrupt */
	tp->tty_quit = (char) ((m_ptr->TTY_SPEK >> 16) & BYTE);	/* quit */
	tp->tty_xon  = (char) ((m_ptr->TTY_SPEK >>  8) & BYTE);	/* CTRL-S */
	tp->tty_xoff = (char) ((m_ptr->TTY_SPEK >>  0) & BYTE);	/* CTRL-Q */
	tp->tty_eof  = (char) ((m_ptr->TTY_FLAGS >> 8) & BYTE);	/* CTRL-D */
	break;

     case TIOCGETP:
	/* Get erase, kill, and flags. */
	erase = ((long) tp->tty_erase) & BYTE;
	kill  = ((long) tp->tty_kill) & BYTE;
	erki  = (erase << 8) | kill;
	flags = (long) tp->tty_mode;
	break;

     case TIOCGETC:
	/* Get intr, quit, xon, xoff, eof. */
	intr  = ((long) tp->tty_intr) & BYTE;
	quit  = ((long) tp->tty_quit) & BYTE;
	xon   = ((long) tp->tty_xon)  & BYTE;
	xoff  = ((long) tp->tty_xoff) & BYTE;
	eof   = ((long) tp->tty_eof)  & BYTE;
	erki  = (intr << 24) | (quit << 16) | (xon << 8) | (xoff << 0);
	flags = (eof <<8);
	break;

     default:
	r = EINVAL;
  }

  /* Send the reply. */
  tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r, flags, erki);
}


/*===========================================================================*
 *				do_setpgrp				     *
 *===========================================================================*/
PRIVATE do_setpgrp(tp, m_ptr)
register struct tty_struct *tp; /* pointer to tty struct */
message *m_ptr;			/* pointer to message sent to task */
{
/* A control process group has changed */

   tp->tty_pgrp = m_ptr->TTY_PGRP;
   tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, OK, 0L, 0L);
}


/*===========================================================================*
 *				do_cancel				     *
 *===========================================================================*/
PRIVATE do_cancel(tp, m_ptr)
register struct tty_struct *tp;	/* pointer to tty_struct */
message *m_ptr;			/* pointer to message sent to task */
{
/* A signal has been sent to a process that is hanging trying to read or write.
 * The pending read or write must be finished off immediately.
 */

  /* First check to see if the process is indeed hanging.  If it is not, don't
   * reply (to avoid race conditions).
   */
  if (tp->tty_inleft == 0 && tp->tty_outleft == 0) return;

  /* Kill off input and output. */
  tp->tty_inhead = tp->tty_inqueue;	/* discard all input */
  tp->tty_intail = tp->tty_inqueue;
  tp->tty_incount = 0;
  tp->tty_lfct = 0;
  tp->tty_inleft = 0;
  tp->tty_outleft = 0;
  tp->tty_waiting = NOT_WAITING;	/* don't send reply */
  tp->tty_inhibited = RUNNING;
  tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINTR, 0L, 0L);
}


/*===========================================================================*
 *				tty_reply				     *
 *===========================================================================*/
PRIVATE tty_reply(code, replyee, proc_nr, status, extra, other)
int code;			/* TASK_REPLY or REVIVE */
int replyee;			/* destination address for the reply */
int proc_nr;			/* to whom should the reply go? */
int status;			/* reply code */
long extra;			/* extra value */
long other;			/* used for IOCTL replies */
{
/* Send a reply to a process that wanted to read or write data. */

  message tty_mess;

  tty_mess.m_type = code;
  tty_mess.REP_PROC_NR = proc_nr;
  tty_mess.REP_STATUS = status;
  tty_mess.TTY_FLAGS = extra;	/* used by IOCTL for flags (mode) */
  tty_mess.TTY_SPEK = other;	/* used by IOCTL for erase and kill chars */
  send(replyee, &tty_mess);
}


/*===========================================================================*
 *				sigchar					     *
 *===========================================================================*/
PRIVATE sigchar(tp, sig)
register struct tty_struct *tp;	/* pointer to tty_struct */
int sig;			/* SIGINT, SIGQUIT, or SIGKILL */
{
/* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard */

  tp->tty_inhibited = RUNNING;	/* do implied CRTL-Q */
  finish(tp, EINTR);		/* send reply */
  tp->tty_inhead = tp->tty_inqueue;	/* discard input */
  tp->tty_intail = tp->tty_inqueue;
  tp->tty_incount = 0;
  tp->tty_lfct = 0;
  if (tp->tty_pgrp)
	cause_sig(tp->tty_pgrp, sig);
}




/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/

#ifdef i8088
/* Now begins the code and data for the device-dependent tty drivers. */

/* Definitions used by the console driver. */
#define COLOR_BASE    0xB800	/* video ram paragraph for color display */
#define MONO_BASE     0xB000	/* video ram address for mono display */
#define C_VID_MASK    0x3FFF	/* mask for 16K video RAM */
#define M_VID_MASK    0x0FFF	/* mask for  4K video RAM */
#define C_RETRACE     0x0300	/* how many characters to display at once */
#define M_RETRACE     0x7000	/* how many characters to display at once */
#define WORD_MASK     0xFFFF	/* mask for 16 bits */
#define OFF_MASK      0x000F	/* mask for  4 bits */
#define BEEP_FREQ     0x0533	/* value to put into timer to set beep freq */
#define B_TIME        0x2000	/* how long to sound the CTRL-G beep tone */
#define BLANK         0x0700	/* determines  cursor color on blank screen */
#define LINE_WIDTH        80	/* # characters on a line */
#define SCR_LINES         25	/* # lines on the screen */
#define CTRL_S            31	/* scan code for letter S (for CRTL-S) */
#define MONOCHROME         1	/* value for tty_ioport tells color vs. mono */
#define CONSOLE            0	/* line number for console */
#define GO_FORWARD         0	/* scroll forward */
#define GO_BACKWARD        1	/* scroll backward */
#define TIMER2          0x42	/* I/O port for timer channel 2 */
#define TIMER3          0x43	/* I/O port for timer channel 3 */
#define KEYBD           0x60	/* I/O port for keyboard data */
#define PORT_B          0x61	/* I/O port for 8255 port B */
#define KBIT            0x80	/* bit used to ack characters to keyboard */
#define LED_CODE        0xED	/* command to keyboard to set LEDs */
#define LED_DELAY       0x80	/* device dependent delay needed */

/* Constants relating to the video RAM and 6845. */
#define M_6845         0x3B0	/* port for 6845 mono */
#define C_6845         0x3D0	/* port for 6845 color */
#define EGA            0x3C0	/* port for EGA card */
#define INDEX              4	/* 6845's index register */
#define DATA               5	/* 6845's data register */
#define CUR_SIZE          10	/* 6845's cursor size register */
#define VID_ORG           12	/* 6845's origin register */
#define CURSOR            14	/* 6845's cursor register */

/* Definitions used for determining if the keyboard is IBM or Olivetti type. */
#define KB_STATUS	0x64	/* Olivetti keyboard status port */
#define BYTE_AVAIL	0x01	/* there is something in KEYBD port */
#define KB_BUSY	        0x02	/* KEYBD port ready to accept a command */
#define DELUXE		0x01	/* this bit is set up iff deluxe keyboard */
#define GET_TYPE	   5	/* command to get keyboard type */
#define OLIVETTI_EQUAL    12	/* the '=' key is 12 on olivetti, 13 on IBM */

/* Global variables used by the console driver. */
PUBLIC  message keybd_mess;	/* message used for console input chars */
PRIVATE vid_retrace;		/* how many characters to display per burst */
PRIVATE unsigned vid_base;	/* base of video ram (0xB000 or 0xB800) */
PUBLIC int vid_mask;		/* 037777 for color (16K) or 07777 for mono */
PRIVATE int vid_port;		/* I/O port for accessing 6845 */


/*===========================================================================*
 *				keyboard				     *
 *===========================================================================*/
PUBLIC keyboard()
{
/* A keyboard interrupt has occurred.  Process it. */

  int val, code, k, raw_bit;
  char stopc;

  /* Fetch the character from the keyboard hardware and acknowledge it. */
  port_in(KEYBD, &code);	/* get the scan code for the key struck */
  port_in(PORT_B, &val);	/* strobe the keyboard to ack the char */
  port_out(PORT_B, val | KBIT);	/* strobe the bit high */
  port_out(PORT_B, val);	/* now strobe it low */

  /* The IBM keyboard interrupts twice per key, once when depressed, once when
   * released.  Filter out the latter, ignoring all but the shift-type keys.
   * The shift-type keys, 29, 42, 54, 56, 58, and 69 must be processed normally.
   */
  k = code - 0200;		/* codes > 0200 mean key release */
  if (k > 0) {
	/* A key has been released. */
	if (k != 29 && k != 42 && k != 54 && k != 56 && k != 58 && k != 69) {
		port_out(INT_CTL, ENABLE);	/* re-enable interrupts */
	 	return;		/* don't call tty_task() */
	}
  } else {
	/* Check to see if character is CTRL-S, to stop output. Setting xoff
	 * to anything other than CTRL-S will not be detected here, but will
	 * be detected later, in the driver.  A general routine to detect any
	 * xoff character here would be complicated since we only have the
	 * scan code here, not the ASCII character.
	 */
	raw_bit = tty_struct[CONSOLE].tty_mode & RAW;
	stopc = tty_struct[CONSOLE].tty_xoff;
	if (raw_bit == 0 && control && code == CTRL_S && stopc == XOFF_CHAR) {
		tty_struct[CONSOLE].tty_inhibited = STOPPED;
		port_out(INT_CTL, ENABLE);
		return;
	}
  }

  /* Check for CTRL-ALT-DEL, and if found, reboot the computer. */
  if (control && alt && code == DEL_CODE) reboot();	/* CTRL-ALT-DEL */

  /* Store the character in memory so the task can get at it later. */
  if ( (k = tty_driver_buf[0]) < tty_driver_buf[1]) {
	/* There is room to store this character; do it. */
	k = k + k;			/* each entry contains two bytes */
	tty_driver_buf[k+2] = code;	/* store the scan code */
	tty_driver_buf[k+3] = CONSOLE;	/* tell which line it came from */
	tty_driver_buf[0]++;		/* increment counter */

	/* Build and send the interrupt message. */