video_console.c   [plain text]


/*
 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * @OSF_FREE_COPYRIGHT@
 * 
 */
/*
 * @APPLE_FREE_COPYRIGHT@
 */
/* MACH PPC - video_console.c 
 *
 * Original based on NetBSD's mac68k/dev/ite.c driver
 *
 * This driver differs in
 *	- MACH driver"ized"
 *	- Uses phys_copy and flush_cache to in several places
 *	  for performance optimizations
 *	- 7x15 font
 *	- Black background and white (character) foreground
 *	- Assumes 6100/7100/8100 class of machine 
 *
 * The original header follows...
 *
 *
 *	NetBSD: ite.c,v 1.16 1995/07/17 01:24:34 briggs Exp	
 *
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1990, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * from: Utah $Hdr: ite.c 1.28 92/12/20$
 *
 *	@(#)ite.c	8.2 (Berkeley) 1/12/94
 */

/*
 * ite.c
 *
 * The ite module handles the system console; that is, stuff printed
 * by the kernel and by user programs while "desktop" and X aren't
 * running.  Some (very small) parts are based on hp300's 4.4 ite.c,
 * hence the above copyright.
 *
 *   -- Brad and Lawrence, June 26th, 1994
 *
 */

#include <vc.h>

#include <mach_kdb.h>
#include <kern/spl.h>
#include <machine/machparam.h>          /* spl definitions */
#include <types.h>
#include <ppc/iso_font.h>
#include <ppc/Firmware.h>

#include <ppc/POWERMAC/video_console_entries.h>
#include <ppc/POWERMAC/video_console.h>
#include <pexpert/pexpert.h>
#include <kern/time_out.h>
#include <kern/lock.h>
#include <kern/debug.h>

#define FAST_JUMP_SCROLL

#define	CHARWIDTH	8
#define	CHARHEIGHT	16

#define ATTR_NONE	0
#define ATTR_BOLD	1
#define ATTR_UNDER	2
#define ATTR_REVERSE	4

enum vt100state_e {
	ESnormal,		/* Nothing yet                             */
	ESesc,			/* Got ESC                                 */
	ESsquare,		/* Got ESC [				   */
	ESgetpars,		/* About to get or getting the parameters  */
	ESgotpars,		/* Finished getting the parameters         */
	ESfunckey,		/* Function key                            */
	EShash,			/* DEC-specific stuff (screen align, etc.) */
	ESsetG0,		/* Specify the G0 character set            */
	ESsetG1,		/* Specify the G1 character set            */
	ESask,
	EScharsize,
	ESignore		/* Ignore this sequence                    */
} vt100state = ESnormal;

struct vc_info			vinfo;

/* Calculated in vccninit(): */
static int vc_wrap_mode = 1, vc_relative_origin = 0;
static int vc_charset_select = 0, vc_save_charset_s = 0;
static int vc_charset[2] = { 0, 0 };
static int vc_charset_save[2] = { 0, 0 };

/* VT100 state: */
#define MAXPARS	16
static int x = 0, y = 0, savex, savey;
static int par[MAXPARS], numpars, hanging_cursor, attr, saveattr;

/* VT100 tab stops & scroll region */
static char tab_stops[255];
static int  scrreg_top, scrreg_bottom;

/* Misc */
void	vc_initialize(void);
void 	vc_flush_forward_buffer(void);
void	vc_store_char(unsigned char);

void	vcattach(void);


/*
 * For the color support (Michel Pollet)
 */
unsigned char vc_color_index_table[33] = 
	{  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	   1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 };

unsigned long vc_color_depth_masks[4] = 
	{ 0x000000FF, 0x00007FFF, 0x00FFFFFF };

unsigned long vc_colors[8][3] = {
	{ 0xFFFFFFFF, 0x00000000, 0x00000000 },	/* black */
	{ 0x23232323, 0x7C007C00, 0x00FF0000 },	/* red	*/
	{ 0xb9b9b9b9, 0x03e003e0, 0x0000FF00 },	/* green */
	{ 0x05050505, 0x7FE07FE0, 0x00FFFF00 },	/* yellow */
	{ 0xd2d2d2d2, 0x001f001f, 0x000000FF},	/* blue	 */
//	{ 0x80808080, 0x31933193, 0x00666699 },	/* blue	 */
	{ 0x18181818, 0x7C1F7C1F, 0x00FF00FF },	/* magenta */
	{ 0xb4b4b4b4, 0x03FF03FF, 0x0000FFFF },	/* cyan	*/
	{ 0x00000000, 0x7FFF7FFF, 0x00FFFFFF }	/* white */
};

unsigned long vc_color_mask = 0;
unsigned long vc_color_fore = 0;
unsigned long vc_color_back = 0;
int vc_normal_background = 1;


/*
 * For the jump scroll and buffering (Michel Pollet)
 * 80*22 means on a 80*24 screen, the screen will
 * scroll jump almost a full screen
 * keeping only what's necessary for you to be able to read ;-)
 */
#define VC_MAX_FORWARD_SIZE	(100*36)

/*
 * Delay between console updates in clock hz units, the larger the
 * delay the fuller the jump-scroll buffer will be and so the faster the
 * (scrolling) output. The smaller the delay, the less jerky the
 * display. Heuristics show that at 10 touch-typists (Mike!) complain
 */
#define VC_CONSOLE_UPDATE_TIMEOUT	5

static unsigned char vc_forward_buffer[VC_MAX_FORWARD_SIZE];
static long vc_forward_buffer_size = 0;
static int vc_forward_buffer_enabled = 0;
static int vc_forward_buffer_busy = 0;
decl_simple_lock_data(,vc_forward_lock)

#ifdef FAST_JUMP_SCROLL
static void (*vc_forward_paintchar) (unsigned char c, int x, int y, int attrs);
static enum {
	PFoff,
	PFwind,
	PFscroll,
	PFunwind
} vc_forward_preflight_mode = PFoff;
static struct {
	enum vt100state_e vt100state;

	int vc_wrap_mode, vc_relative_origin;
	int vc_charset_select, vc_save_charset_s;
	int vc_charset[2];
	int vc_charset_save[2];

	int x, y, savex, savey;
	int par[MAXPARS], numpars, hanging_cursor, attr, saveattr;

	char tab_stops[255];
	int  scrreg_top, scrreg_bottom;

        unsigned long vc_color_fore;
        unsigned long vc_color_back;
} vc_forward_preflight_save;
static int vc_forward_scroll = 0;
#endif FAST_JUMP_SCROLL

/* 
 * New Rendering code from Michel Pollet
 */

/* That function will be called for drawing */
static void (*vc_paintchar) (unsigned char c, int x, int y, int attrs);

#ifdef RENDERALLOCATE
unsigned char *renderedFont = NULL;	/* rendered font buffer */
#else
#define REN_MAX_DEPTH	32
/* that's the size for a 32 bits buffer... */
#define REN_MAX_SIZE 	(128L*1024)
unsigned char renderedFont[REN_MAX_SIZE];
#endif

/* Rendered Font Size */
unsigned long vc_rendered_font_size = REN_MAX_SIZE;
long vc_rendered_error = 0;

/* If the one bit table was reversed */
short vc_one_bit_reversed = 0;

/* Size of a character in the table (bytes) */
int	vc_rendered_char_size = 0;

/*
# Attribute codes: 
# 00=none 01=bold 04=underscore 05=blink 07=reverse 08=concealed
# Text color codes:
# 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white
# Background color codes:
# 40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white
*/

#define VC_RESET_BACKGROUND 40
#define VC_RESET_FOREGROUND 37

static void vc_color_set(int color)
{
	if (vinfo.v_depth < 8)
		return;
	if (color >= 30 && color <= 37)
		vc_color_fore = vc_colors[color-30][vc_color_index_table[vinfo.v_depth]];
	if (color >= 40 && color <= 47) {
		vc_color_back = vc_colors[color-40][vc_color_index_table[vinfo.v_depth]];
		vc_normal_background = color == 40;
	}

}

static void vc_render_font(short olddepth, short newdepth)
{
	int charIndex;	/* index in ISO font */
	union {
		unsigned char  *charptr;
		unsigned short *shortptr;
		unsigned long  *longptr;
	} current; 	/* current place in rendered font, multiple types. */

	unsigned char *theChar;	/* current char in iso_font */

	if (olddepth == newdepth && renderedFont) {
		return;	/* nothing to do */
	}
	
	if (olddepth != 1 && renderedFont) {
#ifdef RENDERALLOCATE
		(void) kmem_free(kernel_map, (vm_offset_t*)renderedFont, vc_rendered_font_size);
#endif
	}
	vc_rendered_font_size = REN_MAX_SIZE;
	if (newdepth == 1) {
#ifdef RENDERALLOCATE
		renderedFont = iso_font;
#endif
		vc_rendered_char_size = 16;
		if (!vc_one_bit_reversed) {	/* reverse the font for the blitter */
			int i;
			for (i = 0; i < ((ISO_CHAR_MAX-ISO_CHAR_MIN+1) * vc_rendered_char_size); i++) {
				if (iso_font[i]) {
					unsigned char mask1 = 0x80;
					unsigned char mask2 = 0x01;
					unsigned char val = 0;
					while (mask1) { 
						if (iso_font[i] & mask1)
							val |= mask2;
						mask1 >>= 1;
						mask2 <<= 1;
					}
					renderedFont[i] = ~val;
				} else renderedFont[i] = 0xff;
			}
			vc_one_bit_reversed = 1;
		}
		return;
	}
	{
		long csize = newdepth / 8;	/* bytes per pixel */
		vc_rendered_char_size = csize ? CHARHEIGHT * (csize * CHARWIDTH) : 
				/* for 2 & 4 */	CHARHEIGHT * (CHARWIDTH/(6-newdepth));
		csize = (ISO_CHAR_MAX-ISO_CHAR_MIN+1) * vc_rendered_char_size;
#ifndef RENDERALLOCATE
		if (csize > vc_rendered_font_size) {
			vc_rendered_error = csize;
			return;
		} else 
			vc_rendered_font_size = csize;
#else
		vc_rendered_font_size = csize;
#endif
	}

#ifdef RENDERALLOCATE
	if (kmem_alloc(kernel_map,
		       (vm_offset_t *)&renderedFont,
		       vc_rendered_font_size) != KERN_SUCCESS) {
		renderedFont = NULL;
		vc_rendered_error = vc_rendered_font_size;
		return;
	}
#endif
	current.charptr = renderedFont;
	theChar = iso_font;
	for (charIndex = ISO_CHAR_MIN; charIndex <= ISO_CHAR_MAX; charIndex++) {
		int line;
		for (line = 0; line < CHARHEIGHT; line++) {
			unsigned char mask = 1;
			do {
				switch (newdepth) {
				case 2: {
					unsigned char value = 0;
					if (*theChar & mask) value |= 0xC0; mask <<= 1;
					if (*theChar & mask) value |= 0x30; mask <<= 1;
					if (*theChar & mask) value |= 0x0C; mask <<= 1;
					if (*theChar & mask) value |= 0x03;
					value = ~value;
					*current.charptr++ = value;
				}
					break;
				case 4:
				{
					unsigned char value = 0;
					if (*theChar & mask) value |= 0xF0; mask <<= 1;
					if (*theChar & mask) value |= 0x0F;
					value = ~value;
					*current.charptr++ = value;
				}
				break;
				case 8: 
					*current.charptr++ = (*theChar & mask) ? 0xff : 0;
					break;
				case 16:
					*current.shortptr++ = (*theChar & mask) ? 0xFFFF : 0;
					break;

				case 32: 
					*current.longptr++ = (*theChar & mask) ? 0xFFFFFFFF : 0;
					break;
				}
				mask <<= 1;
			} while (mask);	/* while the single bit drops to the right */
			theChar++;
		}
	}
}

#ifdef FAST_JUMP_SCROLL
static void vc_paint_char(unsigned char ch, int xx, int yy, int attrs)
{
	switch (vc_forward_preflight_mode) {
		case PFoff:
			vc_forward_paintchar(ch, xx, yy, attrs);
			break;
		case PFwind:
			break;
		case PFscroll:
			break;
		case PFunwind:
			if (yy >= scrreg_top && yy < scrreg_bottom) {
				yy -= vc_forward_scroll;
				if (yy < scrreg_top || yy >= scrreg_bottom)
					break;
			}
			vc_forward_paintchar(ch, xx, yy, attrs);
			break;
	}
}
#endif FAST_JUMP_SCROLL

static void vc_paint_char1(unsigned char ch, int xx, int yy, int attrs) 
{
	unsigned char *theChar;
	unsigned char *where;
	int i;
	
	theChar = (unsigned char*)(renderedFont + (ch * vc_rendered_char_size));
	where = (unsigned char*)(vinfo.v_baseaddr + 
				 (yy * CHARHEIGHT * vinfo.v_rowbytes) + 
				 (xx));

	if (!attrs) for (i = 0; i < CHARHEIGHT; i++) {	/* No attributes ? FLY !!!! */
		*where = *theChar++;
		
		where = (unsigned char*)(((unsigned char*)where)+vinfo.v_rowbytes);
	} else for (i = 0; i < CHARHEIGHT; i++) {	/* a little bit slower */
		unsigned char val = *theChar++, save = val;
		if (attrs & ATTR_BOLD) {		/* bold support */
			unsigned char mask1 = 0xC0, mask2 = 0x40;
			int bit = 0;
			for (bit = 0; bit < 7; bit++) {
				if ((save & mask1) == mask2)
					val &= ~mask2;
				mask1 >>= 1;
				mask2 >>= 1;
			}
		}
		if (attrs & ATTR_REVERSE) val = ~val;
		if (attrs & ATTR_UNDER &&  i == CHARHEIGHT-1) val = ~val;
		*where = val;
		
		where = (unsigned char*)(((unsigned char*)where)+vinfo.v_rowbytes);		
	}

}

static void vc_paint_char2(unsigned char ch, int xx, int yy, int attrs) 
{
	unsigned short *theChar;
	unsigned short *where;
	int i;

	theChar = (unsigned short*)(renderedFont + (ch * vc_rendered_char_size));
	where = (unsigned short*)(vinfo.v_baseaddr + 
				  (yy * CHARHEIGHT * vinfo.v_rowbytes) + 
				  (xx * 2));
	if (!attrs) for (i = 0; i < CHARHEIGHT; i++) {	/* No attributes ? FLY !!!! */
		*where = *theChar++;
		
		where = (unsigned short*)(((unsigned char*)where)+vinfo.v_rowbytes);
	} else for (i = 0; i < CHARHEIGHT; i++) {	/* a little bit slower */
		unsigned short val = *theChar++, save = val;
		if (attrs & ATTR_BOLD) {		/* bold support */
			unsigned short mask1 = 0xF000, mask2 = 0x3000;
			int bit = 0;
			for (bit = 0; bit < 7; bit++) {
				if ((save & mask1) == mask2)
					val &= ~mask2;
				mask1 >>= 2;
				mask2 >>= 2;
			}
		}
		if (attrs & ATTR_REVERSE) val = ~val;
		if (attrs & ATTR_UNDER &&  i == CHARHEIGHT-1) val = ~val;
		*where = val;
		
		where = (unsigned short*)(((unsigned char*)where)+vinfo.v_rowbytes);
	}

}

static void vc_paint_char4(unsigned char ch, int xx, int yy, int attrs) 
{
	unsigned long *theChar;
	unsigned long *where;
	int i;

	theChar = (unsigned long*)(renderedFont + (ch * vc_rendered_char_size));
	where = (unsigned long*)(vinfo.v_baseaddr + 
				 (yy * CHARHEIGHT * vinfo.v_rowbytes) + 
				 (xx * 4));

	if (!attrs) for (i = 0; i < CHARHEIGHT; i++) {	/* No attributes ? FLY !!!! */
		*where = *theChar++;
		
		where = (unsigned long*)(((unsigned char*)where)+vinfo.v_rowbytes);
	} else for (i = 0; i < CHARHEIGHT; i++) {	/* a little bit slower */
		unsigned long val = *theChar++, save = val;
		if (attrs & ATTR_BOLD) {		/* bold support */
			unsigned long mask1 = 0xff000000, mask2 = 0x0F000000;
			int bit = 0;
			for (bit = 0; bit < 7; bit++) {
				if ((save & mask1) == mask2)
					val &= ~mask2;
				mask1 >>= 4;
				mask2 >>= 4;
			}
		}
		if (attrs & ATTR_REVERSE) val = ~val;
		if (attrs & ATTR_UNDER &&  i == CHARHEIGHT-1) val = ~val;
		*where = val;
		
		where = (unsigned long*)(((unsigned char*)where)+vinfo.v_rowbytes);
	}

}

static void vc_paint_char8c(unsigned char ch, int xx, int yy, int attrs) 
{
	unsigned long *theChar;
	unsigned long *where;
	int i;
	
	theChar = (unsigned long*)(renderedFont + (ch * vc_rendered_char_size));
	where = (unsigned long*)(vinfo.v_baseaddr + 
					(yy * CHARHEIGHT * vinfo.v_rowbytes) + 
					(xx * CHARWIDTH));

	if (!attrs) for (i = 0; i < CHARHEIGHT; i++) {	/* No attr? FLY !*/
		unsigned long *store = where;
		int x;
		for (x = 0; x < 2; x++) {
			unsigned long val = *theChar++;
			val = (vc_color_back & ~val) | (vc_color_fore & val);
			*store++ = val;
		}
		
		where = (unsigned long*)(((unsigned char*)where)+vinfo.v_rowbytes);
	} else for (i = 0; i < CHARHEIGHT; i++) {	/* a little slower */
		unsigned long *store = where, lastpixel = 0;
		int x;
		for (x = 0 ; x < 2; x++) {
			unsigned long val = *theChar++, save = val;
			if (attrs & ATTR_BOLD) {	/* bold support */
				if (lastpixel && !(save & 0xFF000000))
					val |= 0xff000000;
				if ((save & 0xFFFF0000) == 0xFF000000)
					val |= 0x00FF0000;
				if ((save & 0x00FFFF00) == 0x00FF0000)
					val |= 0x0000FF00;
				if ((save & 0x0000FFFF) == 0x0000FF00)
					val |= 0x000000FF;
			}
			if (attrs & ATTR_REVERSE) val = ~val;
			if (attrs & ATTR_UNDER &&  i == CHARHEIGHT-1) val = ~val;

			val = (vc_color_back & ~val) | (vc_color_fore & val);
			*store++ = val;
			lastpixel = save & 0xff;
		}
		
		where = (unsigned long*)(((unsigned char*)where)+vinfo.v_rowbytes);		
	}

}
static void vc_paint_char16c(unsigned char ch, int xx, int yy, int attrs) 
{
	unsigned long *theChar;
	unsigned long *where;
	int i;
	
	theChar = (unsigned long*)(renderedFont + (ch * vc_rendered_char_size));
	where = (unsigned long*)(vinfo.v_baseaddr + 
				 (yy * CHARHEIGHT * vinfo.v_rowbytes) + 
				 (xx * CHARWIDTH * 2));

	if (!attrs) for (i = 0; i < CHARHEIGHT; i++) {	/* No attrs ? FLY ! */
		unsigned long *store = where;
		int x;
		for (x = 0; x < 4; x++) {
			unsigned long val = *theChar++;
			val = (vc_color_back & ~val) | (vc_color_fore & val);
			*store++ = val;
		}
		
		where = (unsigned long*)(((unsigned char*)where)+vinfo.v_rowbytes);
	} else for (i = 0; i < CHARHEIGHT; i++) { /* a little bit slower */
		unsigned long *store = where, lastpixel = 0;
		int x;
		for (x = 0 ; x < 4; x++) {
			unsigned long val = *theChar++, save = val;
			if (attrs & ATTR_BOLD) {	/* bold support */
				if (save == 0xFFFF0000) val |= 0xFFFF;
				else if (lastpixel && !(save & 0xFFFF0000))
					val |= 0xFFFF0000;
			}
			if (attrs & ATTR_REVERSE) val = ~val;
			if (attrs & ATTR_UNDER &&  i == CHARHEIGHT-1) val = ~val;

			val = (vc_color_back & ~val) | (vc_color_fore & val);

			*store++ = val;
			lastpixel = save & 0x7fff;
		}
		
		where = (unsigned long*)(((unsigned char*)where)+vinfo.v_rowbytes);		
	}

}
static void vc_paint_char32c(unsigned char ch, int xx, int yy, int attrs) 
{
	unsigned long *theChar;
	unsigned long *where;
	int i;
	
	theChar = (unsigned long*)(renderedFont + (ch * vc_rendered_char_size));
	where = (unsigned long*)(vinfo.v_baseaddr + 
					(yy * CHARHEIGHT * vinfo.v_rowbytes) + 
					(xx * CHARWIDTH * 4));

	if (!attrs) for (i = 0; i < CHARHEIGHT; i++) {	/* No attrs ? FLY ! */
		unsigned long *store = where;
		int x;
		for (x = 0; x < 8; x++) {
			unsigned long val = *theChar++;
			val = (vc_color_back & ~val) | (vc_color_fore & val);
			*store++ = val;
		}
		
		where = (unsigned long*)(((unsigned char*)where)+vinfo.v_rowbytes);
	} else for (i = 0; i < CHARHEIGHT; i++) {	/* a little slower */
		unsigned long *store = where, lastpixel = 0;
		int x;
		for (x = 0 ; x < 8; x++) {
			unsigned long val = *theChar++, save = val;
			if (attrs & ATTR_BOLD) {	/* bold support */
				if (lastpixel && !save)
					val = 0xFFFFFFFF;
			}
			if (attrs & ATTR_REVERSE) val = ~val;
			if (attrs & ATTR_UNDER &&  i == CHARHEIGHT-1) val = ~val;

			val = (vc_color_back & ~val) | (vc_color_fore & val);
			*store++ = val;
			lastpixel = save;
		}
		
		where = (unsigned long*)(((unsigned char*)where)+vinfo.v_rowbytes);		
	}

}

/*
 * That's a plain dumb reverse of the cursor position
 * It do a binary reverse, so it will not looks good when we have
 * color support. we'll see that later
 */
static void reversecursor(void)
{
	union {
		unsigned char  *charptr;
		unsigned short *shortptr;
		unsigned long  *longptr;
	} where;
	int line, col;
	
	where.longptr =  (unsigned long*)(vinfo.v_baseaddr + 
					(y * CHARHEIGHT * vinfo.v_rowbytes) + 
					(x /** CHARWIDTH*/ * vinfo.v_depth));
	for (line = 0; line < CHARHEIGHT; line++) {
		switch (vinfo.v_depth) {
			case 1:
				*where.charptr = ~*where.charptr;
				break;
			case 2:
				*where.shortptr = ~*where.shortptr;
				break;
			case 4:
				*where.longptr = ~*where.longptr;
				break;
/* that code still exists because since characters on the screen are
 * of different colors that reverse function may not work if the
 * cursor is on a character that is in a different color that the
 * current one. When we have buffering, things will work better. MP
 */
#ifdef VC_BINARY_REVERSE
			case 8:
				where.longptr[0] = ~where.longptr[0];
				where.longptr[1] = ~where.longptr[1];
				break;
			case 16:
				for (col = 0; col < 4; col++)
					where.longptr[col] = ~where.longptr[col];
				break;
			case 32:
				for (col = 0; col < 8; col++)
					where.longptr[col] = ~where.longptr[col];
				break;
#else
			case 8:
				for (col = 0; col < 8; col++)
					where.charptr[col] = where.charptr[col] != (vc_color_fore & vc_color_mask) ?
										vc_color_fore & vc_color_mask : vc_color_back & vc_color_mask;
				break;
			case 16:
				for (col = 0; col < 8; col++)
					where.shortptr[col] = where.shortptr[col] != (vc_color_fore & vc_color_mask) ?
										vc_color_fore & vc_color_mask : vc_color_back & vc_color_mask;
				break;
			case 32:
				for (col = 0; col < 8; col++)
					where.longptr[col] = where.longptr[col] != (vc_color_fore & vc_color_mask) ?
										vc_color_fore & vc_color_mask : vc_color_back & vc_color_mask;
				break;
#endif
		}
		where.charptr += vinfo.v_rowbytes;
	}
}


static void 
scrollup(int num)
{
	unsigned long *from, *to, linelongs, i, line, rowline, rowscanline;

	linelongs = (vinfo.v_rowbytes * CHARHEIGHT) >> 2;
	rowline = (vinfo.v_rowbytes) >> 2;
	rowscanline = (vinfo.v_rowscanbytes) >> 2;

#ifdef FAST_JUMP_SCROLL
	if (vc_forward_preflight_mode == PFwind) {
		vc_forward_scroll += num;
		return;
	}
	if (vc_forward_preflight_mode == PFscroll || vc_forward_preflight_mode == PFoff) {
#endif FAST_JUMP_SCROLL

	to = (unsigned long *) vinfo.v_baseaddr + (scrreg_top * linelongs);
	from = to + (linelongs * num);	/* handle multiple line scroll (Michel Pollet) */

	i = (scrreg_bottom - scrreg_top) - num;

	while (i-- > 0) {
		for (line = 0; line < CHARHEIGHT; line++) {
			/*
			 * Only copy what is displayed
			 */
			video_scroll_up((unsigned int) from, 
					(unsigned int) (from+(vinfo.v_rowscanbytes/4)), 
					(unsigned int) to);

			from += rowline;
			to += rowline;
		}
	}

	/* Now set the freed up lines to the background colour */


	to = ((unsigned long *) vinfo.v_baseaddr + (scrreg_top * linelongs))
		+ ((scrreg_bottom - scrreg_top - num) * linelongs);

#ifdef FAST_JUMP_SCROLL
		if (vc_forward_preflight_mode == PFscroll)
			return;
	} else if (vc_forward_preflight_mode == PFunwind) {
		long linestart, linelast;
		vc_forward_scroll -= num;

		linestart = scrreg_bottom - num - vc_forward_scroll;
		linelast  = linestart + num - 1;

		if (linestart >= scrreg_bottom || linelast < scrreg_top)
			return;

		if (linelast >= scrreg_bottom)
			linelast = scrreg_bottom - 1;
		if (linestart < scrreg_top)
			linestart = scrreg_top;

		to = ((unsigned long *) vinfo.v_baseaddr) + (linelongs * linestart);
		num = linelast - linestart + 1;
	}
#endif FAST_JUMP_SCROLL

	for (linelongs = CHARHEIGHT * num;  linelongs-- > 0;) {
		from = to;
		for (i = 0; i < rowscanline; i++) 
			*to++ = vc_color_back;

		to = from + rowline;
	}

}

static void 
scrolldown(int num)
{
	unsigned long *from, *to,  linelongs, i, line, rowline, rowscanline;

	linelongs = (vinfo.v_rowbytes * CHARHEIGHT) >> 2;
	rowline = (vinfo.v_rowbytes) >> 2;
	rowscanline = (vinfo.v_rowscanbytes) >> 2;

#ifdef FAST_JUMP_SCROLL
	if (vc_forward_preflight_mode == PFwind) {
		vc_forward_scroll -= num;
		return;
	}
	if (vc_forward_preflight_mode == PFscroll || vc_forward_preflight_mode == PFoff) {
#endif FAST_JUMP_SCROLL

	to = (unsigned long *) vinfo.v_baseaddr + (linelongs * scrreg_bottom)
		- (rowline - rowscanline);
	from = to - (linelongs * num);	/* handle multiple line scroll (Michel Pollet) */

	i = (scrreg_bottom - scrreg_top) - num;

	while (i-- > 0) {
		for (line = 0; line < CHARHEIGHT; line++) {
			/*
			 * Only copy what is displayed
			 */
			video_scroll_down((unsigned int) from, 
					(unsigned int) (from-(vinfo.v_rowscanbytes/4)), 
					(unsigned int) to);

			from -= rowline;
			to -= rowline;
		}
	}

	/* Now set the freed up lines to the background colour */

	to = (unsigned long *) vinfo.v_baseaddr + (linelongs * scrreg_top);

#ifdef FAST_JUMP_SCROLL
		if (vc_forward_preflight_mode == PFscroll)
			return;
	} else if (vc_forward_preflight_mode == PFunwind) {
		long linestart, linelast;
		vc_forward_scroll += num;

		linestart = scrreg_top - vc_forward_scroll;
		linelast  = linestart + num - 1;

		if (linestart >= scrreg_bottom || linelast < scrreg_top)
			return;

		if (linelast >= scrreg_bottom)
			linelast = scrreg_bottom - 1;
		if (linestart < scrreg_top)
			linestart = scrreg_top;

		to = ((unsigned long *) vinfo.v_baseaddr) + (linelongs * linestart);
		num = linelast - linestart + 1;
	}
#endif FAST_JUMP_SCROLL

	for (line = CHARHEIGHT * num; line > 0; line--) {
		from = to;

		for (i = 0; i < rowscanline; i++) 
			*(to++) = vc_color_back;

		to = from + rowline;
	}

}


static void 
clear_line(int which)
{
	int     start, end, i;

	/*
	 * This routine runs extremely slowly.  I don't think it's
	 * used all that often, except for To end of line.  I'll go
	 * back and speed this up when I speed up the whole vc
	 * module. --LK
	 */

	switch (which) {
	case 0:		/* To end of line	 */
		start = x;
		end = vinfo.v_columns-1;
		break;
	case 1:		/* To start of line	 */
		start = 0;
		end = x;
		break;
	case 2:		/* Whole line		 */
		start = 0;
		end = vinfo.v_columns-1;
		break;
	}

	for (i = start; i <= end; i++) {
		vc_paintchar(' ', i, y, ATTR_NONE);
	}

}

static void 
clear_screen(int which)
{
	unsigned long *p, *endp, *row;
	int      linelongs, col;
	int      rowline, rowlongs;

	rowline = vinfo.v_rowscanbytes / 4;
	rowlongs = vinfo.v_rowbytes / 4;

	p = (unsigned long*) vinfo.v_baseaddr;;
	endp = (unsigned long*) vinfo.v_baseaddr;

	linelongs = vinfo.v_rowbytes * CHARHEIGHT / 4;

	switch (which) {
	case 0:		/* To end of screen	 */
		clear_line(0);
		if (y < vinfo.v_rows - 1) {
			p += (y + 1) * linelongs;
			endp += rowlongs * vinfo.v_height;
		}
		break;
	case 1:		/* To start of screen	 */
		clear_line(1);
		if (y > 1) {
			endp += (y + 1) * linelongs;
		}
		break;
	case 2:		/* Whole screen		 */
		endp += rowlongs * vinfo.v_height;
		break;
	}

	for (row = p ; row < endp ; row += rowlongs) {
		for (col = 0; col < rowline; col++) 
			*(row+col) = vc_color_back;
	}

}

static void
reset_tabs(void)
{
	int i;

	for (i = 0; i<= vinfo.v_columns; i++) {
		tab_stops[i] = ((i % 8) == 0);
	}

}

static void
vt100_reset(void)
{
	reset_tabs();
	scrreg_top    = 0;
	scrreg_bottom = vinfo.v_rows;
	attr = ATTR_NONE;
	vc_charset[0] = vc_charset[1] = 0;
	vc_charset_select = 0;
	vc_wrap_mode = 1;
	vc_relative_origin = 0;
	vc_color_set(VC_RESET_BACKGROUND);
	vc_color_set(VC_RESET_FOREGROUND);	

}

static void 
putc_normal(unsigned char ch)
{
	switch (ch) {
	case '\a':		/* Beep			 */
        {
            spl_t s;

            if(FALSE) {
                /*
                 * No sound hardware, invert the screen twice instead
                 */
                unsigned long *ptr;
                int i, j;
                /* XOR the screen twice */
                for (i = 0; i < 2 ; i++) {
                    /* For each row, xor the scanbytes */
                    for (ptr = (unsigned long*)vinfo.v_baseaddr;
                        ptr < (unsigned long*)(vinfo.v_baseaddr +
                                (vinfo.v_height * vinfo.v_rowbytes));
                        ptr += (vinfo.v_rowbytes /
                                sizeof (unsigned long*)))
                            for (j = 0;
                                j < vinfo.v_rowscanbytes /
                                        sizeof (unsigned long*);
                                j++)
                                    *(ptr+j) =~*(ptr+j);
                }
            }
        }
        break;

	case 127:		/* Delete		 */
	case '\b':		/* Backspace		 */
		if (hanging_cursor) {
			hanging_cursor = 0;
		} else
			if (x > 0) {
				x--;
			}
		break;
	case '\t':		/* Tab			 */
		while (x < vinfo.v_columns && !tab_stops[++x]);
		if (x >= vinfo.v_columns)
			x = vinfo.v_columns-1;
		break;
	case 0x0b:
	case 0x0c:
	case '\n':		/* Line feed		 */
		if (y >= scrreg_bottom -1 ) {
			scrollup(1);
			y = scrreg_bottom - 1;
		} else {
			y++;
		}
		break;
	case '\r':		/* Carriage return	 */
		x = 0;
		hanging_cursor = 0;
		break;
	case 0x0e:  /* Select G1 charset (Control-N) */
		vc_charset_select = 1;
		break;
	case 0x0f:  /* Select G0 charset (Control-O) */
		vc_charset_select = 0;
		break;
	case 0x18 : /* CAN : cancel */
	case 0x1A : /* like cancel */
			/* well, i do nothing here, may be later */
		break;
	case '\033':		/* Escape		 */
		vt100state = ESesc;
		hanging_cursor = 0;
		break;
	default:
		if (ch >= ' ') {
			if (hanging_cursor) {
				x = 0;
				if (y >= scrreg_bottom -1 ) {
					scrollup(1);
					y = scrreg_bottom - 1;
				} else {
					y++;
				}
				hanging_cursor = 0;
			}
			vc_paintchar((ch >= 0x60 && ch <= 0x7f) ? ch + vc_charset[vc_charset_select]
								: ch, x, y, attr);
			if (x == vinfo.v_columns - 1) {
				hanging_cursor = vc_wrap_mode;
			} else {
				x++;
			}
		}
		break;
	}

}

static void 
putc_esc(unsigned char ch)
{
	vt100state = ESnormal;

	switch (ch) {
	case '[':
		vt100state = ESsquare;
		break;
	case 'c':		/* Reset terminal 	 */
		vt100_reset();
		clear_screen(2);
		x = y = 0;
		break;
	case 'D':		/* Line feed		 */
	case 'E':
		if (y >= scrreg_bottom -1) {
			scrollup(1);
			y = scrreg_bottom - 1;
		} else {
			y++;
		}
		if (ch == 'E') x = 0;
		break;
	case 'H':		/* Set tab stop		 */
		tab_stops[x] = 1;
		break;
	case 'M':		/* Cursor up		 */
		if (y <= scrreg_top) {
			scrolldown(1);
			y = scrreg_top;
		} else {
			y--;
		}
		break;
	case '>':
		vt100_reset();
		break;
	case '7':		/* Save cursor		 */
		savex = x;
		savey = y;
		saveattr = attr;
		vc_save_charset_s = vc_charset_select;
		vc_charset_save[0] = vc_charset[0];
		vc_charset_save[1] = vc_charset[1];
		break;
	case '8':		/* Restore cursor	 */
		x = savex;
		y = savey;
		attr = saveattr;
		vc_charset_select = vc_save_charset_s;
		vc_charset[0] = vc_charset_save[0];
		vc_charset[1] = vc_charset_save[1];
		break;
	case 'Z':		/* return terminal ID */
		break;
	case '#':		/* change characters height */
		vt100state = EScharsize;
		break;
	case '(':
		vt100state = ESsetG0;
		break;
	case ')':		/* character set sequence */
		vt100state = ESsetG1;
		break;
	case '=':
		break;
	default:
		/* Rest not supported */
		break;
	}

}

static void
putc_askcmd(unsigned char ch)
{
	if (ch >= '0' && ch <= '9') {
		par[numpars] = (10*par[numpars]) + (ch-'0');
		return;
	}
	vt100state = ESnormal;

	switch (par[0]) {
		case 6:
			vc_relative_origin = ch == 'h';
			break;
		case 7:	/* wrap around mode h=1, l=0*/
			vc_wrap_mode = ch == 'h';
			break;
		default:
			break;
	}

}

static void
putc_charsizecmd(unsigned char ch)
{
	vt100state = ESnormal;

	switch (ch) {
		case '3' :
		case '4' :
		case '5' :
		case '6' :
			break;
		case '8' :	/* fill 'E's */
			{
				int xx, yy;
				for (yy = 0; yy < vinfo.v_rows; yy++)
					for (xx = 0; xx < vinfo.v_columns; xx++)
						vc_paintchar('E', xx, yy, ATTR_NONE);
			}
			break;
	}

}

static void
putc_charsetcmd(int charset, unsigned char ch)
{
	vt100state = ESnormal;

	switch (ch) {
		case 'A' :
		case 'B' :
		default:
			vc_charset[charset] = 0;
			break;
		case '0' :	/* Graphic characters */
		case '2' :
			vc_charset[charset] = 0x21;
			break;
	}

}

static void 
putc_gotpars(unsigned char ch)
{
	int     i;

	if (ch < ' ') {
		/* special case for vttest for handling cursor
		   movement in escape sequences */
		putc_normal(ch);
		vt100state = ESgotpars;
		return;
	}
	vt100state = ESnormal;
	switch (ch) {
	case 'A':		/* Up			 */
		y -= par[0] ? par[0] : 1;
		if (y < scrreg_top)
			y = scrreg_top;
		break;
	case 'B':		/* Down			 */
		y += par[0] ? par[0] : 1;
		if (y >= scrreg_bottom)
			y = scrreg_bottom - 1;
		break;
	case 'C':		/* Right		 */
		x += par[0] ? par[0] : 1;
		if (x >= vinfo.v_columns)
			x = vinfo.v_columns-1;
		break;
	case 'D':		/* Left			 */
		x -= par[0] ? par[0] : 1;
		if (x < 0)
			x = 0;
		break;
	case 'H':		/* Set cursor position	 */
	case 'f':
		x = par[1] ? par[1] - 1 : 0;
		y = par[0] ? par[0] - 1 : 0;
		if (vc_relative_origin)
			y += scrreg_top;
		hanging_cursor = 0;
		break;
	case 'X':		/* clear p1 characters */
		if (numpars) {
			int i;
			for (i = x; i < x + par[0]; i++)
				vc_paintchar(' ', i, y, ATTR_NONE);
		}
		break;
	case 'J':		/* Clear part of screen	 */
		clear_screen(par[0]);
		break;
	case 'K':		/* Clear part of line	 */
		clear_line(par[0]);
		break;
	case 'g':		/* tab stops	 	 */
		switch (par[0]) {
			case 1:
			case 2:	/* reset tab stops */
				/* reset_tabs(); */
				break;				
			case 3:	/* Clear every tabs */
				{
					int i;

					for (i = 0; i <= vinfo.v_columns; i++)
						tab_stops[i] = 0;
				}
				break;
			case 0:
				tab_stops[x] = 0;
				break;
		}
		break;
	case 'm':		/* Set attribute	 */
		for (i = 0; i < numpars; i++) {
			switch (par[i]) {
			case 0:
				attr = ATTR_NONE;
				vc_color_set(VC_RESET_BACKGROUND);
				vc_color_set(VC_RESET_FOREGROUND);	
				break;
			case 1:
				attr |= ATTR_BOLD;
				break;
			case 4:
				attr |= ATTR_UNDER;
				break;
			case 7:
				attr |= ATTR_REVERSE;
				break;
			case 22:
				attr &= ~ATTR_BOLD;
				break;
			case 24:
				attr &= ~ATTR_UNDER;
				break;
			case 27:
				attr &= ~ATTR_REVERSE;
				break;
			case 5:
			case 25:	/* blink/no blink */
				break;
			default:
				vc_color_set(par[i]);
				break;
			}
		}
		break;
	case 'r':		/* Set scroll region	 */
		x = y = 0;
		/* ensure top < bottom, and both within limits */
		if ((numpars > 0) && (par[0] < vinfo.v_rows)) {
			scrreg_top = par[0] ? par[0] - 1 : 0;
			if (scrreg_top < 0)
				scrreg_top = 0;
		} else {
			scrreg_top = 0;
		}
		if ((numpars > 1) && (par[1] <= vinfo.v_rows) && (par[1] > par[0])) {
			scrreg_bottom = par[1];
			if (scrreg_bottom > vinfo.v_rows)
				scrreg_bottom = vinfo.v_rows;
		} else {
			scrreg_bottom = vinfo.v_rows;
		}
		if (vc_relative_origin)
			y = scrreg_top;
		break;
	}

}

static void 
putc_getpars(unsigned char ch)
{
	if (ch == '?') {
		vt100state = ESask;
		return;
	}
	if (ch == '[') {
		vt100state = ESnormal;
		/* Not supported */
		return;
	}
	if (ch == ';' && numpars < MAXPARS - 1) {
		numpars++;
	} else
		if (ch >= '0' && ch <= '9') {
			par[numpars] *= 10;
			par[numpars] += ch - '0';
		} else {
			numpars++;
			vt100state = ESgotpars;
			putc_gotpars(ch);
		}
}

static void 
putc_square(unsigned char ch)
{
	int     i;

	for (i = 0; i < MAXPARS; i++) {
		par[i] = 0;
	}

	numpars = 0;
	vt100state = ESgetpars;

	putc_getpars(ch);

}

void 
vc_putchar(char ch)
{
	if (!ch) {
		return;	/* ignore null characters */
	}
	switch (vt100state) {
		default:vt100state = ESnormal;	/* FALLTHROUGH */
	case ESnormal:
		putc_normal(ch);
		break;
	case ESesc:
		putc_esc(ch);
		break;
	case ESsquare:
		putc_square(ch);
		break;
	case ESgetpars:
		putc_getpars(ch);
		break;
	case ESgotpars:
		putc_gotpars(ch);
		break;
	case ESask:
		putc_askcmd(ch);
		break;
	case EScharsize:
		putc_charsizecmd(ch);
		break;
	case ESsetG0:
		putc_charsetcmd(0, ch);
		break;
	case ESsetG1:
		putc_charsetcmd(1, ch);
		break;
	}

	if (x >= vinfo.v_columns) {
		x = vinfo.v_columns - 1;
	}
	if (x < 0) {
		x = 0;
	}
	if (y >= vinfo.v_rows) {
		y = vinfo.v_rows - 1;
	}
	if (y < 0) {
		y = 0;
	}

}

/*
 * Actually draws the buffer, handle the jump scroll
 */
void vc_flush_forward_buffer(void)
{
	int start = 0;
	int todo = 0;
	spl_t s;

	assert(vc_forward_buffer_enabled);

	s = splhigh();
	simple_lock(&vc_forward_lock);

	if (vc_forward_buffer_busy) {
		/* Bail out if we're already in the middle of a flush. */
		simple_unlock(&vc_forward_lock);
		splx(s);
		return;
	}

	vc_forward_buffer_busy = 1;

	while (todo < vc_forward_buffer_size) {
		todo = vc_forward_buffer_size;

		/* Drop the lock while we update the screen. */
		simple_unlock(&vc_forward_lock);
		splx(s);

		reversecursor();

		do {
			int i;
#ifdef FAST_JUMP_SCROLL
			if ((todo - start) < 2) {
				vc_putchar(vc_forward_buffer[start++]);
			} else {
				assert(vc_forward_scroll == 0);

				vc_forward_preflight_save.vt100state         = vt100state;
				vc_forward_preflight_save.vc_wrap_mode       = vc_wrap_mode;
				vc_forward_preflight_save.vc_relative_origin = vc_relative_origin;
				vc_forward_preflight_save.vc_charset_select  = vc_charset_select;
				vc_forward_preflight_save.vc_save_charset_s  = vc_save_charset_s;
				vc_forward_preflight_save.vc_charset[0]      = vc_charset[0];
				vc_forward_preflight_save.vc_charset[1]      = vc_charset[1];
				vc_forward_preflight_save.vc_charset_save[0] = vc_charset_save[0];
				vc_forward_preflight_save.vc_charset_save[1] = vc_charset_save[1];
				vc_forward_preflight_save.x                  = x;
				vc_forward_preflight_save.y                  = y;
				vc_forward_preflight_save.savex              = savex;
				vc_forward_preflight_save.savey              = savey;
				vc_forward_preflight_save.numpars            = numpars;
				vc_forward_preflight_save.hanging_cursor     = hanging_cursor;
				vc_forward_preflight_save.attr               = attr;
				vc_forward_preflight_save.saveattr           = saveattr;
				vc_forward_preflight_save.scrreg_top         = scrreg_top;
				vc_forward_preflight_save.scrreg_bottom      = scrreg_bottom;
				vc_forward_preflight_save.vc_color_fore      = vc_color_fore;
				vc_forward_preflight_save.vc_color_back      = vc_color_back;
				bcopy( (const char *) par,
				       (char *)       vc_forward_preflight_save.par,
				       (vm_size_t)    sizeof(par) );
				bcopy( (const char *) tab_stops,
				       (char *)       vc_forward_preflight_save.tab_stops,
				       (vm_size_t)    sizeof(tab_stops) );

				vc_forward_preflight_mode = PFwind;

				for (i = start;
				     i < todo &&
				     vc_forward_preflight_save.scrreg_top == scrreg_top &&
				     vc_forward_preflight_save.scrreg_bottom == scrreg_bottom;
				     i++)
					vc_putchar(vc_forward_buffer[i]);

				vt100state         = vc_forward_preflight_save.vt100state;
				vc_wrap_mode       = vc_forward_preflight_save.vc_wrap_mode;
				vc_relative_origin = vc_forward_preflight_save.vc_relative_origin;
				vc_charset_select  = vc_forward_preflight_save.vc_charset_select;
				vc_save_charset_s  = vc_forward_preflight_save.vc_save_charset_s;
				vc_charset[0]      = vc_forward_preflight_save.vc_charset[0];
				vc_charset[1]      = vc_forward_preflight_save.vc_charset[1];
				vc_charset_save[0] = vc_forward_preflight_save.vc_charset_save[0];
				vc_charset_save[1] = vc_forward_preflight_save.vc_charset_save[1];
				x                  = vc_forward_preflight_save.x;
				y                  = vc_forward_preflight_save.y;
				savex              = vc_forward_preflight_save.savex;
				savey              = vc_forward_preflight_save.savey;
				numpars            = vc_forward_preflight_save.numpars;
				hanging_cursor     = vc_forward_preflight_save.hanging_cursor;
				attr               = vc_forward_preflight_save.attr;
				saveattr           = vc_forward_preflight_save.saveattr;
				scrreg_top         = vc_forward_preflight_save.scrreg_top;
				scrreg_bottom      = vc_forward_preflight_save.scrreg_bottom;
				vc_color_fore      = vc_forward_preflight_save.vc_color_fore;
				vc_color_back      = vc_forward_preflight_save.vc_color_back;
				bcopy( (const char *) vc_forward_preflight_save.par,
				       (char *)       par,
				       (vm_size_t)    sizeof(par) );
				bcopy( (const char *) vc_forward_preflight_save.tab_stops,
				       (char *)       tab_stops,
				       (vm_size_t)    sizeof(tab_stops) );

				vc_forward_preflight_mode = PFscroll;

				if (vc_forward_scroll > 0)
					scrollup(vc_forward_scroll > scrreg_bottom - scrreg_top ?
					         scrreg_bottom - scrreg_top : vc_forward_scroll);
				else if (vc_forward_scroll < 0)
					scrolldown(-vc_forward_scroll > scrreg_bottom - scrreg_top ?
					           scrreg_bottom - scrreg_top : -vc_forward_scroll);

				vc_forward_preflight_mode = PFunwind;

				for (; start < i; start++)
					vc_putchar(vc_forward_buffer[start]);

				assert(vc_forward_scroll == 0);

				vc_forward_preflight_mode = PFoff;
			}
#else !FAST_JUMP_SCROLL
			int plaintext = 1;
			int drawlen = start;
			int jump = 0;
			int param = 0, changebackground = 0;
			enum vt100state_e vtState = vt100state;
			/* 
			 * In simple words, here we're pre-parsing the text to look for
			 *  + Newlines, for computing jump scroll
			 *  + /\033\[[0-9;]*]m/ to continue on
			 * any other sequence will stop. We don't want to have cursor
			 * movement escape sequences while we're trying to pre-scroll
			 * the screen.
			 * We have to be extra carefull about the sequences that changes
			 * the background color to prevent scrolling in those 
			 * particular cases.
			 * That parsing was added to speed up 'man' and 'color-ls' a 
			 * zillion time (at least). It's worth it, trust me. 
			 * (mail Nick Stephen for a True Performance Graph)
			 * Michel Pollet
			 */
			for (i = start; i < todo && plaintext; i++) {
				drawlen++;
				switch (vtState) {
					case ESnormal:
						switch (vc_forward_buffer[i]) {
							case '\033':
								vtState = ESesc;
								break;
							case '\n':
								jump++;
								break;
						}
						break;
					case ESesc:
						switch (vc_forward_buffer[i]) {
							case '[':
								vtState = ESgetpars;
								param = 0;
								changebackground = 0;
								break;
							default:
								plaintext = 0;
								break;
						}
						break;
					case ESgetpars:
						if ((vc_forward_buffer[i] >= '0' &&
						    vc_forward_buffer[i] <= '9') ||
						    vc_forward_buffer[i] == ';') {
							if (vc_forward_buffer[i] >= '0' &&
						    	    vc_forward_buffer[i] <= '9')
								param = (param*10)+(vc_forward_buffer[i]-'0');
							else {
								if (param >= 40 && param <= 47)
									changebackground = 1;
								if (!vc_normal_background &&
								    !param)
									changebackground = 1;
								param = 0;
							}
							break; /* continue on */
						}
						vtState = ESgotpars;
						/* fall */
					case ESgotpars:
						switch (vc_forward_buffer[i]) {
							case 'm':
								vtState = ESnormal;
								if (param >= 40 && param <= 47)
									changebackground = 1;
								if (!vc_normal_background &&
								    !param)
									changebackground = 1;
								if (changebackground) {
									plaintext = 0;
									jump = 0;
									/* REALLY don't jump */
								}
								/* Yup ! we've got it */
								break;
							default:
								plaintext = 0;
								break;
						}
						break;
					default:
						plaintext = 0;
						break;
				}
				
			}

			/*
			 * Then we look if it would be appropriate to forward jump
			 * the screen before drawing
			 */
			if (jump && (scrreg_bottom - scrreg_top) > 2) {
				jump -= scrreg_bottom - y - 1;
				if (jump > 0 ) {
					if (jump >= scrreg_bottom - scrreg_top)
						jump = scrreg_bottom - scrreg_top -1;
					y -= jump;
					scrollup(jump);
				}
			}
			/*
			 * and we draw what we've found to the parser
			 */
			for (i = start; i < drawlen; i++)
				vc_putchar(vc_forward_buffer[start++]);
			/*
			 * Continue sending characters to the parser until we're sure we're
			 * back on normal characters.
			 */
			for (i = start; i < todo &&
					vt100state != ESnormal ; i++)
				vc_putchar(vc_forward_buffer[start++]);
#endif !FAST_JUMP_SCROLL
			/* Then loop again if there still things to draw */
		} while (start < todo);

		reversecursor();

		/* Re-acquire the lock while we check our state. */
		s = splhigh();
		simple_lock(&vc_forward_lock);
	}

	vc_forward_buffer_busy = 0;
	vc_forward_buffer_size = 0;

	simple_unlock(&vc_forward_lock);
	splx(s);
}

int
vcputc(int l, int u, int c)
{
	/*
	 * Either we're really buffering stuff or we're not yet because
	 * the probe hasn't been done.
	 */
	if (vc_forward_buffer_enabled)
		vc_store_char(c);
	else
		vc_putchar(c);

	return 0;
}

/*
 * Store characters to be drawn 'later', handle overflows
 */

void
vc_store_char(unsigned char c)
{
	int	flush = 0;
	spl_t	s;

	assert(vc_forward_buffer_enabled);

	s = splhigh();
	simple_lock(&vc_forward_lock);

	/* Spin until the buffer has space for another character. */
	while (vc_forward_buffer_size == VC_MAX_FORWARD_SIZE) {
		simple_unlock(&vc_forward_lock);
		splx(s);
		/* wait */
		s = splhigh();
		simple_lock(&vc_forward_lock);
	}

	assert(vc_forward_buffer_size < VC_MAX_FORWARD_SIZE);

	vc_forward_buffer[vc_forward_buffer_size++] = (unsigned char)c;

	if (vc_forward_buffer_size == 1) {
		/* If we're adding the first character to the buffer,
		 * start the timer, otherwise it is already running.
		 */
		if (debug_mode) {
			flush = 1;
		} else {
			timeout((timeout_fcn_t)vc_flush_forward_buffer,
				(void *)0,
				VC_CONSOLE_UPDATE_TIMEOUT);
		}
	} else if (vc_forward_buffer_size == VC_MAX_FORWARD_SIZE || debug_mode) {
		/*
		 * If there is an overflow or this is an immediate character display
		 * (eg. pre-clock printfs, panics), then we force a draw (take into
		 * account that a flush might already be in progress).
		 */
		if (!vc_forward_buffer_busy) {
			flush = 1;
			untimeout((timeout_fcn_t)vc_flush_forward_buffer, (void *)0);
		}
	}

	simple_unlock(&vc_forward_lock);
	splx(s);

	if (flush) {
		/* 
		 * Immediate character display.. kernel printf uses this. Make sure
		 * get flushed and that panics get fully displayed.
		 */
		vc_flush_forward_buffer();
	}
}

void
vc_initialize(void)
{
#if 0
	GratefulDebInit();				/* (TEST/DEBUG) */
#endif

#if DEBUG && SERIAL_CONSOLE_DEFAULT && !defined(MACH_PE)
	printf("    Video info: %d; video_board=%08X\n", i, vboard);
	printf("    Video name: %s\n", vinfo.v_name);
	printf("    height=%d; width=%d, depth=%d; rowbytes=%d; type=%08X\n",
		vinfo.v_height, vinfo.v_width, vinfo.v_depth, vinfo.v_rowbytes, vinfo.v_type);
	printf("    physical address=%08X\n", vinfo.v_physaddr);
#endif

	vinfo.v_rows = vinfo.v_height / CHARHEIGHT;
	vinfo.v_columns = vinfo.v_width / CHARWIDTH;

	if (vinfo.v_depth >= 8) {
		vinfo.v_rowscanbytes = (vinfo.v_depth / 8) * vinfo.v_width;
	} else {
		vinfo.v_rowscanbytes = vinfo.v_width / (8 / vinfo.v_depth);
	}

#if DEBUG && SERIAL_CONSOLE_DEFAULT  && !defined(MACH_PE)
	printf("    inited=%d\n", vc_initted);
#endif


	vc_render_font(1, vinfo.v_depth);
	vc_color_mask = vc_color_depth_masks[vc_color_index_table[vinfo.v_depth]];
	vt100_reset();
	switch (vinfo.v_depth) {
	default:
	case 1:
		vc_paintchar = vc_paint_char1;
		break;
	case 2:
		vc_paintchar = vc_paint_char2;
		break;
	case 4:
		vc_paintchar = vc_paint_char4;
		break;
	case 8:
		vc_paintchar = vc_paint_char8c;
		break;
	case 16:
		vc_paintchar = vc_paint_char16c;
		break;
	case 32:
		vc_paintchar = vc_paint_char32c;
		break;
	}

#ifdef FAST_JUMP_SCROLL
	vc_forward_paintchar = vc_paintchar;
	vc_paintchar = vc_paint_char;
#endif FAST_JUMP_SCROLL
}

void 
vcattach(void)
{
	if (vinfo.v_depth >= 8)
		printf("\033[31mC\033[32mO\033[33mL\033[34mO\033[35mR\033[0m ");
	printf("video console at 0x%x (%dx%dx%d)\n", vinfo.v_baseaddr,
		vinfo.v_width, vinfo.v_height,  vinfo.v_depth);

	/*
	 * Added for the buffering and jump scrolling 
	 */
	/* Init our lock */
	simple_lock_init(&vc_forward_lock, ETAP_IO_TTY);

	vc_forward_buffer_enabled = 1;

}


struct vc_progress_element {
    unsigned int	version;
    unsigned int	flags;
    unsigned int	time;
    unsigned char	count;
    unsigned char	res[3];
    int			width;
    int			height;
    int			dx;
    int			dy;
    int			transparent;
    unsigned int	res2[3];
    unsigned char	data[0];
};
typedef struct vc_progress_element vc_progress_element;

static vc_progress_element *	vc_progress;
static unsigned char *		vc_progress_data;
static boolean_t		vc_progress_enable;
static unsigned char *		vc_clut;
static unsigned int		vc_progress_tick;
static boolean_t		vc_graphics_mode;
static boolean_t		vc_acquired;
static boolean_t		vc_need_clear;

void vc_blit_rect_8c(	int x, int y,
			int width, int height, 
			int transparent, unsigned char * dataPtr )
{
    volatile unsigned char * dst;
    int line, col;
    unsigned char data;

    dst = (unsigned char *)(vinfo.v_baseaddr +
                                    (y * vinfo.v_rowbytes) +
                                    (x));

    for( line = 0; line < height; line++) {
        for( col = 0; col < width; col++) {
	    data = *dataPtr++;
	    if( data == transparent)
		continue;

            *(dst + col) = data;
	}
        dst = (volatile unsigned char *) (((int)dst) + vinfo.v_rowbytes);
    }

}

void vc_blit_rect_8m(	int x, int y,
			int width, int height,
			int transparent, unsigned char * dataPtr )
{
    volatile unsigned char * dst;
    int line, col;
    unsigned int data;

    dst = (unsigned char *)(vinfo.v_baseaddr +
                                    (y * vinfo.v_rowbytes) +
                                    (x));

    for( line = 0; line < height; line++) {
        for( col = 0; col < width; col++) {
	    data = *dataPtr++;
	    if( data == transparent)
		continue;

            data *= 3;
            *(dst + col) = ((19595 * vc_clut[data + 0] +
                             38470 * vc_clut[data + 1] +
                             7471  * vc_clut[data + 2] ) / 65536);
	}
        dst = (volatile unsigned char *) (((int)dst) + vinfo.v_rowbytes);
    }
}



void vc_blit_rect_16(	int x, int y,
			int width, int height,
			int transparent, unsigned char * dataPtr )
{
    volatile unsigned short * dst;
    int line, col;
    unsigned int data;

    dst = (volatile unsigned short *)(vinfo.v_baseaddr +
                                    (y * vinfo.v_rowbytes) +
                                    (x * 2));

    for( line = 0; line < height; line++) {
        for( col = 0; col < width; col++) {
	    data = *dataPtr++;
	    if( data == transparent)
		continue;

            data *= 3;
            *(dst + col) =	( (0xf8 & (vc_clut[data + 0])) << 7)
                              | ( (0xf8 & (vc_clut[data + 1])) << 2)
                              | ( (0xf8 & (vc_clut[data + 2])) >> 3);
	}
        dst = (volatile unsigned short *) (((int)dst) + vinfo.v_rowbytes);
    }
}

void vc_blit_rect_32(	unsigned int x, unsigned int y,
			unsigned int width, unsigned int height,
			int transparent, unsigned char * dataPtr )
{
    volatile unsigned int * dst;
    int line, col;
    unsigned int data;

    dst = (volatile unsigned int *) (vinfo.v_baseaddr +
                                    (y * vinfo.v_rowbytes) +
                                    (x * 4));

    for( line = 0; line < height; line++) {
        for( col = 0; col < width; col++) {
	    data = *dataPtr++;
	    if( data == transparent)
		continue;

            data *= 3;
            *(dst + col) = 	(vc_clut[data + 0] << 16)
                              | (vc_clut[data + 1] << 8)
                              | (vc_clut[data + 2]);
	}
        dst = (volatile unsigned int *) (((int)dst) + vinfo.v_rowbytes);
    }
}

void vc_blit_rect(	int x, int y,
			int width, int height,
			int transparent, unsigned char * dataPtr )
{
    switch( vinfo.v_depth) {
	case 8:
	    vc_blit_rect_8c( x, y, width, height, transparent, dataPtr);
	    break;
	case 16:
	    vc_blit_rect_16( x, y, width, height, transparent, dataPtr);
	    break;
	case 32:
	    vc_blit_rect_32( x, y, width, height, transparent, dataPtr);
	    break;
    }
}

void vc_progress_task( void * arg )
{
    spl_t		s;
    int			count = (int) arg;
    int			x, y, width, height;
    unsigned char * 	data;

    s = splhigh();
    simple_lock(&vc_forward_lock);

    if( vc_progress_enable) {
        count++;
        if( count >= vc_progress->count)
            count = 0;

	width = vc_progress->width;
	height = vc_progress->height;
	x = vc_progress->dx;
	y = vc_progress->dy;
	data = vc_progress_data;
	data += count * width * height;
	if( 1 & vc_progress->flags) {
	    x += (vinfo.v_width / 2);
	    x += (vinfo.v_height / 2);
	}
	vc_blit_rect( x, y, width, height,
			vc_progress->transparent,data );

        timeout( vc_progress_task, (void *) count,
                 vc_progress_tick );
    }
    simple_unlock(&vc_forward_lock);
    splx(s);
}

void vc_display_icon( vc_progress_element * desc,
			unsigned char * data )
{
    int			x, y, width, height;

    if( vc_acquired && vc_graphics_mode && vc_clut) {

	width = desc->width;
	height = desc->height;
	x = desc->dx;
	y = desc->dy;
	if( 1 & desc->flags) {
	    x += (vinfo.v_width / 2);
	    y += (vinfo.v_height / 2);
	}
	vc_blit_rect( x, y, width, height, desc->transparent, data );
    }
}

boolean_t
vc_progress_set( boolean_t enable )
{
    spl_t		s;

    if( !vc_progress)
	return( FALSE );

    s = splhigh();
    simple_lock(&vc_forward_lock);

    if( vc_progress_enable != enable) {
        vc_progress_enable = enable;
        if( enable)
            timeout(vc_progress_task, (void *) 0,
                    vc_progress_tick );
        else
            untimeout( vc_progress_task, (void *) 0 );
    }

    simple_unlock(&vc_forward_lock);
    splx(s);

    return( TRUE );
}


boolean_t
vc_progress_initialize( vc_progress_element * desc,
			unsigned char * data,
			unsigned char * clut )
{
    if( (!clut) || (!desc) || (!data))
	return( FALSE );
    vc_clut = clut;

    vc_progress = desc;
    vc_progress_data = data;
    vc_progress_tick = vc_progress->time * hz / 1000;

    return( TRUE );
}

// FirmwareC.c needs:
Boot_Video boot_video_info;

extern int disableConsoleOutput;

void vc_clear_screen( void )
{
    reversecursor();
    vt100_reset();
    x = y = 0;
    clear_screen(2);
    reversecursor();
};

void
initialize_screen(Boot_Video * boot_vinfo, unsigned int op)
{
	if( boot_vinfo) {
            bcopy( (const void *) boot_vinfo,
                    (void *) &boot_video_info,
                    sizeof( boot_video_info));

            vinfo.v_name[0] = 0;
            vinfo.v_width = boot_vinfo->v_width;
            vinfo.v_height = boot_vinfo->v_height;
            vinfo.v_depth = boot_vinfo->v_depth;
            vinfo.v_rowbytes = boot_vinfo->v_rowBytes;
            vinfo.v_physaddr = boot_vinfo->v_baseAddr;
            vinfo.v_baseaddr = vinfo.v_physaddr;
            vinfo.v_type = 0;

            vc_initialize();
#if 0
            GratefulDebInit((bootBumbleC *)boot_vinfo);	/* Re-initialize GratefulDeb */
#endif
	}

	switch( op ) {

	    case kPEGraphicsMode:
		vc_graphics_mode = TRUE;
		disableConsoleOutput = TRUE;
		vc_acquired = TRUE;
		break;

	    case kPETextMode:
		vc_graphics_mode = FALSE;
		disableConsoleOutput = FALSE;
		vc_acquired = TRUE;
		vc_clear_screen();
		break;

	    case kPETextScreen:
		vc_progress_set( FALSE );
		disableConsoleOutput = FALSE;
		if( vc_need_clear) {
		    vc_need_clear = FALSE;
                    vc_clear_screen();
		}
		break;

            case kPEEnableScreen:
		if( vc_acquired) {
                    if( vc_graphics_mode)
                        vc_progress_set( TRUE );
                    else
                        vc_clear_screen();
		}
		break;

            case kPEDisableScreen:
		vc_progress_set( FALSE );
		break;

	    case kPEAcquireScreen:
		vc_need_clear = (FALSE == vc_acquired);
		vc_acquired = TRUE;
		vc_progress_set( vc_graphics_mode );
		disableConsoleOutput = vc_graphics_mode;
		if( vc_need_clear && !vc_graphics_mode) {
		    vc_need_clear = FALSE;
                    vc_clear_screen();
		}
		break;

	    case kPEReleaseScreen:
		vc_acquired = FALSE;
		vc_progress_set( FALSE );
		disableConsoleOutput = TRUE;
#if 0
		GratefulDebInit(0);						/* Stop grateful debugger */
#endif
		break;
	}
#if 0
	if( boot_vinfo) GratefulDebInit((bootBumbleC *)boot_vinfo);	/* Re initialize GratefulDeb */
#endif
}