Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!seismo!mcvax!botter!ast From: ast@cs.vu.nl (Andy Tanenbaum) Newsgroups: comp.os.minix Subject: MINIX 1.2 kernel changes Message-ID: <1514@botter.cs.vu.nl> Date: Wed, 1-Jul-87 07:54:08 EDT Article-I.D.: botter.1514 Posted: Wed Jul 1 07:54:08 1987 Date-Received: Thu, 2-Jul-87 04:55:40 EDT Reply-To: ast@cs.vu.nl (Andy Tanenbaum) Distribution: world Organization: VU Informatica, Amsterdam Lines: 1924 : 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 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