[comp.os.minix] MINIX 1.2 kernel changes

ast@cs.vu.nl (Andy Tanenbaum) (07/01/87)

: 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
echo Extracting \p\r\i\n\t\e\r\.\c
sed 's/^X//' > \p\r\i\n\t\e\r\.\c << '+ END-OF-FILE '\p\r\i\n\t\e\r\.\c
X/* This file contains the printer driver. It is a fairly simple driver,
X * supporting only one printer.  Characters that are written to the driver
X * are written to the printer without any changes at all.
X *
X * The valid messages and their parameters are:
X *
X *   TTY_O_DONE:   output completed
X *   TTY_WRITE:    a process wants to write on a terminal
X *   CANCEL:       terminate a previous incomplete system call immediately
X *
X *    m_type      TTY_LINE   PROC_NR    COUNT    ADDRESS
X * -------------------------------------------------------
X * | TTY_O_DONE  |minor dev|         |         |         |
X * |-------------+---------+---------+---------+---------|
X * | TTY_WRITE   |minor dev| proc nr |  count  | buf ptr |
X * |-------------+---------+---------+---------+---------|
X * | CANCEL      |minor dev| proc nr |         |         |
X * -------------------------------------------------------
X * 
X * Note: since only 1 printer is supported, minor dev is not used at present.
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 "const.h"
X#include "type.h"
X#include "glo.h"
X#include "proc.h"
X
X#define NORMAL_STATUS   0x90	/* printer gives this status when idle */
X#define BUSY_STATUS     0x10	/* printer gives this status when busy */
X#define ASSERT_STROBE   0x1D	/* strobe a character to the interface */
X#define NEGATE_STROBE   0x1C	/* enable interrupt on interface */
X#define SELECT          0x0C	/* select printer bit */
X#define INIT_PRINTER    0x08	/* init printer bits */
X#define NO_PAPER        0x20	/* status bit saying that paper is up */
X#define OFF_LINE        0x10	/* status bit saying that printer not online*/
X#define PR_ERROR        0x08	/* something is wrong with the printer */
X#define PR_COLOR_BASE  0x378	/* printer port when color display used */
X#define PR_MONO_BASE   0x3BC	/* printer port when mono display used */
X#define LOW_FOUR         0xF	/* mask for low-order 4 bits */
X#define CANCELED        -999	/* indicates that command has been killed */
X#define DELAY_COUNT      100	/* regulates delay between characters */
X#define DELAY_LOOP      1000	/* delay when printer is busy */
X#define MAX_REP         1000	/* controls max delay when busy */
X#define STATUS_MASK	0xB0	/* mask to filter out status bits */ 
X
XPRIVATE int port_base;		/* I/O port for printer: 0x 378 or 0x3BC */
XPRIVATE int caller;		/* process to tell when printing done (FS) */
XPRIVATE int proc_nr;		/* user requesting the printing */
XPRIVATE int orig_count;		/* original byte count */
XPRIVATE int es;			/* (es, offset) point to next character to */
XPRIVATE int offset;		/* print, i.e., in the user's buffer */
XPUBLIC int pcount;		/* number of bytes left to print */
XPUBLIC int pr_busy;		/* TRUE when printing, else FALSE */
XPUBLIC int cum_count;		/* cumulative # characters printed */
XPUBLIC int prev_ct;		/* value of cum_count 100 msec ago */
X
X/*===========================================================================*
X *				printer_task				     *
X *===========================================================================*/
XPUBLIC printer_task()
X{
X/* Main routine of the printer task. */
X
X  message print_mess;		/* buffer for all incoming messages */
X
X  print_init();			/* initialize */
X
X  while (TRUE) {
X	receive(ANY, &print_mess);
X	switch(print_mess.m_type) {
X	    case TTY_WRITE:	do_write(&print_mess);	break;
X	    case CANCEL   :	do_cancel(&print_mess);	break;
X	    case TTY_O_DONE:	do_done(&print_mess);	break;
X    	    default:					break;
X	}
X  }
X}
X
X
X/*===========================================================================*
X *				do_write				     *
X *===========================================================================*/
XPRIVATE do_write(m_ptr)
Xmessage *m_ptr;			/* pointer to the newly arrived message */
X{
X/* The printer is used by sending TTY_WRITE messages to it. Process one. */
X
X  int i, j, r, value;
X  struct proc *rp;
X  phys_bytes phys;
X  extern phys_bytes umap();
X
X  r = OK;			/* so far, no errors */
X
X  /* Reject command if printer is busy or count is not positive. */
X  if (pr_busy) r = EAGAIN;
X  if (m_ptr->COUNT <= 0) r = EINVAL;
X
X  /* Compute the physical address of the data buffer within user space. */
X  rp = proc_addr(m_ptr->PROC_NR);
X  phys = umap(rp, D, (vir_bytes) m_ptr->ADDRESS, m_ptr->COUNT);
X  if (phys == 0) r = E_BAD_ADDR;
X
X  if (r == OK) {
X  	/* Save information needed later. */
X	lock();			/* no interrupts now please */
X  	caller = m_ptr->m_source;
X  	proc_nr = m_ptr->PROC_NR;
X  	pcount = m_ptr->COUNT;
X  	orig_count = m_ptr->COUNT;
X  	es = (int) (phys >> CLICK_SHIFT);
X  	offset = (int) (phys & LOW_FOUR);
X
X  	/* Start the printer. */
X	for (i = 0; i < MAX_REP; i++) {
X	  	port_in(port_base + 1, &value);
X	  	if ((value&STATUS_MASK) == NORMAL_STATUS) {
X		 	pr_busy = TRUE;
X			pr_char();	/* print first character */
X			r = SUSPEND;	/* tell FS to suspend user until done */
X			break;
X		} else {
X			if ((value&STATUS_MASK) == BUSY_STATUS) {
X				for (j = 0; j <DELAY_LOOP; j++) /* delay */ ;
X				continue;
X			}
X			pr_error(value);
X			r = EIO;
X			break;
X		}
X	}
X  }
X
X  /* Reply to FS, no matter what happened. */
X  if ((value&STATUS_MASK) == BUSY_STATUS) r = EAGAIN;
X  reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r);
X}
X
X
X/*===========================================================================*
X *				do_done					     *
X *===========================================================================*/
XPRIVATE do_done(m_ptr)
Xmessage *m_ptr;			/* pointer to the newly arrived message */
X{
X/* Printing is finished.  Reply to caller (FS). */
X
X  int status;
X
X  status = (m_ptr->REP_STATUS == OK ? orig_count : EIO);
X  if (proc_nr != CANCELED) {
X  	reply(REVIVE, caller, proc_nr, status);
X  	if (status == EIO) pr_error(m_ptr->REP_STATUS);
X  }
X  pr_busy = FALSE;
X}
X
X
X/*===========================================================================*
X *				do_cancel				     *
X *===========================================================================*/
XPRIVATE do_cancel(m_ptr)
Xmessage *m_ptr;			/* pointer to the newly arrived message */
X{
X/* Cancel a print request that has already started.  Usually this means that
X * the process doing the printing has been killed by a signal.
X */
X
X  if (pr_busy == FALSE) return;	/* this statement avoids race conditions */
X  pr_busy = FALSE;		/* mark printer as idle */
X  pcount = 0;			/* causes printing to stop at next interrupt*/
X  proc_nr = CANCELED;		/* marks process as canceled */
X  reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINTR);
X}
X
X
X/*===========================================================================*
X *				reply					     *
X *===========================================================================*/
XPRIVATE reply(code, replyee, process, status)
Xint code;			/* TASK_REPLY or REVIVE */
Xint replyee;			/* destination for message (normally FS) */
Xint process;			/* which user requested the printing */
Xint status;			/* number of  chars printed or error code */
X{
X/* Send a reply telling FS that printing has started or stopped. */
X
X  message pr_mess;
X
X  pr_mess.m_type = code;	/* TASK_REPLY or REVIVE */
X  pr_mess.REP_STATUS = status;	/* count or EIO */
X  pr_mess.REP_PROC_NR = process;	/* which user does this pertain to */
X  send(replyee, &pr_mess);	/* send the message */
X}
X
X
X/*===========================================================================*
X *				pr_error				     *
X *===========================================================================*/
XPRIVATE pr_error(status)
Xint status;			/* printer status byte */
X{
X/* The printer is not ready.  Display a message on the console telling why. */
X
X  if (status & NO_PAPER) printf("Printer is out of paper\n");
X  if ((status & OFF_LINE) == 0) printf("Printer is not on line\n");
X  if ((status & PR_ERROR) == 0) printf("Printer error\n");
X}
X
X
X/*===========================================================================*
X *				print_init				     *
X *===========================================================================*/
XPRIVATE print_init()
X{
X/* Color display uses 0x378 for printer; mono display uses 0x3BC. */
X
X  int i;
X  extern int color;
X
X  port_base = (color ? PR_COLOR_BASE : PR_MONO_BASE);
X  pr_busy = FALSE;
X  port_out(port_base + 2, INIT_PRINTER);
X  for (i = 0; i < DELAY_COUNT; i++) ;	/* delay loop */
X  port_out(port_base + 2, SELECT);
X}
X
X
X/*===========================================================================*
X *				pr_char				     *
X *===========================================================================*/
XPUBLIC pr_char()
X{
X/* This is the interrupt handler.  When a character has been printed, an
X * interrupt occurs, and the assembly code routine trapped to calls pr_char().
X * One annoying problem is that the 8259A controller sometimes generates
X * spurious interrupts to vector 15, which is the printer vector.  Ignore them.
X */
X
X  int value, ch, i;
X  char c;
X  extern char get_byte();
X
X  if (pcount != orig_count) port_out(INT_CTL, ENABLE);
X  if (pr_busy == FALSE) return;	/* spurious 8259A interrupt */
X
X  while (pcount > 0) {
X  	port_in(port_base + 1, &value);	/* get printer status */
X  	if ((value&STATUS_MASK) == NORMAL_STATUS) {
X		/* Everything is all right.  Output another character. */
X		c = get_byte(es, offset);	/* fetch char from user buf */
X		ch = c & BYTE;
X		port_out(port_base, ch);	/* output character */
X		port_out(port_base + 2, ASSERT_STROBE);
X		port_out(port_base + 2, NEGATE_STROBE);
X		offset++;
X		pcount--;
X		cum_count++;	/* count characters output */
X		for (i = 0; i < DELAY_COUNT; i++) ;	/* delay loop */
X	} else if ((value&STATUS_MASK) == BUSY_STATUS) {
X		 	return;		/* printer is busy; wait for interrupt*/
X	} else {
X		 	break;		/* err: send message to printer task */
X	}
X  }
X  
X/* Count is 0 or an error occurred; send message to printer task. */
X  int_mess.m_type = TTY_O_DONE;
X  int_mess.REP_STATUS = (pcount == 0 ? OK : value);
X  interrupt(PRINTER, &int_mess);
X}
X
X
+ END-OF-FILE printer.c
chmod 'u=rw,g=r,o=r' \p\r\i\n\t\e\r\.\c
set `sum \p\r\i\n\t\e\r\.\c`
sum=$1
case $sum in
49958)	:;;
*)	echo 'Bad sum in '\p\r\i\n\t\e\r\.\c >&2
esac
echo Extracting \p\r\o\c\.\c
sed 's/^X//' > \p\r\o\c\.\c << '+ END-OF-FILE '\p\r\o\c\.\c
X/* This file contains essentially all of the process and message handling.
X * It has two main entry points from the outside:
X *
X *   sys_call:   called when a process or task does SEND, RECEIVE or SENDREC
X *   interrupt:	called by interrupt routines to send a message to task
X *
X * It also has five minor entry points:
X *
X *   ready:	put a process on one of the ready queues so it can be run
X *   unready:	remove a process from the ready queues
X *   sched:	a process has run too long; schedule another one
X *   mini_send:	send a message (used by interrupt signals, etc.)
X *   pick_proc:	pick a process to run (used by system initialization)
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 "const.h"
X#include "type.h"
X#include "glo.h"
X#include "proc.h"
X
X/*===========================================================================*
X *				interrupt				     * 
X *===========================================================================*/
XPUBLIC interrupt(task, m_ptr)
Xint task;			/* number of task to be started */
Xmessage *m_ptr;			/* interrupt message to send to the task */
X{
X/* An interrupt has occurred.  Schedule the task that handles it. */
X
X  int i, n, old_map, this_bit;
X
X#ifdef i8088
X  /* Re-enable the 8259A interrupt controller. */
X  port_out(INT_CTL, ENABLE);	/* this re-enables the 8259A controller chip */
X  if (pc_at && task == WINCHESTER)
X	  /* this re-enables the second controller chip */
X	  port_out(INT2_CTL, ENABLE);
X#endif
X
X  /* Try to send the interrupt message to the indicated task. */
X  this_bit = 1 << (-task);
X  if (mini_send(HARDWARE, task, m_ptr) != OK) {
X	/* The message could not be sent to the task; it was not waiting. */
X	old_map = busy_map;	/* save original map of busy tasks */
X	if (task == CLOCK) {
X		lost_ticks++;
X	} else {
X		busy_map |= this_bit;		/* mark task as busy */
X		task_mess[-task] = m_ptr;	/* record message pointer */
X	}
X  } else {
X	/* Hardware interrupt was successfully sent as a message. */
X	busy_map &= ~this_bit;	/* turn off the bit in case it was on */
X	old_map = busy_map;
X  }
X
X  /* See if any tasks that were previously busy are now listening for msgs. */
X  if (old_map != 0) {
X	for (i = 2; i <= NR_TASKS; i++) {
X		/* Check each task looking for one with a pending interrupt. */
X		if ( (old_map>>i) & 1) {
X			/* Task 'i' has a pending interrupt. */
X			n = mini_send(HARDWARE, -i, task_mess[i]);
X			if (n == OK) busy_map &= ~(1 << i);
X		}
X	}
X  }
X
X  /* If a task has just been readied and a user is running, run the task. */
X  if (rdy_head[TASK_Q] != NIL_PROC && (cur_proc >= 0 || cur_proc == IDLE))
X	pick_proc();
X}
X
X
X/*===========================================================================*
X *				sys_call				     * 
X *===========================================================================*/
XPUBLIC sys_call(function, caller, src_dest, m_ptr)
Xint function;			/* SEND, RECEIVE, or BOTH */
Xint caller;			/* who is making this call */
Xint src_dest;			/* source to receive from or dest to send to */
Xmessage *m_ptr;			/* pointer to message */
X{
X/* The only system calls that exist in MINIX are sending and receiving
X * messages.  These are done by trapping to the kernel with an INT instruction.
X * The trap is caught and sys_call() is called to send or receive a message (or
X * both).
X */
X
X  register struct proc *rp;
X  int n;
X
X  /* Check for bad system call parameters. */
X  rp = proc_addr(caller);
X  if (src_dest < -NR_TASKS || (src_dest >= NR_PROCS && src_dest != ANY) ) {
X	rp->p_reg[RET_REG] = E_BAD_SRC;
X	return;
X  }
X  if (function != BOTH && caller >= LOW_USER) {
X	rp->p_reg[RET_REG] = E_NO_PERM;	/* users only do BOTH */
X	return;
X  }
X
X  /* The parameters are ok. Do the call. */
X  if (function & SEND) {
X	n = mini_send(caller, src_dest, m_ptr);	/* func = SEND or BOTH */
X	if (function == SEND || n != OK) rp->p_reg[RET_REG] = n;
X	if (n != OK) return;	/* SEND failed */
X  }
X
X  if (function & RECEIVE) {
X	n = mini_rec(caller, src_dest, m_ptr);      /* func = RECEIVE or BOTH */
X	rp->p_reg[RET_REG] = n;
X  }
X}
X
X/*===========================================================================*
X *				mini_send				     * 
X *===========================================================================*/
XPUBLIC int mini_send(caller, dest, m_ptr)
Xint caller;			/* who is trying to send a message? */
Xint dest;			/* to whom is message being sent? */
Xmessage *m_ptr;			/* pointer to message buffer */
X{
X/* Send a message from 'caller' to 'dest'.  If 'dest' is blocked waiting for
X * this message, copy the message to it and unblock 'dest'.  If 'dest' is not
X * waiting at all, or is waiting for another source, queue 'caller'.
X */
X
X  register struct proc *caller_ptr, *dest_ptr, *next_ptr;
X  vir_bytes vb;			/* message buffer pointer as vir_bytes */
X  vir_clicks vlo, vhi;		/* virtual clicks containing message to send */
X  vir_clicks len;		/* length of data segment in clicks */
X
X  /* User processes are only allowed to send to FS and MM.  Check for this. */
X  if (caller >= LOW_USER && (dest != FS_PROC_NR && dest != MM_PROC_NR))
X	return(E_BAD_DEST);
X  caller_ptr = proc_addr(caller);	/* pointer to source's proc entry */
X  dest_ptr = proc_addr(dest);	/* pointer to destination's proc entry */
X  if (dest_ptr->p_flags & P_SLOT_FREE) return(E_BAD_DEST);	/* dead dest */
X
X  /* Check for messages wrapping around top of memory or outside data seg. */
X  len = caller_ptr->p_map[D].mem_len;
X  vb = (vir_bytes) m_ptr;
X  vlo = vb >> CLICK_SHIFT;	/* vir click for bottom of message */
X  vhi = (vb + MESS_SIZE - 1) >> CLICK_SHIFT;	/* vir click for top of message */
X  if (vhi < vlo || vhi - caller_ptr->p_map[D].mem_vir >= len)return(E_BAD_ADDR);
X
X  /* Check to see if 'dest' is blocked waiting for this message. */
X  if ( (dest_ptr->p_flags & RECEIVING) &&
X		(dest_ptr->p_getfrom == ANY || dest_ptr->p_getfrom == caller) ) {
X	/* Destination is indeed waiting for this message. */
X	cp_mess(caller, caller_ptr->p_map[D].mem_phys, m_ptr, 
X				dest_ptr->p_map[D].mem_phys, dest_ptr->p_messbuf);
X	dest_ptr->p_flags &= ~RECEIVING;	/* deblock destination */
X	if (dest_ptr->p_flags == 0) ready(dest_ptr);
X  } else {
X	/* Destination is not waiting.  Block and queue caller. */
X	if (caller == HARDWARE) return(E_OVERRUN);
X	caller_ptr->p_messbuf = m_ptr;
X	caller_ptr->p_flags |= SENDING;
X	unready(caller_ptr);
X
X	/* Process is now blocked.  Put in on the destination's queue. */
X	if ( (next_ptr = dest_ptr->p_callerq) == NIL_PROC) {
X		dest_ptr->p_callerq = caller_ptr;
X	} else {
X		while (next_ptr->p_sendlink != NIL_PROC)
X			next_ptr = next_ptr->p_sendlink;
X		next_ptr->p_sendlink = caller_ptr;
X	}
X	caller_ptr->p_sendlink = NIL_PROC;
X  }
X  return(OK);
X}
X
X
X/*===========================================================================*
X *				mini_rec				     * 
X *===========================================================================*/
XPRIVATE int mini_rec(caller, src, m_ptr)
Xint caller;			/* process trying to get message */
Xint src;			/* which message source is wanted (or ANY) */
Xmessage *m_ptr;			/* pointer to message buffer */
X{
X/* A process or task wants to get a message.  If one is already queued,
X * acquire it and deblock the sender.  If no message from the desired source
X * is available, block the caller.  No need to check parameters for validity.
X * Users calls are always sendrec(), and mini_send() has checked already.  
X * Calls from the tasks, MM, and FS are trusted.
X */
X
X  register struct proc *caller_ptr, *sender_ptr, *prev_ptr;
X  int sender;
X
X  caller_ptr = proc_addr(caller);	/* pointer to caller's proc structure */
X
X  /* Check to see if a message from desired source is already available. */
X  sender_ptr = caller_ptr->p_callerq;
X  while (sender_ptr != NIL_PROC) {
X	sender = sender_ptr - proc - NR_TASKS;
X	if (src == ANY || src == sender) {
X		/* An acceptable message has been found. */
X		cp_mess(sender, sender_ptr->p_map[D].mem_phys, sender_ptr->p_messbuf,
X					caller_ptr->p_map[D].mem_phys, m_ptr);
X		sender_ptr->p_flags &= ~SENDING;	/* deblock sender */
X		if (sender_ptr->p_flags == 0) ready(sender_ptr);
X		if (sender_ptr == caller_ptr->p_callerq)
X			caller_ptr->p_callerq = sender_ptr->p_sendlink;
X		else
X			prev_ptr->p_sendlink = sender_ptr->p_sendlink;
X		return(OK);
X	}
X	prev_ptr = sender_ptr;
X	sender_ptr = sender_ptr->p_sendlink;
X  }
X
X  /* No suitable message is available.  Block the process trying to receive. */
X  caller_ptr->p_getfrom = src;
X  caller_ptr->p_messbuf = m_ptr;
X  caller_ptr->p_flags |= RECEIVING;
X  unready(caller_ptr);
X
X  /* If MM has just blocked and there are kernel signals pending, now is the
X   * time to tell MM about them, since it will be able to accept the message.
X   */
X  if (sig_procs > 0 && caller == MM_PROC_NR && src == ANY) inform(MM_PROC_NR);
X  return(OK);
X}
X
X
X/*===========================================================================*
X *				pick_proc				     * 
X *===========================================================================*/
XPUBLIC pick_proc()
X{
X/* Decide who to run now. */
X
X  register int q;		/* which queue to use */
X
X  if (rdy_head[TASK_Q] != NIL_PROC) q = TASK_Q;
X  else if (rdy_head[SERVER_Q] != NIL_PROC) q = SERVER_Q;
X  else q = USER_Q;
X
X  /* Set 'cur_proc' and 'proc_ptr'. If system is idle, set 'cur_proc' to a
X   * special value (IDLE), and set 'proc_ptr' to point to an unused proc table
X   * slot, namely, that of task -1 (HARDWARE), so save() will have somewhere to
X   * deposit the registers when a interrupt occurs on an idle machine.
X   * Record previous process so that when clock tick happens, the clock task
X   * can find out who was running just before it began to run.  (While the
X   * clock task is running, 'cur_proc' = CLOCKTASK. In addition, set 'bill_ptr'
X   * to always point to the process to be billed for CPU time.
X   */
X  prev_proc = cur_proc;
X  if (rdy_head[q] != NIL_PROC) {
X	/* Someone is runnable. */
X	cur_proc = rdy_head[q] - proc - NR_TASKS;
X	proc_ptr = rdy_head[q];
X	if (cur_proc >= LOW_USER) bill_ptr = proc_ptr;
X  } else {
X	/* No one is runnable. */
X	cur_proc = IDLE;
X	proc_ptr = proc_addr(HARDWARE);
X	bill_ptr = proc_ptr;
X  }
X}
X
X/*===========================================================================*
X *				ready					     * 
X *===========================================================================*/
XPUBLIC ready(rp)
Xregister struct proc *rp;	/* this process is now runnable */
X{
X/* Add 'rp' to the end of one of the queues of runnable processes. Three
X * queues are maintained:
X *   TASK_Q   - (highest priority) for runnable tasks
X *   SERVER_Q - (middle priority) for MM and FS only
X *   USER_Q   - (lowest priority) for user processes
X */
X
X  register int q;		/* TASK_Q, SERVER_Q, or USER_Q */
X  int r;
X
X  lock();			/* disable interrupts */
X  r = (rp - proc) - NR_TASKS;	/* task or proc number */
X  q = (r < 0 ? TASK_Q : r < LOW_USER ? SERVER_Q : USER_Q);
X
X  /* See if the relevant queue is empty. */
X  if (rdy_head[q] == NIL_PROC)
X	rdy_head[q] = rp;	/* add to empty queue */
X  else
X	rdy_tail[q]->p_nextready = rp;	/* add to tail of nonempty queue */
X  rdy_tail[q] = rp;		/* new entry has no successor */
X  rp->p_nextready = NIL_PROC;
X  restore();			/* restore interrupts to previous state */
X}
X
X
X/*===========================================================================*
X *				unready					     * 
X *===========================================================================*/
XPUBLIC unready(rp)
Xregister struct proc *rp;	/* this process is no longer runnable */
X{
X/* A process has blocked. */
X
X  register struct proc *xp;
X  int r, q;
X
X  lock();			/* disable interrupts */
X  r = rp - proc - NR_TASKS;
X  q = (r < 0 ? TASK_Q : r < LOW_USER ? SERVER_Q : USER_Q);
X  if ( (xp = rdy_head[q]) == NIL_PROC) return;
X  if (xp == rp) {
X	/* Remove head of queue */
X	rdy_head[q] = xp->p_nextready;
X	pick_proc();
X  } else {
X	/* Search body of queue.  A process can be made unready even if it is
X	 * not running by being sent a signal that kills it.
X	 */
X	while (xp->p_nextready != rp)
X		if ( (xp = xp->p_nextready) == NIL_PROC) return;
X	xp->p_nextready = xp->p_nextready->p_nextready;
X	while (xp->p_nextready != NIL_PROC) xp = xp->p_nextready;
X	rdy_tail[q] = xp;
X  }
X  restore();			/* restore interrupts to previous state */
X}
X
X
X/*===========================================================================*
X *				sched					     * 
X *===========================================================================*/
XPUBLIC sched()
X{
X/* The current process has run too long.  If another low priority (user)
X * process is runnable, put the current process on the end of the user queue,
X * possibly promoting another user to head of the queue.
X */
X
X  lock();			/* disable interrupts */
X  if (rdy_head[USER_Q] == NIL_PROC) {
X	restore();		/* restore interrupts to previous state */
X	return;
X  }
X
X  /* One or more user processes queued. */
X  rdy_tail[USER_Q]->p_nextready = rdy_head[USER_Q];
X  rdy_tail[USER_Q] = rdy_head[USER_Q];
X  rdy_head[USER_Q] = rdy_head[USER_Q]->p_nextready;
X  rdy_tail[USER_Q]->p_nextready = NIL_PROC;
X  pick_proc();
X  restore();			/* restore interrupts to previous state */
X}
+ END-OF-FILE proc.c
chmod 'u=rw,g=r,o=r' \p\r\o\c\.\c
set `sum \p\r\o\c\.\c`
sum=$1
case $sum in
00455)	:;;
*)	echo 'Bad sum in '\p\r\o\c\.\c >&2
esac
echo Extracting \t\t\y\.\c
sed 's/^X//' > \t\t\y\.\c << '+ END-OF-FILE '\t\t\y\.\c
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 on a terminal (input interrupt)
X *   TTY_O_DONE:   a character has been output (output completed 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 *   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|         |         |         |         |         |
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 * | 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_TTYS            1	/* how many 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       16	/* size of overrun input buffer */
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 DEL_CODE   (char) 83	/* DEL for use in CTRL-ALT-DEL reboot */
X#define AT_SIGN         0220	/* code to yield for CTRL-@ */
X
X#define F1                59	/* scan code for function key F1 */
X#define F2                60	/* scan code for function key F2 */
X#define F10               68	/* scan code for function key F9 */
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 seen, 2 = ESC + x seen */
X  char tty_echar;		/* first character following an ESC */
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_column;		/* current column number (0-origin) */
X  int tty_row;			/* current row (0 at bottom 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
X  /* Miscellaneous. */
X  int tty_ioport;		/* I/O port number for this terminal */
X} tty_struct[NR_TTYS];
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
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 */
XPUBLIC  int color;		/* 1 if console is color, 0 if it is mono */
XPUBLIC scan_code;		/* scan code for '=' saved by bootstrap */
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',0177
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
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  while (TRUE) {
X	receive(ANY, &tty_mess);
X	tp = &tty_struct[tty_mess.TTY_LINE];
X	switch(tty_mess.m_type) {
X	    case TTY_CHAR_INT:	do_charint(&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 CANCEL   :	do_cancel(tp, &tty_mess);	break;
X	    case TTY_O_DONE:	/* reserved for future use (RS-232 terminals)*/
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_charint				     *
X *===========================================================================*/
XPRIVATE do_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;
X  char *ptr, *copy_ptr, ch;
X  struct tty_struct *tp;
X
X  lock();			/* prevent races by disabling interrupts */
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  unlock();			/* re-enable interrupts */
X
X  /* Loop on the accumulated characters, processing each in turn. */
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;
X  char make_break();
X  tp = &tty_struct[line];	/* set 'tp' to point to proper struct */
X  /* Function keys are temporarily being used for debug dumps. */
X  if (ch >= F1 && ch <= F10) {	/* Check for function keys F1, F2, ... F10 */
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)
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		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		cause_sig(LOW_USER + 1 + line, 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 (ch == tp->tty_xon) {
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  *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	if (capslock) {
X		if (unsh[c] >= 'a' && unsh[c] <= 'z') 
X			code = sh[c];
X		else
X			code = unsh[c];
X	} else {
X		code = (shift1 || shift2 ? sh[c] : unsh[c]);
X	} 
X	if (control && c < TOP_ROW) code = sh[c];	/* CTRL-(top row) */
X	if (c > 70 && numlock) code = sh[c];	/* numlock depressed */
X  } else {
X	/* (Olivetti M24 or AT&T 6300) with Olivetti-style keyboard. */
X	code = (shift1 || shift2 || capslock ? m24[c] : unm24[c]);
X	if (control && c < TOP_ROW) code = sh[c];	/* CTRL-(top row) */
X	if (c > 70 && numlock) code = m24[c];	/* numlock depressed */
X  }
X  code &= BYTE;
X  if (code < 0200 || code >= 0206) {
X	/* Ordinary key, i.e. not shift, control, alt, etc. */
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) capslock = 1 - capslock; break;	/* caps lock */
X    case 5:	if (make) numlock  = 1 - numlock;  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) out_char(tp, c);
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  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, E_TRY_AGAIN,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  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 replyee, caller;
X
X  tp->tty_rwords = 0;
X  tp->tty_outleft = 0;
X  if (tp->tty_waiting == NOT_WAITING) return;
X  replyee = (int) tp->tty_otcaller;
X  caller = (int) tp->tty_outproc;
X  tty_reply(TASK_REPLY, 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
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
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	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_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_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  /* 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 and output. */
X  tp->tty_inhead = tp->tty_inqueue;	/* discard all input */
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_outleft = 0;
X  tp->tty_waiting = NOT_WAITING;	/* don't send reply */
X  tp->tty_inhibited = RUNNING;
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/*****************************************************************************/
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
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  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  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
X  /* Update terminal data structure. */
X  count = offset - offset1;	/* # characters printed */
X  tp->tty_phys += count;	/* advance physical data pointer */
X  tp->tty_cum += count;		/* number of characters printed */
X
X  /* If all data has been copied to the terminal, send the reply. */
X  if (tp->tty_outleft == 0) finish(tp, tp->tty_cum);
X}
X
X
X/*===========================================================================*
X *				out_char				     *
X *===========================================================================*/
XPRIVATE out_char(tp, c)
Xregister struct tty_struct *tp;	/* pointer to tty struct */
Xchar c;				/* character to be output */
X{
X/* Output a character on the console. Check for escape sequences, including
X *   ESC 32+x 32+y to move cursor to (x, y)
X *   ESC ~ 0       to clear from cursor to end of screen
X *   ESC ~ 1       to reverse scroll the screen 1 line
X *   ESC z x       to set the attribute byte to x (z is a literal here)
X */
X
X  /* Check to see if we are part way through an escape sequence. */
X  if (tp->tty_esc_state == 1) {
X	tp->tty_echar = c;
X	tp->tty_esc_state = 2;
X	return;
X  }
X
X  if (tp->tty_esc_state == 2) {
X	escape(tp, tp->tty_echar, c);
X	tp->tty_esc_state = 0;
X	return;
X  }
X
X  switch(c) {
X	case 007:		/* ring the bell */
X		flush(tp);	/* print any chars queued for output */
X		beep(BEEP_FREQ);/* BEEP_FREQ gives bell tone */
X		return;
X
X	case 013:		/* CTRL-K */
X		move_to(tp, tp->tty_column, tp->tty_row + 1);
X		return;
X
X	case 014:		/* CTRL-L */
X		move_to(tp, tp->tty_column + 1, tp->tty_row);
X		return;
X
X	case 016:		/* CTRL-N */
X		move_to(tp, tp->tty_column + 1, tp->tty_row);
X		return;
X
X	case '\b':		/* backspace */
X		move_to(tp, tp->tty_column - 1, tp->tty_row);
X		return;
X
X	case '\n':		/* line feed */
X		if (tp->tty_mode & CRMOD) out_char(tp, '\r');
X		if (tp->tty_row == 0) 
X			scroll_screen(tp, GO_FORWARD);
X		else
X			tp->tty_row--;
X		move_to(tp, tp->tty_column, tp->tty_row);
X		return;
X
X	case '\r':		/* carriage return */
X		move_to(tp, 0, tp->tty_row);
X		return;
X
X	case '\t':		/* tab */
X		if ( (tp->tty_mode & XTABS) == XTABS) {
X			do {
X				out_char(tp, ' ');
X			} while (tp->tty_column & TAB_MASK);
X			return;
X		}
X		/* Ignore tab is XTABS is off--video RAM has no hardware tab */
X		return;
X
X	case 033:		/* ESC - start of an escape sequence */
X		flush(tp);	/* print any chars queued for output */
X		tp->tty_esc_state = 1;	/* mark ESC as seen */
X		return;
X
X	default:		/* printable chars are stored in ramqueue */
X		if (tp->tty_column >= LINE_WIDTH) return;	/* long line */
X		if (tp->tty_rwords == TTY_RAM_WORDS) flush(tp);
X		tp->tty_ramqueue[tp->tty_rwords++] = tp->tty_attribute | c;
X		tp->tty_column++;	/* next column */
X		return;
X  }
X}
X
X
X/*===========================================================================*
X *				scroll_screen				     *
X *===========================================================================*/
XPRIVATE scroll_screen(tp, dir)
Xregister struct tty_struct *tp;	/* pointer to tty struct */
Xint dir;			/* GO_FORWARD or GO_BACKWARD */
X{
X  int amount, offset;
X
X  amount = (dir == GO_FORWARD ? 2 * LINE_WIDTH : -2 * LINE_WIDTH);
X  tp->tty_org = (tp->tty_org + amount) & vid_mask;
X  if (dir == GO_FORWARD)
X	offset = (tp->tty_org + 2 * (SCR_LINES - 1) * LINE_WIDTH) & vid_mask;
X  else
X	offset = tp->tty_org;
X
X  /* Blank the new line at top or bottom. */
X  vid_copy(NIL_PTR, vid_base, offset, LINE_WIDTH);
X  set_6845(VID_ORG, tp->tty_org >> 1);	/* 6845 thinks in words */
X}
X
X
X/*===========================================================================*
X *				flush					     *
X *===========================================================================*/
XPRIVATE flush(tp)
Xregister struct tty_struct *tp;	/* pointer to tty struct */
X{
X/* Have the characters in 'ramqueue' transferred to the screen. */
X
X  if (tp->tty_rwords == 0) return;
X  vid_copy(tp->tty_ramqueue, vid_base, tp->tty_vid, tp->tty_rwords);
X
X  /* Update the video parameters and cursor. */
X  tp->tty_vid = (tp->tty_vid + 2 * tp->tty_rwords);
X  set_6845(CURSOR, tp->tty_vid >> 1);	/* cursor counts in words */
X  tp->tty_rwords = 0;
X}
X
X
X/*===========================================================================*
X *				move_to					     *
X *===========================================================================*/
XPRIVATE move_to(tp, x, y)
Xstruct tty_struct *tp;		/* pointer to tty struct */
Xint x;				/* column (0 <= x <= 79) */
Xint y;				/* row (0 <= y <= 24, 0 at bottom) */
X{
X/* Move the cursor to (x, y). */
X
X  flush(tp);			/* flush any pending characters */
X  if (x < 0 || x >= LINE_WIDTH || y < 0 || y >= SCR_LINES) return;
X  tp->tty_column = x;		/* set x co-ordinate */
X  tp->tty_row = y;		/* set y co-ordinate */
X  tp->tty_vid = (tp->tty_org + 2*(SCR_LINES-1-y)*LINE_WIDTH + 2*x);
X  set_6845(CURSOR, tp->tty_vid >> 1);	/* cursor counts in words */
X}
X
X
X/*===========================================================================*
X *				escape					     *
X *===========================================================================*/
XPRIVATE escape(tp, x, y)
Xregister struct tty_struct *tp;	/* pointer to tty struct */
Xchar x;				/* escape sequence is ESC x y; this is x */
Xchar y;				/* escape sequence is ESC x y; this is y */
X{
X/* Handle an escape sequence. */
X
X  int n, ct, vx;
X
X
X  /* Check for ESC z attribute - used to change attribute byte. */
X  if (x == 'z') {
X	/* Set attribute byte */
X	tp->tty_attribute = y << 8;
X	return;
X  }
X  /* Check for ESC ~ n -  used for clear screen, reverse scroll. */
X  if (x == '~') {
X	if (y == '0') {
X		/* Clear from cursor to end of screen */
X		n = 2 * LINE_WIDTH * (tp->tty_row + 1) - 2 * tp->tty_column;
X		vx = tp->tty_vid;
X		while (n > 0) {
X			ct = MIN(n, vid_retrace);
X			vid_copy(NIL_PTR, vid_base, vx, ct/2);
X			vx += ct;
X			n -= ct;
X		}
X	} else if (y == '1') {
X		/* Reverse scroll. */
X		scroll_screen(tp, GO_BACKWARD);
X	}
X	return;
X  }
X
X  /* Must be cursor movement (or invalid). */
X  move_to(tp, x - 32, y - 32);
X}
X
X
X/*===========================================================================*
X *				set_6845				     *
X *===========================================================================*/
XPRIVATE set_6845(reg, val)
Xint reg;			/* which register pair to set */
Xint val;			/* 16-bit value to set it to */
X{
X/* Set a register pair inside the 6845.  
X * Registers 10-11 control the format of the cursor (how high it is, etc).
X * Registers 12-13 tell the 6845 where in video ram to start (in WORDS)
X * Registers 14-15 tell the 6845 where to put the cursor (in WORDS)
X *
X * Note that registers 12-15 work in words, i.e. 0x0000 is the top left
X * character, but 0x0001 (not 0x0002) is the next character.  This addressing
X * is different from the way the 8088 addresses the video ram, where 0x0002
X * is the address of the next character.
X */
X  port_out(vid_port + INDEX, reg);	/* set the index register */
X  port_out(vid_port + DATA, (val>>8) & BYTE);	/* output high byte */
X  port_out(vid_port + INDEX, reg + 1);	/* again */
X  port_out(vid_port + DATA, val&BYTE);	/* output low byte */
X}
X
X
X/*===========================================================================*
X *				beep					     *
X *===========================================================================*/
XPRIVATE beep(f)
Xint f;				/* this value determines beep frequency */
X{
X/* Making a beeping sound on the speaker (output for CRTL-G).  The beep is
X * kept short, because interrupts must be disabled during beeping, and it
X * is undesirable to keep them off too long.  This routine works by turning
X * on the bits in port B of the 8255 chip that drive the speaker.
X */
X
X  int x, k;
X
X  lock();			/* disable interrupts */
X  port_out(TIMER3,0xB6);	/* set up timer channel 2 mode */
X  port_out(TIMER2, f&BYTE);	/* load low-order bits of frequency in timer */
X  port_out(TIMER2,(f>>8)&BYTE);	/* now high-order bits of frequency in timer */
X  port_in(PORT_B,&x);		/* acquire status of port B */
X  port_out(PORT_B, x|3);	/* turn bits 0 and 1 on to beep */
X  for (k = 0; k < B_TIME; k++);	/* delay loop while beeper sounding */
X  port_out(PORT_B, x);		/* restore port B the way it was */
X  unlock();			/* re-enable interrupts */
X}
X
X
X/*===========================================================================*
X *				tty_init				     *
X *===========================================================================*/
XPRIVATE tty_init()
X{
X/* Initialize the tty tables. */
X
X  register struct tty_struct *tp;
X  int i;
X  phys_bytes phy1, phy2, vram;
X
X  /* Tell the EGA card, if any, to simulate a 16K CGA card. */
X  port_out(EGA + INDEX, 4);	/* register select */
X  port_out(EGA + DATA, 1);	/* no extended memory to be used */
X
X  for (tp = &tty_struct[0]; tp < &tty_struct[NR_TTYS]; tp++) {
X	tp->tty_inhead = tp->tty_inqueue;
X	tp->tty_intail = tp->tty_inqueue;
X	tp->tty_mode = CRMOD | XTABS | ECHO;
X	tp->tty_devstart = console;
X	tp->tty_erase = ERASE_CHAR;
X	tp->tty_kill  = KILL_CHAR;
X	tp->tty_intr  = INTR_CHAR;
X	tp->tty_quit  = QUIT_CHAR;
X	tp->tty_xon   = XON_CHAR;
X	tp->tty_xoff  = XOFF_CHAR;
X	tp->tty_eof   = EOT_CHAR;
X  }
X
X  tty_struct[0].tty_makebreak = TWO_INTS;	/* tty 0 is console */
X  if (color) {
X	vid_base = COLOR_BASE;
X	vid_mask = C_VID_MASK;
X	vid_port = C_6845;
X	vid_retrace = C_RETRACE;
X  } else {
X	vid_base = MONO_BASE;
X	vid_mask = M_VID_MASK;
X	vid_port = M_6845;
X	vid_retrace = M_RETRACE;
X  }
X  tty_struct[0].tty_attribute = BLANK;
X  tty_driver_buf[1] = MAX_OVERRUN;	/* set up limit on keyboard buffering */
X  set_6845(CUR_SIZE, 31);		/* set cursor shape */
X  set_6845(VID_ORG, 0);			/* use page 0 of video ram */
X  move_to(&tty_struct[0], 0, 0);	/* move cursor to lower left corner */
X
X  /* Determine which keyboard type is attached.  The bootstrap program asks 
X   * the user to type an '='.  The scan codes for '=' differ depending on the
X   * keyboard in use.
X   */
X  if (scan_code == OLIVETTI_EQUAL) olivetti = TRUE;
X}
X
X
X/*===========================================================================*
X *				putc					     *
X *===========================================================================*/
XPUBLIC putc(c)
Xchar c;				/* character to print */
X{
X/* This procedure is used by the version of printf() that is linked with
X * the kernel itself.  The one in the library sends a message to FS, which is
X * not what is needed for printing within the kernel.  This version just queues
X * the character and starts the output.
X */
X
X  out_char(&tty_struct[0], c);
X}
X
X
X/*===========================================================================*
X *				func_key				     *
X *===========================================================================*/
XPRIVATE func_key(ch)
Xchar ch;			/* scan code for a function key */
X{
X/* This procedure traps function keys for debugging purposes.  When MINIX is
X * fully debugged, it should be removed.
X */
X
X  if (ch == F1) p_dmp();	/* print process table */
X  if (ch == F2) map_dmp();	/* print memory map */
X}
X#endif
+ END-OF-FILE tty.c
chmod 'u=rw,g=r,o=r' \t\t\y\.\c
set `sum \t\t\y\.\c`
sum=$1
case $sum in
34166)	:;;
*)	echo 'Bad sum in '\t\t\y\.\c >&2
esac
exit 0

mikpe@smidefix.liu.se (Mikael Pettersson) (07/05/87)

In article <1514@botter.cs.vu.nl> ast@cs.vu.nl (Andy Tanenbaum) writes:
>: 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
>echo Extracting \p\r\i\n\t\e\r\.\c
>sed 's/^X//' > \p\r\i\n\t\e\r\.\c << '+ END-OF-FILE '\p\r\i\n\t\e\r\.\c
>X/* This file contains the printer driver. It is a fairly simple driver,

[about 200 lines of printer driver code deleted]

>X/*===========================================================================*
>X *				X  tp->tty_org = (tp->tty_org + amount) & vid_mask;
>X  if (dir == GO_FORWARD)
>X	offset = (tp->tty_org + 2 * (SCR_LINES - 1) * LINE


This is the end of the file. The total number of lines received is 223,
although the header indicates that the file should be 1924 lines long.
The path was: liuida!enea!mcvax!botter!ast

Obviously the file was truncated somewhere along this path. If this is a
global problem then perhaps a repost should be made. If it's local could
someone *please* mail it to me.

--
Mikael Pettersson
Dept. of Comp. & Info. Sci, Univ. of Linkoping, Sweden
Internet: mikpe@ida.liu.se
UUCP:     mikpe@liuida.uucp -or- {mcvax,munnari,seismo}!enea!liuida!mikpe
ARPA:     mikpe%ida.liu.se@seismo.CSS.GOV
-- 
Internet: mikpe@ida.liu.se
UUCP:     {mcvax,munnari,seismo}!enea!liuida!mikpe
ARPA:     mikpe%ida.liu.se@seismo.CSS.GOV