[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

try this out...



hi!

 this started as an idea last week, and now...

 (please don't pass this around until i know its reasonably stable.
works for me, i'm using it right now but as usual that doesn't say
much...  i would also like some comment from Eric because it contains
parts which are his code.  thanx...)

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	README
#	vcon.c
#	vtdev.c
#	vcon.h
#	vtdev.h
#	Makefile
# This archive created: 18-September-1993 18:13:06 CES
# By:	nox@jelal.north.de (Juergen Lock)
cat << \SHAR_EOF > README
From: Juergen Lock <nox@jelal.north.de>

here is something you've all been waiting for... virtual terminals
for MiNT :-)  now you can...

 use up to 10 shells/whatever without magnifying glasses or 19" screens
 run GEM on the console, top on vt01 and gdb' a GEM program from vt02...
 send MiNTs debug output to another terminal than the debugged program
 when some stupid program hangs and MiNT is still alive just switch to
another terminal and send the thing a signal. (ever heared of vp/ix?
oops, wrong CPU...)
 temporarily suspend GEM while you need your cycles for something more
important than polling the mouse ;)  i.e. just send SIGSTOP and SIGCONT
later. (now if MiNT only had swapping...)
 show people that its not MiNT that is slow, only GEM :-)  also the
screen writing code in vtdev.c is mostly based on MiNTs fasttext.c
(including hardware scrolling for all terminals except the console)
so i guess output is faster than any GEM window writing could ever get.

 how to use:

 this should run ok on any machine where MiNTs /dev/fasttext worked
(worked because you won't need it anymore then) and where
Setscreen (-1, base, -1) allows setting the displayed screen
independent of the one GEM (or VDI actually) writes to.  if it doesn't
work for you tell me, or better yet just fix it and send diffs. :-)

 with init simply put something like this into your rc.local:

--------cut------
if [ -f /usr/etc/vcon ]; then
	echo "starting virtual consoles"
	vcon
	mv /dev/console /dev/con00
	mv /dev/vt00 /dev/console
	rm /etc/ttytab
	ln /etc/ttytab.vt /etc/ttytab
else
	rm /etc/ttytab
	ln /etc/ttytab.con /etc/ttytab
fi
-------------

 (the mv.s are for programs that open /dev/console so they get the
new one... writing to the old one should be no problem, but reading is.)

 ttytab.vt has entries for console and vt01..09 (of course not all of
them need be `on' :-), ttytab.con only has the original console.  and
remember GEM can only run on console...

 without init put something like this in mint.cnf (and then set CON to
the new console).  of course without init you'll have to do its job
yourself, i.e. put gettys or shells etc. on the new terminals `by hand'.

 you can also add this to termcap:

stv52|MiNT virtual console:\
    :ti=\Ev\Ee\Ez_:am:te=\Ev\Et@\Ee\Ez_:\
    :al=\EL:bs:cd=\EJ:ce=\EK:cl=\EE:cm=\EY%+ %+ :co#80:\
    :dl=\EM:do=\EB:ho=\EH:\
    :li#25:se=\Eq:so=\Ep:up=\EA:nd=\EC:\
    :rs=\Ez_\Eb@\EcA:\
    :ue=\EzH:us=\EyH:md=\E(:me=\E)\Eq:\
    :ms:pt:\
    :sr=2*\EI:\
    :sf=2*^J:\
    :kl=#K:kr=#M:ku=#H:kd=#P:\
    :kI=#R:kh=#G:kH=#O:kP=#I:kN=#Q:\
    :k0=#D:k1=#;:k2=#<:k3=#=:\
    :k4=#>:k5=#?:k6=#@:k7=#A:k8=#B:k9=#C:\
    :s0=#]:s1=#T:s2=#U:s3=#V:\
    :s4=#W:s5=#X:s6=#Y:s7=#Z:s8=#[:s9=#\\:\
    :vi=\Ef:ve=\Et@\Ee:vs=\Et :\
    :cQ=\Et@:cV=\Et :

(and add a new console type to gettytab that sets tt i.e. TERM =stv52),
then less etc know how to underline text wich i also have added.
btw the k.. and s.. with # are scancodes for elvis, others might want
escape sequences...

 oh and to switch between terminals just hit alt-fkey... f10 is the
console.  when it `bing's at you that means the terminal has no screen
memory because no process has it open... with init you can turn it on in
ttytab and send init SIGHUP.  and when it `bing's at you while you type
that means the terminals `keyboard queue' is full... to empty it hit
alt-undo.  (if its empty alt-undo gets passed thru, so programs can
still use it.)

 ok, happy testing...
	Juergen
SHAR_EOF
cat << \SHAR_EOF > vcon.c
/*
 * virtual terminals for MiNT, 1st try
 *
 * vt01..9 are fast hardware-scrolling text-terminals, vt00 is the
 * original console and can still be used for GEM. (i hope :)
 * to change terminals hit alt-function key (f1 == vt01... f10 == vt00),
 * alt-undo kills a terminals key buffer. (useful when your auto-repeat
 * is faster than a program processes input, etc)
 *
 * how it works:  each open vt.. device has a screen buffer and input pipe.
 * keyboard input is read and scanned for control and alt-f keys by a
 * daemon (in main()) that changes screens and sends input and signals to
 * the correct processes.
 * vt00 uses the original screen memory, vt01..9 share one bigger screen
 * for display and store the other screens contents in line buffers
 * so they can be `hardware-scrolled' without taking extra memory.
 * (just move pointers around.. line offsets actually)  closed `stored'
 * terminals don't take up screen memory.
 *
 * send bugs & comments to:  Juergen Lock <nox@jelal.north.de>
 */

#include <osbind.h>
#include <sysvars.h>
#undef flock
#undef	_sysbase
#define	_sysbase (* ((OSHEADER **) 0x4f2))
#include <mintbind.h>
#include <setjmp.h>
#include <support.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "vcon.h"
#include "vtdev.h"

/* kernel information */
struct kerinfo *kernel;

struct dev_descr devinfo[] = {
	&vcon_device, 0, O_TTY, ttys+0, 0L, 0L, 0L, 0L,	/* vt00 (console) */
	&vcon_device, 1, O_TTY, ttys+1, 0L, 0L, 0L, 0L,	/* vt01 */
	&vcon_device, 2, O_TTY, ttys+2, 0L, 0L, 0L, 0L,	/* vt02 */
	&vcon_device, 3, O_TTY, ttys+3, 0L, 0L, 0L, 0L,	/* vt03 */
	&vcon_device, 4, O_TTY, ttys+4, 0L, 0L, 0L, 0L,	/* vt04 */
	&vcon_device, 5, O_TTY, ttys+5, 0L, 0L, 0L, 0L,	/* vt05 */
	&vcon_device, 6, O_TTY, ttys+6, 0L, 0L, 0L, 0L,	/* vt06 */
	&vcon_device, 7, O_TTY, ttys+7, 0L, 0L, 0L, 0L,	/* vt07 */
	&vcon_device, 8, O_TTY, ttys+8, 0L, 0L, 0L, 0L,	/* vt08 */
	&vcon_device, 9, O_TTY, ttys+9, 0L, 0L, 0L, 0L	/* vt09 */
};

#define MAX_VT ((sizeof devinfo)/sizeof (struct dev_descr))

struct tty ttys[MAX_VT];

struct sgttyb con;
int conflags;
short hardscroll = -1;
struct tchars con_tc, tc0;
struct ltchars con_ltc, ltc0;

int pfd[MAX_VT], cfd, vcurrent, pgrp;
short leaving;

static int xcurrent;

void xflash();

long xsetcurrent()
{
	return setcurrent (xcurrent);
}

void xwakeselect()
{
	WAKESELECT(ttys[vcurrent].rsel);
}

void con_raw()
{
	Fcntl(0, &con, TIOCGETP);
	Fcntl(0, &con_tc, TIOCGETC);
	Fcntl(0, &con_ltc, TIOCGLTC);
	conflags = con.sg_flags;
	con.sg_flags &= ~(CBREAK|ECHO|TOSTOP);
	Fcntl(0, &con, TIOCSETN);
	Fcntl(0, &tc0, TIOCSETC);
	Fcntl(0, &ltc0, TIOCSLTC);
}

void con_sane()
{
	if (cfd) {
		/* try to uninstall gracefully... */
		int i, opencnt = 0;
		char *vt00name=ttyname(cfd), *oldcname=ttyname(0), *s;
		long vpgrp;

		/* /dev/vt00 might be renamed /dev/console... */
		if (vt00name && (s = strrchr (vt00name, '/')) &&
		    !strcmp (s, "/console"))
			rename (vt00name, "u:/dev/vt00");
		/* move original console device in place */
		if (oldcname && (s = strrchr (oldcname, '/')) &&
		    strcmp (s, "/console"))
			rename (oldcname, "u:/dev/console");

		/* tell processes their tty is going away */
		leaving = 1;
		for (i = 0; i < MAX_VT; ++i) {
			if (ttys[i].use_cnt > !i) {
				++opencnt;
				if ((vpgrp = ttys[vcurrent].pgrp))
					killpg(vpgrp, SIGHUP);
			}
		}
		/* wait until all the devices are closed... */
		while (opencnt) {
			opencnt = 0;
			/* sleep(1);  (save space...) */
			(void) Fselect (1000, 0l, 0l, 0l);
			for (i = 0; i < MAX_VT; ++i)
				if (ttys[i].use_cnt > !i)
					++opencnt;
		}
		xcurrent = 0;
		Supexec (xsetcurrent);
		Fclose (cfd);

		/* ..and remove them */
		for (i = 0; i < MAX_VT; ++i) {
			char name[] = "u:\\dev\\vt00";

			name[sizeof "u:\\dev\\vt0"-1] = i+'0';
			Fdelete (name);
		}
#if 1
		vpgrp = 0;
		Fcntl(0, &vpgrp, TIOCSPGRP);
#endif
	}
	con.sg_flags=conflags;	/* restore console  */
	Fcntl(0, &con, TIOCSETN);
	Fcntl(0, &con_tc, TIOCSETC);
	Fcntl(0, &con_ltc, TIOCSLTC);
}

/* handle fatal signals */
void trap(sig)
int sig;
{
	con_sane();
	signal(sig, SIG_DFL);
	/* die! */
	kill(getpid(), sig);
}

/* pass signal to process on the pty */
void trap_int(sig)
int sig;
{
	int vpgrp;

	/* see who is on the tty and if its a different process group... */
	if (ttys[vcurrent].use_cnt && (vpgrp = ttys[vcurrent].pgrp) != pgrp) {
		/* if yes, pass the signal */
		signal(sig, SIG_IGN);
		killpg(vpgrp, sig);
		signal(sig, trap_int);
	}
}

static	OSHEADER *syshdr;

void getsyshd()
{
	syshdr = _sysbase->os_beg;
}

/* get a (TOS version dependant) pointer to the current shift/alt
   keyboard status
*/
char *getpkbshift()
{
	/* get OS header */
	(void) Supexec(getsyshd);
	/* TOS 1.(0)2 or newer has it in the header */
	if (syshdr->os_version > 0x100)
		return syshdr->pkbshift;
	else
	/* TOS 1.0 */
		return (char *) 0x0e1bL;
}

/* on MiNT fork and vfork both block until the child does either exec or
   dies.  only tfork doesn't block but it works like a subroutine call... */
static jmp_buf	tforkj;

static int in_tfork(arg)
int arg;
{
	/* wait for parent to die before we can longjmp back */
	while (getppid () > 1)
		(void) Fselect (1000, 0l, 0l, 0l);
	longjmp (tforkj, 1);
	/*NOTREACHED*/
}

/* queue up characters for a terminal */

void csend (vcurrent, fd, cbuf, bufp)
int vcurrent, fd;
long *cbuf, *bufp;
{
	long bytes = (char *)bufp - (char *)cbuf;

	if (bytes) {
		/* pipe full? */
		if (Foutstat (fd) < bytes)
			/* bing... */
			Fputchar (0, 07l, 0);
		else {
			/* else send buffer */
			Fwrite (fd, bytes, cbuf);
			/* if someone select()ed this terminal wake 'em up */
			if (ttys[vcurrent].rsel)
				Supexec (xwakeselect);
		}
	}
}

/*
 * main:  initialization stuff, and a daemon that handles input from
 * the physical console
 */

main()
{
	int i;
	long pgrp;
	char *s;
	volatile char *pkbshift = getpkbshift();
	long cbuf[0x80], *bufp;

	/* sanity check */
	if (!(s = ttyname (0)) || (!(s = strrchr (s, '/'))) ||
	    strcmp (s, "/console")) {
		Cconws ("Sorry this is fast not `clean' software :)  console only...\r\n");
		exit (1);
	}

	/* stdin RAW, catch signals...  */
	pgrp = getpid(/*0*/);
	Fcntl(0, &con, TIOCGETP);
	Fcntl(0, &con_tc, TIOCGETC);
	Fcntl(0, &con_ltc, TIOCGLTC);
	conflags = con.sg_flags;
	signal(SIGHUP, trap);
	signal(SIGTERM, trap);
	signal(SIGINT, trap_int);
	signal(SIGQUIT, trap_int);
	con_raw();

	for (i = 0; i < MAX_VT; ++i) {
		char name[] = "u:\\pipe\\q$vt00";

		name[sizeof "u:\\pipe\\q$vt0"-1] = i+'0';
		if ((pfd[i] = Fcreate (name, FA_RDONLY|FA_CHANGED)) < 0) {
			con_sane();
			Cconws ("Huh?  unable to create pipe...\r\n");
			exit (1);
		}
		/* hmm.  tfork goes thru Pexec, and Pexec looks at
		   close-on-exec flags...
		*/
		Fcntl (pfd[i], 0, F_SETFD);
	}
	for (i = 0; i < MAX_VT; ++i) {
		char name[] = "u:\\dev\\vt00";

		name[sizeof "u:\\dev\\vt0"-1] = i+'0';
		kernel = (struct kerinfo *)Dcntl(DEV_INSTALL, name, devinfo+i);
		if (!kernel || ((long)kernel) == -32) {
			con_sane();
			Cconws ("Uh :(  unable to install vt device(s)!\r\n");
			exit (1);
		}
	}

	if ((cfd = Fopen ("u:\\dev\\vt00", O_RDWR)) < 0) {
		con_sane();
		Cconws ("Help!!  Fopen new console device failed.\r\n");
		exit (1);
	}
	Fcntl (cfd, 0, F_SETFD);
#if 0
	/* better put this in rc.local... (or mint.cnf) */
	Frename (0, "u:\\dev\\console", "u:\\dev\\con00");
	Frename (0, "u:\\dev\\vt00", "u:\\dev\\console");
#endif

	/* hack until MiNT gets a real nonblocking fork...  one day :)
	*/
	if (!setjmp(tforkj) && tfork (in_tfork, 0l) >= 0)
		_exit (0);

	/* ok parent continues, has to close old /dev/console now... */
	pgrp = setpgrp(/*pgrp, pgrp*/);
	Fcntl(0, &pgrp, TIOCSPGRP);
	Fcntl(0, (void *) O_NDELAY, F_SETFL);
	/* hmm part 2: tfork resets signals too... */
	signal(SIGHUP, trap);
	signal(SIGTERM, trap);
	signal(SIGINT, trap_int);
	signal(SIGQUIT, trap_int);

	/* now the daemon part
	   poll keyboard, switch between terminals, start/stop output,
	   send signals, flash cursor...
	*/
	bufp = cbuf;
	for (;;) {
		char name[] = "u:\\pipe\\q$vt00";
		int fd = pfd[vcurrent], scan;
		char cshift;
		unsigned long l;

		/* empty buffer if full */
		if (bufp == cbuf+sizeof cbuf) {
			csend (vcurrent, fd, cbuf, bufp);
			bufp = cbuf;
		}
		cshift = *pkbshift;
		/* if nothing to read empty buffer and do a select */
		if ((l=Fgetchar (0, 0)) == MiNTEOF) {
			csend (vcurrent, fd, cbuf, bufp);
			bufp = cbuf;
			for (;;) {
				int ctimeout = 0;
				long rfd = 1;

				if (vcurrent) {
					SCREEN *v = v0x+vcurrent-1;
					ctimeout = 500;

					if ((CURS_FLASH|CURS_ON) ==
					    (v->flags & (CURS_FLASH|CURS_ON)))
						ctimeout = v->period*20;
				}
				if (Fselect (ctimeout, &rfd, (long *)0, (long *)0)) {
					/* show cursor if flashing and off */
					if (vcurrent) {
						SCREEN *v = v0x+vcurrent-1;

						if ((CURS_FLASH|CURS_ON) ==
						    (v->flags & (CURS_FLASH|CURS_ON|CURS_FSTATE)))
							Supexec (xflash);
					}
					/* then go read whats there */
					break;
				}
				/* select timed out, flash cursor & try again */
				Supexec (xflash);
			}
			continue;
		}
		if ((cshift & ~0x10) == 8)  switch (scan=(char) (l>>16)) {
			int xfd;	/* ALT-something */

			case 0x61:	/* ALT-UNDO: flush pipe if !empty */
				name[sizeof "u:\\pipe\\q$vt0"-1] = vcurrent+'0';
				if ((xfd = Fopen (name, O_RDONLY)) >= 0
				    && Finstat (xfd) > 0) {
					Fcntl (xfd, (char *) 0, TIOCFLUSH);
					Fclose (xfd);
					bufp = cbuf;
					continue;
				} else if (bufp > cbuf) {
					bufp = cbuf;
					continue;
				}
				break;
			case 0x3b:	/* ALT-F1: vt01 */
			case 0x3c:	/* ALT-F2: vt02 */
			case 0x3d:	/* .	*/
			case 0x3e:	/* .	*/
			case 0x3f:	/* .	*/
			case 0x40:
			case 0x41:
			case 0x42:
			case 0x43:	/* ALT-F9:  vt09 */
				scan += 10;
				/*FALLTHRU*/
			case 0x44:	/* ALT-F10: vt00 */
				if ((scan -= 0x44) < MAX_VT) {
					csend (vcurrent, fd, cbuf, bufp);
					bufp = cbuf;
					xcurrent = scan;
					if (Supexec (xsetcurrent))
						Fputchar (0, 07l, 0);
					continue;
				}
			break;
		} else	if ((ttys[vcurrent].state & TS_COOKED) ||
			    (cshift & 0xc) == 0xc) {
			char ch = (char) l;
			int sig = 0;

			if (!ch)
				;	/* do nothing */
			else if (ch == ttys[vcurrent].tc.t_intrc)
				sig = SIGINT;
			else if (ch == ttys[vcurrent].tc.t_quitc)
				sig = SIGQUIT;
			else if (ch == ttys[vcurrent].ltc.t_suspc)
				sig = SIGTSTP;
			else if (ch == ttys[vcurrent].tc.t_stopc) {
				ttys[vcurrent].state |= TS_HOLD;
				continue;
			}
			else if (ch == ttys[vcurrent].tc.t_startc) {
				ttys[vcurrent].state &= ~TS_HOLD;
				continue;
			}
			if (sig) {
				ttys[vcurrent].state &= ~TS_HOLD;
				if (!(ttys[vcurrent].sg.sg_flags & T_NOFLSH)) {
					Fcntl (fd, (char *) 0, TIOCFLUSH);
					bufp = cbuf;
				}
				killpg (ttys[vcurrent].pgrp, sig);
				continue;
			}
			else if (ttys[vcurrent].state & TS_HOLD) {
				continue;
			}
		}
		*bufp++ = l;
	}
}

SHAR_EOF
cat << \SHAR_EOF > vtdev.c
/*
virtual terminal devices, based on MiNTs fasttext.c, that is...

Copyright 1991,1992 Eric R. Smith.
Copyright 1992,1993 Atari Corporation.
All rights reserved.
*/

#include <stddef.h>
#include <errno.h>
#include <osbind.h>
#include "vcon.h"
#include "vtdev.h"

#ifdef __GNUC__
#define INLINE inline
#define ITYPE long	/* gcc's optimizer likes 32 bit integers */
#else
#define INLINE
#define ITYPE int
#endif

#define CONDEV	(2)

SCREEN *v00, v0x[N_VT-1];

static void paint P_((SCREEN *, int, char *)),
	 paint8c P_((SCREEN *, int, char *)),
	 paint816m P_((SCREEN *, int, char *));

INLINE static void curs_off P_((SCREEN *)), curs_on P_((SCREEN *));
INLINE static void flash P_((SCREEN *));
static void normal_putch P_((SCREEN *, int));
static void escy_putch P_((SCREEN *, int));
static void quote_putch P_((SCREEN *, int));

static	char *chartab[256];

static int fgmask[MAX_PLANES], bgmask[MAX_PLANES];

static long scrnsize;

short hardscroll;
static char *hardbase, *oldbase;

#define base (*((char **)0x44eL))
#define V_BASE(v) ((v) == v00 ? base : (v)->v.t.vbase)
#define V_LINE(v, lx4) ((v) == v00 ? (base + *(long *)(rowoff+(lx4))) : \
			((v)->v.t.vbase + *(long *)((v)->v.t.rowlist+(lx4))))
#define V_LINEAR_P(v) ((v) == v00 || (v)->v.t.on)
#define VT_SCREEN(vt) ((vt) ? v0x+(vt)-1 : v00)
#define escy1 (*((short *)0x4acL))
#define V_ESCY1(v) ((v) == v00 ? escy1 : (v)->v.t.vescy1)
#define V_FGMASK(v) ((v) == v00 ? fgmask : (v)->v.t.fgmask)
#define V_BGMASK(v) ((v) == v00 ? bgmask : (v)->v.t.bgmask)
#define _hz_200 (*((long *)0x4baL))

static Vfunc v00state;
#define V_STATE(v) ((v) == v00 ? &v00state : &(v)->v.t.state)

static short hardline;
static void (*vpaint) P_((SCREEN *, int, char *));
static char *rowoff;
static short qfd[N_VT], q_fl[N_VT];

void exchangeb P_((void *, void *, long));
void init P_((void));
int setcurrent P_((int));
void hardware_scroll P_((SCREEN *));
INLINE static char *PLACE P_((SCREEN *, int, int));
INLINE static void gotoxy P_((SCREEN *, int, int));
INLINE static void clrline P_((SCREEN *, int));
INLINE static void clear P_((SCREEN *));
INLINE static void clrchars P_((SCREEN *, int, int, int));
INLINE static void clrfrom P_((SCREEN *, int, int, int, int));
INLINE static void delete_line P_((SCREEN *, int));
INLINE static void insert_line P_((SCREEN *, int));
static void setbgcol P_((SCREEN *, int));
static void setfgcol P_((SCREEN *, int));
static void setcurs P_((SCREEN *, int));
static void putesc P_((SCREEN *, int));
static void escy1_putch P_((SCREEN *, int));
#if 0
INLINE static void put_ch P_((SCREEN *, int));
#else
INLINE static void put_ch00 P_((SCREEN *, int));
INLINE static void put_ch0x P_((SCREEN *, int));
#endif

/* routines for flashing the cursor for screen v */
/* flash(v): invert the character currently under the cursor */

INLINE static void
flash(v)
	SCREEN *v;
{
	char *place;
	ITYPE i, j, vplanes;

	vplanes = v->planes + v->planes;
	place = v->cursaddr;

	for (j = v->cheight; j > 0; --j) {
		for (i = 0; i < vplanes; i+=2)
			place[i] = ~place[i];

		place += v->planesiz;
	}
	v->curstimer = v->period;
}

/* actually flash cursor (called from vcon.c) */

void xflash()
{
	SCREEN *v = v0x+vcurrent-1;

	/* vt00's cursor is handled by TOS... */
	if (!vcurrent || v->hidecnt)
		return;
	if ((CURS_FLASH|CURS_ON) == (v->flags & (CURS_FLASH|CURS_ON))) {
		flash(v);
		v->flags ^= CURS_FSTATE;
	}
}

/* make sure the cursor is off */

INLINE
static void
curs_off(v)
	SCREEN *v;
{
	if (v->flags & CURS_ON) {
		if (v->flags & CURS_FSTATE) {
			flash(v);
			v->flags &= ~CURS_FSTATE;
		}
	}
}

/* OK, show the cursor again (if appropriate) */

INLINE static void
curs_on(v)
	SCREEN *v;
{
	if (v->hidecnt) return;

	if (v->flags & CURS_ON) {
#if 0
	/* if the cursor is flashing, we cheat a little and leave it off
	 * to be turned on again (if necessary) by the VBL routine
	 */
		if (v->flags & CURS_FLASH) {
			v->curstimer = 2;
			return;
		}
#endif
		if (!(v->flags & CURS_FSTATE)) {
			/* if you can't see the cursor there's no
			   reason to flash it */
			if ((v->flags & CURS_FLASH) &&
			    v != v00 && (!vcurrent || !v->v.t.on))
				return;
			v->flags |= CURS_FSTATE;
			flash(v);
		}
	}
}

#ifdef __GNUC__
#define lineA0()				\
({	register char *retvalue __asm__("d0");	\
	__asm__ volatile("			\
	.word	0xa000 "			\
	: "=r"(retvalue)			\
	:					\
	: "d0", "d1", "d2", "a0", "a1", "a2"    \
	);					\
	retvalue;				\
})
#endif

/* init vt0[1-9] SCREEN struct */

void
init_screen(v, vbase, rowlist, on)
	SCREEN *v;
	char *vbase, *rowlist;
	short on;
{
	static char initv00[sizeof (SCREEN) - offsetof (SCREEN, cheight)];

	if (on)
		memmove (initv00, (char *)&v00->cheight, sizeof (initv00));
	bzero ((char *)v, offsetof (SCREEN, cheight));
	memmove ((char *)&v->cheight, initv00, sizeof (initv00));

	v->v.t.vbase = vbase;
	v->v.t.rowlist = rowlist;
	v->v.t.on = on;
	v->v.t.state = normal_putch;
	v->cursaddr = vbase;
	v->cx = 0; v->cy = 0;
	v->flags = CURS_ON|CURS_FLASH|FWRAP;
	setbgcol(v, v->bgcol);
	setfgcol(v, v->fgcol);
	clear(v);
}

void
init()
{
	SCREEN *v;
	int i, j;
	char *data, *foo;
	static char chardata[256*16];
	register int linelen;

	foo = lineA0();
	v = v00 = (SCREEN *)(foo - 346);
	
	/* Ehem... The screen might be bigger than 32767 bytes.
	   Let's do some casting... 
	   Erling
	*/
	linelen = v->linelen;
	scrnsize = (v->maxy+1)*(long)linelen;
	rowoff = (char *)kmalloc((long)((v->maxy+1) * sizeof(long) * (N_VT-1)));
	if (rowoff == 0) {
		FATAL("Insufficient memory for screen offset table!");
	} else {
		long off, *lptr = (long *)rowoff;
		SCREEN *vp = v0x+1;

		for (i=0, off=0; i<=v->maxy; i++) {
			*lptr++ = off;
			off += linelen;
		}
		for (i=0; i<N_VT-1; i++) {
			(vp++)->v.t.rowlist = (char *)lptr;
			lptr += v->maxy+1;
		}
	}
	if (hardscroll == -1) {
	/* request for auto-setting */
		hardscroll = v->maxy+1;
	}
	if (!hardbase) {
		hardbase = (char *)(((long)kcore(SCNSIZE(v)+256L)+255L)
					   & 0xffffff00L);
		if (hardbase == 0)
			FATAL("Insufficient memory for second screen buffer!");
		init_screen(v0x, hardbase, rowoff, V_FREE);
	}
	hardline = 0;
	if (v->cheight == 8 && v->planes == 2) {
		foo = &chardata[0];
		vpaint = paint8c;
		for (i = 0; i < 256; i++) {
			chartab[i] = foo;
			data = v->fontdata + i;
			for (j = 0; j < 8; j++) {
				*foo++ = *data;
				data += v->form_width;
			}
		}
	} else if ((v->cheight == 16 || v->cheight == 8) && v->planes == 1) {
		foo = &chardata[0];
		vpaint = paint816m;
		for (i = 0; i < 256; i++) {
			chartab[i] = foo;
			data = v->fontdata + i;
			for (j = 0; j < v->cheight; j++) {
				*foo++ = *data;
				data += v->form_width;
			}
		}
	}
	else
		vpaint = paint;

	if (v->hidecnt == 0) {
	/*
	 * make sure the cursor is set up correctly and turned on
	 */
		(void)Cursconf(0,0);	/* turn cursor off */

		v->flags &= ~CURS_FSTATE;

	/* now turn the cursor on the way we like it */
		v->curstimer = v->period;
		v->hidecnt = 0;
		v->flags |= CURS_ON;
		curs_on(v);
	} else {
		(void)Cursconf(0,0);
		v->flags &= ~CURS_ON;
		v->hidecnt = 1;
	}

	/* setup bgmask and fgmask */
	setbgcol(v, v->bgcol);
	setfgcol(v, v->fgcol);
	*V_STATE(v) = normal_putch;
}

/* deinit, must be called after last close */

void
deinit()
{
	kfree (rowoff);
}

/* exchange memory, assumes pointers word-aligned and bytes
   multiple of sizeof long...  (faster implementations welcome :-)
*/

INLINE
void
exchangeb(x1, x2, bytes)
	void *x1, *x2;
	long bytes;
{
	long *p, *q, t;

	for (p = x1, q = x2; bytes > 0; bytes -= sizeof (long)) {
		t = *p;
		*p++ = *q;
		*q++ = t;
	}
}

/*
 * PLACE(v, x, y): the address corresponding to the upper left hand corner of
 * the character at position (x,y) on screen v
 */
INLINE static
char *PLACE(v, x, y)
	SCREEN *v;
	int x, y;
{
	char *place;
	int i, j;

	if (V_LINEAR_P(v)) {
		place = V_BASE(v) + x;
		if (y == v->maxy)
			place += scrnsize - v->linelen;
		else if (y) {
			y+=y;	/* Make Y into index for longword array. */
			y+=y;	/* Two word-size adds are faster than a 2-bit shift. */
			place += *(long *)(rowoff + y);
		}
	} else {
		y+=y;	/* Make Y into index for longword array. */
		y+=y;	/* Two word-size adds are faster than a 2-bit shift. */
		place = V_LINE(v, y) + x;
	}
	if ((j = v->planes-1)) {
		i = (x & 0xfffe);
		do place += i;
		while (--j);
	}
	return place;
}

int
setcurrent(vt)
	int vt;
{
	static int v0xcurrent = 1;
	SCREEN *v = VT_SCREEN(vt);

	/* are we changing to a `stored' screen? */
	if (vt && vt != v0xcurrent) {
		SCREEN *oldv = VT_SCREEN(v0xcurrent);
		char *foo, *vline = oldv->v.t.vbase;
		int i;

		if (!v->v.t.vbase)
			/* sorry terminal closed, has no screen memory */
			return 1;

		/* exchange screen contents... */
		for (i=0; i<=v->maxy*sizeof (long); i+=sizeof (long)) {
			exchangeb (vline, V_LINE(v, i), v->linelen);
			vline += v->linelen;
		}
		/* and pointers... */
		foo = oldv->v.t.vbase;
		oldv->v.t.vbase = v->v.t.vbase;
		v->v.t.vbase = foo;
		foo = oldv->v.t.rowlist;
		oldv->v.t.rowlist = v->v.t.rowlist;
		v->v.t.rowlist = foo;

		/* free screen memory if told so */
		if (oldv->v.t.on == V_FREE) {
			oldv->v.t.on = 0;
			kfree (oldv->v.t.vbase);
			oldv->v.t.vbase = 0;
		} else {
			oldv->v.t.on = 0;
			oldv->cursaddr = PLACE(oldv, oldv->cx, oldv->cy);
		}
		v->v.t.on = V_USED;
		v->cursaddr = PLACE(v, v->cx, v->cy);
		v0xcurrent = vt;
	}
	vcurrent = vt;
	if (vt && (v->flags & CURS_FLASH))
		curs_on(v);
	Setscreen(-1l, V_BASE(v), -1);
	return 0;
}

/*
 * paint(v, c, place): put character 'c' at position 'place' on screen
 * v. It is assumed that x, y are proper coordinates!
 * Specialized versions (paint8c and paint816m) of this routine follow;
 * they assume 8 line high characters, medium res. and 8 or 16 line/mono,
 * respectively.
 */

static void
paint(v, c, place)
	SCREEN *v;
	int c;
	char *place;
{
	char *data, d, doinverse;
	ITYPE j, planecount;
	int vplanes;
	long vform_width, vplanesiz;
	int *fgmaskv = V_FGMASK(v), *bgmaskv = V_BGMASK(v);

	vplanes = v->planes;

	data = v->fontdata + c;
	doinverse = (v->flags & FINVERSE) ? 0xff : 0;
	vform_width = v->form_width;
	vplanesiz = v->planesiz;

	for (j = v->cheight-1; j > 0; --j) {
		d = *data ^ doinverse;
		for (planecount = 0; planecount < vplanes; planecount++)
		  place[planecount << 1]
		    = ((d & (char) fgmaskv[planecount])
		       | (~d & (char) bgmaskv[planecount]));
		place += vplanesiz;
		data += vform_width;
	}
	d = ((v->flags & FUNDERLINE) ? -1 : *data) ^ doinverse;
	for (planecount = 0; planecount < vplanes; planecount++)
	  place[planecount << 1]
	    = ((d & (char) fgmaskv[planecount])
	       | (~d & (char) bgmaskv[planecount]));
}

static void
paint8c(v, c, place)
	SCREEN *v;
	int c;
	char *place;
{
	char *data;
	char d, doinverse, dounderline;
	char bg0, bg1, fg0, fg1;
	long vplanesiz;
	int *m;

	data = chartab[c];

	doinverse = (v->flags & FINVERSE) ? 0xff : 0;
	dounderline = (v->flags & FUNDERLINE) ? 0xff : 0;
	vplanesiz = v->planesiz;
	m = V_BGMASK(v);
	bg0 = *m++;
	bg1 = *m++;
	m = V_FGMASK(v);
	fg0 = *m++;
	fg1 = *m++;

	if (!doinverse && !bg0 && !bg1 && fg0 && fg1) {
		/* line 1 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 2 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 3 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 4 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 5 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 6 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 7 */
		d = *data++;
		*place = d;
		place[2] = d;
		place += vplanesiz;

		/* line 8 */
		d = *data | dounderline;
		*place = d;
		place[2] = d;
	} else {
		/* line 1 */
		d = *data++ ^ doinverse;
		*place = ((d & fg0) | (~d & bg0));
		place[2] = ((d & fg1) | (~d & bg1));
		place += vplanesiz;

		/* line 2 */
		d = *data++ ^ doinverse;
		*place = ((d & fg0) | (~d & bg0));
		place[2] = ((d & fg1) | (~d & bg1));
		place += vplanesiz;

		/* line 3 */
		d = *data++ ^ doinverse;
		*place = ((d & fg0) | (~d & bg0));
		place[2] = ((d & fg1) | (~d & bg1));
		place += vplanesiz;

		/* line 4 */
		d = *data++ ^ doinverse;
		*place = ((d & fg0) | (~d & bg0));
		place[2] = ((d & fg1) | (~d & bg1));
		place += vplanesiz;

		/* line 5 */
		d = *data++ ^ doinverse;
		*place = ((d & fg0) | (~d & bg0));
		place[2] = ((d & fg1) | (~d & bg1));
		place += vplanesiz;

		/* line 6 */
		d = *data++ ^ doinverse;
		*place = ((d & fg0) | (~d & bg0));
		place[2] = ((d & fg1) | (~d & bg1));
		place += vplanesiz;

		/* line 7 */
		d = *data++ ^ doinverse;
		*place = ((d & fg0) | (~d & bg0));
		place[2] = ((d & fg1) | (~d & bg1));
		place += vplanesiz;

		/* line 8 */
		d = (*data | dounderline) ^ doinverse;
		*place = ((d & fg0) | (~d & bg0));
		place[2] = ((d & fg1) | (~d & bg1));
	}
}

static void
paint816m(v, c, place)
	SCREEN *v;
	int c;
	char *place;
{
	char *data;
	char d, doinverse, dounderline;
	long vplanesiz;

	data = chartab[c];
	doinverse = (v->flags & FINVERSE) ? 0xff : 0;
	doinverse ^= (d = V_BGMASK(v)[0]);
	dounderline = (v->flags & FUNDERLINE) ? 0xff : 0;
	vplanesiz = v->planesiz;

	if (d == V_FGMASK(v)[0])
	  {
	    /* fgcol and bgcol are the same -- easy */
	    *place = d;
	    place += vplanesiz;
	    *place = d;
	    place += vplanesiz;
	    *place = d;
	    place += vplanesiz;
	    *place = d;
	    place += vplanesiz;
	    *place = d;
	    place += vplanesiz;
	    *place = d;
	    place += vplanesiz;
	    *place = d;
	    place += vplanesiz;
	    *place = d;
	    if (v->cheight == 8)
		return;
	    place += vplanesiz;
	    *place = d;
	    place += vplanesiz;
	    *place = d;
	    place += vplanesiz;
	    *place = d;
	    place += vplanesiz;
	    *place = d;
	    place += vplanesiz;
	    *place = d;
	    place += vplanesiz;
	    *place = d;
	    place += vplanesiz;
	    *place = d;
	    place += vplanesiz;
	    *place = d;
	  }
	else if (!doinverse) {
		/* line 1 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 2 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 3 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 4 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 5 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 6 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 7 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 8 */
		d = *data++;
		if (v->cheight == 8) {
			*place = d | dounderline;
			return;
		}
		*place = d;

		place += vplanesiz;

		/* line 9 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 10 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 11 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 12 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 13 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 14 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 15 */
		d = *data++;
		*place = d;
		place += vplanesiz;

		/* line 16 */
		d = *data;
		*place = d | dounderline;
	} else {
		/* line 1 */
		d = ~*data++;
		*place = d;
		place += vplanesiz;

		/* line 2 */
		d = ~*data++;
		*place = d;
		place += vplanesiz;

		/* line 3 */
		d = ~*data++;
		*place = d;
		place += vplanesiz;

		/* line 4 */
		d = ~*data++;
		*place = d;
		place += vplanesiz;

		/* line 5 */
		d = ~*data++;
		*place = d;
		place += vplanesiz;

		/* line 6 */
		d = ~*data++;
		*place = d;
		place += vplanesiz;

		/* line 7 */
		d = ~*data++;
		*place = d;
		place += vplanesiz;

		/* line 8 */
		d = ~*data++;
		if (v->cheight == 8) {
			*place = d | dounderline;
			return;
		}
		*place = d;

		place += vplanesiz;

		/* line 9 */
		d = ~*data++;
		*place = d;
		place += vplanesiz;

		/* line 10 */
		d = ~*data++;
		*place = d;
		place += vplanesiz;

		/* line 11 */
		d = ~*data++;
		*place = d;
		place += vplanesiz;

		/* line 12 */
		d = ~*data++;
		*place = d;
		place += vplanesiz;

		/* line 13 */
		d = ~*data++;
		*place = d;
		place += vplanesiz;

		/* line 14 */
		d = ~*data++;
		*place = d;
		place += vplanesiz;

		/* line 15 */
		d = ~*data++;
		*place = d;
		place += vplanesiz;

		/* line 16 */
		d = ~*data;
		*place = d | dounderline;
	}
}

/*
 * gotoxy (v, x, y): move current cursor address of screen v to (x, y)
 * makes sure that (x, y) will be legal
 */

INLINE static void
gotoxy(v, x, y)
	SCREEN *v;
	int x, y;
{
	if (x > v->maxx) x = v->maxx;
	else if (x < 0) x = 0;
	if (y > v->maxy) y = v->maxy;
	else if (y < 0) y = 0;

	v->cx = x;
	v->cy = y;
	v->cursaddr = PLACE(v, x, y);
}

/*
 * clrline(v, r): clear line r of screen v
 */

INLINE static void
clrline(v, r)
	SCREEN *v;
	int r;
{
	int *dst, *m;
	long nwords;
	int i, vplanes = v->planes;

	/* Hey, again the screen might be bigger than 32767 bytes.
	   Do another cast... */
	r += r;
	r += r;
	dst = (int *)(V_LINE(v, r));
	if (v->bgcol == 0)
	  zero((char *)dst, v->linelen);
	else if (vplanes == 1)
	  memset ((char *)dst, *V_BGMASK(v), v->linelen);
	else
	  {
	    /* do it the hard way */
	    for (nwords = v->linelen >> 1; nwords > 0; nwords -= vplanes)
	      {
		m = V_BGMASK(v);
		for (i = 0; i < vplanes; i++)
		  *dst++ = *m++;
	      }
	  }
}
	
/*
 * clear(v): clear the whole screen v
 */

INLINE static void
clear(v)
	SCREEN *v;
{
	int i, vplanes = v->planes;
	int *dst = (int *) V_BASE(v), *m;
	long nwords;

	if (!V_LINEAR_P(v))
	  memmove (v->v.t.rowlist, rowoff, ((v->maxy+1) * sizeof(long)));
	if (v->bgcol == 0)
	  zero((char *)dst, scrnsize);
	else if (vplanes == 1)
	  memset ((char *)dst, *V_BGMASK(v), scrnsize);
	else
	  {
	    /* do it the hard way */
	    for (nwords = scrnsize >> 1; nwords > 0; nwords -= vplanes)
	      {
		m = V_BGMASK(v);
		for (i = 0; i < vplanes; i++)
		  *dst++ = *m++;
	      }
	  }
}

/*
 * clrchars(v, x, y, n): clear n chars starting at position (x,y) on screen v
 */

/*INLINE*/ static void
clrchars(v, x, y, n)
	SCREEN *v;
	int x, y, n;
{
	int i, j, vplanes;
	char *place;
	int *m, *l;

	if (!x && n == v->maxx+1) {
		clrline(v, y);
		return;
	}
	vplanes = v->planes + v->planes;

	if (y == v->cy && x == v->cx)
		place = v->cursaddr;
	else
		place = PLACE(v, x, y);

	l = V_BGMASK(v);
	if (vplanes > 2) {
		if (x & 1) {
			char *p = place;
			for (j = v->cheight; j > 0; --j) {
				char *q = p;
				m = l;
				for (i = 0; i < vplanes; i += 2) {
					*q++ = (char) *m++;
					++q;
				}
				p += v->planesiz;
			}
			place += vplanes-1;
			--n;
		}
		if (n > 1) {
			int nbytes = n*(vplanes>>1);
			char *p = place;
			place += nbytes;

			if (v->bgcol == 0) {
				for (j = v->cheight; j > 0; --j) {
					bzero(p, nbytes);
					p += v->planesiz;
				}
			} else {
				for (j = v->cheight; j > 0; --j) {
					short *q = (short *)p;
					int k;

					for (k = n; k > 1; k -= 2) {
						m = l;
						for (i = 0; i < vplanes; i += 2)
							*q++ = *m++;
					}
					p += v->planesiz;
				}
			}
		}
		if (n & 1) {
			for (j = v->cheight; j > 0; --j) {
				char *p = place;
				m = l;
				for (i = 0; i < vplanes; i += 2) {
					*p++ = (char) *m++;
					++p;
				}
			}
			place += v->planesiz;
		}
	} else {
		for (j = v->cheight; j > 0; --j) {
			memset (place, *l, n);
			place += v->planesiz;
		}
	}
}

/*
 * clrfrom(v, x1, y1, x2, y2): clear screen v from position (x1,y1) to
 * position (x2, y2) inclusive. It is assumed that y2 >= y1.
 */

INLINE static void
clrfrom(v, x1, y1, x2, y2)
	SCREEN *v;
	int x1,y1,x2,y2;
{
	int i;

	clrchars(v, x1, y1, (y2 == y1 ? x2 : v->maxx)-x1+1);
	if (y2 > y1) {
		for (i = y1+1; i < y2; i++)
			clrline(v, i);
		clrchars(v, 0, y2, x2);
	}
}

/*
 * scroll a screen in hardware; if we still have hardware scrolling lines left,
 * just move the physical screen base, otherwise copy the screen back to the
 * hardware base and start over
 */
void
hardware_scroll(v)
	SCREEN *v;
{

	++hardline;
	if (hardline < hardscroll) { /* just move the screen */
		v->v.t.vbase += v->linelen;
	} else {
		hardline = 0;
		quickmove(hardbase, v->v.t.vbase + v->linelen, scrnsize - v->linelen);
		v->v.t.vbase = hardbase;
	}
	v->cursaddr = PLACE(v, v->cx, v->cy);
	if (vcurrent)
		Setscreen(-1l, v->v.t.vbase, -1);
}

/*
 * delete_line(v, r): delete line r of screen v. The screen below this
 * line is scrolled up, and the bottom line is cleared.
 */

#define scroll(v) delete_line(v, 0)

INLINE static void
delete_line(v, r)
	SCREEN *v;
	int r;
{
	long *src, *dst, nbytes;

	/* if this screen needs not be linear (i.e. its `stored' not shown)
	   then just adjust the line offset table...
	*/
	if (!V_LINEAR_P(v)) {
		register int i = r + r;
		long t;
		i += i;
		src = (long *)(v->v.t.rowlist+i);
		i = v->maxy - r;
		i += i;
		i += i;
		t = *src;
		memmove (src, src+1, i);
		i = v->maxy + v->maxy;
		i += i;
		*(long *)(v->v.t.rowlist+i) = t;
		clrline(v, v->maxy);
		return;
	}
	if (r == 0) {
		if (v != v00 & hardscroll > 0) {
			hardware_scroll(v);
			clrline(v, v->maxy);
			return;
		}
		nbytes = scrnsize - v->linelen;
	} else {
		register int i = v->maxy - r;
		i += i;
		i += i;
		nbytes = *(long *)(rowoff+i);
	}

	/* Sheeze, how many times do we really have to cast... 
	   Erling.	
	*/

	r += r;
	r += r;
	dst = (long *)(V_BASE(v) + *(long *)(rowoff + r));
	src = (long *)( ((long)dst) + v->linelen);

	quickmove(dst, src, nbytes);

/* clear the last line */
	clrline(v, v->maxy);
}

void
hardware_scroll_down(v)
	SCREEN *v;
{

	--hardline;
	if (hardline >= 0) { /* just move the screen */
		v->v.t.vbase -= v->linelen;
	} else {
		hardline = hardscroll - 1;
		v->v.t.vbase = hardbase + (long) hardline*v->linelen;
		memmove(v->v.t.vbase + v->linelen, hardbase, scrnsize - v->linelen);
	}
	v->cursaddr = PLACE(v, v->cx, v->cy);
	if (vcurrent)
		Setscreen(-1l, v->v.t.vbase, -1);
}

/*
 * insert_line(v, r): scroll all of the screen starting at line r down,
 * and then clear line r.
 */

INLINE static void
insert_line(v, r)
	SCREEN *v;
	int r;
{
	long *src, *dst;
	int i, j, linelen;

	if (!V_LINEAR_P(v)) {
		long t;
		i = r + r;
		i += i;
		src = (long *)(v->v.t.rowlist+i);
		i = v->maxy + v->maxy;
		i += i;
		t = *(long *)(v->v.t.rowlist+i);
		i = v->maxy - r;
		i += i;
		i += i;
		memmove (src+1, src, i);
		*src = t;
		clrline(v, r);
		return;
	}
	if (!r && v != v00 & hardscroll > 0) {
		hardware_scroll_down(v);
		clrline(v, 0);
		return;
	}
	i = v->maxy - 1;
	i += i;
	i += i;
	j = r+r;
	j += j;
	linelen = v->linelen;
	src = (long *)(V_BASE(v) + *(long *)(rowoff + i));
	dst = (long *)((long)src + linelen);
	for (; i >= j ; i -= 4) {
	/* move line i to line i+1 */
		quickmove(dst, src, linelen);
		dst = src;
		src = (long *)((long) src - linelen);
	}

/* clear line r */
	clrline(v, r);
}

/*
 * special states for handling ESC b x and ESC c x. Note that for now,
 * color is ignored.
 */

static void
setbgcol(v, c)
	SCREEN *v;
	int c;
{
	int i;
	int *m = V_BGMASK(v);

	v->bgcol = c & ((1 << v->planes)-1);
	for (i = 0; i < v->planes; i++)
	    *m++ = (v->bgcol & (1 << i)) ? -1 : 0;
	*V_STATE(v) = normal_putch;
}

static void
setfgcol(v, c)
	SCREEN *v;
	int c;
{
	int i;
	int *m = V_FGMASK(v);

	v->fgcol = c & ((1 << v->planes)-1);
	for (i = 0; i < v->planes; i++)
	    *m++ = (v->fgcol & (1 << i)) ? -1 : 0;
	*V_STATE(v) = normal_putch;
}

static void
setcurs(v, c)
	SCREEN *v;
	int c;
{
	c -= ' ';
	if (!c) {
		v->flags &= ~CURS_FLASH;
	} else {
		v->flags |= CURS_FLASH;
		v->period = (unsigned char) c;
	}
	*V_STATE(v) = normal_putch;
}

/* set special effects...  FIXME: only inverse and underline do anything */
static void
seffect_putch(v, c)
	SCREEN *v;
	int c;
{
	v->flags |= ((c & 0x10) ? FINVERSE : 0)|((c & 0x8) ? FUNDERLINE : 0);
	*V_STATE(v) = normal_putch;
}

/* clear special effects */
static void
ceffect_putch(v, c)
	SCREEN *v;
	int c;
{
	v->flags &= ~(((c & 0x10) ? FINVERSE : 0)|((c & 0x8) ? FUNDERLINE : 0));
	*V_STATE(v) = normal_putch;
}

static void
quote_putch(v, c)
	SCREEN *v;
	int c;
{
	(*vpaint)(v, c, v->cursaddr);
	*V_STATE(v) = normal_putch;
}

/*
 * putesc(v, c): handle the control sequence ESC c
 */

static void
putesc(v, c)
	SCREEN *v;
	int c;
{
	int i;
	int cx, cy;

	cx = v->cx; cy = v->cy;

	switch (c) {
	case 'A':		/* cursor up */
		if (cy) {
moveup:			v->cy = --cy;
			if (V_LINEAR_P(v))
				v->cursaddr -= v->linelen;
			else {
				long *r;
				i = cy + cy;
				i += i;
				r = (long *)(v->v.t.rowlist+i);
				v->cursaddr -= r[1] - r[0];
			}
		}
		break;
	case 'B':		/* cursor down */
		if (cy < v->maxy) {
			v->cy = ++cy;
			if (V_LINEAR_P(v))
				v->cursaddr += v->linelen;
			else {
				long *r;
				i = cy + cy;
				i += i;
				r = (long *)(v->v.t.rowlist+i);
				v->cursaddr += r[0] - r[-1];
			}
		}
		break;
	case 'C':		/* cursor right */
		if (cx < v->maxx) {
			if ((i = v->planes-1) && (cx & 1))
				v->cursaddr += i + i;
			v->cx = ++cx;
			v->cursaddr++;
		}
		break;
	case 'D':		/* cursor left */
		if (cx) {
			v->cx = --cx;
			v->cursaddr--;
			if ((i = v->planes-1) && (cx & 1))
				v->cursaddr -= i + i;
		}
		break;
	case 'E':		/* clear home */
		clear(v);
		/* fall through... */
	case 'H':		/* cursor home */
		v->cx = 0; v->cy = 0;
		v->cursaddr = V_LINE(v, 0);
		break;
	case 'I':		/* cursor up, insert line */
		if (cy == 0) {
			insert_line(v, 0);
			if (!V_LINEAR_P(v)) {
				long *r;
				i = cy + cy;
				i += i;
				r = (long *)(v->v.t.rowlist+i);
				v->cursaddr -= r[1] - r[0];
			}
		}
		else
			goto moveup;
		break;
	case 'J':		/* clear below cursor */
		clrfrom(v, cx, cy, v->maxx, v->maxy);
		break;
	case 'K':		/* clear remainder of line */
		clrfrom(v, cx, cy, v->maxx, cy);
		break;
	case 'L':		/* insert a line */
		v->cx = 0;
		i = cy + cy;
		i += i;
		insert_line(v, cy);
		v->cursaddr = V_LINE(v, i);
		break;
	case 'M':		/* delete line */
		v->cx = 0;
		i = cy + cy;
		i += i;
		delete_line(v, cy);
		v->cursaddr = V_LINE(v, i);
		break;
	case 'Q':		/* EXTENSION: quote-next-char */
		*V_STATE(v) = quote_putch;
		return;
	case 'Y':
		*V_STATE(v) = escy_putch;
		return;		/* YES, this should be 'return' */

	case 'b':
		*V_STATE(v) = setfgcol;
		return;
	case 'c':
		*V_STATE(v) = setbgcol;
		return;
	case 'd':		/* clear to cursor position */
		clrfrom(v, 0, 0, cx, cy);
		break;
	case 'e':		/* enable cursor */
		v->flags |= CURS_ON;
		v->hidecnt = 1;	/* so --v->hidecnt shows the cursor */
		break;
	case 'f':		/* cursor off */
		v->hidecnt++;
		v->flags &= ~CURS_ON;
		break;
	case 'j':		/* save cursor position */
		v->savex = v->cx;
		v->savey = v->cy;
		break;
	case 'k':		/* restore saved position */
		gotoxy(v, v->savex, v->savey);
		break;
	case 'l':		/* clear line */
		v->cx = 0;
		i = cy + cy;
		i += i;
		v->cursaddr = V_LINE(v, i);
		clrline(v, cy);
		break;
	case 'o':		/* clear from start of line to cursor */
		clrfrom(v, 0, cy, cx, cy);
		break;
	case 'p':		/* reverse video on */
		v->flags |= FINVERSE;
		break;
	case 'q':		/* reverse video off */
		v->flags &= ~FINVERSE;
		break;
	case 't':		/* EXTENSION: set cursor flash rate */
		*V_STATE(v) = setcurs;
		return;
	case 'v':		/* wrap on */
		v->flags |= FWRAP;
		break;
	case 'w':
		v->flags &= ~FWRAP;
		break;
	case 'y':		/* EXTENSION: set special effects */
		*V_STATE(v) = seffect_putch;
		curs_on(v);
		return;
	case 'z':		/* EXTENSION: clear special effects */
		*V_STATE(v) = ceffect_putch;
		curs_on(v);
		return;
	}
	*V_STATE(v) = normal_putch;
}

/*
 * escy1_putch(v, c): for when an ESC Y + char has been seen
 */
static void
escy1_putch(v, c)
	SCREEN *v;
	int c;
{
	/* some (un*x) termcaps seem to always set the hi bit on
	   cm args (cm=\EY%+ %+ :) -> drop that unless the screen
	   is bigger.	-nox
	*/
	gotoxy(v, (c-' ') & (v->maxx|0x7f), (V_ESCY1(v)-' ') & (v->maxy|0x7f));
	*V_STATE(v) = normal_putch;
}

/*
 * escy_putch(v, c): for when an ESC Y has been seen
 */
static void
escy_putch(v, c)
	SCREEN *v;
	int c;
{
	V_ESCY1(v) = c;
	*V_STATE(v) = escy1_putch;
}

/*
 * normal_putch(v, c): put character 'c' on screen 'v'. This is the default
 * for when no escape, etc. is active
 */

static void
normal_putch(v, c)
	SCREEN *v;
	int c;
{
	register int i;

/* control characters */
	if (c < ' ') {
		switch (c) {
		case '\r':
col0:			v->cx = 0;
			i = v->cy + v->cy;
			i += i;
			v->cursaddr = V_LINE(v, i);
			return;
		case '\n':
			if (v->cy == v->maxy) {
				scroll(v);
				if (!V_LINEAR_P(v)) {
					long *r;
					i = v->cy + v->cy;
					i += i;
					r = (long *)(v->v.t.rowlist+i);
					v->cursaddr += r[0] - r[-1];
				}
			} else {
				v->cy++;
				if (V_LINEAR_P(v))
					v->cursaddr += v->linelen;
				else {
					long *r;
					i = v->cy + v->cy;
					i += i;
					r = (long *)(v->v.t.rowlist+i);
					v->cursaddr += r[0] - r[-1];
				}
			}
			return;
		case '\b':
			if (v->cx) {
				v->cx--;
				v->cursaddr--;
				if ((i = v->planes-1) && (v->cx & 1))
					v->cursaddr -= i+i;
			}
			return;
		case '\007':		/* BELL */
			(void)bconout(CONDEV, 7);
			return;
		case '\033':		/* ESC */
			*V_STATE(v) = putesc;
			return;
		case '\t':
			if (v->cx < v->maxx) {
			/* this can't be register for an ANSI compiler */
				union {
					long l;
					short i[2];
				} j;
				j.l = 0;
				j.i[1] = 8 - (v->cx & 7);
				v->cx += j.i[1];
				if (v->cx - v->maxx > 0) {
					j.i[1] = v->cx - v->maxx;
					v->cx = v->maxx;
				}
				v->cursaddr += j.l;
				if ((i = v->planes-1)) {
					if (j.l & 1)
						j.i[1]++;
					do v->cursaddr += j.l;
					while (--i);
				}
			}
			return;
		default:
			return;
		}
	}

	(*vpaint)(v, c, v->cursaddr);
	v->cx++;
	if (v->cx > v->maxx) {
		if (v->flags & FWRAP) {
			normal_putch(v, '\n');
			goto col0;
		} else {
			v->cx = v->maxx;
		}
	} else {
		v->cursaddr++;
		if ((i = v->planes-1) && !(v->cx & 1))	/* new word */
			v->cursaddr += i + i;
	}
}

INLINE static void
put_ch00(v, c)
	SCREEN *v;
	int c;
{
	(*v00state)(v, c & 0x00ff);
}

INLINE static void
put_ch0x(v, c)
	SCREEN *v;
	int c;
{
	(*v->v.t.state)(v, c & 0x00ff);
}

static long ARGS_ON_STACK screen_open	P_((FILEPTR *f));
static long ARGS_ON_STACK screen_read	P_((FILEPTR *f, char *buf, long nbytes));
static long ARGS_ON_STACK screen_write P_((FILEPTR *f, const char *buf, long nbytes));
static long ARGS_ON_STACK screen_lseek P_((FILEPTR *f, long where, int whence));
static long ARGS_ON_STACK screen_ioctl P_((FILEPTR *f, int mode, void *buf));
static long ARGS_ON_STACK screen_close P_((FILEPTR *f, int pid));
static long ARGS_ON_STACK screen_select P_((FILEPTR *f, long p, int mode));
static void ARGS_ON_STACK screen_unselect P_((FILEPTR *f, long p, int mode));

static long ARGS_ON_STACK screen_datime	P_((FILEPTR *f, short *time, int rwflag));

DEVDRV vcon_device = {
	screen_open, screen_write, screen_read, screen_lseek, screen_ioctl,
	screen_datime, screen_close, screen_select, screen_unselect
};

static long ARGS_ON_STACK 
screen_open(f)
	FILEPTR *f;
{
	int fd, vt = f->fc.aux;
	char name[] = "u:\\pipe\\q$vt00";

	if (!rowoff) {
		init();
	} else if (!ttys[0].use_cnt || leaving)
		/* if we're init'ed already and vt00 is closed that means
		   we're uninistalling... */
		return -EACCESS;
	if (!((struct tty *)f->devinfo)->use_cnt) {
		SCREEN *v = VT_SCREEN(vt);

		/* init and alloc screen memory if necessary */
		if (vt) {
			if (!v->v.t.vbase) {
				char *vbase = (char *)kmalloc(scrnsize);
				if (!vbase)
					return -ENOMEM;
				init_screen (v, vbase, v->v.t.rowlist, 0);
			} else if (v->v.t.on == V_FREE)
				v->v.t.on = V_USED;
		}
		/* is there a better way??? */
		name[sizeof "u:\\pipe\\q$vt0"-1] = vt+'0';
		if ((fd = FOPEN (name, O_RDONLY|O_GLOBAL)) < 0)
			return fd;
		qfd[vt] = fd;
		q_fl[vt] = 0;
	}

	f->flags |= O_TTY;
	return 0;
}

static long ARGS_ON_STACK 
screen_close(f, pid)
	FILEPTR *f;
	int pid;
{
	UNUSED(pid);

	if (!((struct tty *)f->devinfo)->use_cnt) {
		int vt = f->fc.aux;

		/* close pipe */
		FCLOSE (qfd[vt]);

		/* last close on vt00 means uninstall... */
		if (!vt)
			deinit();
		/* otherwise it means free screen memory */
		else {
			SCREEN *v = VT_SCREEN(vt);

			if (v->v.t.on)
				v->v.t.on = V_FREE;
			else {
				kfree (v->v.t.vbase);
				v->v.t.vbase = 0;
			}
		}
	}
	return 0;
}

static long ARGS_ON_STACK 
screen_write(f, buf, bytes)
	FILEPTR *f; const char *buf; long bytes;
{
	int vt = f->fc.aux;
	SCREEN *v = VT_SCREEN(vt);
	long *r;
	long ret = 0;
	int c;
	long tick;

	UNUSED(f);

	/* tty_write is calling us with no more than one line or 128
	   chars at a time but still never(?) allows task-switches
	   while doing a longer write... checkkeys() does this when
	   it detects a keyboard interrupt but we cant call that.
	   instead we look for 0->1 (_hz_200 & 3) ticks that happened
	   while we were writing (there are 50 of them in a second)
	   and yield() when found one.  (comments?)
	*/
#if 0
	(void)checkkeys();
#else
	tick = _hz_200;
#endif
	v->hidecnt++;
	v->flags |= CURS_UPD;		/* for TOS 1.0 */
	curs_off(v);
	r = (long *)buf;
	if (vt) {
		while (bytes > 0) {
			c = (int) *r++;
			put_ch0x(v, c);
			bytes -= 4; ret+= 4;
		}
	} else {
		while (bytes > 0) {
			c = (int) *r++;
			put_ch00(v, c);
			bytes -= 4; ret+= 4;
		}
	}
	if (v->hidecnt > 0)
		--v->hidecnt;
	else
		v->hidecnt = 0;
	curs_on(v);
	v->flags &= ~CURS_UPD;
#if 1
	if (tick != _hz_200 && !(tick & 3))
		yield();
#endif
	return ret;
}

static long ARGS_ON_STACK 
screen_read(f, buf, bytes)
	FILEPTR *f; char *buf; long bytes;
{
	int vt = f->fc.aux;

	if ((f->flags & O_NDELAY) != q_fl[vt])
		FCNTL (qfd[vt], (long)(q_fl[vt] = f->flags&O_NDELAY), F_SETFL);
	return FREAD (qfd[vt], bytes, buf);
}

static long ARGS_ON_STACK 
screen_lseek(f, where, whence)
	FILEPTR *f;
	long where;
	int whence;
{
/* terminals always are at position 0 */
	UNUSED(f); UNUSED(where);
	UNUSED(whence);
	return 0;
}

static long ARGS_ON_STACK 
screen_ioctl(f, mode, buf)
	FILEPTR *f; int mode; void *buf;
{
	int vt = f->fc.aux;
	long *r = (long *)buf;
	struct winsize *w;

	UNUSED(f);

	if (mode == FIONREAD) {
		*r = FINSTAT (qfd[vt]);
		if (*r > 0)
			*r >>= 2;
	}
	else if (mode == FIONWRITE) {
		*r = 0x400;
	}
	else if (mode == TIOCFLUSH) {
		return FCNTL (qfd[vt], r, TIOCFLUSH);
	}
	else if (mode == TIOCGWINSZ) {
		SCREEN *v = VT_SCREEN(vt);
		w = (struct winsize *)buf;
		w->ws_row = v->maxy+1;
		w->ws_col = v->maxx+1;
	}
	else if (mode >= TCURSOFF && mode <= TCURSGRATE) {
		SCREEN *v = VT_SCREEN(vt);
		switch(mode) {
		case TCURSOFF:
			curs_off(v);
			v->hidecnt++;
			v->flags &= ~CURS_ON;
			break;
		case TCURSON:
			v->flags |= CURS_ON;
			v->hidecnt = 0;
			curs_on(v);
			break;
		case TCURSBLINK:
			curs_off(v);
			v->flags |= CURS_FLASH;
			curs_on(v);
			break;
		case TCURSSTEADY:
			curs_off(v);
			v->flags &= ~CURS_FLASH;
			curs_on(v);
			break;
		case TCURSSRATE:
			v->period = *((short *)buf);
			break;
		case TCURSGRATE:
			return v->period;
		}
	} else
		return -EINVAL;

	return 0;
}

static long ARGS_ON_STACK 
screen_select(f, p, mode)
	FILEPTR *f; long p; int mode;
{
	struct tty *tty = (struct tty *)f->devinfo;
	int vt = f->fc.aux;

	if (mode == O_RDONLY) {
		if (FINSTAT (qfd[vt])) {
			return 1;
		}
		if (tty) {
		/* avoid collisions with other processes */
			if (!tty->rsel)
				tty->rsel = p;
		}
		return 0;
	} else if (mode == O_WRONLY) {
		return 1;
	}
	/* default -- we don't know this mode, return 0 */
	return 0;
}

static void ARGS_ON_STACK 
screen_unselect(f, p, mode)
	FILEPTR *f;
	long p;
	int mode;
{
	struct tty *tty = (struct tty *)f->devinfo;

	if (tty) {
		if (mode == O_RDONLY && tty->rsel == p)
			tty->rsel = 0;
		else if (mode == O_WRONLY && tty->wsel == p)
			tty->wsel = 0;
	}
}

long ARGS_ON_STACK 
screen_datime(f, timeptr, rwflag)
	FILEPTR *f;
	short *timeptr;
	int rwflag;
{
	int vt = f->fc.aux;

	if (rwflag)
		return -EACCESS;
	return FDATIME (timeptr, qfd[vt], 0);
}
SHAR_EOF
cat << \SHAR_EOF > vcon.h
#if 1

#ifdef __GNUC__
#define EXITING volatile	/* function never returns */
#else
#define EXITING
#endif

/* define how to call functions with stack parameter passing */
#ifdef __TURBOC__
#define ARGS_ON_STACK cdecl
#else
#define ARGS_ON_STACK
#endif

/* define to indicate unused variables */
#ifdef __TURBOC__
#define UNUSED(x)	(void)x
#else
#define UNUSED(x)
#endif

#ifdef __STDC__
#define P_(x) x
#else
#define P_(x) ()
#define const
#define volatile
#endif

typedef unsigned short	ushort;
typedef long ARGS_ON_STACK (*Func)();
#include "file.h"
#else
#include "filesys.h"
#endif

#define CTRL(x) ((x) & 0x1f)
#ifndef T_NOFLSH
#define T_NOFLSH	0x0040		/* don't flush buffer when signals
					   are received */
#endif

#if !RAW
#undef RAW
#undef ECHO
#define RAW T_RAW
#define ECHO T_ECHO
#define CRMOD T_CRMOD
#define CBREAK T_CBREAK
#define TOSTOP T_TOSTOP
#define XKEY T_XKEY
#endif

extern struct kerinfo *kernel;
#define CCONWS (void)(*kernel->dos_tab[0x09])

#define FOPEN (*kernel->dos_tab[0x3d])
#define FCLOSE (*kernel->dos_tab[0x3e])
#define FREAD (*kernel->dos_tab[0x3f])
#define MXALLOC (*kernel->dos_tab[0x44])
#define FDATIME (*kernel->dos_tab[0x44])
#define FCNTL (*kernel->dos_tab[0x104])
#define FINSTAT (*kernel->dos_tab[0x105])
#define FGETCHAR (*kernel->dos_tab[0x107])

#define SPRINTF (*kernel->sprintf)
#define DEBUG (*kernel->debug)
#define ALERT (*kernel->alert)
#define TRACE (*kernel->trace)
#define FATAL (*kernel->fatal)
#define KMALLOC (*kernel->kmalloc)
#define KFREE (*kernel->kfree)
#define SLEEP (*kernel->sleep)
#define WAKESELECT (*kernel->wakeselect)

SHAR_EOF
cat << \SHAR_EOF > vtdev.h
/* should be equivalent... atleast for mintlib. */
#include <string.h>
#define quickmove memmove
#define zero bzero
/* only possible because main open()s /dev/vt00 that calls this. */
#define kcore(x) MXALLOC((long)(x),0)
/* should this do something? */
#define checkkeys() (0)
#define kmalloc KMALLOC
#define kfree KFREE
#define bconout Bconout
#define yield() SLEEP(1, 0l)

/* number of terminals (max) */
#define N_VT  10

#define MAX_PLANES 8

#define ALT_1 0x780000L
#define ALT_2 0x790000L
#define ALT_0 0x810000L

struct screen;

typedef void (*Vfunc) P_((struct screen *, int));

typedef struct screen {
	short	hidecnt;	/* cursor hide count */
	short	mcurx, mcury;	/* current mouse X, Y position */
	char	mdraw;
	char	mouseflag;
	long	junk1;
	short	savex, savey;	/* saved X, Y position */
	short	msavelen;	/* mouse save stuff */
	long	msaveaddr;
	short	msavestat;
	union {
		long	msavearea[64];
/* additional stuff, NOT for vt00! */
		struct {
			Vfunc	state;
			short	vescy1;
			int	fgmask[MAX_PLANES];
			int	bgmask[MAX_PLANES];
			char	*vbase;		/* screens address */
			char	*rowlist;	/* pointer to line offsets */
			short	on;		/* 0 if screen `stored'... */
#define V_USED	1	/* not stored, open */
#define V_FREE	2	/* not stored, closed, free screen memory later */
		} t;
	} v;
	long	user_tim, next_tim; /* time vector stuff */
	long	user_but, user_cur,
		user_mot;	/* more user vectors */
	short	cheight;	/* character height */
	short	maxx;		/* number of characters across - 1 */
	short	maxy;		/* number of characters high - 1 */
	short	linelen;	/* length (in bytes) of a line of characters */	
	short	bgcol;		/* background color */
	short	fgcol;		/* foreground color */
	char	*cursaddr;	/* cursor address */
	short	v_cur_of;	/* ??? */
	short	cx, cy;		/* current (x,y) position of cursor */
	char	period;		/* cursor flash period (in frames) */	
	char	curstimer;	/* cursor flash timer */
	char	*fontdata;	/* pointer to font data */
	short	firstcode;	/* first ASCII code in font */
	short	lastcode;	/* last ASCII code in font */
	short	form_width;	/* # bytes/scanline in font data */
	short	xpixel;
	char	*fontoff;	/* pointer to font offset table */
	char	flags;		/* e.g. cursor on/off */
	char	reserved;
	short	ypixel;
	short	width;		/* length of a screen scan line */
	short	planes;		/* number of planes on screen */
	short	planesiz;	/* length of a screen scan line */
} SCREEN;

#define SCNSIZE(v) ( (((long)v->maxy + hardscroll + 2)) * v->linelen )

/* possible flags for cursor state, etc. */
#define CURS_FLASH	0x01		/* cursor flashing */
#define CURS_FSTATE	0x02		/* cursor in flash state */
#define CURS_ON		0x04		/* cursor on */
#define FWRAP		0x08		/* wrap cursor at end of line */
#define FINVERSE	0x10		/* invert text */
#define FUNDERLINE	0x20		/* EXTENSION: underline text */
#define CURS_UPD	0x40		/* cursor update flag */

extern DEVDRV vcon_device;
extern struct tty ttys[];
extern struct dev_descr devinfo[];
extern int vcurrent;
extern short hardscroll, leaving;
extern SCREEN *v00, v0x[], *current;
int setcurrent();
SHAR_EOF
cat << \SHAR_EOF > Makefile
# Makefile for vcon; nothing special...

CC = gcc
CFLAGS = -g -mshort -O2 -Wall
LDFLAGS = -g -mshort

vcon: vcon.o vtdev.o
	$(CC) -G $(LDFLAGS) vcon.o vtdev.o -ovcon
	@echo done.

vcon.sym: vcon.o vtdev.o
	$(CC) -B/usr/lib/sym- $(LDFLAGS) vcon.o vtdev.o -ovcon.sym
	@echo done.

SHAR_EOF
#	End of shell archive
exit 0
-- 
J"urgen Lock / nox@jelal.north.de / UUCP: ..!uunet!unido!uniol!jelal!nox
								...ohne Gewehr
PGP public key fingerprint =  8A 18 58 54 03 7B FC 12  1F 8B 63 C7 19 27 CF DA