[comp.os.minix] V1.3 posting #22 kernel/tty.c part 2 of 2

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

	keybd_mess.m_type = TTY_CHAR_INT;
	keybd_mess.ADDRESS = tty_driver_buf;
	interrupt(TTY, &keybd_mess);	/* send a message to the tty task */
  } else {
	/* Too many characters have been buffered.  Discard excess. */
	port_out(INT_CTL, ENABLE);	/* re-enable 8259A controller */
  }
}


/*===========================================================================*
 *				console					     *
 *===========================================================================*/
PRIVATE console(tp)
register struct tty_struct *tp;	/* tells which terminal is to be used */
{
/* Copy as much data as possible to the output queue, then start I/O.  On
 * memory-mapped terminals, such as the IBM console, the I/O will also be
 * finished, and the counts updated.  Keep repeating until all I/O done.
 */

  extern char get_byte();
  int count;
  char c;
  unsigned segment, offset, offset1;

  /* Loop over the user bytes one at a time, outputting each one. */
  segment = (tp->tty_phys >> 4) & WORD_MASK;
  offset = tp->tty_phys & OFF_MASK;
  offset1 = offset;
  count = 0;

  while (tp->tty_outleft > 0 && tp->tty_inhibited == RUNNING) {
	c = get_byte(segment, offset);	/* fetch 1 byte from user space */
	out_char(tp, c);	/* write 1 byte to terminal */
	offset++;		/* advance one character in user buffer */
	tp->tty_outleft--;	/* decrement count */
  }
  flush(tp);			/* clear out the pending characters */

  /* Update terminal data structure. */
  count = offset - offset1;	/* # characters printed */
  tp->tty_phys += count;	/* advance physical data pointer */
  tp->tty_cum += count;		/* number of characters printed */

  /* If all data has been copied to the terminal, send the reply. */
  if (tp->tty_outleft == 0) finish(tp, tp->tty_cum);
}


/*===========================================================================*
 *				out_char				     *
 *===========================================================================*/
PRIVATE out_char(tp, c)
register struct tty_struct *tp;	/* pointer to tty struct */
char c;				/* character to be output */
{
/* Output a character on the console.  Check for escape sequences first. */

  if (tp->tty_esc_state > 0) {
	parse_escape(tp, c);
	return;
  }

  switch(c) {
	case 007:		/* ring the bell */
		flush(tp);	/* print any chars queued for output */
		beep(BEEP_FREQ);/* BEEP_FREQ gives bell tone */
		return;

	case 013:		/* CTRL-K */
		move_to(tp, tp->tty_column, tp->tty_row - 1);
		return;

	case 014:		/* CTRL-L */
		move_to(tp, tp->tty_column + 1, tp->tty_row);
		return;

	case 016:		/* CTRL-N */
		move_to(tp, tp->tty_column + 1, tp->tty_row);
		return;

	case '\b':		/* backspace */
		move_to(tp, tp->tty_column - 1, tp->tty_row);
		return;

	case '\n':		/* line feed */
		if (tp->tty_mode & CRMOD) out_char(tp, '\r');
		if (tp->tty_row == SCR_LINES-1) 
			scroll_screen(tp, GO_FORWARD);
		else
			tp->tty_row++;
		move_to(tp, tp->tty_column, tp->tty_row);
		return;

	case '\r':		/* carriage return */
		move_to(tp, 0, tp->tty_row);
		return;

	case '\t':		/* tab */
		if ( (tp->tty_mode & XTABS) == XTABS) {
			do {
				out_char(tp, ' ');
			} while (tp->tty_column & TAB_MASK);
			return;
		}
		/* Ignore tab if XTABS is off--video RAM has no hardware tab */
		return;

	case 033:		/* ESC - start of an escape sequence */
		flush(tp);	/* print any chars queued for output */
		tp->tty_esc_state = 1;	/* mark ESC as seen */
		return;

	default:		/* printable chars are stored in ramqueue */
		if (tp->tty_column >= LINE_WIDTH) return;	/* long line */
		if (tp->tty_rwords == TTY_RAM_WORDS) flush(tp);
		tp->tty_ramqueue[tp->tty_rwords++]=tp->tty_attribute|(c & BYTE);
		tp->tty_column++;	/* next column */
		return;
  }
}

/*===========================================================================*
 *				scroll_screen				     *
 *===========================================================================*/
PRIVATE scroll_screen(tp, dir)
register struct tty_struct *tp;	/* pointer to tty struct */
int dir;			/* GO_FORWARD or GO_BACKWARD */
{
  int amount, offset, bytes;

  bytes = 2 * (SCR_LINES - 1) * LINE_WIDTH;	/* 2 * 24 * 80 bytes */

  /* Scrolling the screen is a real nuisance due to the various incompatible
   * video cards.  This driver supports hardware scrolling (mono and CGA cards)
   * and software scrolling (EGA cards).
   */
  if (softscroll) {
	/* Software scrolling for non-IBM compatible EGA cards. */
	if (dir == GO_FORWARD) {
		scr_up(vid_base);
		vid_copy(NIL_PTR, vid_base, tp->tty_org+bytes, LINE_WIDTH);
	} else {
		scr_down(vid_base);
		vid_copy(NIL_PTR, vid_base, tp->tty_org, LINE_WIDTH);
	}
  } else {
	/* Normal scrolling using the 6845 registers. */
	amount = (dir == GO_FORWARD ? 2 * LINE_WIDTH : -2 * LINE_WIDTH);
	tp->tty_org = (tp->tty_org + amount) & vid_mask;
	if (dir == GO_FORWARD)
		offset = (tp->tty_org + bytes) & vid_mask;
	else
		offset = tp->tty_org;

	/* Blank the new line at top or bottom. */
	vid_copy(NIL_PTR, vid_base, offset, LINE_WIDTH);
	set_6845(VID_ORG, tp->tty_org >> 1);	/* 6845 thinks in words */
  }
}


/*===========================================================================*
 *				flush					     *
 *===========================================================================*/
PRIVATE flush(tp)
register struct tty_struct *tp;	/* pointer to tty struct */
{
/* Have the characters in 'ramqueue' transferred to the screen. */

  if (tp->tty_rwords == 0) return;
  vid_copy((char *)tp->tty_ramqueue, vid_base, tp->tty_vid, tp->tty_rwords);

  /* Update the video parameters and cursor. */
  tp->tty_vid = (tp->tty_vid + 2 * tp->tty_rwords);
  set_6845(CURSOR, tp->tty_vid >> 1);	/* cursor counts in words */
  tp->tty_rwords = 0;
}


/*===========================================================================*
 *				move_to					     *
 *===========================================================================*/
PRIVATE move_to(tp, x, y)
struct tty_struct *tp;		/* pointer to tty struct */
int x;				/* column (0 <= x <= 79) */
int y;				/* row (0 <= y <= 24, 0 at top) */
{
/* Move the cursor to (x, y). */

  flush(tp);			/* flush any pending characters */
  if (x < 0 || x >= LINE_WIDTH || y < 0 || y >= SCR_LINES) return;
  tp->tty_column = x;		/* set x co-ordinate */
  tp->tty_row = y;		/* set y co-ordinate */
  tp->tty_vid = (tp->tty_org + 2*y*LINE_WIDTH + 2*x);
  set_6845(CURSOR, tp->tty_vid >> 1);	/* cursor counts in words */
}


/*===========================================================================*
 *				escape					     *
 *===========================================================================*/
PRIVATE parse_escape(tp, c)
register struct tty_struct *tp;	/* pointer to tty struct */
char c;				/* next character in escape sequence */
{
/* The following ANSI escape sequences are currently supported:
 *   ESC M         to reverse index the screen
 *   ESC [ y ; x H to move cursor to (x, y) [default (1,1)]
 *   ESC [ 0 J     to clear from cursor to end of screen
 *   ESC [ n m     to set the screen rendition
 *			n: 0 = normal [default]
 *			   7 = reverse
 */

  switch (tp->tty_esc_state) {
	case 1: 		/* ESC seen */
		tp->tty_esc_intro = '\0';
		tp->tty_esc_parmp = tp->tty_esc_parmv;
		tp->tty_esc_parmv[0] = tp->tty_esc_parmv[1] = 0;
		switch (c) {
		  case '[': 	/* Control Sequence Introducer */
			tp->tty_esc_intro = c;
			tp->tty_esc_state = 2; 
			break;
		  case 'M': 	/* Reverse Index */
			do_escape(tp, c);
			break;
		  default: 
			tp->tty_esc_state = 0; 
			break;
		}
		break;

	case 2: 		/* ESC [ seen */
		if (c >= '0' && c <= '9') {
			if (tp->tty_esc_parmp 
					< tp->tty_esc_parmv + MAX_ESC_PARMS)
				*tp->tty_esc_parmp =
				  *tp->tty_esc_parmp * 10 + (c - '0');
			break;
		}
		else if (c == ';') {
			if (++tp->tty_esc_parmp 
					< tp->tty_esc_parmv + MAX_ESC_PARMS)
				*tp->tty_esc_parmp = 0;
			break;
		}
		else {
			do_escape(tp, c);
		}
		break;
	default:		/* illegal state */
		tp->tty_esc_state = 0;
		break;
  }
}


/*===========================================================================*
 *				do_escape				     *
 *===========================================================================*/
PRIVATE do_escape(tp, c)
register struct tty_struct *tp;	/* pointer to tty struct */
char c;				/* next character in escape sequence */
{
  int n, ct, vx;

  /* Handle a sequence beginning with just ESC */
  if (tp->tty_esc_intro == '\0') {
    switch (c) {
	case 'M':		/* Reverse Index */
		if (tp->tty_row == 0)
			scroll_screen(tp, GO_BACKWARD);
		else
			tp->tty_row--;
		move_to(tp, tp->tty_column, tp->tty_row);
		break;
	default: break;
    }
  }
  else
  /* Handle a sequence beginning with ESC [ and parameters */
  if (tp->tty_esc_intro == '[') {
    switch (c) {
	case 'H':		/* Position cursor */
		move_to(tp, 
			MAX(1, MIN(LINE_WIDTH, tp->tty_esc_parmv[1])) - 1,
			MAX(1, MIN(SCR_LINES, tp->tty_esc_parmv[0])) - 1 );
		break;
	case 'J':		/* Clear from cursor to end of screen */
		if (tp->tty_esc_parmv[0] == 0) {
			n = 2 * ((SCR_LINES - (tp->tty_row + 1)) * LINE_WIDTH
				+ LINE_WIDTH - (tp->tty_column));
			vx = tp->tty_vid;
			while (n > 0) {
				ct = MIN(n, vid_retrace);
				vid_copy(NIL_PTR, vid_base, vx, ct/2);
				vx += ct;
				n -= ct;
			}
		}
		break;
	case 'm':		/* Set graphic rendition */
 		switch (tp->tty_esc_parmv[0]) {
 			case 1: /*  BOLD  (light green on black)  */
 				tp->tty_attribute = 0x0A << 8;
 				break;
 
 			case 4: /*  UNDERLINE  (blue on red)  */
 				tp->tty_attribute = 0x41 << 8;
 				break;
 
 			case 5: /*  BLINKING  (light grey on black)  */
				tp->tty_attribute = 0x87 << 8;
 				break;
 
 			case 7: /*  REVERSE  (black on light grey)  */
 				tp->tty_attribute = 0x70 << 8;
  				break;

 			default: tp->tty_attribute = 0007 << 8;
 				break;
 		}
		break;
	default:
		break;
    }
  }
  tp->tty_esc_state = 0;
}


/*===========================================================================*
 *				set_6845				     *
 *===========================================================================*/
PRIVATE set_6845(reg, val)
int reg;			/* which register pair to set */
int val;			/* 16-bit value to set it to */
{
/* Set a register pair inside the 6845.  
 * Registers 10-11 control the format of the cursor (how high it is, etc).
 * Registers 12-13 tell the 6845 where in video ram to start (in WORDS)
 * Registers 14-15 tell the 6845 where to put the cursor (in WORDS)
 *
 * Note that registers 12-15 work in words, i.e. 0x0000 is the top left
 * character, but 0x0001 (not 0x0002) is the next character.  This addressing
 * is different from the way the 8088 addresses the video ram, where 0x0002
 * is the address of the next character.
 */
  port_out(vid_port + INDEX, reg);	/* set the index register */
  port_out(vid_port + DATA, (val>>8) & BYTE);	/* output high byte */
  port_out(vid_port + INDEX, reg + 1);	/* again */
  port_out(vid_port + DATA, val&BYTE);	/* output low byte */
}


/*===========================================================================*
 *				beep					     *
 *===========================================================================*/
PRIVATE beep(f)
int f;				/* this value determines beep frequency */
{
/* Making a beeping sound on the speaker (output for CRTL-G).  The beep is
 * kept short, because interrupts must be disabled during beeping, and it
 * is undesirable to keep them off too long.  This routine works by turning
 * on the bits in port B of the 8255 chip that drive the speaker.
 */

  int x, k;

  lock();			/* disable interrupts */
  port_out(TIMER3,0xB6);	/* set up timer channel 2 mode */
  port_out(TIMER2, f&BYTE);	/* load low-order bits of frequency in timer */
  port_out(TIMER2,(f>>8)&BYTE);	/* now high-order bits of frequency in timer */
  port_in(PORT_B,&x);		/* acquire status of port B */
  port_out(PORT_B, x|3);	/* turn bits 0 and 1 on to beep */
  for (k = 0; k < B_TIME; k++);	/* delay loop while beeper sounding */
  port_out(PORT_B, x);		/* restore port B the way it was */
  unlock();			/* re-enable interrupts */
}


/*===========================================================================*
 *				set_leds				     *
 *===========================================================================*/
PRIVATE set_leds()
{
/* Set the LEDs on the caps lock and num lock keys */

  int leds, dummy, i;

  if (pc_at == 0) return;	/* PC/XT doesn't have LEDs */
  leds = (numlock<<1) | (capslock<<2);	/* encode LED bits */
  port_out(KEYBD, LED_CODE);	/* prepare keyboard to accept LED values */
  port_in(KEYBD, &dummy);	/* keyboard sends ack; accept it */
  for (i = 0; i < LED_DELAY; i++) ;	/* delay needed */
  port_out(KEYBD, leds);	/* give keyboard LED values */
  port_in(KEYBD, &dummy);	/* keyboard sends ack; accept it */
}


/*===========================================================================*
 *				tty_init				     *
 *===========================================================================*/
PRIVATE tty_init()
{
/* Initialize the tty tables. */

  register struct tty_struct *tp;

  /* Tell the EGA card, if any, to simulate a 16K CGA card. */
  port_out(EGA + INDEX, 4);	/* register select */
  port_out(EGA + DATA, 1);	/* no extended memory to be used */

  for (tp = &tty_struct[0]; tp < &tty_struct[NR_CONS]; tp++) {
	tp->tty_inhead = tp->tty_inqueue;
	tp->tty_intail = tp->tty_inqueue;
	tp->tty_mode = CRMOD | XTABS | ECHO;
	tp->tty_devstart = console;
	tp->tty_makebreak = TWO_INTS;
	tp->tty_attribute = BLANK;
	tp->tty_erase = ERASE_CHAR;
	tp->tty_kill  = KILL_CHAR;
	tp->tty_intr  = INTR_CHAR;
	tp->tty_quit  = QUIT_CHAR;
	tp->tty_xon   = XON_CHAR;
	tp->tty_xoff  = XOFF_CHAR;
	tp->tty_eof   = EOT_CHAR;
  }

  if (color) {
	vid_base = COLOR_BASE;
	vid_mask = C_VID_MASK;
	vid_port = C_6845;
	vid_retrace = C_RETRACE;
  } else {
	vid_base = MONO_BASE;
	vid_mask = M_VID_MASK;
	vid_port = M_6845;
	vid_retrace = M_RETRACE;
  }
  tty_driver_buf[1] = MAX_OVERRUN;	/* set up limit on keyboard buffering */
  set_6845(CUR_SIZE, 31);		/* set cursor shape */
  set_6845(VID_ORG, 0);			/* use page 0 of video ram */
  move_to(&tty_struct[0], 0, SCR_LINES-1); /* move cursor to lower left */

  /* Determine which keyboard type is attached.  The bootstrap program asks 
   * the user to type an '='.  The scan codes for '=' differ depending on the
   * keyboard in use.
   */
  if (scan_code == OLIVETTI_EQUAL) olivetti = TRUE;
}


/*===========================================================================*
 *				putc					     *
 *===========================================================================*/
PUBLIC putc(c)
char c;				/* character to print */
{
/* This procedure is used by the version of printf() that is linked with
 * the kernel itself.  The one in the library sends a message to FS, which is
 * not what is needed for printing within the kernel.  This version just queues
 * the character and starts the output.
 */

  out_char(&tty_struct[0], c);
}


/*===========================================================================*
 *				func_key				     *
 *===========================================================================*/
PRIVATE func_key(ch)
char ch;			/* scan code for a function key */
{
/* This procedure traps function keys for debugging purposes.  When MINIX is
 * fully debugged, it should be removed.
 */

  if (ch == F1) p_dmp();	/* print process table */
  if (ch == F2) map_dmp();	/* print memory map */
  if (ch == F3) {		/* hardware vs. software scrolling */
	softscroll = 1 - softscroll;	/* toggle scroll mode */
	tty_struct[0].tty_org = 0;
	move_to(&tty_struct[0], 0, SCR_LINES-1); /* cursor to lower left */
	set_6845(VID_ORG, 0);
	if (softscroll)
		printf("\033[H\033[JSoftware scrolling enabled.\n");
	else
		printf("\033[H\033[JHardware scrolling enabled.\n");
  }
if(ch == F5)prlog(); if (ch==F5+1) init_rs232();/*DEBUG*/
#ifdef AM_KERNEL
#ifndef NONET
  if (ch == F4) net_init();	/* re-initialise the ethernet card */
#endif NONET
#endif AM_KERNEL
  if (ch == F9) sigchar(&tty_struct[0], SIGKILL);	/* issue SIGKILL */
}
#endif


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


#ifdef i8088
/* Now begins the code and data for the device RS232 drivers. */

/* Definitions used by the RS232 driver. */
#define	RS_BUF_SIZE		 256	/* output buffer per serial line */
#define PRIMARY                0x3F8	/* I/O port of primary RS232 */
#define SECONDARY              0x2F8	/* I/O port of secondary RS232 */

/* Constants relating to the 8250. */
#define	RS232_INTERRUPT_ID_REG	   2	/* address of interrupt id register */
#define	RS232_LINE_CONTROL	   3	/* address of line control register */
#define	RS232_MODEM_CONTROL	   4	/* address of modem control register */
#define	RS232_LINE_STATUS	   5	/* address of line status register */
#define	RS232_RATE_DIVISOR	   0	/* address of baud rate divisor reg */
#define	RS232_TRANSMIT_HOLDING	   0	/* address of transmitter holding reg*/
#define	RS232_RECEIVER_DATA_REG	   0	/* address of receiver data register */
#define	RS232_INTERRUPTS	   1	/* address of interrupt enable reg */
#define	LINE_CONTROLS		0x0B	/* odd parity,1 stop bit,8 data bits */
#define	MODEM_CONTROLS		0x0B	/* RTS & DTR */
#define	ADDRESS_DIVISOR		0x80	/* value to address divisor */
#define	RS232_INTERRUPT_CLASSES	0x03	/* receiver Data Ready & xmt empty */
#define	UART_FREQ  	     115200L	/* UART timer frequency */

/* Line control setting related constants. */
#define ODD			   0
#define	EVEN			   1
#define NONE			  -1
#define	PARITY_ON_OFF		0x08	/* position of parity bit in line reg*/
#define	PARITY_TYPE_SHIFT	   4	/* shift count for parity_type bit */
#define	STOP_BITS_SHIFT		   2	/* shift count for # stop_bits */
#define DATA_LEN                   8	/* how much to shift sg_mode for len */

/* RS232 interrupt types. */
#define TRANSMITTER_READY	0x02	/* transmitter ready to accept data */
#define RECEIVER_READY		0x04	/* data received interrupt */
#define INT_TYPE_MASK		0x06	/* mask to mask out interrupt type */
#define	INT_PENDING		0x01	/* position of interrupt-pending bit */

/* Status register values */
#define DATA_REGISTER_EMPTY	0x20	/* mask to see if data reg is empty */
#define DATA_RECEIVED		0x01	/* mask to see if data has arrived */

/* Global variables used by the RS232 driver */
PUBLIC	message	rs232_mess;
PRIVATE	int	first_rs_write_int_seen = FALSE;
PRIVATE	struct rs_struct{
	int	rs_base;		/* 0x3F8 for primary, 0x2F8 secondary*/
	int	rs_busy;		/* line is idle or not */
	int	rs_left;		/* # chars left in buffer to output */
	char	*rs_next;		/* pointer to next char to output */
	char	rs_buf[RS_BUF_SIZE];	/* output buffer */
} rs_struct[NR_RS_LINES];


/*===========================================================================*
 *				rs232				 	     *
 *===========================================================================*/
PUBLIC rs232(unit)
int unit;				/* which unit caused the interrupt */
{
  int interrupt_type, t;
  struct rs_struct *rs;

  /* When an RS232 interrupt occurs, mpx88.s catches it and calls rs232().
   * Because more than one interrupt condition can occur at the same
   * time, the conditions are presented in the interrupt-identification
   * register in priority order, we have to keep scanning until the 
   * interrupt-pending bit goes down.  Only one communications port is really
   * supported here because the other vector is used by the Ethernet.
   */

Event(1); /*DEBUG*/ 
Count(ct_rs232);  /*DEBUG*/
  rs = &rs_struct[unit - NR_CONS];
  do {
	port_in(rs->rs_base + RS232_INTERRUPT_ID_REG, & interrupt_type); 
	t = interrupt_type & INT_TYPE_MASK;
	switch(t) {
	    case RECEIVER_READY:	/* a character has arrived */
Event(2); /*DEBUG*/
		rs_read_int(unit);
		break;

	    case TRANSMITTER_READY:	/* a character has been output */
Event(3); /*DEBUG*/
		rs_write_int(unit);
		break;

	    default:			/* something unexpected has happened */
		panic("RS232_task got illegal interrupt. Type: ", t);
	}
  }
  while(interrupt_type & INT_PENDING);	/* multiple interrupts are possible */
Event(4); /*DEBUG*/
}


/*===========================================================================*
 *				rs_read_int			 	     *
 *===========================================================================*/
PRIVATE	rs_read_int(line)
int line;
{
  int val, k, base;

Event(5); /*DEBUG*/
Count(ct_rd); /*DEBUG*/
  base = rs_struct[line - NR_CONS].rs_base;

  /* Fetch the character from the RS232 hardware. */
  port_in(base + RS232_RECEIVER_DATA_REG, &val);

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

	/* Build and send the interrupt message */
	rs232_mess.m_type = TTY_CHAR_INT;
	rs232_mess.ADDRESS = tty_driver_buf;
	interrupt(TTY, &rs232_mess);	/* send a message to the tty task */
  } else {
	/* Too many character have been buffered. Discard excess */
	port_out(INT_CTL, ENABLE);	/* re-enable 8259A controller */
  }
Event(7); /*DEBUG*/
}


/*===========================================================================*
 *				rs_write_int	  		 	     *
 *===========================================================================*/
PRIVATE	rs_write_int(line)
int line;
{
/* An output ready interrupt has occurred, or a write has been done to an idle
 * line.  In both cases, start the output.
 */

  char byte;
  struct rs_struct *rs;

Event(8); /*DEBUG*/
Count(ct_wt); /*DEBUG*/
  if (!first_rs_write_int_seen) {
	first_rs_write_int_seen = TRUE;
	port_out(INT_CTL, ENABLE);	/* re-enable 8259A controller */
	return;
  }

Event(9); /*DEBUG*/
  rs = &rs_struct[line-NR_CONS];

  /* If there is more output queued, output the next character. */
  if (rs->rs_left > 0) {
	byte = *rs->rs_next;
	putc(byte);		/* DEBUG print to screen!!!!! */
Event(24); /*DEBUG*/
	write_byte(rs, byte);	/* Write next char to UART */
Event(25); /*DEBUG*/
	rs->rs_next++;
	rs->rs_left--;			/* One char done */
	port_out(INT_CTL, ENABLE);	/* re-enable 8259A controller */
	return;
  }
Event(10); /*DEBUG*/
  /* The current output buffer is finished.  Send a message to the tty task. */
  rs->rs_busy = FALSE;
  rs->rs_next = &rs->rs_buf[0];
  rs->rs_left = 0;
  rs232_mess.m_type = TTY_O_DONE;	/* build the message */
  rs232_mess.TTY_LINE = line;		/* which line is finished */
  interrupt(TTY, &rs232_mess);		/* send the message to the tty task */
Event(11); /*DEBUG*/
}


/*===========================================================================*
 *				write_byte			 	     *
 *===========================================================================*/
PRIVATE	write_byte(rs, byte)
struct rs_struct *rs;			/* which tty */
char byte;				/* byte to write */
{
Event(12); /*DEBUG*/
Count(ct_wb); /*DEBUG*/
  port_out(rs->rs_base + RS232_TRANSMIT_HOLDING, (int) byte);
}


/*===========================================================================*
 *				serial_out				     * 
 *===========================================================================*/
PRIVATE	serial_out(tp)
register struct tty_struct *tp;	/* tells which terminal is to be used */
{
/* Copy as much data as possible to the output queue, then start I/O if
 * necessary.
 */
  int room, bytes, line;
  phys_bytes tty_phys;
  struct rs_struct *rs;

Event(13); /*DEBUG*/
Count(ct_ser); /*DEBUG*/
  if (tp->tty_inhibited != RUNNING) return;

  line = tp - &tty_struct[0];		/* line is index into tty_struct */
  rs = &rs_struct[line - NR_CONS];	/* 0 to NR_CONS - 1 are consoles */
	
  /* Compute how many bytes can be copied to rs_buf and copy them. */
  lock();
  room = &rs->rs_buf[RS_BUF_SIZE] - (rs->rs_next + rs->rs_left);
  bytes = MIN(room, tp->tty_outleft);	/* # bytes to copy */
  tty_phys = umap(proc_addr(TTY), D, (vir_bytes) rs->rs_next + rs->rs_left, 1);
  phys_copy(tp->tty_phys, tty_phys, (long) bytes);
  rs->rs_left += bytes;			/* more bytes are now in rs_buf */
  tp->tty_cum += bytes;			/* update cumulative total */
  tp->tty_phys += bytes;		/* next time, take different bytes */
  tp->tty_outleft -= bytes;		/* fewer bytes still pending */
  unlock();
Event(14); /*DEBUG*/
Count(ct_ser1);/*DEBUG*/	
  if (!rs->rs_busy) {			/* is the line idle? */
	rs->rs_busy = TRUE;		/* if so, mark it as busy */
	rs_write_int(line);		/* and start it going */
  }
Event(23); /*DEBUG*/
Count(ct_ser2); /*DEBUG*/
}


/*===========================================================================*
 *				do_tty_o_done				     *
 *===========================================================================*/
PRIVATE do_tty_o_done(tp, m_ptr)
register struct tty_struct *tp;	/* pointer to tty_struct */
message *m_ptr;			/* pointer to message sent to task */
{
/* The current block of characters has been output.  See if there are more. */

  int count, replyee, caller, line;
  register struct rs_struct *rs;

Event(15); /*DEBUG*/
Count(ct_o_done); /*DEBUG*/
  /* Mark rs_buf as empty */
  lock();
  line = tp - tty_struct;
  rs = &rs_struct[line - NR_CONS];
  rs->rs_left = 0;
  rs->rs_busy = FALSE;
  rs->rs_next = &rs->rs_buf[0];
  unlock();

  /* See if more characters are pending. */
  if (tp->tty_outleft > 0) {
	/* The output is not yet completed.  Transmit another chunk. */
	serial_out(tp);
	return;
  }

Event(16); /*DEBUG*/
  /* There is nothing more to send.  Return status. */
  tp->tty_rwords = 0;
  count = tp->tty_cum;

  /* Tell hanging writer that chars have been sent */
  replyee = (int) tp->tty_otcaller;
  caller = (int) tp->tty_outproc;
  tty_reply(REVIVE, replyee, caller, count, 0L, 0L);
Event(17); /*DEBUG*/
}


/*===========================================================================*
 *				rs_out_char				     *
 *===========================================================================*/
PRIVATE rs_out_char(tp, c)
register struct tty_struct *tp;	/* pointer to tty struct */
char c;				/* character to be output */
{
/* Output a character on an RS232 line. */

  int line;
  register struct rs_struct *rs;

  /* See if there is room to store a character, and if so, do it. */
Event(18); /*DEBUG*/
Count(ct_out_char); /*DEBUG*/
  lock();
  line = tp - tty_struct;
  rs = &rs_struct[line - NR_CONS];
  if (rs->rs_next == &rs->rs_buf[RS_BUF_SIZE]) return;	/* full, tough luck */
  *rs->rs_next++ = c;		/* store the character */
  rs->rs_left++;
  unlock();

  if (!rs->rs_busy) {		/* if terminal line is idle, start it */
	rs->rs_busy = TRUE;
	rs_write_int(line);
  }
Event(19); /*DEBUG*/
}


/*===========================================================================*
 *				init_rs232				     * 
 *===========================================================================*/
PRIVATE	init_rs232()
{
  register struct tty_struct *tp;
  register struct rs_struct *rs;
  int line;

Event(20); /*DEBUG*/
  for (tp = &tty_struct[NR_CONS]; tp < &tty_struct[NR_CONS+NR_RS_LINES]; tp++){
	tp->tty_inhead = tp->tty_inqueue;
	tp->tty_intail = tp->tty_inqueue;
	tp->tty_mode = CRMOD | XTABS /* | ECHO */;
	tp->tty_devstart = serial_out;
	tp->tty_erase	= ERASE_CHAR;
	tp->tty_kill	= KILL_CHAR;
	tp->tty_intr	= INTR_CHAR;
	tp->tty_quit	= QUIT_CHAR;
	tp->tty_xon	= XON_CHAR;
	tp->tty_xoff	= XOFF_CHAR;
	tp->tty_eof	= EOT_CHAR;
	tp->tty_makebreak = ONE_INT;	/* RS232 only interrupts once/char */
  }

  rs_struct[0].rs_base = PRIMARY;
  rs_struct[1].rs_base = SECONDARY;

  for (rs = &rs_struct[0]; rs < &rs_struct[NR_RS_LINES]; rs++) {
	line = rs - rs_struct;
	rs->rs_next = & rs->rs_buf[0];
	rs->rs_left = 0;
	rs->rs_busy = FALSE;
	config_rs232(line, 1200, EVEN, 1, 7);	/* set line ctl and baudrate */
	port_out(rs->rs_base + RS232_MODEM_CONTROL, MODEM_CONTROLS);
	port_out(rs->rs_base + RS232_INTERRUPTS, RS232_INTERRUPT_CLASSES);
  }
}


/*===========================================================================*
 *				set_uart			 	     *
 *===========================================================================*/
PRIVATE set_uart(line, mode, speeds)
int line;			/* which line number (>= NR_CONS) */
int mode;			/* sgtty.h sg_mode word */
int speeds;			/* low byte is input speed, next is output */
{
/* Set the UART parameters. */
  int baudrate, parity, stop_bits, data_bits;

Event(21); /*DEBUG*/
  baudrate = 100 * (speeds & BYTE);
  if (baudrate == 100) baudrate = 110;
  parity = NONE;
  if (mode & ODDP) parity = ODD;
  if (mode & EVENP) parity = EVEN;
  stop_bits = (baudrate == 110 ? 2 : 1);
  data_bits = 5 + ((mode >> DATA_LEN) & 03);
  config_rs232(line, baudrate, parity, stop_bits, data_bits);
}


/*===========================================================================*
 *				config_rs232			 	     *
 *===========================================================================*/
PRIVATE	config_rs232(line, baud_rate, parity, stop_bits, data_bits)
int line, baud_rate, parity, stop_bits, data_bits;
{
  /* Set various line control parameters for RS232 I/O.
   * Parity may be any of ODD, EVEN or NONE.
   * StopBits may be 1 or 2.
   * DataBits may be 5 to 8.
   *
   * If DataBits == 5 and StopBits == 2, UART will generate 1.5 stop bits
   */

  int line_controls = 0, base, freq;

Event(22); /*DEBUG*/
  base = rs_struct[line - NR_CONS].rs_base;

  /* First tell line control register to address baud rate divisor */
  port_out(base + RS232_LINE_CONTROL, ADDRESS_DIVISOR);

  /* Now set the baud rate. */
  freq = (int) (UART_FREQ / baud_rate);
  port_out(base + RS232_RATE_DIVISOR, freq & BYTE);
  port_out(base + RS232_RATE_DIVISOR+1, (freq >> 8) & BYTE);


  /* Put parity_type bits in line_controls */
  if (parity != NONE) {
	line_controls |= PARITY_ON_OFF;
	line_controls |= (parity << PARITY_TYPE_SHIFT);
  }

  /* Put #stop_bits bits in line_controls */
  if (stop_bits == 1 || stop_bits == 2)
	line_controls |= (stop_bits - 1) << STOP_BITS_SHIFT;

  /* Put #data_bits bits in line_controls */
  if (data_bits >=5 && data_bits <= 8)
	line_controls |= (data_bits - 5);

  port_out(base + RS232_LINE_CONTROL, line_controls);
}

#endif

/*________________________________________________________________________*/

Event(x)
{  log[logpt++] = x;  if (logpt >= LOGSIZE) logpt = 0;
   if (rs_struct[0].rs_base > 0x3F8){
	printf("Event %d hit\n",x); prlog();   while(1);
    }
}

prlog()
{
  int i, j;
  i = logpt; j = 0;
  printf("\n\nEvents: ");
  while(j < LOGSIZE) { printf("%d ", log[i]); i++; if (i == LOGSIZE) i=0; j++;}
  printf("\n\nCounts: rs232=%u  serial_out=%u   ",ct_rs232, ct_ser);
  printf("(serial1=%u  serial2=%u)\n", ct_ser1, ct_ser2);
  printf("  rs_read_int=%u  rs_write_int=%u  write_byte=%u",ct_rd,ct_wt,ct_wb);
  printf("  do_tty_o_done=%u  rs_out_char=%u\n", ct_o_done, ct_out_char);

  printf("\nrs_struct[0].  rs_base=%x  rs_busy=%d rs_left=%d rs_next=%d\n",
        rs_struct[0].rs_base, rs_struct[0].rs_busy, rs_struct[0].rs_left,
        rs_struct[0].rs_next - &rs_struct[0].rs_buf[0]);
  printf("rs_buf = %o %o %o %o %o\n", rs_struct[0].rs_buf[0], rs_struct[0].rs_buf[1], rs_struct[0].rs_buf[2],rs_struct[0].rs_buf[3], rs_struct[0].rs_buf[4]);
}