#if (CHIP == M68000)

/* This driver does not deal with multiple consoles and parameter passing
   through tp. Also the tty struct fields row and column are not maintained.

   Multiple console implemented. Also some code cleanups and speedups have
   been done. Cursor control is now local to this file. There are other parts
   of this source, such as the escape sequence handling and the character
   painting functions, which need a cleanup. F.e. the pc-minix specific
   fields should be deleted from tty_struct, and a pointer to the virterm
   structure should be inserted there.			K.-U. Bloem, --kub
*/

/*
 * The video driver for the Atari ST
 */
#include "kernel.h"
#include <sgtty.h>

#include "staddr.h"
#include "stvideo.h"
#include "stsound.h"

#include "tty.h"

/* Constants relating to the video RAM and fonts */

#define	VIDEORAM	32000	/* size of video ram */
#define NCOL		80	/* characters on a row */

#define	M_LIN_SCR	400	/* video lines on mono screen */
#define	M_BYT_LIN	80	/* bytes in mono video line */
#define	FONTM		16	/* 25 lines mono (or 8 for 50 lines) */

#define	C_LIN_SCR	200	/* video lines on color screen */
#define	C_BYT_LIN	160	/* bytes in color video line */
#define	FONTC		8	/* 25 lines color */


/* Structure to keep data about virtual consoles in. Each virtual console
 * has its own screen ram, color table, keyboard setting etc. Exchanging the
 * currently displayed virtual console just requires a reprogramming of the
 * video controller with the new data from virterm, as well as setting some
 * variables for the keyboard driver.
 */
PRIVATE struct virterm {
		/* vdu hardware controller data */
	unsigned short vrgb[16];/* color table entry */
	unsigned char  vres;	/* screen resolution */
	char	mono;		/* 1 if mono, 0 if color */
	char	*vram;		/* base of video ram */
		/* font and cursor data */
	int	linc;		/* video lines in char */
	int	bytc;		/* bytes in video line */
	int	nrow;		/* char rows on screen */
	int	bytr;		/* bytes in row of chars */
	char	*curs;		/* cursor position in video RAM */
	int	curmode;	/* cursor mode, 1 = cursor on */
		/* terminal output state */
	char	attr;		/* current attribute byte */
	int	ccol;		/* current char column */
	int	crow;		/* current char row */
	char	savattr;	/* saved attribute byte */
	int	savccol;	/* saved char column */
	int	savcrow;	/* saved char row */
	char	vbuf[20];	/* partial escape sequence */
	char	*next;		/* next char in vbuf[] */
		/* terminal input state */
	int	vkeypad;	/* keyboard keypad state */
	int	vapmode;	/* keyboard cursor state */
} virterm[NR_CONS];

/* This is a pointer to the currently displayed virtual console device. The
 * keyboard driver needs to know to which tty it must deliver keys from the
 * keyboard. Moreover the kernel sometimes needs to output some text. This
 * should go to the current console device, since things may block after
 * text output.
 */
PUBLIC struct tty_struct *cur_cons = &tty_struct[0];

/* forward declarations for internal procedures */

/* general char output driver and escape sequence handling */
FORWARD _PROTOTYPE( void vdu_char, (struct tty_struct *tp,
			struct virterm *v, int c)			);
FORWARD _PROTOTYPE( void vductrl, (struct tty_struct *tp,
			struct virterm *v, int c)			);
FORWARD _PROTOTYPE( void vduesc, (struct tty_struct *tp,
			struct virterm *v, int c)			);
FORWARD _PROTOTYPE( void vduansi, (struct tty_struct *tp,
			struct virterm *v, int c)			);
FORWARD _PROTOTYPE( int vduparam, (struct virterm *v)			);
/* character printing and cursor management */
FORWARD _PROTOTYPE( void vducursor, (struct virterm *v, int onoff)	);
FORWARD _PROTOTYPE( void paint, (struct virterm *v, int c)		);
FORWARD _PROTOTYPE( void beep, (void)					);
FORWARD _PROTOTYPE( void moveto, (struct virterm *v, int r, int c)	);
/* screen modifications apart from char printing */
FORWARD _PROTOTYPE( void vscroll, (struct virterm *v,
			int up, int rl, int ru, int n)			);
FORWARD _PROTOTYPE( void hscroll, (struct virterm *v,
			int rt, int r, int cl, int cu, int n)		);
FORWARD _PROTOTYPE( void clrarea, (struct virterm *v,
			int rl, int cl, int ru, int cu)			);
FORWARD _PROTOTYPE( void clrlines, (struct virterm *v, int r, int n)	);
FORWARD _PROTOTYPE( void clrchars, (struct virterm *v, int r, int c, int n) );

/* Fast cursor positioning macroes for selected directions [see moveto()] */

#define	MOVERT(v)	if ((v)->ccol < NCOL-1) {			\
				(v)->ccol++; (v)->curs++;		\
				if (!(v)->mono && !((v)->ccol & 1))	\
					(v)->curs += 2;			\
			}
#define	MOVELT(v)	if ((v)->ccol > 0) {				\
				(v)->ccol--; (v)->curs--;		\
				if (!(v)->mono && ((v)->ccol & 1))	\
					(v)->curs -= 2;			\
			}
#define	MOVEDN(v)	if ((v)->crow < v->nrow-1) {			\
				(v)->crow++; (v)->curs += (v)->bytr;	\
			}
#define	MOVEUP(v)	if ((v)->crow > 0) {				\
				(v)->crow--; (v)->curs -= (v)->bytr;	\
			}
#define	MOVECR(v)	{ if ((v)->mono)				\
				(v)->curs -= (v)->ccol;			\
			  else						\
				(v)->curs -= ((v)->ccol<<1)-((v)->ccol&1); \
			(v)->ccol = 0; }

/*===========================================================================*
 *				flush					     *
 *===========================================================================*/
PUBLIC void flush(tp)
register struct tty_struct *tp;
{
/* There is really no semantic for flush() on 68000. The vdu driver does not
 * store characters in tp->tty_ramqueue, so simply return here.
 */

  return;
}

/*===========================================================================*
 *				console					     *
 *===========================================================================*/
PUBLIC void 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
 * or output is suspended.
 */

  register struct virterm *v = &virterm[tp->tty_line + NR_CONS];
  int count = 0;
  char *charptr = (char *)tp->tty_phys;

  vducursor(v, 0);
  while (tp->tty_outleft > 0 && tp->tty_inhibited == RUNNING) {
	vdu_char(tp, v, *charptr++);	/* write 1 byte to terminal */
	count++;
	tp->tty_outleft--;	/* decrement count */
  }
  vducursor(v, 1);

  /* Update terminal data structure. */
  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				     *
 *===========================================================================*/
PUBLIC void out_char(tp, c)
register struct tty_struct *tp;	/* virtual terminal to use */
char c;				/* character to be output */
{
/* send character to VDU, with cursor control, for kernel putc() call.
 */

  register struct virterm *v = &virterm[tp->tty_line + NR_CONS];

  vducursor(v, 0);
  vdu_char(tp, v, c);
  vducursor(v, 1);
}

/*===========================================================================*
 *				vdu_char				     *
 *===========================================================================*/
PRIVATE void vdu_char(tp, v, c)
struct tty_struct *tp;		/* virtual terminal to use */
register struct virterm *v;	/* video descriptor for virt console */
register char c;		/* character to be output */
{
/* send character to VDU, collecting escape sequences
 */

  if (v->next == (char *)0) {
	if ((c & ~0x1F) == 0) {		/* control character */
		vductrl(tp, v, c);
		return;
	} else if (c != 0x7F) {
		if (c & 0x80)		/* map to font char 00 - 1F */
			c &= 0x1F;
		paint(v, c);		/* print normal char to screen */
		MOVERT(v);
		return;
	} else				/* do nothing on DEL */
		return;
  }
  if (v->next == v->vbuf && c == '[') {	/* ANSI esc sequence detected */
	*v->next++ = c;
	return;
  }
  if (c >= 060 && (v->next == v->vbuf || v->vbuf[0] != '[')) {
	vduesc(tp, v, c);		/* end of non-ANSI escape sequence */
	v->next = 0;
	return;
  }
  if (c >= 0100) {			/* end of ANSI escape sequence */
	vduansi(tp, v, c);
	v->next = 0;
	return;
  }
  if (c == '\030' || c == '\032') {	/* CAN or SUB cancels esc sequence */
	v->next = 0;		/* should all other ctrl chars terminate */
	/* no error char */	/* the escape sequence too ???		 */
	return;
  }
  if (v->next <= &v->vbuf[sizeof(v->vbuf)])
	*v->next++ = c;			/* record part of esc sequence */
}

/*
 * control character
 */
PRIVATE void vductrl(tp, v, c)
register struct tty_struct *tp;
register struct virterm *v;
char c;
{
  switch (c) {
  case 007: /* BEL */
	beep(); return;
  case 010: /* BS */
	MOVELT(v); return;
  case 011: /* HT */
	do {
		if ((tp->tty_mode & XTABS) == XTABS)
			paint(v, ' ');
		MOVERT(v);
	} while (v->ccol < NCOL-1 && (v->ccol & TAB_MASK));
	return;
  case 012: /* LF */
	if (tp->tty_mode & CRMOD)
		MOVECR(v);
	/*FALLTHROUGH*/
  case 013: /* VT */
  case 014: /* FF */
	if (v->crow == v->nrow - 1)
		vscroll(v, FALSE, 0, v->nrow-1, 1);
	else
		MOVEDN(v);
	return;
  case 015: /* CR */
	MOVECR(v);
	return;
  case 033: /* ESC */
	v->next = v->vbuf;
	return;
  default:
	return;
  }
}

/*
 * execute non-ANSI escape sequence
 */
PRIVATE void vduesc(tp, v, c)
struct tty_struct *tp;
register struct virterm *v;
char c;
{
  if (v->next >= &v->vbuf[sizeof(v->vbuf)-1])
	return;
  *v->next = (char)c;
  switch (v->vbuf[0]) {
  case '8': /* DECRC: restore cursor */
	v->ccol = v->savccol;
	v->crow = v->savcrow;
	v->attr = v->savattr;
	moveto(v, v->crow, v->ccol);
	return;
  case '7': /* DECSC: save cursor */
	v->savccol = v->ccol;
	v->savcrow = v->crow;
	v->savattr = v->attr;
	return;
  case '=': /* DECKPAM: keypad application mode */
	v->vkeypad = TRUE;
	if (tp == cur_cons) vduswitch(tp);
	return;
  case '>': /* DECKPNM: keypad numeric mode */
	v->vkeypad = FALSE;
	if (tp == cur_cons) vduswitch(tp);
	return;
  case 'E': /* NEL: next line */
	vductrl(tp, v, '\r');
	/*FALLTHROUGH*/
  case 'D': /* IND: index */
	vductrl(tp, v, '\n');
	return;
  case 'M': /* RI: reverse index */
	if (v->crow == 0)
		vscroll(v, TRUE, 0, v->nrow-1, 1);
	else
		MOVEUP(v);
	return;
  case 'c': /* RIS: reset to initial state */
	vduinit(tp);
	v->vkeypad = v->vapmode = FALSE;
	if (tp == cur_cons) vduswitch(tp);
	return;
  default:
	return;
  }
}

/*
 * execute ANSI escape sequence
 */
PRIVATE void vduansi(tp, v, c)
struct tty_struct *tp;
register struct virterm *v;
char c;
{
  static unsigned colors[] = {		/* color table for ESC [ m */
	RGB_BLACK,
	RGB_RED,
	RGB_GREEN,
	RGB_YELLOW,
	RGB_BLUE,
	RGB_MAGENTA,
	RGB_CYAN,
	RGB_WHITE,
	RGB_LGREY,
	RGB_DGREY,
  };
  register int n, m;			/* used for escape sequence params */

  if (v->next >= &v->vbuf[sizeof(v->vbuf)])
	return;
  *v->next = 0;				/* prepare for parameter scanning */
  v->next = &v->vbuf[1];
  n = vduparam(v);			/* all but one sequence use this */
  m = (n <= 0 ? 1 : n);

  switch (c) {
  case 'A': /* CUU: cursor up */
	while (m--) MOVEUP(v);
	return;
  case 'B': /* CUD: cursor down */
	while (m--) MOVEDN(v);
	return;
  case 'C': /* CUF: cursor forward */
	while (m--) MOVERT(v);
	return;
  case 'D': /* CUB: cursor backward */
	while (m--) MOVELT(v);
	return;
  case 'H': /* CUP: cursor position */
  case 'f': /* HVP: horizontal and vertical position */
	m = vduparam(v);
	n = (n <= 0 ? 1 : (n > v->nrow ? v->nrow : n)) - 1;
	m = (m <= 0 ? 1 : (m > NCOL    ? NCOL    : m)) - 1;
	moveto(v, n, m);
	return;
  case 'J': /* ED: erase in display */
	if	(n <= 0)   clrarea(v, v->crow, v->ccol, v->nrow-1, NCOL-1);
	else if (n == 1)   clrarea(v, 0, 0, v->crow, v->ccol);
	else if (n == 2)   clrarea(v, 0, 0, v->nrow-1, NCOL-1);
	return;
  case 'K': /* EL: erase in line */
	if	(n <= 0)   clrchars(v, v->crow, v->ccol, NCOL - v->ccol);
	else if (n == 1)   clrchars(v, v->crow, 0, v->ccol + 1);
	else if (n == 2)   clrchars(v, v->crow, 0, NCOL);
	return;
#ifdef NEEDED
  case 'n': /* DSR: device status report */
	if (m == 6)
		kbdinput("\033[%d;%dR", v->crow+1, v->ccol+1);
	return;
#endif
  case 'm': /* SGR: set graphic rendition */
	do {
		if (n <= 0)			v->attr &= ~3;
		else if (n == 4 || n == 7)	v->attr |= 3;
		else if (n >= 30 && n <= 39)	v->vrgb[1] = colors[n - 30];
		else if (n >= 40 && n <= 49)	v->vrgb[0] = colors[n - 40];
	} while ((n = vduparam(v)) >= 0);
	if (tp == cur_cons) vduswitch(tp);
	return;
  case 'L': /* IL: insert line */
	vscroll(v, TRUE, v->crow, v->nrow-1, m);
	return;
  case 'M': /* DL: delete line */
	vscroll(v, FALSE, v->crow, v->nrow-1, m);
	return;
  case '@': /* ICH: insert char */
	hscroll(v, TRUE, v->crow, v->ccol, NCOL-1, m);
	return;
  case 'P': /* DCH: delete char */
	hscroll(v, FALSE, v->crow, v->ccol, NCOL-1, m);
	return;
  case 'l': /* RM: reset mode */
  case 'h': /* SM: set mode */
	if (v->next[0] == '?') { /* DEC private modes */
		if (v->next[1] == '5' && v->mono) /* DECSCNM */
			v->vrgb[0] = c == 'l' ? RGB_BLACK : RGB_WHITE;
		else if (v->next[1] == '1')	  /* DECCKM */
			v->vapmode = c == 'l' ? TRUE : FALSE;
		if (tp == cur_cons) vduswitch(tp);
	}
	return;
  case '~': /* Minix-ST specific escape sequence */
	/*
	 * Handle the following escape sequence:
	 *   ESC [ l;a;m;r;g;b '~'
	 * where
	 *   if l present:
	 *	0: 25 lines if mono
	 *	1: 50 lines if mono
	 *   if a present:
	 *	low 4 bits are attribute byte value (see paint())
	 *   if m present:
	 *	interpret r;g;b as colors for map register m
	 *	only assign color if r, g or b present
	 */
	if (v->mono) {		/* select mono font size */
	    if (n == 0) {			/* 25 lines */
		v->linc = 16;			/* 16 */
		v->nrow = M_LIN_SCR/16;		/* 25 */
		v->bytr = M_BYT_LIN*16;		/* 1280 */
		moveto(v, v->nrow - 1, 0);
	    } else if (n == 1) {		/* 50 lines */
		v->linc = 8;			/* 8 */
		v->nrow = M_LIN_SCR/8;		/* 50 */
		v->bytr = M_BYT_LIN*8;		/* 640 */
		moveto(v, v->nrow - 1, 0);
	    }
	}
	if ((n = vduparam(v)) >= 0)	/* attribute byte */
		v->attr = n & 0x0F;
	if ((n = vduparam(v)) >= 0) {	/* change a color */
		register short *w = (short *)&v->vrgb[n & 0x0F];

		if ((n = vduparam(v)) >= 0)
			*w = (*w & ~0x0700) | ((n & 7) << 8);
		if ((n = vduparam(v)) >= 0)
			*w = (*w & ~0x0070) | ((n & 7) << 4);
		if ((n = vduparam(v)) >= 0)
			*w = (*w & ~0x0007) | ((n & 7) << 0);
		if (tp == cur_cons) vduswitch(tp);
	}
	return;
  default:
	return;
  }
}

/*
 * compute next parameter out of ANSI sequence
 */
PRIVATE int vduparam(v)
register struct virterm *v;
{
  register char c;
  register int i;

  i = -1;
  c = *v->next++;
  if (c >= '0' && c <= '9') {
	i = 0;
	do {
		i *= 10;
		i += (c - '0');
		c = *v->next++;
	} while (c >= '0' && c <= '9');
  }
  if (c != ';')
	v->next--;
  return(i);
}

/*===========================================================================*
 *				manipulate videoram 			     *
 *===========================================================================*/
/* The routines in this part are all quite cryptic, but are optimized for max
 * speed. --kub
 */

/*
 * clear part of screen between two points inclusive
 */
PRIVATE void clrarea(v, rl, cl, ru, cu)
struct virterm *v;
int rl, cl;
int ru, cu;
{
  if (++cu == NCOL) {
	cu = 0;
	ru++;
  }
  if (cl > 0 && rl < ru) {
	clrchars(v, rl, cl, NCOL-cl);
	cl = 0;
	rl++;
  }
  clrlines(v, rl, ru-rl);
  clrchars(v, ru, cl, cu-cl);
}

/*
 * scroll lines around on the screen. newly created lines are blanked out
 */
PRIVATE void vscroll(v, up, rl, ru, n)
register struct virterm *v;
int up;				/* flag: TRUE = scroll up rather then down */
int rl, ru, n;			/* lower and upper row affected, scroll count */
{
  register char *src, *dst;	/* pointers into the video mem */

  if (n <= 0) return;		/* safety first ... */
  if (n > ru-rl)
	n = ru-rl + 1;

  if (up) {
  	src = &v->vram[(ru-n) * v->bytr];
  	dst = &v->vram[ ru    * v->bytr];
	for (; ru >= rl+n; ru--) {
		phys_copy( (phys_bytes) src, (phys_bytes) dst,
			   (phys_bytes) v->bytr );
		src -= v->bytr; dst -= v->bytr;
	}
	clrlines(v, rl, n);
  } else {
	/* We are using phys_copy() to move all lines at once, but src and
	 * dest may overlap. Trust in phys_copy copying from low to high !
	 * clean way : for (; rl <= ru-n; rl++) phys_copy(...); (see above)
	 */
	phys_copy((phys_bytes) &v->vram[(rl+n) * v->bytr],
		  (phys_bytes) &v->vram[ rl    * v->bytr],
		  (phys_bytes) (ru-rl-n+1) * v->bytr);
	clrlines(v, ru-n+1, n);
  }
}

/*
 * clear lines from r to r+n-1.
 */
PRIVATE void clrlines(v, r, n)
register struct virterm	*v;
int r, n;
{
  register phys_bytes	p, i;

  if (n <= 0) return;

  /* The ST video chips enforce the screen to be on a 256 byte boundary. As
   * long as CLICK_SIZE is smaller than that we can use zeroclicks for the
   * operation. Otherwise use a normal, but slow loop for clearing.
   */
  p = (phys_bytes) &v->vram[r * v->bytr];
  i = (phys_bytes) v->bytr * n;

  if ((p & (CLICK_SIZE-1)) == 0 && (i & (CLICK_SIZE-1)) == 0) {
	zeroclicks( (phys_clicks) (p >> CLICK_SHIFT),
		    (phys_clicks) (i >> CLICK_SHIFT) );
  } else {
	register long *q = (long *)p;

	i = i >> 4;	/* 16 bytes per loop */
	while (i--) {
		*q++ = 0; *q++ = 0; *q++ = 0; *q++ = 0;
	}
  }
}

/* All the character copying/clearing functions need some cleanup ! */

/*
 * scroll chars around in one line. newly created chars are blanked out
 */
PRIVATE void hscroll(v, rt, r, cl, cu, n)
struct virterm *v;
int rt;				/* flag: TRUE = scroll right rather then left */
int r;				/* row number */
int cl, cu;			/* column scroll edges */
int n;				/* scroll count */
{
  char *src, *dst;		/* ptrs into video memory */

/*--------------------------------------------------
 * inline the character copy loop, for greater speed
 * (not really necessary; not called very often --kub)
 */
  register char *p, *q;		/* work pointers into vmem */
  register int nl;		/* line counter */
  register int bl = v->bytc;	/* line distance */
#define cpchr_m(s, d) {	p = (s); q = (d); nl = v->linc;	\
			do {				\
				*q = *p;		\
				p += bl; q += bl;	\
			} while (--nl != 0); }
#define cpchr_c(s, d) {	p = (s); q = (d); nl = v->linc;	\
			do {				\
				*q = *p; *(q+2) = *(p+2); \
				p += bl; q += bl;	\
			} while (--nl != 0); }
/*------------------------------------------------*/

  if (n <= 0) return;		/* safety checks */
  if (n > cu-cl)
	n = cu-cl + 1;

  if (rt) {
	if (v->mono) {
		src = &v->vram[(r * v->bytr) + (cu-n)];
		dst = &v->vram[(r * v->bytr) +  cu   ];
		while (cu >= cl + n) {
			cpchr_m(src, dst);
			src--; dst--;
			cu--;
		}
	} else {	/* 0->0, 1->1, 2->4, 3->5, 4->8, ... */
		src = &v->vram[(r * v->bytr) + ((cu-n) << 1) - ((cu-n) & 1)];
		dst = &v->vram[(r * v->bytr) + ( cu    << 1) - ( cu    & 1)];
		while (cu >= cl + n) {
			cpchr_c(src, dst);
			if (!((long)src & 1)) src -= 3; else src--;
			if (!((long)dst & 1)) dst -= 3; else dst--;
			cu--;
		}
	}
  } else {
	if (v->mono) {
		src = &v->vram[(r * v->bytr) + (cl+n)];
		dst = &v->vram[(r * v->bytr) +  cl   ];
		while (cl <= cu - n) {
			cpchr_m(src, dst);
			src++; dst++;
			cl++;
		}
	} else {	/* 0->0, 1->1, 2->4, 3->5, 4->8, ... */
		src = &v->vram[(r * v->bytr) + ((cl+n) << 1) - ((cl+n) & 1)];
		dst = &v->vram[(r * v->bytr) + ( cl    << 1) - ( cl    & 1)];
		while (cl <= cu - n) {
			cpchr_c(src, dst);
			if ((long)src & 1) src += 3; else src++;
			if ((long)dst & 1) dst += 3; else dst++;
			cl++;
		}
	}
  }
  clrchars(v, r, cl, n);
}

/*
 * clear n chars on pos (r,c).
 */
PRIVATE void clrchars(v, r, c, n)
register struct virterm *v;
int r, c, n;
{
  register char *p, *q;		/* ptr into video memory */
  register int nl;		/* scan lines per char */
  register int bl = v->bytc;	/* bytes per scan line */

  if (n > NCOL-c)
	n = NCOL-c;
  if (n <= 0) return;

  if (v->mono) {
	p = q = &v->vram[(r * v->bytr) + c];
	while (n != 0) {
		if ((c & 1) || (n < sizeof(long))) {
			nl = v->linc >> 1;	/* align to word boundary */
			do {
				*p = 0;	p += bl;
				*p = 0;	p += bl;
			} while (--nl != 0);
			p = ++q;
			n--;
		}
		while (n >= sizeof(long)) {	/* then clear 4 chars at once */
			nl = v->linc >> 1;
			do {
				*(long *)p = 0; p += bl;
				*(long *)p = 0; p += bl;
			} while (--nl != 0);
			p = (q += sizeof(long));
			n -= sizeof(long);
		}
	}
  } else {		/* 0->0, 1->1, 2->4, 3->5, 4->8, ... */
	p = q = &v->vram[(r * v->bytr) + (c << 1) - (c & 1)];
	while (n != 0) {		/* same as aboce, for color display */
		if ((c & 1) || (n < sizeof(long))) {
			nl = v->linc;
			do {
				*p = 0;
				*(p+2) = 0;
				p += bl;
			} while (--nl != 0);
			if ((long)q & 1)	/* bit plane alignment */
				p = (q += 3);
			else	p = ++q;
			n--;
		}
		while (n >= sizeof(long)) {
			nl = v->linc;
			do {
				*(long *)p = 0;
				*((long *)p+1) = 0;
				p += bl;
			} while (--nl != 0);
			p = (q += 2*sizeof(long));
			n -= sizeof(long);
		}
	}
  }
}

/*===========================================================================*
 *				vducursor				     *
 *===========================================================================*/
PRIVATE void vducursor(v, onoff)
register struct virterm *v;
int	onoff;
{
/* (un-)display the cursor. onoff may eventually later have more semantic,
 * such as blinking. At the moment, (non)zero means cursor off(on).
 */

  register char *vp;
  register int nl;
  register int bl;

  if (onoff == v->curmode) return;	/* no action if no change */
  v->curmode = onoff;

  bl = v->bytc;
  if ((v->attr & 4) == 0) {		/* plane 0: color and mono */
	nl = v->linc >> 1;
	vp = v->curs;
	do {
		*vp = ~(*vp); vp += bl;
		*vp = ~(*vp); vp += bl;
	} while (--nl != 0);
  }
  if (v->mono == 0 && (v->attr & 8) == 0) {	/* plane 1: color */
	nl = v->linc >> 1;
	vp = v->curs + 2;
	do {
		*vp = ~(*vp); vp += bl;
		*vp = ~(*vp); vp += bl;
	} while (--nl != 0);
  }
}

/*===========================================================================*
 *				paint					     *
 *===========================================================================*/
PRIVATE void paint(v, c)
register struct virterm *v;
char c;
{
/* copy from font memory into video memory. Implementation tries to make
 * inner loop as fast as possible by repeating instructions rather then
 * loop over them.
 *
 * attributes:
 *   0000xxx1: invert plane 0
 *   0000xx1x: invert plane 1 (color only)
 *   0000x1xx: inhibit plane 0
 *   00001xxx: inhibit plane 1 (color only)
 */

  register unsigned char *fp;	/* ptr into font */
/*--------------------------------------------------
 * inline the character paint loop, for greater speed
 */
  register unsigned char *vp;	/* ptr into video memory */
  register int nl;		/* scan lines per char */
  register int bl = v->bytc;	/* bytes per scan line */
#define	pntchr(adr,inv)	{ vp = (adr); nl = v->linc >> 2;\
			if ((inv) == 0) do {		\
				*vp = *fp++; vp += bl;	\
				*vp = *fp++; vp += bl;	\
				*vp = *fp++; vp += bl;	\
				*vp = *fp++; vp += bl;	\
			    } while (--nl != 0);	\
			else do {			\
				*vp = ~(*fp++);vp += bl;\
				*vp = ~(*fp++);vp += bl;\
				*vp = ~(*fp++);vp += bl;\
				*vp = ~(*fp++);vp += bl;\
			    } while (--nl != 0);	\
			}
/*--------------------------------------------------*/

  c &= 0x7F;
  if (v->linc == 16)
	fp = &font16[c<<4];
  else
	fp = &font8[c<<3];
  if ((v->attr & 4) == 0) {		/* plane 0: color and mono */
	pntchr( (unsigned char *)v->curs, v->attr & 1 );
	fp -= v->linc;
  }
  if (v->mono == 0 && (v->attr & 8) == 0) {	/* plane 1: color */
	pntchr( (unsigned char *)v->curs + 2, v->attr & 2 );
  }
}

/*===========================================================================*
 *				beep					     *
 *===========================================================================*/
PRIVATE void beep()
{
/* ring the console bell. This is independent from virtual consoles, just
 * because ringing the bell is an alarm signal for the user. He can then
 * switch to the appropriate virtual terminal and see what has happened.
 */

  register int	i, s;
  static char	sound[] = {
	0xA8,0x01,0xA9,0x01,0xAA,0x01,0x00,
	0xF8,0x10,0x10,0x10,0x00,0x20,0x03
  };

  s = lock();
  for (i = 0; i < sizeof(sound); i++) {
	SOUND->sd_selr = i;
	SOUND->sd_wdat = sound[i];
  }
  restore(s);
}

/*===========================================================================*
 *				moveto					     *
 *===========================================================================*/
PRIVATE void moveto(v, r, c)
register struct virterm *v;
int r, c;
{
/* move cursor to another position. The index operation to v->vram costs time
 * due to the multiplication, thus use the quick-and-dirty macroes for simple
 * moves.
 */

  if (r < 0 || r >= v->nrow || c < 0 || c >= NCOL)
	return;
  v->crow = r;
  v->ccol = c;
  if (v->mono)
	v->curs = &v->vram[(r * v->bytr) + c];
  else				/* 0->0, 1->1, 2->4, 3->5, 4->8, ... */
	v->curs = &v->vram[(r * v->bytr) + (c << 1) - (c & 1)];
}

/*===========================================================================*
 *				vduswitch				     *
 *===========================================================================*/
PUBLIC void vduswitch(tp)
struct tty_struct *tp;
{
/* switch to another virtual console. The keyboard driver selects a new
 * virtual console by handling some special alt-key sequences. This procedure
 * is internally used to make changes to the current console visible. It's the
 * only place where keyboard variables and video controller are changed.
 */

  register struct virterm	*v = &virterm[tp->tty_line + NR_CONS];
  register int i;

  keypad = v->vkeypad;			/* keyboard initialization */
  app_mode = v->vapmode;

  VIDEO->vd_res = v->vres;		/* screen resolution */
  VIDEO->vd_ramm = (phys_bytes) v->vram >> 8;	/* screen image address */
  VIDEO->vd_ramh = (phys_bytes) v->vram >> 16;
  for (i = 0; i < 16; i++)		/* color table */
	VIDEO->vd_rgb[i] = v->vrgb[i];

  cur_cons = tp;
}

/*===========================================================================*
 *				vduinit					     *
 *===========================================================================*/
PUBLIC void vduinit(tp)
struct tty_struct *tp;
{
/* Initialize a virtual console. This routine is not only called during system
 * startup, thus be careful with memory allocation. Don't allocate if that has
 * already been done. All other fields may be safely overwritten. Make changes
 * visible if they occur on the current virtual console.
 */

  register struct virterm	*v = &virterm[tp->tty_line + NR_CONS];
  char	*p;

  if (VIDEO->vd_res & RES_HIGH) {
	v->vres = RES_HIGH;
	v->mono = 1;
	v->linc = FONTM;		/* 16 (or 8) */
	v->bytc = M_BYT_LIN;		/* 80 */
	v->nrow = M_LIN_SCR/FONTM;	/* 25 (or 50) */
	v->bytr = M_BYT_LIN*FONTM;	/* 1280 (or 640) */
	v->attr = 0;			/* display mono plane */
	v->vrgb[0] = RGB_BLACK;		/* background */
  } else {
	v->vres = RES_MID;
	v->mono = 0;
	v->linc = FONTC;		/* 8 */
	v->bytc = C_BYT_LIN;		/* 160 */
	v->nrow = C_LIN_SCR/FONTC;	/* 25 */
	v->bytr = C_BYT_LIN*FONTC;	/* 1280 */
	v->attr = 0x08;			/* inhibit second plane */
	v->vrgb[1] = RGB_WHITE;		/* foreground */
	v->vrgb[0] = RGB_BLACK;		/* background */
  }
  if (v->vram == (char *)0) {		/* no image allocated yet */
	if (tp->tty_line + NR_CONS == 0)
	    /* First console gets original screen image */
	    v->vram = (char *)(	((long)(VIDEO->vd_ramh & 0xFF) << 16) |
				((long)(VIDEO->vd_ramm & 0xFF) << 8)  );
	else {
	    /* subtract video memory from physical top of memory */
	    phys_copy((phys_bytes)0x436, (phys_bytes)&p, (phys_bytes)sizeof(p));
	    p -= 0x8000; /* VIDEORAM will eventually work here ? */
	    phys_copy((phys_bytes)&p, (phys_bytes)0x436, (phys_bytes)sizeof(p));
	    v->vram = p;
	}
  }
  v->vkeypad = keypad;			/* default keyboard settings */
  v->vapmode = app_mode;
  v->curmode = 0;			/* clear screen wipes cursor */
  clrarea(v, 0, 0, v->nrow-1, NCOL-1);	/* clear the entire screen */
  moveto(v, 0, 0);			/* move cursor to upper left corner */
  vducursor(v, 1);			/* switch cursor on */
  if (tp == cur_cons) vduswitch(tp);	/* initialize vdu controller */
}
#endif
