[comp.os.minix] Full tty.c

ast@cs.vu.nl (Andy Tanenbaum) (07/14/88)

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin:/usr/ucb
echo Extracting 'tty.c.part1'
sed 's/^X//' > 'tty.c.part1' << '+ END-OF-FILE ''tty.c.part1'
X/* This file contains the terminal driver, both for the IBM console and regular
X * ASCII terminals.  It is split into two sections, a device-independent part
X * and a device-dependent part.  The device-independent part accepts
X * characters to be printed from programs and queues them in a standard way
X * for device-dependent output.  It also accepts input and queues it for
X * programs. This file contains 2 main entry points: tty_task() and keyboard().
X * When a key is struck on a terminal, an interrupt to an assembly language
X * routine is generated.  This routine saves the machine state and registers
X * and calls keyboard(), which enters the character in an internal table, and
X * then sends a message to the terminal task.  The main program of the terminal
X * task is tty_task(). It accepts not only messages about typed input, but
X * also requests to read and write from terminals, etc. 
X *
X * The device-dependent part interfaces with the IBM console and ASCII
X * terminals.  The IBM keyboard is unusual in that keystrokes yield key numbers
X * rather than ASCII codes, and furthermore, an interrupt is generated when a
X * key is depressed and again when it is released.  The IBM display is memory
X * mapped, so outputting characters such as line feed, backspace and bell are
X * tricky.
X *
X * The valid messages and their parameters are:
X *
X *   TTY_CHAR_INT: a character has been typed (character arrived interrupt)
X *   TTY_READ:     a process wants to read from a terminal
X *   TTY_WRITE:    a process wants to write on a terminal
X *   TTY_IOCTL:    a process wants to change a terminal's parameters
X *   TTY_SETPGRP:  indicate a change in a control terminal
X *   CANCEL:       terminate a previous incomplete system call immediately
X *
X *    m_type      TTY_LINE   PROC_NR    COUNT   TTY_SPEK  TTY_FLAGS  ADDRESS
X * ---------------------------------------------------------------------------
X * | TTY_CHAR_INT|         |         |         |         |         |array ptr|
X * |-------------+---------+---------+---------+---------+---------+---------|
X * | TTY_O_DONE  |minor dev|         |         |         |         |array ptr|
X * |-------------+---------+---------+---------+---------+---------+---------|
X * | TTY_READ    |minor dev| proc nr |  count  |         |         | buf ptr |
X * |-------------+---------+---------+---------+---------+---------+---------|
X * | TTY_WRITE   |minor dev| proc nr |  count  |         |         | buf ptr |
X * |-------------+---------+---------+---------+---------+---------+---------|
X * | TTY_IOCTL   |minor dev| proc nr |func code|erase etc|  flags  |         |
X * |-------------+---------+---------+---------+---------+---------+---------|
X * | TTY_SETPGRP |minor dev| proc nr |         |         |         |         |
X * |-------------+---------+---------+---------+---------+---------+---------
X * | CANCEL      |minor dev| proc nr |         |         |         |         |
X * ---------------------------------------------------------------------------
X */
X
X#include "../h/const.h"
X#include "../h/type.h"
X#include "../h/callnr.h"
X#include "../h/com.h"
X#include "../h/error.h"
X#include "../h/sgtty.h"
X#include "../h/signal.h"
X#include "const.h"
X#include "type.h"
X#include "glo.h"
X#include "proc.h"
X
X#define NR_CONS            1	/* how many consoles can system handle */
X#define	NR_RS_LINES	   1	/* how many rs232 terminals can system handle*/
X#define TTY_IN_BYTES     200	/* input queue size */
X#define TTY_RAM_WORDS    320	/* ram buffer size */
X#define TTY_BUF_SIZE     256	/* unit for copying to/from queues */
X#define TAB_SIZE           8	/* distance between tabs */
X#define TAB_MASK          07	/* mask for tty_column when tabbing */
X#define MAX_OVERRUN      100	/* size of overrun input buffer */
X#define MAX_ESC_PARMS      2	/* number of escape sequence params allowed */
X
X#define ERASE_CHAR      '\b'	/* default erase character */
X#define KILL_CHAR        '@'	/* default kill character */
X#define INTR_CHAR (char)0177	/* default interrupt character */
X#define QUIT_CHAR (char) 034	/* default quit character */
X#define XOFF_CHAR (char) 023	/* default x-off character (CTRL-S) */
X#define XON_CHAR  (char) 021	/* default x-on character (CTRL-Q) */
X#define EOT_CHAR  (char) 004	/* CTRL-D */
X#define MARKER    (char) 000	/* non-escaped CTRL-D stored as MARKER */
X#define AT_SIGN         0220	/* code to yield for CTRL-@ */
X#define SCODE1            71	/* scan code for Home on numeric pad */
X#define SCODE2            81	/* scan code for PgDn on numeric pad */
X#define DEL_CODE   (char) 83	/* DEL for use in CTRL-ALT-DEL reboot */
X#define ESC       (char) 033	/* escape */
X#define BRACKET          '['	/* Part of the ESC [ letter escape seq */
X
X#define F1                59	/* scan code for function key F1 */
X#define F2                60	/* scan code for function key F2 */
X#define F3                61	/* scan code for function key F3 */
X#define F4                62	/* scan code for function key F4 */
X#define F5                63	/* scan code for function key F5 */
X#define F9                67	/* scan code for function key F9 */
X#define F10               68	/* scan code for function key F10 */
X#define TOP_ROW           14	/* codes below this are shifted if CTRL */
X
XPRIVATE struct tty_struct {
X  /* Input queue.  Typed characters are stored here until read by a program. */
X  char tty_inqueue[TTY_IN_BYTES];    /* array used to store the characters */
X  char *tty_inhead;		/* pointer to place where next char goes */
X  char *tty_intail;		/* pointer to next char to be given to prog */
X  int tty_incount;		/* # chars in tty_inqueue */
X  int tty_lfct;			/* # line feeds in tty_inqueue */
X
X  /* Output section. */
X  int tty_ramqueue[TTY_RAM_WORDS];	/* buffer for video RAM */
X  int tty_rwords;		/* number of WORDS (not bytes) in outqueue */
X  int tty_org;			/* location in RAM where 6845 base points */
X  int tty_vid;			/* current position of cursor in video RAM */
X  char tty_esc_state;		/* 0=normal, 1=ESC, 2=ESC[ */
X  char tty_esc_intro;		/* Distinguishing character following ESC */
X  int tty_esc_parmv[MAX_ESC_PARMS];	/* list of escape parameters */
X  int *tty_esc_parmp;		/* pointer to current escape parameter */
X  int tty_attribute;		/* current attribute byte << 8 */
X  int (*tty_devstart)();	/* routine to start actual device output */
X
X  /* Terminal parameters and status. */
X  int tty_mode;			/* terminal mode set by IOCTL */
X  int tty_speed;		/* low byte is ispeed; high byte is ospeed */
X  int tty_column;		/* current column number (0-origin) */
X  int tty_row;			/* current row (0 at top of screen) */
X  char tty_busy;		/* 1 when output in progress, else 0 */
X  char tty_escaped;		/* 1 when '\' just seen, else 0 */
X  char tty_inhibited;		/* 1 when CTRL-S just seen (stops output) */
X  char tty_makebreak;		/* 1 for terminals that interrupt twice/key */
X  char tty_waiting;		/* 1 when output process waiting for reply */
X
X  /* User settable characters: erase, kill, interrupt, quit, x-on; x-off. */
X  char tty_erase;		/* char used to erase 1 char (init ^H) */
X  char tty_kill;		/* char used to erase a line (init @) */
X  char tty_intr;		/* char used to send SIGINT  (init DEL) */
X  char tty_quit;		/* char used for core dump   (init CTRL-\) */
X  char tty_xon;			/* char used to start output (init CTRL-Q)*/
X  char tty_xoff;		/* char used to stop output  (init CTRL-S) */
X  char tty_eof;			/* char used to stop output  (init CTRL-D) */
X
X  /* Information about incomplete I/O requests is stored here. */
X  char tty_incaller;		/* process that made the call (usually FS) */
X  char tty_inproc;		/* process that wants to read from tty */
X  char *tty_in_vir;		/* virtual address where data is to go */
X  int tty_inleft;		/* how many chars are still needed */
X  char tty_otcaller;		/* process that made the call (usually FS) */
X  char tty_outproc;		/* process that wants to write to tty */
X  char *tty_out_vir;		/* virtual address where data comes from */
X  phys_bytes tty_phys;		/* physical address where data comes from */
X  int tty_outleft;		/* # chars yet to be copied to tty_outqueue */
X  int tty_cum;			/* # chars copied to tty_outqueue so far */
X  int tty_pgrp;			/* slot number of controlling process */
X
X  /* Miscellaneous. */
X  int tty_ioport;		/* I/O port number for this terminal */
X} tty_struct[NR_CONS+NR_RS_LINES];
X
X/* Values for the fields. */
X#define NOT_ESCAPED        0	/* previous character on this line not '\' */
X#define ESCAPED            1	/* previous character on this line was '\' */
X#define RUNNING            0	/* no CRTL-S has been typed to stop the tty */
X#define STOPPED            1	/* CTRL-S has been typed to stop the tty */
X#define INACTIVE           0	/* the tty is not printing */
X#define BUSY               1	/* the tty is printing */
X#define ONE_INT            0	/* regular terminals interrupt once per char */
X#define TWO_INTS           1	/* IBM console interrupts two times per char */
X#define NOT_WAITING        0	/* no output process is hanging */
X#define WAITING            1	/* an output process is waiting for a reply */
X#define COMPLETED          2	/* output done; send a completion message */
X
XPRIVATE char tty_driver_buf[2*MAX_OVERRUN+2]; /* driver collects chars here */
XPRIVATE char tty_copy_buf[2*MAX_OVERRUN];  /* copy buf used to avoid races */
XPRIVATE char tty_buf[TTY_BUF_SIZE];	/* scratch buffer to/from user space */
XPRIVATE int shift1, shift2, capslock, numlock;	/* keep track of shift keys */
XPRIVATE int control, alt;	/* keep track of key statii */
XPRIVATE int caps_off = 1;	/* 1 = normal position, 0 = depressed */
XPRIVATE int num_off = 1;	/* 1 = normal position, 0 = depressed */
XPRIVATE int softscroll = 0;	/* 1 = software scrolling, 0 = hardware */
XPRIVATE int output_done;	/* number of RS232 output messages to be sent*/
XPUBLIC int color;		/* 1 if console is color, 0 if it is mono */
XPUBLIC int scan_code;		/* scan code for '=' saved by bootstrap */
X
X
X/* Scan codes to ASCII for unshifted keys */
XPRIVATE char unsh[] = {
X 0,033,'1','2','3','4','5','6',        '7','8','9','0','-','=','\b','\t',
X 'q','w','e','r','t','y','u','i',      'o','p','[',']',015,0202,'a','s',
X 'd','f','g','h','j','k','l',';',      047,0140,0200,0134,'z','x','c','v',
X 'b','n','m',',','.','/',0201,'*',     0203,' ',0204,0241,0242,0243,0244,0245,
X 0246,0247,0250,0251,0252,0205,0210,0267,  0270,0271,0211,0264,0265,0266,0214
X,0261,0262,0263,'0',0177
X};
X
X/* Scan codes to ASCII for shifted keys */
XPRIVATE char sh[] = {
X 0,033,'!','@','#','$','%','^',        '&','*','(',')','_','+','\b','\t',
X 'Q','W','E','R','T','Y','U','I',      'O','P','{','}',015,0202,'A','S',
X 'D','F','G','H','J','K','L',':',      042,'~',0200,'|','Z','X','C','V',
X 'B','N','M','<','>','?',0201,'*',    0203,' ',0204,0221,0222,0223,0224,0225,
X 0226,0227,0230,0231,0232,0204,0213,'7',  '8','9',0211,'4','5','6',0214,'1',
X '2','3','0','.'
X};
X
X
X/* Scan codes to ASCII for Olivetti M24 for unshifted keys. */
XPRIVATE char unm24[] = {
X 0,033,'1','2','3','4','5','6',        '7','8','9','0','-','^','\b','\t',
X 'q','w','e','r','t','y','u','i',      'o','p','@','[','\r',0202,'a','s',
X 'd','f','g','h','j','k','l',';',      ':',']',0200,'\\','z','x','c','v',
X 'b','n','m',',','.','/',0201,'*',     0203,' ',0204,0241,0242,0243,0244,0245,
X 0246,0247,0250,0251,0252,023,0210,0267,0270,0271,0211,0264,0265,0266,0214,0261,
X0262,0263,'0','.',' ',014,0212,'\r',   0264,0262,0266,0270,032,0213,' ','/',
X 0253,0254,0255,0256,0257,0215,0216,0217
X};
X
X/* Scan codes to ASCII for Olivetti M24 for shifted keys. */
XPRIVATE char m24[] = {
X 0,033,'!','"','#','$','%','&',        047,'(',')','_','=','~','\b','\t',
X 'Q','W','E','R' ,'T','Y','U','I',     'O','P',0140,'{','\r',0202,'A','S',
X 'D','F','G','H','J','K','L','+',      '*','}',0200,'|','Z','X','C','V',
X 'B','N','M','<','>','?',0201,'*',     0203,' ',0204,0221,0222,0223,0224,0225,
X 0226,0227,0230,0231,0232,0270,023,'7', '8','9',0211,'4','5','6',0214,'1',
X '2','3',0207,0177,0271,014,0272,'\r', '\b','\n','\f',036,032,0273,0274,'/',
X 0233,0234,0235,0236,0237,0275,0276,0277
X};
X
Xchar scode_map[] = {'H', 'A', 'V', 'S', 'D', 'G', 'C', 'T', 'Y', 'B', 'U'};
X
X/*===========================================================================*
X *				tty_task				     *
X *===========================================================================*/
XPUBLIC tty_task()
X{
X/* Main routine of the terminal task. */
X
X  message tty_mess;		/* buffer for all incoming messages */
X  register struct tty_struct *tp;
X
X  tty_init();			/* initialize */
X  init_rs232();
X
X  while (TRUE) {
X	receive(ANY, &tty_mess);
X	tp = &tty_struct[tty_mess.TTY_LINE];
X	switch(tty_mess.m_type) {
X	    case TTY_O_DONE:	/* same action as TTY_CHAR_INT */
X	    case TTY_CHAR_INT:	do_int(&tty_mess);		break;
X	    case TTY_READ:	do_read(tp, &tty_mess);		break;
X	    case TTY_WRITE:	do_write(tp, &tty_mess);	break;
X	    case TTY_IOCTL:	do_ioctl(tp, &tty_mess);	break;
X	    case TTY_SETPGRP:   do_setpgrp(tp, &tty_mess);	break;
X	    case CANCEL:	do_cancel(tp, &tty_mess);	break;
X	    default:		tty_reply(TASK_REPLY, tty_mess.m_source, 
X					tty_mess.PROC_NR, EINVAL, 0L, 0L);
X	}
X  }
X}
X
X
X/*===========================================================================*
X *				do_int					     *
X *===========================================================================*/
XPRIVATE do_int(m_ptr)
Xmessage *m_ptr;
X{
X/* The TTY task can generate two kinds of interrupts:
X *	- a character has been typed on the console or an RS232 line
X *	- an RS232 line has completed a write request (on behalf of a user)
X * If either interrupt happens and the TTY task is idle, the task gets the
X * interrupt message immediately and processes it.  However, if the TTY
X * task is busy, a bit is set in 'busy_map' and the message pointer stored.
X * If multiple messages happen, the bit is only set once.  No input data is
X * lost even if this happens because all the input messages say is that there
X * is some input.  The actual input is in the tty_driver_buf array, so losing
X * a message just means that when the one interrupt-generated message is given
X * to the TTY task, it will find multiple characters in tty_driver_buf.
X *
X * The introduction of RS232 lines has complicated this situation somewhat. Now
X * a message can mean that some RS232 line has finished transmitting all the
X * output given to it.  If a character is typed at the instant an RS232 line
X * finishes, one of the two messages may be overwritten because MINIX only
X * provides single buffering for interrupt messages (in proc.c).To avoid losing
X * information, whenever an RS232 line finishes, the flag tty_waiting is set
X * to COMPLETED and kept that way until its completion is processed and a 
X * message sent to FS saying that output is done.  The number of RS232 lines in
X * COMPLETED state is kept in output_done, which is checked on each interrupt,
X * so that a lost TTY_O_DONE line completion interrupt will be quickly
X * recovered.
X *
X * In short, when this procedure is called, it can check for RS232 line done
X * by inspecting output_done and it can check for characters in the input
X * buffer by inspecting tty_driver_buf[0].  Thus losing a message to the TTY
X * task is not serious because the underlying conditions are explicitly checked
X * for on each interrupt.
X */
X
X  /* First check to see if any RS232 lines have completed. */
X  if (output_done > 0) {
X	/* If a message is sent to FS for RS232 done, don't process any input
X	 * characters now for fear of sending a second message to FS, which 
X	 * would be lost.
X	 */
X	if (tty_o_done()) {
X		return;
X	}
X  }
X  charint(m_ptr);			/* check for input characters */
X}
X
X
X/*===========================================================================*
X *				charint					     *
X *===========================================================================*/
XPRIVATE charint(m_ptr)
Xmessage *m_ptr;			/* message containing pointer to char(s) */
X{
X/* A character has been typed.  If a character is typed and the tty task is
X * not able to service it immediately, the character is accumulated within
X * the tty driver.  Thus multiple chars may be accumulated.  A single message
X * to the tty task may have to process several characters.
X */
X
X  int m, n, count, replyee, caller, old_state;
X  char *ptr, *copy_ptr, ch;
X  struct tty_struct *tp;
X
X  old_state = lock();
X  ptr = m_ptr->ADDRESS;		/* pointer to accumulated char array */
X  copy_ptr = tty_copy_buf;	/* ptr to shadow array where chars copied */
X  n = *ptr;			/* how many chars have been accumulated */
X  count = n;			/* save the character count */
X  n = n + n;			/* each char occupies 2 bytes */
X  ptr += 2;			/* skip count field at start of array */
X  while (n-- > 0)
X	*copy_ptr++ = *ptr++;	/* copy the array to safety */
X  ptr = m_ptr->ADDRESS;
X  *ptr = 0;			/* accumulation count set to 0 */
X  restore(old_state);
X
X  /* Loop on the accumulated characters, processing each in turn. */
X  if (count == 0) return;	/* on TTY_O_DONE interrupt, count might be 0 */
X  copy_ptr = tty_copy_buf;
X  while (count-- > 0) {
X	ch = *copy_ptr++;	/* get the character typed */
X	n = *copy_ptr++;	/* get the line number it came in on */
X	in_char(n, ch);		/* queue the char and echo it */
X
X	/* See if a previously blocked reader can now be satisfied. */
X	tp = &tty_struct[n];	/* pointer to struct for this character */
X	if (tp->tty_inleft > 0 ) {	/* does anybody want input? */
X		m = tp->tty_mode & (CBREAK | RAW);
X		if (tp->tty_lfct > 0 || (m != 0 && tp->tty_incount > 0)) {
X			m = rd_chars(tp);
X
X			/* Tell hanging reader that chars have arrived. */
X			replyee = (int) tp->tty_incaller;
X			caller = (int) tp->tty_inproc;
X			tty_reply(REVIVE, replyee, caller, m, 0L, 0L);
X		}
X	}
X  }
X}
X
X
X/*===========================================================================*
X *				in_char					     *
X *===========================================================================*/
XPRIVATE in_char(line, ch)
Xint line;			/* line number on which char arrived */
Xchar ch;			/* scan code for character that arrived */
X{
X/* A character has just been typed in.  Process, save, and echo it. */
X
X  register struct tty_struct *tp;
X  int mode, sig, scode;
X  char make_break();
X
X  scode = ch;			/* save the scan code */
X  tp = &tty_struct[line];	/* set 'tp' to point to proper struct */
X
X  /* Function keys are temporarily being used for debug dumps. */
X  if (line == 0 && ch >= F1 && ch <= F10) {	/* Check for function keys */
X	func_key(ch);		/* process function key */
X	return;
X  }
X  if (tp->tty_incount >= TTY_IN_BYTES) return;	/* no room, discard char */
X  mode = tp->tty_mode & (RAW | CBREAK);
X  if (tp->tty_makebreak == TWO_INTS)
X	ch = make_break(ch);	/* console give 2 ints/ch */
X  else
X	if (mode != RAW) ch &= 0177;	/* 7-bit chars except in raw mode */
X  if (ch == 0) return;
X
X  /* Processing for COOKED and CBREAK mode contains special checks. */
X  if (mode == COOKED || mode == CBREAK) {
X	/* Handle erase, kill and escape processing. */
X	if (mode == COOKED) {
X		/* First erase processing (rub out of last character). */
X		if (ch == tp->tty_erase && tp->tty_escaped == NOT_ESCAPED) {
X			if (chuck(tp) != -1) {	/* remove last char entered */
X				echo(tp, '\b');	/* remove it from the screen */
X				echo(tp, ' ');
X				echo(tp, '\b');
X			}
X			return;
X		}
X
X		/* Now do kill processing (remove current line). */
X		if (ch == tp->tty_kill && tp->tty_escaped == NOT_ESCAPED) {
X			while( chuck(tp) == OK) /* keep looping */ ;
X			echo(tp, tp->tty_kill);
X			echo (tp, '\n');
X			return;
X		}
X
X		/* Handle EOT and the escape symbol (backslash). */
X		if (tp->tty_escaped == NOT_ESCAPED) {
X			/* Normal case: previous char was not backslash. */
X			if (ch == '\\') {
X				/* An escaped symbol has just been typed. */
X				tp->tty_escaped = ESCAPED;
X				echo(tp, ch);
X				return;	/* do not store the '\' */
X			}
X			/* CTRL-D means end-of-file, unless it is escaped. It
X			 * is stored in the text as MARKER, and counts as a
X			 * line feed in terms of knowing whether a full line
X			 * has been typed already.
X			 */
X			if (ch == tp->tty_eof) ch = MARKER;
X		} else {
X			/* Previous character was backslash. */
X			tp->tty_escaped = NOT_ESCAPED;	/* turn escaping off */
X			if (ch != tp->tty_erase && ch != tp->tty_kill &&
X						   ch != tp->tty_eof) {
X				/* Store the escape previously skipped over */
X				*tp->tty_inhead++ = '\\';
X				tp->tty_incount++;
X				if (tp->tty_inhead ==
X						&tp->tty_inqueue[TTY_IN_BYTES])
X					tp->tty_inhead = tp->tty_inqueue;
X			}
X		}
X	}
X	/* Both COOKED and CBREAK modes come here; first map CR to LF. */
X	if (ch == '\r' && (tp->tty_mode & CRMOD)) ch = '\n';
X
X	/* Check for interrupt and quit characters. */
X	if (ch == tp->tty_intr || ch == tp->tty_quit) {
X		sig = (ch == tp->tty_intr ? SIGINT : SIGQUIT);
X		sigchar(tp, sig);
X		return;
X	}
X
X	/* Check for and process CTRL-S (terminal stop). */
X	if (ch == tp->tty_xoff) {
X		tp->tty_inhibited = STOPPED;
X		return;
X	}
X
X	/* Check for and process CTRL-Q (terminal start). */
X	if (tp->tty_inhibited == STOPPED) {
X		tp->tty_inhibited = RUNNING;
X		(*tp->tty_devstart)(tp);	/* resume output */
X		return;
X	}
X  }
X
X  /* All 3 modes come here. */
X  if (ch == '\n' || ch == MARKER) tp->tty_lfct++;	/* count line feeds */
X
X  /* The numeric pad generates ASCII escape sequences: ESC [ letter */
X  if (line == 0 && scode >= SCODE1 && scode <= SCODE2 && 
X		shift1 == 0 && shift2 == 0 && numlock == 0) {
X	/* This key is to generate a three-character escape sequence. */
X	*tp->tty_inhead++ = ESC; /* put ESC in the input queue */
X	if (tp->tty_inhead == &tp->tty_inqueue[TTY_IN_BYTES])
X		tp->tty_inhead = tp->tty_inqueue;      /* handle wraparound */
X	tp->tty_incount++;
X	echo(tp, 'E');
X	*tp->tty_inhead++ = BRACKET; /* put ESC in the input queue */
X	if (tp->tty_inhead == &tp->tty_inqueue[TTY_IN_BYTES])
X		tp->tty_inhead = tp->tty_inqueue;      /* handle wraparound */
X	tp->tty_incount++;
X	echo(tp, BRACKET);
X	ch = scode_map[scode-SCODE1];	/* generate the letter */
X  }
X
X  *tp->tty_inhead++ = ch;	/* save the character in the input queue */
X  if (tp->tty_inhead == &tp->tty_inqueue[TTY_IN_BYTES])
X	tp->tty_inhead = tp->tty_inqueue;	/* handle wraparound */
X  tp->tty_incount++;
X  echo(tp, ch);
X}
X
X
X#ifdef i8088
X/*===========================================================================*
X *				make_break				     *
X *===========================================================================*/
XPRIVATE char make_break(ch)
Xchar ch;			/* scan code of key just struck or released */
X{
X/* This routine can handle keyboards that interrupt only on key depression,
X * as well as keyboards that interrupt on key depression and key release.
X * For efficiency, the interrupt routine filters out most key releases.
X */
X
X  int c, make, code;
X
X
X  c = ch & 0177;		/* high-order bit set on key release */
X  make = (ch & 0200 ? 0 : 1);	/* 1 when key depressed, 0 when key released */
X  if (olivetti == FALSE) {
X	/* Standard IBM keyboard. */
X	code = (shift1 || shift2 ? sh[c] : unsh[c]);
X	if (control && c < TOP_ROW) code = sh[c];	/* CTRL-(top row) */
X	if (c > 70 && numlock) 		/* numlock depressed */
X		code = (shift1 || shift2 ? unsh[c] : sh[c]);
X  } else {
X	/* (Olivetti M24 or AT&T 6300) with Olivetti-style keyboard. */
X	code = (shift1 || shift2 ? m24[c] : unm24[c]);
X	if (control && c < TOP_ROW) code = sh[c];	/* CTRL-(top row) */
X	if (c > 70 && numlock) 		/* numlock depressed */
X		code = (shift1 || shift2 ? unm24[c] : m24[c]);
X  }
X  code &= BYTE;
X  if (code < 0200 || code >= 0206) {
X	/* Ordinary key, i.e. not shift, control, alt, etc. */
X	if (capslock)
X		if (code >= 'A' && code <= 'Z')
X			code += 'a' - 'A';
X		else if (code >= 'a' && code <= 'z')
X			code -= 'a' - 'A';
X	if (alt) code |= 0200;	/* alt key ORs 0200 into code */
X	if (control) code &= 037;
X	if (code == 0) code = AT_SIGN;	/* @ is 0100, so CTRL-@ = 0 */
X	if (make == 0) code = 0;	/* key release */
X	return(code);
X  }
X
X  /* Table entries 0200 - 0206 denote special actions. */
X  switch(code - 0200) {
X    case 0:	shift1 = make;		break;	/* shift key on left */
X    case 1:	shift2 = make;		break;	/* shift key on right */
X    case 2:	control = make;		break;	/* control */
X    case 3:	alt = make;		break;	/* alt key */
X    case 4:	if (make && caps_off) {
X			capslock = 1 - capslock;
X			set_leds();
X		}
X		caps_off = 1 - make;
X		break;	/* caps lock */
X    case 5:	if (make && num_off) {
X			numlock  = 1 - numlock;
X			set_leds();
X		}
X		num_off = 1 - make;
X		break;	/* num lock */
X  }
X  return(0);
X}
X#endif
X
X
X/*===========================================================================*
X *				echo					     *
X *===========================================================================*/
XPRIVATE echo(tp, c)
Xregister struct tty_struct *tp;	/* terminal on which to echo */
Xregister char c;		/* character to echo */
X{
X/* Echo a character on the terminal. */
X
X  if ( (tp->tty_mode & ECHO) == 0) return;	/* if no echoing, don't echo */
X  if (c != MARKER) {
X	if (tp - tty_struct < NR_CONS)
X		out_char(tp, c);	/* echo to console */
X	else
X		rs_out_char(tp, c);	/* echo to RS232 line */
X  }
X  flush(tp);			/* force character out onto the screen */
X}
X
X
X/*===========================================================================*
X *				chuck					     *
X *===========================================================================*/
XPRIVATE int chuck(tp)
Xregister struct tty_struct *tp;	/* from which tty should chars be removed */
X{
X/* Delete one character from the input queue.  Used for erase and kill. */
X
X  char *prev;
X
X  /* If input queue is empty, don't delete anything. */
X  if (tp->tty_incount == 0) return(-1);
X
X  /* Don't delete '\n' or '\r'. */
X  prev = (tp->tty_inhead != tp->tty_inqueue ? tp->tty_inhead - 1 :
X					     &tp->tty_inqueue[TTY_IN_BYTES-1]);
X  if (*prev == '\n' || *prev == '\r') return(-1);
X  tp->tty_inhead = prev;
X  tp->tty_incount--;
X  return(OK);			/* char erasure was possible */
X}
X
X
X/*===========================================================================*
X *				do_read					     *
X *===========================================================================*/
XPRIVATE do_read(tp, m_ptr)
Xregister struct tty_struct *tp;	/* pointer to tty struct */
Xmessage *m_ptr;			/* pointer to message sent to the task */
X{
X/* A process wants to read from a terminal. */
X
X  int code, caller;
X
X
X  if (tp->tty_inleft > 0) {	/* if someone else is hanging, give up */
X	tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EIO, 0L, 0L);
X	return;
X  }
X
X  /* Copy information from the message to the tty struct. */
X  tp->tty_incaller = m_ptr->m_source;
X  tp->tty_inproc = m_ptr->PROC_NR;
X  tp->tty_in_vir = m_ptr->ADDRESS;
X  tp->tty_inleft = m_ptr->COUNT;
X
X  /* Try to get chars.  This call either gets enough, or gets nothing. */
X  code = rd_chars(tp);
X
X  caller = (int) tp->tty_inproc;
X  tty_reply(TASK_REPLY, m_ptr->m_source, caller, code, 0L, 0L);
X}
X
X
X/*===========================================================================*
X *				rd_chars				     *
X *===========================================================================*/
XPRIVATE int rd_chars(tp)
Xregister struct tty_struct *tp;	/* pointer to terminal to read from */
X{
X/* A process wants to read from a terminal.  First check if enough data is
X * available. If so, pass it to the user.  If not, send FS a message telling
X * it to suspend the user.  When enough data arrives later, the tty driver
X * copies it to the user space directly and notifies FS with a message.
X */
X
X  int cooked, ct, user_ct, buf_ct, cum, enough, eot_seen;
X  vir_bytes in_vir, left;
X  phys_bytes user_phys, tty_phys;
X  char ch, *tty_ptr;
X  struct proc *rp;
X  extern phys_bytes umap();
X
X  cooked = ( (tp->tty_mode & (RAW | CBREAK)) ? 0 : 1);	/* 1 iff COOKED mode */
X  if (tp->tty_incount == 0 || (cooked && tp->tty_lfct == 0)) return(SUSPEND);
X  rp = proc_addr(tp->tty_inproc);
X  in_vir = (vir_bytes) tp-> tty_in_vir;
X  left = (vir_bytes) tp->tty_inleft;
X  if ( (user_phys = umap(rp, D, in_vir, left)) == 0) return(E_BAD_ADDR);
X  tty_phys = umap(proc_addr(TTY), D, (vir_bytes) tty_buf, TTY_BUF_SIZE);
X  cum = 0;
X  enough = 0;
X  eot_seen = 0;
X
X  /* The outer loop iterates on buffers, one buffer load per iteration. */
X  while (tp->tty_inleft > 0) {
X	buf_ct = MIN(tp->tty_inleft, tp->tty_incount);
X	buf_ct = MIN(buf_ct, TTY_BUF_SIZE);
X	ct = 0;
X	tty_ptr = tty_buf;
X
X	/* The inner loop fills one buffer. */
X	while(buf_ct-- > 0) {
X		ch = *tp->tty_intail++;
X		if (tp->tty_intail == &tp->tty_inqueue[TTY_IN_BYTES])
X			tp->tty_intail = tp->tty_inqueue;
X		*tty_ptr++ = ch;
X		ct++;
X		if (ch == '\n' || ch == MARKER) {
X			tp->tty_lfct--;
X			if (cooked && ch == MARKER) eot_seen++;
X			enough++;	/* exit loop */
X			if (cooked) break;	/* only provide 1 line */
X		}
X	}
X
X	/* Copy one buffer to user space.  Be careful about CTRL-D.  In cooked
X	 * mode it is not transmitted to user programs, and is not counted as
X	 * a character as far as the count goes, but it does occupy space in 
X	 * the driver's tables and must be counted there.
X	 */
X	user_ct = (eot_seen ? ct - 1 : ct);	/* bytes to copy to user */
X	phys_copy(tty_phys, user_phys, (phys_bytes) user_ct);
X	user_phys += user_ct;
X	cum += user_ct;
X	tp->tty_inleft -= ct;
X	tp->tty_incount -= ct;
X	if (tp->tty_incount == 0 || enough) break;
X  }
X
X  tp->tty_inleft = 0;
X  return(cum);
X}
X
X
X/*===========================================================================*
X *				finish					     *
X *===========================================================================*/
XPRIVATE finish(tp, code)
Xregister struct tty_struct *tp;	/* pointer to tty struct */
Xint code;			/* reply code */
X{
X/* A command has terminated (possibly due to DEL).  Tell caller. */
X
X  int line, result, replyee, caller;
X
X  tp->tty_rwords = 0;
X  tp->tty_outleft = 0;
X  if (tp->tty_waiting == NOT_WAITING) return;
X  line = tp - tty_struct;
X  result = (line < NR_CONS ? TASK_REPLY : REVIVE);
X  replyee = (int) tp->tty_otcaller;
X  caller = (int) tp->tty_outproc;
X  tty_reply(result, replyee, caller, code, 0L, 0L);
X  tp->tty_waiting = NOT_WAITING;
X}
X
X
X/*===========================================================================*
X *				do_write				     *
X *===========================================================================*/
XPRIVATE do_write(tp, m_ptr)
Xregister struct tty_struct *tp;	/* pointer to tty struct */
Xmessage *m_ptr;			/* pointer to message sent to the task */
X{
X/* A process wants to write on a terminal. */
X
X  vir_bytes out_vir, out_left;
X  struct proc *rp;
X  extern phys_bytes umap();
X  int caller,replyee;
X
X  /* If the slot is already in use, better return an error than mess it up. */
X  if (tp->tty_outleft > 0) {	/* if someone else is hanging, give up */
X	tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EIO, 0L, 0L);
X	return;
X  }
X
X  /* Copy message parameters to the tty structure. */
X  tp->tty_otcaller = m_ptr->m_source;
X  tp->tty_outproc = m_ptr->PROC_NR;
X  tp->tty_out_vir = m_ptr->ADDRESS;
X  tp->tty_outleft = m_ptr->COUNT;
X  tp->tty_waiting = WAITING;
X  tp->tty_cum = 0;
X
X  /* Compute the physical address where the data is in user space. */
X  rp = proc_addr(tp->tty_outproc);
X  out_vir = (vir_bytes) tp->tty_out_vir;
X  out_left = (vir_bytes) tp->tty_outleft;
X  if ( (tp->tty_phys = umap(rp, D, out_vir, out_left)) == 0) {
X	/* Buffer address provided by user is outside its address space. */
X	tp->tty_cum = E_BAD_ADDR;
X	tp->tty_outleft = 0;
X  }
X
X  /* Copy characters from the user process to the terminal. */
X  (*tp->tty_devstart)(tp);	/* copy data to queue and start I/O */
X
X  /* If output is for a bitmapped terminal as the IBM-PC console, the output-
X   * routine will return at once so there is no need to suspend the caller,
X   * on ascii terminals however, the call is suspended and later revived.
X   */
X  if (m_ptr->TTY_LINE != 0) {
X	caller = (int) tp->tty_outproc;
X	replyee = (int) tp->tty_otcaller;
X	tty_reply(TASK_REPLY, replyee, caller, SUSPEND, 0L, 0L);
X  }
X}
X
X
X/*===========================================================================*
X *				do_ioctl				     *
X *===========================================================================*/
XPRIVATE do_ioctl(tp, m_ptr)
Xregister struct tty_struct *tp;	/* pointer to tty_struct */
Xmessage *m_ptr;			/* pointer to message sent to task */
X{
X/* Perform IOCTL on this terminal. */
X
X  long flags, erki, erase, kill, intr, quit, xon, xoff, eof;
X  int r;
X
X  r = OK;
X  flags = 0;
X  erki = 0;
X  switch(m_ptr->TTY_REQUEST) {
X     case TIOCSETP:
X	/* Set erase, kill, and flags. */
X	tp->tty_erase = (char) ((m_ptr->TTY_SPEK >> 8) & BYTE);	/* erase  */
X	tp->tty_kill  = (char) ((m_ptr->TTY_SPEK >> 0) & BYTE);	/* kill  */
X	tp->tty_mode  = (int) m_ptr->TTY_FLAGS;	/* mode word */
X	if (m_ptr->TTY_SPEED != 0) tp->tty_speed = m_ptr->TTY_SPEED;
X	if (tp-tty_struct >= NR_CONS)
X		set_uart(tp - tty_struct, tp->tty_mode, tp->tty_speed);
X	break;
X
X     case TIOCSETC:
X	/* Set intr, quit, xon, xoff, eof (brk not used). */
X	tp->tty_intr = (char) ((m_ptr->TTY_SPEK >> 24) & BYTE);	/* interrupt */
X	tp->tty_quit = (char) ((m_ptr->TTY_SPEK >> 16) & BYTE);	/* quit */
X	tp->tty_xon  = (char) ((m_ptr->TTY_SPEK >>  8) & BYTE);	/* CTRL-S */
X	tp->tty_xoff = (char) ((m_ptr->TTY_SPEK >>  0) & BYTE);	/* CTRL-Q */
X	tp->tty_eof  = (char) ((m_ptr->TTY_FLAGS >> 8) & BYTE);	/* CTRL-D */
X	break;
X
X     case TIOCGETP:
X	/* Get erase, kill, and flags. */
X	erase = ((long) tp->tty_erase) & BYTE;
X	kill  = ((long) tp->tty_kill) & BYTE;
X	erki  = (erase << 8) | kill;
X	flags = ( (long) tp->tty_speed << 16) | (long) tp->tty_mode;
X	break;
X
X     case TIOCGETC:
X	/* Get intr, quit, xon, xoff, eof. */
X	intr  = ((long) tp->tty_intr) & BYTE;
X	quit  = ((long) tp->tty_quit) & BYTE;
X	xon   = ((long) tp->tty_xon)  & BYTE;
X	xoff  = ((long) tp->tty_xoff) & BYTE;
X	eof   = ((long) tp->tty_eof)  & BYTE;
X	erki  = (intr << 24) | (quit << 16) | (xon << 8) | (xoff << 0);
X	flags = (eof <<8);
X	break;
X
X     default:
X	r = EINVAL;
X  }
X
X  /* Send the reply. */
X  tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r, flags, erki);
X}
X
X
X/*===========================================================================*
X *				do_setpgrp				     *
X *===========================================================================*/
XPRIVATE do_setpgrp(tp, m_ptr)
Xregister struct tty_struct *tp; /* pointer to tty struct */
Xmessage *m_ptr;			/* pointer to message sent to task */
X{
X/* A control process group has changed */
X
X   tp->tty_pgrp = m_ptr->TTY_PGRP;
X   tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, OK, 0L, 0L);
X}
X
X
X/*===========================================================================*
X *				do_cancel				     *
X *===========================================================================*/
XPRIVATE do_cancel(tp, m_ptr)
Xregister struct tty_struct *tp;	/* pointer to tty_struct */
Xmessage *m_ptr;			/* pointer to message sent to task */
X{
X/* A signal has been sent to a process that is hanging trying to read or write.
X * The pending read or write must be finished off immediately.
X */
X
X  int mode;
X
X  /* First check to see if the process is indeed hanging.  If it is not, don't
X   * reply (to avoid race conditions).
X   */
X  if (tp->tty_inleft == 0 && tp->tty_outleft == 0) return;
X
X  /* Kill off input/output. */
X  mode = m_ptr->COUNT;
X  if (mode & R_BIT) {
X	/* Process was reading when killed.  Clean up input. */
X	tp->tty_inhead = tp->tty_inqueue;	/* discard all data */
X	tp->tty_intail = tp->tty_inqueue;
X	tp->tty_incount = 0;
X	tp->tty_lfct = 0;
X	tp->tty_inleft = 0;
X	tp->tty_inhibited = RUNNING;
X  }
X  if (mode & W_BIT) {
X	/* Process was writing when killed.  Clean up output. */
X	tp->tty_outleft = 0;
X	tp->tty_waiting = NOT_WAITING;	/* don't send reply */
X  }
X  tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINTR, 0L, 0L);
X}
X
X
X/*===========================================================================*
X *				tty_reply				     *
X *===========================================================================*/
XPRIVATE tty_reply(code, replyee, proc_nr, status, extra, other)
Xint code;			/* TASK_REPLY or REVIVE */
Xint replyee;			/* destination address for the reply */
Xint proc_nr;			/* to whom should the reply go? */
Xint status;			/* reply code */
Xlong extra;			/* extra value */
Xlong other;			/* used for IOCTL replies */
X{
X/* Send a reply to a process that wanted to read or write data. */
X
X  message tty_mess;
X
X  tty_mess.m_type = code;
X  tty_mess.REP_PROC_NR = proc_nr;
X  tty_mess.REP_STATUS = status;
X  tty_mess.TTY_FLAGS = extra;	/* used by IOCTL for flags (mode) */
X  tty_mess.TTY_SPEK = other;	/* used by IOCTL for erase and kill chars */
X  send(replyee, &tty_mess);
X}
X
X
X/*===========================================================================*
X *				sigchar					     *
X *===========================================================================*/
XPRIVATE sigchar(tp, sig)
Xregister struct tty_struct *tp;	/* pointer to tty_struct */
Xint sig;			/* SIGINT, SIGQUIT, or SIGKILL */
X{
X/* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard */
X
X  tp->tty_inhibited = RUNNING;	/* do implied CRTL-Q */
X  finish(tp, EINTR);		/* send reply */
X  tp->tty_inhead = tp->tty_inqueue;	/* discard input */
X  tp->tty_intail = tp->tty_inqueue;
X  tp->tty_incount = 0;
X  tp->tty_lfct = 0;
X  if (tp >= &tty_struct[NR_CONS]) rs_sig(tp);	/* RS232 only */
X  if (tp->tty_pgrp) cause_sig(tp->tty_pgrp, sig);
X}
X
X
X
X
X/*****************************************************************************/
X/*****************************************************************************/
X/*****************************************************************************/
X/*****************************************************************************/
X/*****************************************************************************/
X
X#ifdef i8088
X/* Now begins the code and data for the device-dependent tty drivers. */
X
X/* Definitions used by the console driver. */
X#define COLOR_BASE    0xB800	/* video ram paragraph for color display */
X#define MONO_BASE     0xB000	/* video ram address for mono display */
X#define C_VID_MASK    0x3FFF	/* mask for 16K video RAM */
X#define M_VID_MASK    0x0FFF	/* mask for  4K video RAM */
X#define C_RETRACE     0x0300	/* how many characters to display at once */
X#define M_RETRACE     0x7000	/* how many characters to display at once */
X#define WORD_MASK     0xFFFF	/* mask for 16 bits */
X#define OFF_MASK      0x000F	/* mask for  4 bits */
X#define BEEP_FREQ     0x0533	/* value to put into timer to set beep freq */
X#define B_TIME        0x2000	/* how long to sound the CTRL-G beep tone */
X#define BLANK         0x0700	/* determines  cursor color on blank screen */
X#define LINE_WIDTH        80	/* # characters on a line */
X#define SCR_LINES         25	/* # lines on the screen */
X#define CTRL_S            31	/* scan code for letter S (for CRTL-S) */
X#define MONOCHROME         1	/* value for tty_ioport tells color vs. mono */
X#define CONSOLE            0	/* line number for console */
X#define GO_FORWARD         0	/* scroll forward */
X#define GO_BACKWARD        1	/* scroll backward */
X#define TIMER2          0x42	/* I/O port for timer channel 2 */
X#define TIMER3          0x43	/* I/O port for timer channel 3 */
X#define KEYBD           0x60	/* I/O port for keyboard data */
X#define PORT_B          0x61	/* I/O port for 8255 port B */
X#define KBIT            0x80	/* bit used to ack characters to keyboard */
X#define LED_CODE        0xED	/* command to keyboard to set LEDs */
X#define LED_DELAY       0x80	/* device dependent delay needed */
X
X/* Constants relating to the video RAM and 6845. */
X#define M_6845         0x3B0	/* port for 6845 mono */
X#define C_6845         0x3D0	/* port for 6845 color */
X#define EGA            0x3C0	/* port for EGA card */
X#define INDEX              4	/* 6845's index register */
X#define DATA               5	/* 6845's data register */
X#define CUR_SIZE          10	/* 6845's cursor size register */
X#define VID_ORG           12	/* 6845's origin register */
X#define CURSOR            14	/* 6845's cursor register */
X
X/* Definitions used for determining if the keyboard is IBM or Olivetti type. */
X#define KB_STATUS	0x64	/* Olivetti keyboard status port */
X#define BYTE_AVAIL	0x01	/* there is something in KEYBD port */
X#define KB_BUSY	        0x02	/* KEYBD port ready to accept a command */
X#define DELUXE		0x01	/* this bit is set up iff deluxe keyboard */
X#define GET_TYPE	   5	/* command to get keyboard type */
X#define OLIVETTI_EQUAL    12	/* the '=' key is 12 on olivetti, 13 on IBM */
X
X/* Global variables used by the console driver. */
XPUBLIC  message keybd_mess;	/* message used for console input chars */
XPRIVATE vid_retrace;		/* how many characters to display per burst */
XPRIVATE unsigned vid_base;	/* base of video ram (0xB000 or 0xB800) */
XPUBLIC int vid_mask;		/* 037777 for color (16K) or 07777 for mono */
XPRIVATE int vid_port;		/* I/O port for accessing 6845 */
X
X
X/*===========================================================================*
X *				keyboard				     *
X *===========================================================================*/
XPUBLIC keyboard()
X{
X/* A keyboard interrupt has occurred.  Process it. */
X
X  int val, code, k, raw_bit;
X  char stopc;
X
X  /* Fetch the character from the keyboard hardware and acknowledge it. */
X  port_in(KEYBD, &code);	/* get the scan code for the key struck */
X  port_in(PORT_B, &val);	/* strobe the keyboard to ack the char */
X  port_out(PORT_B, val | KBIT);	/* strobe the bit high */
X  port_out(PORT_B, val);	/* now strobe it low */
X
X  /* The IBM keyboard interrupts twice per key, once when depressed, once when
X   * released.  Filter out the latter, ignoring all but the shift-type keys.
X   * The shift-type keys 29, 42, 54, 56, 58, and 69 must be processed normally.
X   */
X  k = code - 0200;		/* codes > 0200 mean key release */
X  if (k > 0) {
X	/* A key has been released. */
X	if (k != 29 && k != 42 && k != 54 && k != 56 && k != 58 && k != 69) {
X		port_out(INT_CTL, ENABLE);	/* re-enable interrupts */
X	 	return;		/* don't call tty_task() */
X	}
X  } else {
X	/* Check to see if character is CTRL-S, to stop output. Setting xoff
X	 * to anything other than CTRL-S will not be detected here, but will
X	 * be detected later, in the driver.  A general routine to detect any
X	 * xoff character here would be complicated since we only have the
X	 * scan code here, not the ASCII character.
X	 */
X	raw_bit = tty_struct[CONSOLE].tty_mode & RAW;
X	stopc = tty_struct[CONSOLE].tty_xoff;
X	if (raw_bit == 0 && control && code == CTRL_S && stopc == XOFF_CHAR) {
X		tty_struct[CONSOLE].tty_inhibited = STOPPED;
X		port_out(INT_CTL, ENABLE);
X		return;
X	}
X  }
X
X  /* Check for CTRL-ALT-DEL, and if found, reboot the computer. */
X  if (control && alt && code == DEL_CODE) reboot();	/* CTRL-ALT-DEL */
X
X  /* Store the character in memory so the task can get at it later.
X   * tty_driver_buf[0] is the current count, and tty_driver_buf[1] is the
X   * maximum allowed to be stored.
X   */
X  if ( (k = tty_driver_buf[0]) < tty_driver_buf[1]) {
X	/* There is room to store this character; do it. */
X	k = k + k;			/* each entry contains two bytes */
X	tty_driver_buf[k+2] = code;	/* store the scan code */
X	tty_driver_buf[k+3] = CONSOLE;	/* tell which line it came from */
X	tty_driver_buf[0]++;		/* increment counter */
X
X	/* Build and send the interrupt message. */
X	keybd_mess.m_type = TTY_CHAR_INT;
X	keybd_mess.ADDRESS = tty_driver_buf;
X	interrupt(TTY, &keybd_mess);	/* send a message to the tty task */
X  } else {
X	/* Too many characters have been buffered.  Discard excess. */
X	port_out(INT_CTL, ENABLE);	/* re-enable 8259A controller */
X  }
X}
X
X
X/*===========================================================================*
X *				console					     *
X *===========================================================================*/
XPRIVATE console(tp)
Xregister struct tty_struct *tp;	/* tells which terminal is to be used */
X{
X/* Copy as much data as possible to the output queue, then start I/O.  On
X * memory-mapped terminals, such as the IBM console, the I/O will also be
X * finished, and the counts updated.  Keep repeating until all I/O done.
X */
X
X  extern char get_byte();
X  int count;
X  char c;
X  unsigned segment, offset, offset1;
X
X  /* Loop over the user bytes one at a time, outputting each one. */
X  segment = (tp->tty_phys >> 4) & WORD_MASK;
X  offset = tp->tty_phys & OFF_MASK;
X  offset1 = offset;
X  count = 0;
X
X  while (tp->tty_outleft > 0 && tp->tty_inhibited == RUNNING) {
X	c = get_byte(segment, offset);	/* fetch 1 byte from user space */
X	out_char(tp, c);	/* write 1 byte to terminal */
X	offset++;		/* advance one character in user buffer */
X	tp->tty_outleft--;	/* decrement count */
X  }
X  flush(tp);			/* clear out the pending characters */
X
+ END-OF-FILE tty.c.part1
chmod 'u=rw,g=r,o=r' 'tty.c.part1'
set `wc -c 'tty.c.part1'`
count=$1
case $count in
44390)	:;;
*)	echo 'Bad character count in ''tty.c.part1' >&2
		echo 'Count should be 44390' >&2
esac
exit 0