/*-
 * Copyright (c) 1999, 2002 Hellmuth Michaelis
 *
 * Copyright (c) 1992, 1995 Hellmuth Michaelis and Joerg Wunsch.
 *
 * Copyright (C) 1992, 1993 Soeren Schmidt.
 *
 * All rights reserved.
 *
 * For the sake of compatibility, portions of this code regarding the
 * X server interface are taken from Soeren Schmidt's syscons driver.
 *
 * 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
 *	Hellmuth Michaelis, Joerg Wunsch and Soeren Schmidt.
 * 4. The name authors may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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.
 */

/*---------------------------------------------------------------------------*
 *
 *	pcvt_ext.c	VT220 Driver Extended Support Routines
 *	------------------------------------------------------
 *
 * 	Last Edit-Date: [Fri Mar  8 19:57:55 2002]
 *
 * $FreeBSD: src/sys/i386/isa/pcvt/pcvt_ext.c,v 1.35 2005/01/06 22:18:17 imp Exp $
 *
 *---------------------------------------------------------------------------*/

#include <i386/isa/pcvt/pcvt_hdr.h>	/* global include */

#define ISSIGVALID(sig) ((sig) > 0 && (sig) < NSIG)

static int  s3testwritable( void );
static int  et4000_col( int );
static int  wd90c11_col( int );
static int  tri9000_col( int );
static int  v7_1024i_col( int );
static int  s3_928_col( int );
static int  cl_gd542x_col( int );

#ifdef XSERVER
static void fallback_to_auto(struct video_state *vsx);
#endif

/* storage to save video timing values of 80 columns text mode */
static union {
	u_char generic[11];
	u_char et4000[11];
	u_char wd90c11[12];
	u_char tri9000[13];
	u_char v7_1024i[17];
	u_char s3_928[32];
	u_char cirrus[13];
}
savearea;

static int regsaved = 0;	/* registers are saved to savearea */

/*---------------------------------------------------------------------------*
 *
 *	Find out which video board we are running on, taken from:
 *	Richard Ferraro: Programmers Guide to the EGA and VGA Cards
 *	and from David E. Wexelblat's SuperProbe Version 1.0.
 *	When a board is found, for which 132 column switching is
 *	provided, the global variable "can_do_132col" is set to 1,
 *	also the global variable vga_family is set to what we found.
 *
 *	###############################################################
 *	## THIS IS GETTING MORE AND MORE A LARGE SPAGHETTI HACK !!!! ##
 *	###############################################################
 *
 *---------------------------------------------------------------------------*/
u_char
vga_chipset(void)
{
	u_char *ptr;
	u_char byte, oldbyte, old1byte, newbyte;

#if PCVT_132GENERIC
	can_do_132col = 1;	/* assumes everyone can do 132 col */
#else
	can_do_132col = 0;	/* assumes noone can do 132 col */
#endif /* PCVT_132GENERIC */

	vga_family = VGA_F_NONE;

/*---------------------------------------------------------------------------*
 * 	check for Western Digital / Paradise chipsets
 *---------------------------------------------------------------------------*/

	ptr = (u_char *)Crtat;

	if(color)
		ptr += (0xc007d - 0xb8000);
	else
		ptr += (0xc007d - 0xb0000);

	if((*ptr++ == 'V') && (*ptr++ == 'G') &&
	   (*ptr++ == 'A') && (*ptr++ == '='))
	{
		int wd90c10;

		vga_family = VGA_F_WD;

		outb(addr_6845, 0x2b);
		oldbyte = inb(addr_6845+1);
		outb(addr_6845+1, 0xaa);
		newbyte = inb(addr_6845+1);
		outb(addr_6845+1, oldbyte);
		if(newbyte != 0xaa)
			return(VGA_PVGA);	/* PVGA1A chip */

		outb(TS_INDEX, 0x12);
		oldbyte = inb(TS_DATA);
		outb(TS_DATA, oldbyte & 0xbf);
		newbyte = inb(TS_DATA) & 0x40;
		if(newbyte != 0)
			return(VGA_WD90C00);	/* WD90C00 chip */

		outb(TS_DATA, oldbyte | 0x40);
		newbyte = inb(TS_DATA) & 0x40;
		if(newbyte == 0)
			return(VGA_WD90C00);	/* WD90C00 chip */

		outb(TS_DATA, oldbyte);

		wd90c10 = 0;
		outb(TS_INDEX, 0x10);
		oldbyte = inb(TS_DATA);

		outb(TS_DATA, oldbyte & 0xfb);
		newbyte = inb(TS_DATA) & 0x04;
		if(newbyte != 0)
			wd90c10 = 1;

		outb(TS_DATA, oldbyte | 0x04);
		newbyte = inb(TS_DATA) & 0x04;
		if(newbyte == 0)
			wd90c10 = 1;

		outb(TS_DATA, oldbyte);

		if(wd90c10)
			return(VGA_WD90C10);
		else
		{
			can_do_132col = 1;
			return(VGA_WD90C11);
		}
	}

/*---------------------------------------------------------------------------*
 *	check for Trident chipsets
 *---------------------------------------------------------------------------*/

	outb(TS_INDEX, 0x0b);
	oldbyte = inb(TS_DATA);


	outb(TS_INDEX, 0x0b);
	outb(TS_DATA, 0x00);

	byte = inb(TS_DATA);	/* chipset type */


	outb(TS_INDEX, 0x0e);
	old1byte = inb(TS_DATA);

	outb(TS_DATA, 0);
	newbyte = inb(TS_DATA);

	outb(TS_DATA, (old1byte ^ 0x02));

	outb(TS_INDEX, 0x0b);
	outb(TS_DATA, oldbyte);

	if((newbyte & 0x0f) == 0x02)
	{
		/* is a trident chip */

		vga_family = VGA_F_TRI;

		switch(byte)
		{
			case 0x01:
				return(VGA_TR8800BR);

			case 0x02:
				return(VGA_TR8800CS);

			case 0x03:
				can_do_132col = 1;
				return(VGA_TR8900B);

			case 0x04:
			case 0x13:
	/* Haven't tried, but should work */
				can_do_132col = 1;
				return(VGA_TR8900C);

			case 0x23:
				can_do_132col = 1;
				return(VGA_TR9000);

			case 0x33:
				can_do_132col = 1;
				return(VGA_TR8900CL);

			case 0x83:
				return(VGA_TR9200);

			case 0x93:
				return(VGA_TR9100);

			default:
				return(VGA_TRUNKNOWN);
		}
	}

/*---------------------------------------------------------------------------*
 *	check for Tseng Labs ET3000/4000 chipsets
 *---------------------------------------------------------------------------*/

	outb(GN_HERCOMPAT, 0x06);
	if(color)
		outb(GN_DMCNTLC, 0xa0);
	else
		outb(GN_DMCNTLM, 0xa0);

	/* read old value */

	if(color)
		inb(GN_INPSTAT1C);
	else
		inb(GN_INPSTAT1M);
	outb(ATC_INDEX, ATC_MISC);
	oldbyte = inb(ATC_DATAR);

	/* write new value */

	if(color)
		inb(GN_INPSTAT1C);
	else
		inb(GN_INPSTAT1M);
	outb(ATC_INDEX, ATC_MISC);
	newbyte = oldbyte ^ 0x10;
	outb(ATC_DATAW, newbyte);

	/* read back new value */
	if(color)
		inb(GN_INPSTAT1C);
	else
		inb(GN_INPSTAT1M);
	outb(ATC_INDEX, ATC_MISC);
	byte = inb(ATC_DATAR);

	/* write back old value */
	if(color)
		inb(GN_INPSTAT1C);
	else
		inb(GN_INPSTAT1M);
	outb(ATC_INDEX, ATC_MISC);
	outb(ATC_DATAW, oldbyte);

	if(byte == newbyte)	/* ET3000 or ET4000 */
	{
		vga_family = VGA_F_TSENG;

		outb(addr_6845, CRTC_EXTSTART);
		oldbyte = inb(addr_6845+1);
		newbyte = oldbyte ^ 0x0f;
		outb(addr_6845+1, newbyte);
		byte = inb(addr_6845+1);
		outb(addr_6845+1, oldbyte);

		if(byte == newbyte)
		{
			can_do_132col = 1;
			return(VGA_ET4000);
		}
		else
		{
			return(VGA_ET3000);
		}
	}

/*---------------------------------------------------------------------------*
 *	check for Video7 VGA chipsets
 *---------------------------------------------------------------------------*/

	outb(TS_INDEX, TS_EXTCNTL);	/* enable extensions */
	outb(TS_DATA, 0xea);

	outb(addr_6845, CRTC_STARTADRH);
	oldbyte = inb(addr_6845+1);

	outb(addr_6845+1, 0x55);
	newbyte = inb(addr_6845+1);

	outb(addr_6845, CRTC_V7ID);	/* id register */
	byte = inb(addr_6845+1);	/* read id */

	outb(addr_6845, CRTC_STARTADRH);
	outb(addr_6845+1, oldbyte);

	outb(TS_INDEX, TS_EXTCNTL);	/* disable extensions */
	outb(TS_DATA, 0xae);

	if(byte == (0x55 ^ 0xea))
	{					/* is Video 7 */

		vga_family = VGA_F_V7;

		outb(TS_INDEX, TS_EXTCNTL);	/* enable extensions */
		outb(TS_DATA, 0xea);

		outb(TS_INDEX, TS_V7CHIPREV);
		byte = inb(TS_DATA);

		outb(TS_INDEX, TS_EXTCNTL);	/* disable extensions */
		outb(TS_DATA, 0xae);

		if(byte < 0xff && byte >= 0x80)
			return(VGA_V7VEGA);
		if(byte < 0x7f && byte >= 0x70)
			return(VGA_V7FWVR);
		if(byte < 0x5a && byte >= 0x50)
			return(VGA_V7V5);
		if(byte < 0x4a && byte > 0x40)
		{
			can_do_132col = 1;
			return(VGA_V71024I);
		}
		return(VGA_V7UNKNOWN);
	}

/*---------------------------------------------------------------------------*
 *	check for S3 chipsets
 *---------------------------------------------------------------------------*/

	outb(addr_6845, 0x38);		/* reg 1 lock register */
	old1byte = inb(addr_6845+1);	/* get old value */

	outb(addr_6845, 0x38);
	outb(addr_6845+1, 0x00);	/* lock registers */

	if(s3testwritable() == 0)	/* check if locked */
	{
		outb(addr_6845, 0x38);
		outb(addr_6845+1, 0x48);	/* unlock registers */

		if(s3testwritable() == 1 )	/* check if unlocked */
		{
			vga_family = VGA_F_S3;	/* FAMILY S3  */

			outb(addr_6845, 0x30);	/* chip id/rev reg */
			byte = inb(addr_6845+1);

			switch(byte & 0xf0)
			{
				case 0x80:
					switch(byte & 0x0f)
					{
						case 0x01:
							outb(addr_6845, 0x38);
							outb(addr_6845+1, old1byte);
							return VGA_S3_911;

						case 0x02:
							outb(addr_6845, 0x38);
							outb(addr_6845+1, old1byte);
							return VGA_S3_924;

						default:
							outb(addr_6845, 0x38);
							outb(addr_6845+1, old1byte);
							return VGA_S3_UNKNOWN;
					}
					break;

				case 0xA0:
					outb(addr_6845, 0x38);
					outb(addr_6845+1, old1byte);
					return VGA_S3_80x;

				case 0x90:
				case 0xb0:
					outb(addr_6845, 0x38);
					outb(addr_6845+1, old1byte);
					can_do_132col = 1;
					return VGA_S3_928;

				default:
					outb(addr_6845, 0x38);
					outb(addr_6845+1, old1byte);
					return VGA_S3_UNKNOWN;
			}
		}
	}

/*---------------------------------------------------------------------------*
 *	check for Cirrus chipsets
 *---------------------------------------------------------------------------*/

	outb(TS_INDEX, 6);
	oldbyte = inb(TS_DATA);
	outb(TS_INDEX, 6);
	outb(TS_DATA, 0x12);
	outb(TS_INDEX, 6);
	newbyte = inb(TS_DATA);
	outb(addr_6845, 0x27);
	byte = inb(addr_6845 + 1);
	outb(TS_INDEX, 6);
	outb(TS_DATA, oldbyte);
	if (newbyte == 0x12) {
		vga_family = VGA_F_CIR;
		can_do_132col = 1;
		switch ((byte & 0xfc) >> 2) {
		case 0x22:
			switch (byte & 3) {
			case 0:
				return VGA_CL_GD5402;
			case 1:
				return VGA_CL_GD5402r1;
			case 2:
				return VGA_CL_GD5420;
			case 3:
				return VGA_CL_GD5420r1;
			}
			break;
		case 0x23:
			return VGA_CL_GD5422;
		case 0x25:
			return VGA_CL_GD5424;
		case 0x24:
			return VGA_CL_GD5426;
		case 0x26:
			return VGA_CL_GD5428;
		}
	}

	return(VGA_UNKNOWN);
}

/*---------------------------------------------------------------------------
 * test if index 35 lower nibble is writable (taken from SuperProbe 1.0)
 *---------------------------------------------------------------------------*/
static int
s3testwritable(void)
{
	u_char old, new1, new2;

	outb(addr_6845, 0x35);
	old = inb(addr_6845+1);			/* save */

	outb(addr_6845, 0x35);
	outb(addr_6845+1, (old & 0xf0));	/* write 0 */

	outb(addr_6845, 0x35);
	new1 = (inb(addr_6845+1)) & 0x0f;	/* must read 0 */

	outb(addr_6845, 0x35);
	outb(addr_6845+1, (old | 0x0f));	/* write 1 */

	outb(addr_6845, 0x35);
	new2 = (inb(addr_6845+1)) & 0x0f;	/* must read 1 */

	outb(addr_6845, 0x35);
	outb(addr_6845+1, old);			/* restore */

	return((new1==0) && (new2==0x0f));
}

/*---------------------------------------------------------------------------*
 *	return ptr to string describing vga type
 *---------------------------------------------------------------------------*/
char *
vga_string(int number)
{
	static char *vga_tab[] = {
		"generic",
		"et4000",
		"et3000",
		"pvga1a",
		"wd90c00",
		"wd90c10",
		"wd90c11",
		"v7 vega",
		"v7 fast",
		"v7 ver5",
		"v7 1024i",
		"unknown v7",
		"tvga 8800br",
		"tvga 8800cs",
		"tvga 8900b",
		"tvga 8900c",
		"tvga 8900cl",
		"tvga 9000",
		"tvga 9100",
		"tvga 9200",
		"unknown trident",
		"s3 911",
		"s3 924",
		"s3 801/805",
		"s3 928",
		"unkown s3",
		"cl-gd5402",
		"cl-gd5402r1",
		"cl-gd5420",
		"cl-gd5420r1",
		"cl-gd5422",
		"cl-gd5424",
		"cl-gd5426",
		"cl-gd5428"
	};
	return(vga_tab[number]);
}

/*---------------------------------------------------------------------------*
 *	toggle vga 80/132 column operation
 *---------------------------------------------------------------------------*/
int
vga_col(struct video_state *svsp, int cols)
{
	int ret = 0;

	if(adaptor_type != VGA_ADAPTOR)
		return(0);

	switch(vga_type)
	{
		case VGA_ET4000:
			ret = et4000_col(cols);
			break;

		case VGA_WD90C11:
			ret = wd90c11_col(cols);
			break;

		case VGA_TR8900B:
		case VGA_TR8900C:
		case VGA_TR8900CL:
		case VGA_TR9000:
			ret = tri9000_col(cols);
			break;

		case VGA_V71024I:
			ret = v7_1024i_col(cols);
			break;

		case VGA_S3_928:
			ret = s3_928_col(cols);
			break;

		case VGA_CL_GD5402:
		case VGA_CL_GD5402r1:
		case VGA_CL_GD5420:
		case VGA_CL_GD5420r1:
		case VGA_CL_GD5422:
		case VGA_CL_GD5424:
		case VGA_CL_GD5426:
		case VGA_CL_GD5428:
			ret = cl_gd542x_col(cols);
			break;

		default:

#if PCVT_132GENERIC
			ret = generic_col(cols);
#endif /* PCVT_132GENERIC */

			break;
	}

	if(ret == 0)
		return(0);	/* failed */

	svsp->maxcol = cols;

	return(1);
}

#if PCVT_132GENERIC
/*---------------------------------------------------------------------------*
 *	toggle 80/132 column operation for "generic" SVGAs
 *	NB: this is supposed to work on any (S)VGA as long as the monitor
 *	is able to sync down to 21.5 kHz horizontally. The resulting
 *	vertical frequency is only 50 Hz, so if there is some better board
 *	specific algorithm, we avoid using this generic one.
 *	REPORT ANY FAILURES SO WE CAN IMPROVE THIS
 *---------------------------------------------------------------------------*/

#if PCVT_EXP_132COL
/*
 *	Some improved (i.e. higher scan rates) figures for the horizontal
 *	timing. USE AT YOUR OWN RISK, THIS MIGHT DAMAGE YOUR MONITOR DUE
 *	TO A LOSS OF HORIZONTAL SYNC!
 *	The figures have been tested with an ET3000 board along with a
 *	NEC MultiSync 3D monitor. If you are playing here, consider
 *	testing with several screen pictures (dark background vs. light
 *	background, even enlightening the border color may impact the
 *	result - you can do this e.g. by "scon -p black,42,42,42")
 *	Remember that all horizontal timing values must be dividable
 *	by 8! (The scheme below is taken so that nifty kernel hackers
 *	are able to patch the figures at run-time.)
 *
 *	The actual numbers result in 23 kHz line scan and 54 Hz vertical
 *	scan.
 */
#endif /* PCVT_EXP_132COL */

int
generic_col(int cols)
{
	u_char *sp;
	u_char byte;

#if !PCVT_EXP_132COL

	/* stable figures for any multisync monitor that syncs down to 22 kHz*/
	static volatile u_short htotal = 1312;
	static volatile u_short displayend = 1056;
	static volatile u_short blankstart = 1072;
	static volatile u_short syncstart = 1112;
	static volatile u_short syncend = 1280;

#else /* PCVT_EXP_132COL */

	/* reduced sync-pulse width and sync delays */
	static volatile u_short htotal = 1232;
	static volatile u_short displayend = 1056;
	static volatile u_short blankstart = 1056;
	static volatile u_short syncstart = 1104;
	static volatile u_short syncend = 1168;

#endif /* PCVT_EXP_132COL */

	vga_screen_off();

	/* enable access to first 7 CRTC registers */

	outb(addr_6845, CRTC_VSYNCE);
	byte = inb(addr_6845+1);
	outb(addr_6845, CRTC_VSYNCE);
	outb(addr_6845+1, byte & 0x7f);

	if(cols == SCR_COL132)		/* switch 80 -> 132 */
	{
		/* save state of board for 80 columns */

		if(!regsaved)
		{
			regsaved = 1;

			sp = savearea.generic;

			outb(addr_6845, 0x00);	/* Horizontal Total */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x01);	/* Horizontal Display End */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x02);	/* Horizontal Blank Start */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x03);	/* Horizontal Blank End */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x05);	/* Horizontal Retrace End */
			*sp++ = inb(addr_6845+1);

			outb(addr_6845, 0x13);	/* Row Offset Register */
			*sp++ = inb(addr_6845+1);

			outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
			*sp++ = inb(TS_DATA);

			if(color)
				inb(GN_INPSTAT1C);
			else
				inb(GN_INPSTAT1M);
			/* ATC Mode control */
			outb(ATC_INDEX, ATC_MODE | ATC_ACCESS);
			*sp++ = inb(ATC_DATAR);

			if(color)
				inb(GN_INPSTAT1C);
			else
				inb(GN_INPSTAT1M);
			/* ATC Horizontal Pixel Panning */
			outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS);
			*sp++ = inb(ATC_DATAR);

			*sp++ = inb(GN_MISCOUTR); /* Misc output register */
		}

		/* setup chipset for 132 column operation */


		outb(addr_6845, 0x00);	/* Horizontal Total */
		outb(addr_6845+1, (htotal / 8) - 5);
		outb(addr_6845, 0x01);	/* Horizontal Display End */
		outb(addr_6845+1, (displayend / 8) - 1);
		outb(addr_6845, 0x02);	/* Horizontal Blank Start */
		outb(addr_6845+1, blankstart / 8);
		outb(addr_6845, 0x03);	/* Horizontal Blank End */
		outb(addr_6845+1, ((syncend / 8) & 0x1f) | 0x80);
		outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
		outb(addr_6845+1, syncstart / 8);
		outb(addr_6845, 0x05);	/* Horizontal Retrace End */
		outb(addr_6845+1,
		     (((syncend / 8) & 0x20) * 4)
		     | ((syncend / 8) & 0x1f));

		outb(addr_6845, 0x13);	/* Row Offset Register */
		outb(addr_6845+1, 0x42);

		outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
		outb(TS_DATA, 0x01);	/* 8 dot char clock */

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		outb(ATC_INDEX, ATC_MODE | ATC_ACCESS); /* ATC Mode control */
		outb(ATC_DATAW, 0x08);	/* Line graphics disable */

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS); /* ATC Horizontal Pixel Panning */
		outb(ATC_DATAW, 0x00);

		/* Misc output register */
		/* use the 28.322 MHz clock */
		outb(GN_MISCOUTW, (inb(GN_MISCOUTR) & ~0x0c) | 4);
	}
	else	/* switch 132 -> 80 */
	{
		if(!regsaved)			/* failsafe */
		{
			/* disable access to first 7 CRTC registers */
			outb(addr_6845, CRTC_VSYNCE);
			outb(addr_6845+1, byte);
			vga_screen_on();
			return(0);
		}

		sp = savearea.generic;

		outb(addr_6845, 0x00);	/* Horizontal Total */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x01);	/* Horizontal Display End */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x02);	/* Horizontal Blank Start */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x03);	/* Horizontal Blank End */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x05);	/* Horizontal Retrace End */
		outb(addr_6845+1, *sp++);

		outb(addr_6845, 0x13);	/* Row Offset Register */
		outb(addr_6845+1, *sp++);

		outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
		outb(TS_DATA, *sp++);

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		/* ATC Mode control */
		outb(ATC_INDEX, ATC_MODE | ATC_ACCESS);
		outb(ATC_DATAW, *sp++);

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		/* ATC Horizontal Pixel Panning */
		outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS);
		outb(ATC_DATAW, *sp++);

		outb(GN_MISCOUTW, *sp++);	/* Misc output register */
	}

	/* disable access to first 7 CRTC registers */

	outb(addr_6845, CRTC_VSYNCE);
	outb(addr_6845+1, byte);

	vga_screen_on();

	return(1);
}
#endif /* PCVT_132GENERIC */

/*---------------------------------------------------------------------------*
 *	toggle 80/132 column operation for ET4000 based boards
 *---------------------------------------------------------------------------*/
int
et4000_col(int cols)
{
	u_char *sp;
	u_char byte;

	vga_screen_off();

	/* enable access to first 7 CRTC registers */

	outb(addr_6845, CRTC_VSYNCE);
	byte = inb(addr_6845+1);
	outb(addr_6845, CRTC_VSYNCE);
	outb(addr_6845+1, byte & 0x7f);

	if(cols == SCR_COL132)		/* switch 80 -> 132 */
	{
		/* save state of board for 80 columns */

		if(!regsaved)
		{
			regsaved = 1;

			sp = savearea.et4000;

			outb(addr_6845, 0x00);	/* Horizontal Total */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x01);	/* Horizontal Display End */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x02);	/* Horizontal Blank Start */
			*sp++ = inb(addr_6845+1);

			outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x05);	/* Horizontal Retrace End */
			*sp++ = inb(addr_6845+1);

			outb(addr_6845, 0x13);	/* Row Offset Register */
			*sp++ = inb(addr_6845+1);

			outb(addr_6845, 0x34);	/* 6845 Compatibility */
			*sp++ = inb(addr_6845+1);

			outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
			*sp++ = inb(TS_DATA);

			if(color)
				inb(GN_INPSTAT1C);
			else
				inb(GN_INPSTAT1M);
			/* ATC Mode control */
			outb(ATC_INDEX, ATC_MODE | ATC_ACCESS);
			*sp++ = inb(ATC_DATAR);

			if(color)
				inb(GN_INPSTAT1C);
			else
				inb(GN_INPSTAT1M);
			/* ATC Horizontal Pixel Panning */
			outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS);
			*sp++ = inb(ATC_DATAR);

			*sp++ = inb(GN_MISCOUTR);	/* Misc output register */
		}

		/* setup chipset for 132 column operation */

		outb(addr_6845, 0x00);	/* Horizontal Total */
		outb(addr_6845+1, 0x9f);
		outb(addr_6845, 0x01);	/* Horizontal Display End */
		outb(addr_6845+1, 0x83);
		outb(addr_6845, 0x02);	/* Horizontal Blank Start */
		outb(addr_6845+1, 0x84);

		outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
		outb(addr_6845+1, 0x8b);
		outb(addr_6845, 0x05);	/* Horizontal Retrace End */
		outb(addr_6845+1, 0x80);

		outb(addr_6845, 0x13);	/* Row Offset Register */
		outb(addr_6845+1, 0x42);

		outb(addr_6845, 0x34);	/* 6845 Compatibility */
		outb(addr_6845+1, 0x0a);

		outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
		outb(TS_DATA, 0x01);	/* 8 dot char clock */

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		outb(ATC_INDEX, ATC_MODE | ATC_ACCESS); /* ATC Mode control */
		outb(ATC_DATAW, 0x08);	/* Line graphics disable */

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS); /* ATC Horizontal Pixel Panning */
		outb(ATC_DATAW, 0x00);

		/* Misc output register */

		outb(GN_MISCOUTW, (inb(GN_MISCOUTR) & ~0x0c));
	}
	else	/* switch 132 -> 80 */
	{
		if(!regsaved)			/* failsafe */
		{
			/* disable access to first 7 CRTC registers */
			outb(addr_6845, CRTC_VSYNCE);
			outb(addr_6845+1, byte);
			vga_screen_on();
			return(0);
		}

		sp = savearea.et4000;

		outb(addr_6845, 0x00);	/* Horizontal Total */
		outb(addr_6845+1, *sp++);

		outb(addr_6845, 0x01);	/* Horizontal Display End */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x02);	/* Horizontal Blank Start */
		outb(addr_6845+1, *sp++);


		outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x05);	/* Horizontal Retrace End */
		outb(addr_6845+1, *sp++);

		outb(addr_6845, 0x13);	/* Row Offset Register */
		outb(addr_6845+1, *sp++);

		outb(addr_6845, 0x34);	/* 6845 Compatibility */
		outb(addr_6845+1, *sp++);

		outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
		outb(TS_DATA, *sp++);

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		/* ATC Mode control */
		outb(ATC_INDEX, ATC_MODE | ATC_ACCESS);
		outb(ATC_DATAW, *sp++);

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		/* ATC Horizontal Pixel Panning */
		outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS);
		outb(ATC_DATAW, *sp++);

		outb(GN_MISCOUTW, *sp++);	/* Misc output register */
	}

	/* disable access to first 7 CRTC registers */

	outb(addr_6845, CRTC_VSYNCE);
	outb(addr_6845+1, byte);

	vga_screen_on();

	return(1);
}

/*---------------------------------------------------------------------------*
 *	toggle 80/132 column operation for WD/Paradise based boards
 *
 *	when this card does 132 cols, the char map select register (TS_INDEX,
 *	TS_FONTSEL) function bits get REDEFINED. whoever did design this,
 *	please don't cross my way ever .......
 *
 *---------------------------------------------------------------------------*/
int
wd90c11_col(int cols)
{
	u_char *sp;
	u_char byte;
	int i;

	vga_screen_off();

	/* enable access to first 7 CRTC registers */

	outb(addr_6845, CRTC_VSYNCE);
	byte = inb(addr_6845+1);
	outb(addr_6845, CRTC_VSYNCE);
	outb(addr_6845+1, byte & 0x7f);

	/* enable access to WD/Paradise "control extensions" */

	outb(GDC_INDEX, GDC_PR5GPLOCK);
	outb(GDC_INDEX, 0x05);
	outb(addr_6845, CRTC_PR10);
	outb(addr_6845, 0x85);
	outb(TS_INDEX, TS_UNLOCKSEQ);
	outb(TS_DATA, 0x48);

	if(cols == SCR_COL132)		/* switch 80 -> 132 */
	{
		/* save state of board for 80 columns */

		if(!regsaved)
		{
			regsaved = 1;

			/* save current fonts */

			sp = savearea.wd90c11;

			outb(addr_6845, 0x00);	/* Horizontal Total */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x01);	/* Horizontal Display End */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x02);	/* Horizontal Blank Start */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x03);	/* Horizontal Blank End */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x05);	/* Horizontal Retrace End */
			*sp++ = inb(addr_6845+1);

			outb(addr_6845, 0x13);	/* Row Offset Register */
			*sp++ = inb(addr_6845+1);

			outb(addr_6845, 0x2e);	/* misc 1 */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x2f);	/* misc 2 */
			*sp++ = inb(addr_6845+1);

			outb(TS_INDEX, 0x10);/* Timing Sequencer */
			*sp++ = inb(TS_DATA);
			outb(TS_INDEX, 0x12);/* Timing Sequencer */
			*sp++ = inb(TS_DATA);

			*sp++ = inb(GN_MISCOUTR);	/* Misc output register */
		}

		/* setup chipset for 132 column operation */

		outb(addr_6845, 0x00);	/* Horizontal Total */
		outb(addr_6845+1, 0x9c);
		outb(addr_6845, 0x01);	/* Horizontal Display End */
		outb(addr_6845+1, 0x83);
		outb(addr_6845, 0x02);	/* Horizontal Blank Start */
		outb(addr_6845+1, 0x84);
		outb(addr_6845, 0x03);	/* Horizontal Blank End */
		outb(addr_6845+1, 0x9f);
		outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
		outb(addr_6845+1, 0x8a);
		outb(addr_6845, 0x05);	/* Horizontal Retrace End */
		outb(addr_6845+1, 0x1c);

		outb(addr_6845, 0x13);	/* Row Offset Register */
		outb(addr_6845+1, 0x42);

		outb(addr_6845, 0x2e);	/* misc 1 */
		outb(addr_6845+1, 0x04);
		outb(addr_6845, 0x2f);	/* misc 2 */
		outb(addr_6845+1, 0x00);

		outb(TS_INDEX, 0x10);/* Timing Sequencer */
		outb(TS_DATA, 0x21);
		outb(TS_INDEX, 0x12);/* Timing Sequencer */
		outb(TS_DATA, 0x14);

		outb(GN_MISCOUTW, (inb(GN_MISCOUTR) | 0x08));	/* Misc output register */

		vsp->wd132col = 1;
	}
	else	/* switch 132 -> 80 */
	{
		if(!regsaved)			/* failsafe */
		{
			/* disable access to first 7 CRTC registers */

			outb(addr_6845, CRTC_VSYNCE);
			outb(addr_6845+1, byte);

			/* disable access to WD/Paradise "control extensions" */

			outb(GDC_INDEX, GDC_PR5GPLOCK);
			outb(GDC_INDEX, 0x00);
			outb(addr_6845, CRTC_PR10);
			outb(addr_6845, 0x00);
			outb(TS_INDEX, TS_UNLOCKSEQ);
			outb(TS_DATA, 0x00);

			vga_screen_on();

			return(0);
		}

		sp = savearea.wd90c11;

		outb(addr_6845, 0x00);	/* Horizontal Total */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x01);	/* Horizontal Display End */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x02);	/* Horizontal Blank Start */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x03);	/* Horizontal Blank End */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x05);	/* Horizontal Retrace End */
		outb(addr_6845+1, *sp++);

		outb(addr_6845, 0x13);	/* Row Offset Register */
		outb(addr_6845+1, *sp++);

		outb(addr_6845, 0x2e);	/* misc 1 */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x2f);	/* misc 2 */
		outb(addr_6845+1, *sp++);

		outb(TS_INDEX, 0x10);/* Timing Sequencer */
		outb(addr_6845+1, *sp++);
		outb(TS_INDEX, 0x12);/* Timing Sequencer */
		outb(addr_6845+1, *sp++);

		outb(GN_MISCOUTW, *sp++);	/* Misc output register */

		vsp->wd132col = 0;
	}

	/* restore fonts */

	for(i = 0; i < totalfonts; i++)
		if(saved_charsets[i])
			vga_move_charset(i, 0, 0);

	select_vga_charset(vsp->vga_charset);

	/* disable access to first 7 CRTC registers */

	outb(addr_6845, CRTC_VSYNCE);
	outb(addr_6845+1, byte);

	/* disable access to WD/Paradise "control extensions" */

	outb(GDC_INDEX, GDC_PR5GPLOCK);
	outb(GDC_INDEX, 0x00);
	outb(addr_6845, CRTC_PR10);
	outb(addr_6845, 0x00);
	outb(TS_INDEX, TS_UNLOCKSEQ);
	outb(TS_DATA, 0x00);

	vga_screen_on();

	return(1);
}

/*---------------------------------------------------------------------------*
 *	toggle 80/132 column operation for TRIDENT 9000 based boards
 *---------------------------------------------------------------------------*/
int
tri9000_col(int cols)
{
	u_char *sp;
	u_char byte;

	vga_screen_off();

	/* sync reset is necessary to preserve memory contents ... */

	outb(TS_INDEX, TS_SYNCRESET);
	outb(TS_DATA, 0x01);	/* synchronous reset */

	/* disable protection of misc out and other regs */

	outb(addr_6845, CRTC_MTEST);
	byte = inb(addr_6845+1);
	outb(addr_6845, CRTC_MTEST);
	outb(addr_6845+1, byte & ~0x50);

	/* enable access to first 7 CRTC registers */

	outb(addr_6845, CRTC_VSYNCE);
	byte = inb(addr_6845+1);
	outb(addr_6845, CRTC_VSYNCE);
	outb(addr_6845+1, byte & 0x7f);

	if(cols == SCR_COL132)		/* switch 80 -> 132 */
	{
		/* save state of board for 80 columns */

		if(!regsaved)
		{
			regsaved = 1;

			sp = savearea.tri9000;

			outb(addr_6845, 0x00);	/* Horizontal Total */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x01);	/* Horizontal Display End */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x02);	/* Horizontal Blank Start */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x03);	/* Horizontal Blank End */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x05);	/* Horizontal Retrace End */
			*sp++ = inb(addr_6845+1);

			outb(addr_6845, 0x13);
			*sp++ = inb(addr_6845+1);

			outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
			*sp++ = inb(TS_DATA);

			outb(TS_INDEX, TS_HWVERS);/* Hardware Version register */
			outb(TS_DATA, 0x00);	  /* write ANYTHING switches to OLD */
			outb(TS_INDEX, TS_MODEC2);
			*sp++ = inb(TS_DATA);

			outb(TS_INDEX, TS_HWVERS);/* Hardware Version register */
			inb(TS_DATA);		  /* read switches to NEW */
			outb(TS_INDEX, TS_MODEC2);
			*sp++ = inb(TS_DATA);

			if(color)
				inb(GN_INPSTAT1C);
			else
				inb(GN_INPSTAT1M);
			/* ATC Mode control */
			outb(ATC_INDEX, ATC_MODE | ATC_ACCESS);
			*sp++ = inb(ATC_DATAR);

			if(color)
				inb(GN_INPSTAT1C);
			else
				inb(GN_INPSTAT1M);
			/* ATC Horizontal Pixel Panning */
			outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS);
			*sp++ = inb(ATC_DATAR);

			*sp++ = inb(GN_MISCOUTR);	/* Misc output register */
		}

		/* setup chipset for 132 column operation */

		outb(addr_6845, 0x00);	/* Horizontal Total */
		outb(addr_6845+1, 0x9b);
		outb(addr_6845, 0x01);	/* Horizontal Display End */
		outb(addr_6845+1, 0x83);
		outb(addr_6845, 0x02);	/* Horizontal Blank Start */
		outb(addr_6845+1, 0x84);
		outb(addr_6845, 0x03);	/* Horizontal Blank End */
		outb(addr_6845+1, 0x1e);
		outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
		outb(addr_6845+1, 0x87);
		outb(addr_6845, 0x05);	/* Horizontal Retrace End */
		outb(addr_6845+1, 0x1a);

		outb(addr_6845, 0x13);	/* Row Offset Register */
		outb(addr_6845+1, 0x42);

		outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
		outb(TS_DATA, 0x01);	/* 8 dot char clock */

		outb(TS_INDEX, TS_HWVERS);/* Hardware Version register */
		outb(TS_DATA, 0x00);	  /* write ANYTHING switches to OLD */
		outb(TS_INDEX, TS_MODEC2);
		outb(TS_DATA, 0x00);

		outb(TS_INDEX, TS_HWVERS);/* Hardware Version register */
		inb(TS_DATA);		  /* read switches to NEW */
		outb(TS_INDEX, TS_MODEC2);
		outb(TS_DATA, 0x01);

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		outb(ATC_INDEX, ATC_MODE | ATC_ACCESS); /* ATC Mode control */
		outb(ATC_DATAW, 0x08);	/* Line graphics disable */

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS); /* ATC Horizontal Pixel Panning */
		outb(ATC_DATAW, 0x00);

		outb(GN_MISCOUTW, (inb(GN_MISCOUTR) | 0x0c));	/* Misc output register */
	}
	else	/* switch 132 -> 80 */
	{
		if(!regsaved)			/* failsafe */
		{
			/* disable access to first 7 CRTC registers */
			outb(addr_6845, CRTC_VSYNCE);
			outb(addr_6845+1, byte);

			outb(TS_INDEX, TS_SYNCRESET);
			outb(TS_DATA, 0x03);	/* clear synchronous reset */

			vga_screen_on();

			return(0);
		}

		sp = savearea.tri9000;

		outb(addr_6845, 0x00);	/* Horizontal Total */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x01);	/* Horizontal Display End */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x02);	/* Horizontal Blank Start */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x03);	/* Horizontal Blank End */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x05);	/* Horizontal Retrace End */
		outb(addr_6845+1, *sp++);

		outb(addr_6845, 0x13);	/* Row Offset Register */
		outb(addr_6845+1, *sp++);

		outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
		outb(TS_DATA, *sp++);

		outb(TS_INDEX, TS_HWVERS);/* Hardware Version register */
		outb(TS_DATA, 0x00);	  /* write ANYTHING switches to OLD */
		outb(TS_INDEX, TS_MODEC2);/* Timing Sequencer */
		outb(TS_DATA, *sp++);

		outb(TS_INDEX, TS_HWVERS);/* Hardware Version register */
		inb(TS_DATA);		  /* read switches to NEW */
		outb(TS_INDEX, TS_MODEC2);/* Timing Sequencer */
		outb(TS_DATA, *sp++);

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		/* ATC Mode control */
		outb(ATC_INDEX, ATC_MODE | ATC_ACCESS);
		outb(ATC_DATAW, *sp++);

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		/* ATC Horizontal Pixel Panning */
		outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS);
		outb(ATC_DATAW, *sp++);

		outb(GN_MISCOUTW, *sp++);	/* Misc output register */
	}

	/* disable access to first 7 CRTC registers */

	outb(addr_6845, CRTC_VSYNCE);
	outb(addr_6845+1, byte);

	outb(TS_INDEX, TS_SYNCRESET);
	outb(TS_DATA, 0x03);	/* clear synchronous reset */

	vga_screen_on();

	return(1);
}

/*---------------------------------------------------------------------------*
 *	toggle 80/132 column operation for Video7 VGA 1024i
 *---------------------------------------------------------------------------*/
int
v7_1024i_col(int cols)
{
	u_char *sp;
	u_char byte;
	u_char save__byte;

	vga_screen_off();

	/* enable access to first 7 CRTC registers */

	/* first, enable read access to vertical retrace start/end */
	outb(addr_6845, CRTC_HBLANKE);
	byte = inb(addr_6845+1);
	outb(addr_6845, CRTC_HBLANKE);
	outb(addr_6845+1, (byte | 0x80));

	/* second, enable access to protected registers */
	outb(addr_6845, CRTC_VSYNCE);
	save__byte = byte = inb(addr_6845+1);
	byte |= 0x20;	/* no irq 2 */
	byte &= 0x6f;	/* wr enable, clr irq flag */
	outb(addr_6845, CRTC_VSYNCE);
	outb(addr_6845+1, byte);

	outb(TS_INDEX, TS_EXTCNTL);	/* enable extensions */
	outb(TS_DATA, 0xea);


	if(cols == SCR_COL132)		/* switch 80 -> 132 */
	{
		/* save state of board for 80 columns */

		if(!regsaved)
		{
			regsaved = 1;

			sp = savearea.v7_1024i;

			outb(addr_6845, 0x00);	/* Horizontal Total */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x01);	/* Horizontal Display End */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x02);	/* Horizontal Blank Start */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x03);	/* Horizontal Blank End */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x05);	/* Horizontal Retrace End */
			*sp++ = inb(addr_6845+1);

			outb(addr_6845, 0x13);	/* Row Offset Register */
			*sp++ = inb(addr_6845+1);

			outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
			*sp++ = inb(TS_DATA);

			if(color)
				inb(GN_INPSTAT1C);
			else
				inb(GN_INPSTAT1M);
			/* ATC Mode control */
			outb(ATC_INDEX, ATC_MODE | ATC_ACCESS);
			*sp++ = inb(ATC_DATAR);

			if(color)
				inb(GN_INPSTAT1C);
			else
				inb(GN_INPSTAT1M);
			/* ATC Horizontal Pixel Panning */
			outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS);
			*sp++ = inb(ATC_DATAR);

			outb(TS_INDEX, 0x83);
			*sp++ = inb(TS_DATA);

			outb(TS_INDEX, 0xa4);
			*sp++ = inb(TS_DATA);

			outb(TS_INDEX, 0xe0);
			*sp++ = inb(TS_DATA);

			outb(TS_INDEX, 0xe4);
			*sp++ = inb(TS_DATA);

			outb(TS_INDEX, 0xf8);
			*sp++ = inb(TS_DATA);

			outb(TS_INDEX, 0xfd);
			*sp++ = inb(TS_DATA);

			*sp++ = inb(GN_MISCOUTR);	/* Misc output register */
		}

		/* setup chipset for 132 column operation */

		outb(addr_6845, 0x00);	/* Horizontal Total */
		outb(addr_6845+1, 0x9c);
		outb(addr_6845, 0x01);	/* Horizontal Display End */
		outb(addr_6845+1, 0x83);
		outb(addr_6845, 0x02);	/* Horizontal Blank Start */
		outb(addr_6845+1, 0x86);
		outb(addr_6845, 0x03);	/* Horizontal Blank End */
		outb(addr_6845+1, 0x9e);
		outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
		outb(addr_6845+1, 0x89);
		outb(addr_6845, 0x05);	/* Horizontal Retrace End */
		outb(addr_6845+1, 0x1c);

		outb(addr_6845, 0x13);	/* Row Offset Register */
		outb(addr_6845+1, 0x42);

		outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
		outb(TS_DATA, 0x01);	/* 8 dot char clock */

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		outb(ATC_INDEX, ATC_MODE | ATC_ACCESS); /* ATC Mode control */
		outb(ATC_DATAW, 0x08);	/* Line graphics disable */

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS); /* ATC Horizontal Pixel Panning */
		outb(ATC_DATAW, 0x00);

		outb(TS_INDEX, TS_SYNCRESET);
		outb(TS_DATA, 0x01);	/* synchronous reset */

		outb(TS_INDEX, 0x83);
		outb(TS_DATA, 0xa0);

		outb(TS_INDEX, 0xa4);
		outb(TS_DATA, 0x1c);

		outb(TS_INDEX, 0xe0);
		outb(TS_DATA, 0x00);

		outb(TS_INDEX, 0xe4);
		outb(TS_DATA, 0xfe);

		outb(TS_INDEX, 0xf8);
		outb(TS_DATA, 0x1b);

		outb(TS_INDEX, 0xfd);
		outb(TS_DATA, 0x33);

		byte = inb(GN_MISCOUTR);
		byte |= 0x0c;
		outb(GN_MISCOUTW, byte);	/* Misc output register */

		outb(TS_INDEX, TS_SYNCRESET);
		outb(TS_DATA, 0x03);	/* clear synchronous reset */
	}
	else	/* switch 132 -> 80 */
	{
		if(!regsaved)			/* failsafe */
		{
			outb(TS_INDEX, TS_EXTCNTL);	/* disable extensions */
			outb(TS_DATA, 0xae);

			/* disable access to first 7 CRTC registers */
			outb(addr_6845, CRTC_VSYNCE);
			outb(addr_6845+1, byte);
			vga_screen_on();
			return(0);
		}

		sp = savearea.v7_1024i;

		outb(addr_6845, 0x00);	/* Horizontal Total */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x01);	/* Horizontal Display End */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x02);	/* Horizontal Blank Start */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x03);	/* Horizontal Blank End */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x05);	/* Horizontal Retrace End */
		outb(addr_6845+1, *sp++);

		outb(addr_6845, 0x13);	/* Row Offset Register */
		outb(addr_6845+1, *sp++);

		outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
		outb(TS_DATA, *sp++);

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		/* ATC Mode control */
		outb(ATC_INDEX, ATC_MODE | ATC_ACCESS);
		outb(ATC_DATAW, *sp++);

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		/* ATC Horizontal Pixel Panning */
		outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS);
		outb(ATC_DATAW, *sp++);

		outb(TS_INDEX, TS_SYNCRESET);
		outb(TS_DATA, 0x01);	/* synchronous reset */

		outb(TS_INDEX, 0x83);
		outb(TS_DATA, *sp++);

		outb(TS_INDEX, 0xa4);
		outb(TS_DATA, *sp++);

		outb(TS_INDEX, 0xe0);
		outb(TS_DATA, *sp++);

		outb(TS_INDEX, 0xe4);
		outb(TS_DATA, *sp++);

		outb(TS_INDEX, 0xf8);
		outb(TS_DATA, *sp++);

		outb(TS_INDEX, 0xfd);
		outb(TS_DATA, *sp++);

		outb(GN_MISCOUTW, *sp++);	/* Misc output register */

		outb(TS_INDEX, TS_SYNCRESET);
		outb(TS_DATA, 0x03);	/* clear synchronous reset */
	}

	outb(TS_INDEX, TS_EXTCNTL);	/* disable extensions */
	outb(TS_DATA, 0xae);

	/* disable access to first 7 CRTC registers */

	outb(addr_6845, CRTC_VSYNCE);
	outb(addr_6845+1, save__byte);

	vga_screen_on();

	return(1);
}

/*---------------------------------------------------------------------------*
 *	toggle 80/132 column operation for S3 86C928 based boards
 *---------------------------------------------------------------------------*/
int
s3_928_col(int cols)
{
	u_char *sp;
	u_char byte;

	vga_screen_off();

	outb(addr_6845, 0x38);
	outb(addr_6845+1, 0x48);	/* unlock registers */
	outb(addr_6845, 0x39);
	outb(addr_6845+1, 0xa0);	/* unlock registers */

	/* enable access to first 7 CRTC registers */

	outb(addr_6845, CRTC_VSYNCE);
	byte = inb(addr_6845+1);
	outb(addr_6845, CRTC_VSYNCE);
	outb(addr_6845+1, byte & 0x7f);

	if(cols == SCR_COL132)		/* switch 80 -> 132 */
	{
		/* save state of board for 80 columns */

		if(!regsaved)
		{
			regsaved = 1;

			sp = savearea.s3_928;

			outb(addr_6845, 0x00);	/* Horizontal Total */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x01);	/* Horizontal Display End */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x02);	/* Horizontal Blank Start */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x03);	/* Horizontal Blank End */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x05);	/* Horizontal Retrace End */
			*sp++ = inb(addr_6845+1);

			outb(addr_6845, 0x13);	/* Row Offset Register */
			*sp++ = inb(addr_6845+1);

			outb(addr_6845, 0x34);	/* Backward Compat 3 Reg */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x3b);	/* Data Xfer Exec Position */
			*sp++ = inb(addr_6845+1);

			outb(addr_6845, 0x42);	/* (Clock) Mode Control */
			*sp++ = inb(addr_6845+1);

			outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
			*sp++ = inb(TS_DATA);

			if(color)
				inb(GN_INPSTAT1C);
			else
				inb(GN_INPSTAT1M);
			/* ATC Mode control */
			outb(ATC_INDEX, ATC_MODE | ATC_ACCESS);
			*sp++ = inb(ATC_DATAR);

			if(color)
				inb(GN_INPSTAT1C);
			else
				inb(GN_INPSTAT1M);
			/* ATC Horizontal Pixel Panning */
			outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS);
			*sp++ = inb(ATC_DATAR);

			*sp++ = inb(GN_MISCOUTR);	/* Misc output register */
		}

		/* setup chipset for 132 column operation */

		outb(addr_6845, 0x00);	/* Horizontal Total */
		outb(addr_6845+1, 0x9a);
		outb(addr_6845, 0x01);	/* Horizontal Display End */
		outb(addr_6845+1, 0x83);
		outb(addr_6845, 0x02);	/* Horizontal Blank Start */
		outb(addr_6845+1, 0x86);
		outb(addr_6845, 0x03);	/* Horizontal Blank End */
		outb(addr_6845+1, 0x9d);
		outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
		outb(addr_6845+1, 0x87);
		outb(addr_6845, 0x05);	/* Horizontal Retrace End */
		outb(addr_6845+1, 0x1b);

		outb(addr_6845, 0x13);	/* Row Offset Register */
		outb(addr_6845+1, 0x42);

		outb(addr_6845, 0x34);
		outb(addr_6845+1, 0x10);/* enable data xfer pos control */
		outb(addr_6845, 0x3b);
		outb(addr_6845+1, 0x90);/* set data xfer pos value */

		outb(addr_6845, 0x42);	/* (Clock) Mode Control */
		outb(addr_6845+1, 0x02);/* Select 40MHz Clock */

		outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
		outb(TS_DATA, 0x01);	/* 8 dot char clock */

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		outb(ATC_INDEX, ATC_MODE | ATC_ACCESS); /* ATC Mode control */
		outb(ATC_DATAW, 0x08);	/* Line graphics disable */

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS); /* ATC Horizontal Pixel Panning */
		outb(ATC_DATAW, 0x00);

		/* Misc output register */

		outb(GN_MISCOUTW, (inb(GN_MISCOUTR) | 0x0c));
	}
	else	/* switch 132 -> 80 */
	{
		if(!regsaved)			/* failsafe */
		{
			/* disable access to first 7 CRTC registers */
			outb(addr_6845, CRTC_VSYNCE);
			outb(addr_6845+1, byte);

			outb(addr_6845, 0x38);
			outb(addr_6845+1, 0x00);	/* lock registers */
			outb(addr_6845, 0x39);
			outb(addr_6845+1, 0x00);	/* lock registers */

			vga_screen_on();
			return(0);
		}

		sp = savearea.s3_928;

		outb(addr_6845, 0x00);	/* Horizontal Total */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x01);	/* Horizontal Display End */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x02);	/* Horizontal Blank Start */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x03);	/* Horizontal Blank End */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x05);	/* Horizontal Retrace End */
		outb(addr_6845+1, *sp++);

		outb(addr_6845, 0x13);	/* Row Offset Register */
		outb(addr_6845+1, *sp++);

		outb(addr_6845, 0x34);
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x3b);
		outb(addr_6845+1, *sp++);

		outb(addr_6845, 0x42);	/* Mode control */
		outb(addr_6845+1, *sp++);

		outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
		outb(TS_DATA, *sp++);

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		/* ATC Mode control */
		outb(ATC_INDEX, ATC_MODE | ATC_ACCESS);
		outb(ATC_DATAW, *sp++);

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		/* ATC Horizontal Pixel Panning */
		outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS);
		outb(ATC_DATAW, *sp++);

		outb(GN_MISCOUTW, *sp++);	/* Misc output register */
	}

	/* disable access to first 7 CRTC registers */

	outb(addr_6845, CRTC_VSYNCE);
	outb(addr_6845+1, byte);

	outb(addr_6845, 0x38);
	outb(addr_6845+1, 0x00);	/* lock registers */
	outb(addr_6845, 0x39);
	outb(addr_6845+1, 0x00);	/* lock registers */

	vga_screen_on();

	return(1);
}

/*---------------------------------------------------------------------------*
 *	toggle 80/132 column operation for Cirrus Logic 542x based boards
 *---------------------------------------------------------------------------*/
int
cl_gd542x_col(int cols)
{
	u_char *sp;
	u_char byte;

	vga_screen_off();

	/* enable access to first 7 CRTC registers */

	outb(addr_6845, CRTC_VSYNCE);
	byte = inb(addr_6845+1);
	outb(addr_6845, CRTC_VSYNCE);
	outb(addr_6845+1, byte & 0x7f);

	/* enable access to cirrus extension registers */
	outb(TS_INDEX, 6);
	outb(TS_DATA, 0x12);

	if(cols == SCR_COL132)		/* switch 80 -> 132 */
	{
		/* save state of board for 80 columns */

		if(!regsaved)
		{
			regsaved = 1;

			sp = savearea.cirrus;

			outb(addr_6845, 0x00);	/* Horizontal Total */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x01);	/* Horizontal Display End */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x02);	/* Horizontal Blank Start */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x03);	/* Horizontal Blank End */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
			*sp++ = inb(addr_6845+1);
			outb(addr_6845, 0x05);	/* Horizontal Retrace End */
			*sp++ = inb(addr_6845+1);

			outb(addr_6845, 0x13);	/* Row Offset Register */
			*sp++ = inb(addr_6845+1);

			outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
			*sp++ = inb(TS_DATA);


			if(color)
				inb(GN_INPSTAT1C);
			else
				inb(GN_INPSTAT1M);
			/* ATC Mode control */
			outb(ATC_INDEX, ATC_MODE | ATC_ACCESS);
			*sp++ = inb(ATC_DATAR);

			if(color)
				inb(GN_INPSTAT1C);
			else
				inb(GN_INPSTAT1M);
			/* ATC Horizontal Pixel Panning */
			outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS);
			*sp++ = inb(ATC_DATAR);

			/* VCLK2 Numerator Register */
			outb(TS_INDEX, 0xd);
			*sp++ = inb(TS_DATA);

			/* VCLK2 Denominator and Post-Scalar Value Register */
			outb(TS_INDEX, 0x1d);
			*sp++ = inb(TS_DATA);

			/* Misc output register */
			*sp++ = inb(GN_MISCOUTR);
		}

		/* setup chipset for 132 column operation */

		outb(addr_6845, 0x00);	/* Horizontal Total */
		outb(addr_6845+1, 0x9f);
		outb(addr_6845, 0x01);	/* Horizontal Display End */
		outb(addr_6845+1, 0x83);
		outb(addr_6845, 0x02);	/* Horizontal Blank Start */
		outb(addr_6845+1, 0x84);
		outb(addr_6845, 0x03);	/* Horizontal Blank End */
		outb(addr_6845+1, 0x82);
		outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
		outb(addr_6845+1, 0x8a);
		outb(addr_6845, 0x05);	/* Horizontal Retrace End */
		outb(addr_6845+1, 0x9e);

		outb(addr_6845, 0x13);	/* Row Offset Register */
		outb(addr_6845+1, 0x42);

		/* set VCLK2 to 41.164 MHz ..... */
		outb(TS_INDEX, 0xd);	/* VCLK2 Numerator Register */
		outb(TS_DATA, 0x45);

		outb(TS_INDEX, 0x1d);	/* VCLK2 Denominator and */
		outb(TS_DATA, 0x30);   /* Post-Scalar Value Register */

		/* and use it. */
		outb(GN_MISCOUTW, (inb(GN_MISCOUTR) & ~0x0c) | (2 << 2));

		outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
		outb(TS_DATA, 0x01);	/* 8 dot char clock */

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		outb(ATC_INDEX, ATC_MODE | ATC_ACCESS); /* ATC Mode control */
		outb(ATC_DATAW, 0x08);	/* Line graphics disable */

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS); /* ATC Horizontal Pixel Panning */
		outb(ATC_DATAW, 0x00);
	}
	else	/* switch 132 -> 80 */
	{
		if(!regsaved)			/* failsafe */
		{
			/* disable access to first 7 CRTC registers */
			outb(addr_6845, CRTC_VSYNCE);
			outb(addr_6845+1, byte);

			/* disable access to cirrus extension registers */
			outb(TS_INDEX, 6);
			outb(TS_DATA, 0);

			vga_screen_on();
			return(0);
		}

		sp = savearea.cirrus;

		outb(addr_6845, 0x00);	/* Horizontal Total */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x01);	/* Horizontal Display End */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x02);	/* Horizontal Blank Start */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x03);	/* Horizontal Blank End */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x04);	/* Horizontal Retrace Start */
		outb(addr_6845+1, *sp++);
		outb(addr_6845, 0x05);	/* Horizontal Retrace End */
		outb(addr_6845+1, *sp++);

		outb(addr_6845, 0x13);	/* Row Offset Register */
		outb(addr_6845+1, *sp++);

		outb(TS_INDEX, TS_MODE);/* Timing Sequencer */
		outb(TS_DATA, *sp++);

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		/* ATC Mode control */
		outb(ATC_INDEX, ATC_MODE | ATC_ACCESS);
		outb(ATC_DATAW, *sp++);

		if(color)
			inb(GN_INPSTAT1C);
		else
			inb(GN_INPSTAT1M);
		/* ATC Horizontal Pixel Panning */
		outb(ATC_INDEX, ATC_HORPIXPAN | ATC_ACCESS);
		outb(ATC_DATAW, *sp++);

		/* VCLK2 Numerator Register */
		outb(TS_INDEX, 0xd);
		outb(TS_DATA, *sp++);

		/* VCLK2 Denominator and Post-Scalar Value Register */
		outb(TS_INDEX, 0x1d);
		outb(TS_DATA, *sp++);

		outb(GN_MISCOUTW, *sp++);	/* Misc output register */
	}

	/* disable access to cirrus extension registers */
	outb(TS_INDEX, 6);
	outb(TS_DATA, 0);

	/* disable access to first 7 CRTC registers */

	outb(addr_6845, CRTC_VSYNCE);
	outb(addr_6845+1, byte);

	vga_screen_on();

	return(1);
}

#ifdef XSERVER
/*---------------------------------------------------------------------------*
 *	switch screen from text mode to X-mode and vice versa
 *---------------------------------------------------------------------------*/
void
switch_screen(int n, int oldgrafx, int newgrafx)
{
#if PCVT_SCREENSAVER
	static unsigned saved_scrnsv_tmo = 0;
#endif	/* PCVT_SCREENSAVER */

	int cols = vsp->maxcol;		/* get current col val */

	if(n < 0 || n >= totalscreens)
		return;

	if(!oldgrafx && newgrafx)
	{
		/* switch from text to graphics */

#if PCVT_SCREENSAVER
		if((saved_scrnsv_tmo = scrnsv_timeout))
			pcvt_set_scrnsv_tmo(0);	/* screensaver off */
#endif /* PCVT_SCREENSAVER */

		async_update(UPDATE_STOP);	/* status display off */
	}

	if(!oldgrafx)
	{
		/* switch from text mode */

		/* video board memory -> kernel memory */
		bcopy(vsp->Crtat, vsp->Memory,
		      vsp->screen_rows * vsp->maxcol * CHR);

		vsp->Crtat = vsp->Memory;	/* operate in memory now */
	}

	/* update global screen pointers/variables */
	current_video_screen = n;	/* current screen no */

	vsp = &vs[n];			/* current video state ptr */

	if(oldgrafx && !newgrafx)
	{
		/* switch from graphics to text mode */
		unsigned i;

		/* restore fonts */
		for(i = 0; i < totalfonts; i++)
			if(saved_charsets[i])
				vga_move_charset(i, 0, 0);

#if PCVT_SCREENSAVER
		/* activate screen saver */
		if(saved_scrnsv_tmo)
			pcvt_set_scrnsv_tmo(saved_scrnsv_tmo);
#endif /* PCVT_SCREENSAVER */

		/* re-initialize lost MDA information */
		if(adaptor_type == MDA_ADAPTOR)
		{
		    /*
		     * Due to the fact that HGC registers are write-only,
		     * the Xserver can only make guesses about the state
		     * the HGC adaptor has been before turning on X mode.
		     * Thus, the display must be re-enabled now, and the
		     * cursor shape and location restored.
		     */
		    outb(GN_DMCNTLM, 0x28); /* enable display, text mode */
		    outb(addr_6845, CRTC_CURSORH); /* select high register */
		    outb(addr_6845+1,
			 ((vsp->Crtat + vsp->cur_offset) - Crtat) >> 8);
		    outb(addr_6845, CRTC_CURSORL); /* select low register */
		    outb(addr_6845+1,
			 ((vsp->Crtat + vsp->cur_offset) - Crtat));

		    outb(addr_6845, CRTC_CURSTART); /* select high register */
		    outb(addr_6845+1, vsp->cursor_start);
		    outb(addr_6845, CRTC_CUREND); /* select low register */
		    outb(addr_6845+1, vsp->cursor_end);
		}

		/* make status display happy */
		async_update(UPDATE_START);
	}

	if(!newgrafx)
	{
		/* to text mode */

		/* kernel memory -> video board memory */
		bcopy(vsp->Crtat, Crtat,
		      vsp->screen_rows * vsp->maxcol * CHR);

		vsp->Crtat = Crtat;		/* operate on screen now */

		outb(addr_6845, CRTC_STARTADRH);
		outb(addr_6845+1, 0);
		outb(addr_6845, CRTC_STARTADRL);
		outb(addr_6845+1, 0);
	}

	select_vga_charset(vsp->vga_charset);

	if(vsp->maxcol != cols)
		vga_col(vsp, vsp->maxcol);	/* select 80/132 columns */

 	outb(addr_6845, CRTC_CURSORH);	/* select high register */
	outb(addr_6845+1, vsp->cur_offset >> 8);
	outb(addr_6845, CRTC_CURSORL);	/* select low register */
	outb(addr_6845+1, vsp->cur_offset);

	if(vsp->cursor_on)
	{
		outb(addr_6845, CRTC_CURSTART);	/* select high register */
		outb(addr_6845+1, vsp->cursor_start);
		outb(addr_6845, CRTC_CUREND);	/* select low register */
		outb(addr_6845+1, vsp->cursor_end);
	}
	else
	{
		sw_cursor(0);
	}

	if(adaptor_type == VGA_ADAPTOR)
	{
		unsigned i;

		/* switch VGA DAC palette entries */
		for(i = 0; i < NVGAPEL; i++)
			vgapaletteio(i, &vsp->palette[i], 1);
	}

	if(!newgrafx)
	{
		update_led();	/* update led's */
		update_hp(vsp);	/* update fkey labels, if present */

		/* if we switch to a vt with force 24 lines mode and	*/
		/* pure VT emulation and 25 rows charset, then we have	*/
		/* to clear the last line on display ...		*/

		if(vsp->force24 && (vsp->vt_pure_mode == M_PUREVT) &&
			(vgacs[vsp->vga_charset].screen_size == SIZ_25ROWS))
		{
			fillw(' ', vsp->Crtat + vsp->screen_rows * vsp->maxcol,
				vsp->maxcol);
		}
	}
}

/*---------------------------------------------------------------------------*
 *	Change specified vt to VT_AUTO mode
 *	xxx Maybe this should also reset VT_GRAFX mode; since switching and
 *	graphics modes are not going to work without VT_PROCESS mode.
 *---------------------------------------------------------------------------*/
static void
set_auto_mode (struct video_state *vsx)
{
	unsigned ostatus = vsx->vt_status;
	vsx->smode.mode = VT_AUTO;
	vsx->proc = NULL;
	vsx->pid = 0;
	vsx->vt_status &= ~(VT_WAIT_REL|VT_WAIT_ACK);

	if (ostatus & VT_WAIT_ACK)
	{
		if(vsp == vsx && vt_switch_pending == current_video_screen + 1)
			vt_switch_pending = 0;
	}
	if (ostatus & VT_WAIT_REL)
	{
		int new_screen = vt_switch_pending - 1;
		if (vsp == vsx && vt_switch_pending)
		{
			vt_switch_pending = 0;
			vgapage (new_screen);
		}
	}
}

/*---------------------------------------------------------------------------*
 *	Exported function; to be called when a vt is closed down.
 *
 *	Ideally, we would like to be able to recover from an X server crash;
 *	but in reality, if the server crashes hard while in control of the
 *	vga board, then you're not likely to be able to use pcvt ttys
 *	without rebooting.
 *---------------------------------------------------------------------------*/
void
reset_usl_modes (struct video_state *vsx)
{
	/* Clear graphics mode */
	if (vsx->vt_status & VT_GRAFX) {
	    vsx->vt_status &= ~VT_GRAFX;
	    if (vsp == vsx)
		switch_screen(current_video_screen, 1, 0);
	}

	/* Take kbd out of raw mode */
	if (pcvt_kbd_raw && vsp == vsx) {
#if PCVT_SCANSET > 1
		kbd_emulate_pc(0);
#endif /* PCVT_SCANSET > 1 */
		pcvt_kbd_raw = 0;
	}

	/* Clear process controlled mode */
	set_auto_mode (vsx);
}

/*
 * Fallback to VT_AUTO if controlling process died.
 */
static void
fallback_to_auto(struct video_state *vsx)
{
	struct proc *p;

	if(vsx->proc) {
		p = pfind(vsx->pid);
		if (p != NULL) {
			PROC_UNLOCK(p);
			if (vsx->proc != p)
				set_auto_mode(vsx);
		}
	}
}

/*---------------------------------------------------------------------------*
 *	switch to virtual screen n (0 ... PCVT_NSCREENS-1), VT_USL version
 *	(the name vgapage() stands for historical reasons)
 *---------------------------------------------------------------------------*/
int
vgapage(int new_screen)
{
	int x;

	if(new_screen < 0 || new_screen >= totalscreens)
		return EINVAL;

	/* fallback to VT_AUTO if controlling processes died */
	fallback_to_auto(vsp);
	fallback_to_auto(&vs[new_screen]);

	if (!vt_switch_pending && new_screen == current_video_screen)
		return 0;

	if(vt_switch_pending && vt_switch_pending != new_screen + 1) {
		/* Try resignaling uncooperative X-window servers */
		if (vsp->smode.mode == VT_PROCESS) {
			if (vsp->vt_status & VT_WAIT_REL) {
				if(vsp->smode.relsig) {
					PROC_LOCK(vsp->proc);
					psignal(vsp->proc, vsp->smode.relsig);
					PROC_UNLOCK(vsp->proc);
				}
			} else if (vsp->vt_status & VT_WAIT_ACK) {
				if(vsp->smode.acqsig) {
					PROC_LOCK(vsp->proc);
					psignal(vsp->proc, vsp->smode.acqsig);
					PROC_UNLOCK(vsp->proc);
				}
			}
		}
		return EAGAIN;
	}

	vt_switch_pending = new_screen + 1;

	if(vsp->smode.mode == VT_PROCESS)
	{
		/* we cannot switch immediately here */
		vsp->vt_status |= VT_WAIT_REL;
		if(vsp->smode.relsig) {
			PROC_LOCK(vsp->proc);
			psignal(vsp->proc, vsp->smode.relsig);
			PROC_UNLOCK(vsp->proc);
		}
	}
	else
	{
		struct video_state *old_vsp = vsp;

		switch_screen(new_screen,
			      vsp->vt_status & VT_GRAFX,
			      vs[new_screen].vt_status & VT_GRAFX);

		x = spltty();

		if(old_vsp->vt_status & VT_WAIT_ACT)
		{
			old_vsp->vt_status &= ~VT_WAIT_ACT;
			wakeup(&old_vsp->smode);
		}

		if(vsp->vt_status & VT_WAIT_ACT)
		{
			vsp->vt_status &= ~VT_WAIT_ACT;
			wakeup(&vsp->smode);
		}

		splx(x);

		if(vsp->smode.mode == VT_PROCESS)
		{
			/* if _new_ vt is under process control... */
			vsp->vt_status |= VT_WAIT_ACK;
			if(vsp->smode.acqsig) {
				PROC_LOCK(vsp->proc);
				psignal(vsp->proc, vsp->smode.acqsig);
				PROC_UNLOCK(vsp->proc);
			}
		}
		else
		{
			/* we are committed */
			vt_switch_pending = 0;

			/*
			 * XXX: If pcvt is acting as the systems console,
			 * avoid panics going to the debugger while we are in
			 * process mode.
			 */
			if(pcvt_is_console)
				cnavailable(pcvt_consptr, TRUE);
		}
	}
	return 0;
}

/*---------------------------------------------------------------------------*
 *	ioctl handling for VT_USL mode
 *---------------------------------------------------------------------------*/
int
usl_vt_ioctl(struct cdev *dev, int cmd, caddr_t data, int flag, struct thread *td)
{
	struct proc *p;
	int i, j, error, opri;
	struct vt_mode newmode;

	switch(cmd)
	{

	case VT_SETMODE:
		newmode = *(struct vt_mode *)data;
		p = td->td_proc;

		opri = spltty();

		if (newmode.mode != VT_PROCESS) {
			struct video_state *vsx = &vs[minor(dev)];
			if (vsx->smode.mode == VT_PROCESS) {
				if (vsx->proc != p) {
					splx(opri);
					return EPERM;
				}
				set_auto_mode(vsx);
			}
			splx(opri);
			return 0;
		}

		/*
		 * NB: XFree86-3.1.1 does the following:
		 *		VT_ACTIVATE (vtnum)
		 *		VT_WAITACTIVE (vtnum)
		 *		VT_SETMODE (VT_PROCESS)
		 * So it is possible that the screen was switched
		 * between the WAITACTIVE and the SETMODE (here).  This
		 * can actually happen quite frequently, and it was
		 * leading to dire consequences. Now it is detected by
		 * requiring that minor(dev) match current_video_screen.
		 * An alternative would be to operate on vs[minor(dev)]
		 * instead of *vsp, but that would leave the server
		 * confused, because it would believe that its vt was
		 * currently activated.
		 */
		if (minor(dev) != current_video_screen) {
			splx(opri);
			return EPERM;
		}

		/* Check for server died */
		fallback_to_auto(vsp);

		/* Check for server already running */
		if (vsp->smode.mode == VT_PROCESS && vsp->proc != p)
		{
			splx(opri);
			return EBUSY; /* already in use on this VT */
		}

		if (!ISSIGVALID(newmode.relsig) || !ISSIGVALID(newmode.acqsig)
		    || !ISSIGVALID(newmode.frsig))
		{
			splx(opri);
			return EINVAL;
		}

		vsp->smode = newmode;
		vsp->proc = p;
		vsp->pid = p->p_pid;

		/*
		 * XXX: If pcvt is acting as the systems console,
		 * avoid panics going to the debugger while we are in
		 * process mode.
		 */
		if(pcvt_is_console)
			cnavailable(pcvt_consptr, (newmode.mode != VT_PROCESS));

		splx(opri);
		return 0;

	case VT_GETMODE:
		*(struct vt_mode *)data = vsp->smode;
		return 0;

	case VT_RELDISP:
		p = td->td_proc;

		if (minor(dev) != current_video_screen)
			return EPERM;
		if (vsp->smode.mode != VT_PROCESS)
			return EINVAL;
		if (vsp->proc != p)
			return EPERM;
		switch(*(int *)data) {
		case VT_FALSE:
			/* process refuses to release screen; abort */
			if(vt_switch_pending
			   && (vsp->vt_status & VT_WAIT_REL)) {
				vsp->vt_status &= ~VT_WAIT_REL;
				vt_switch_pending = 0;
				return 0;
			}
			break;

		case VT_TRUE:
			/* process releases its VT */
			if(vt_switch_pending
			   && (vsp->vt_status & VT_WAIT_REL)) {
				int new_screen = vt_switch_pending - 1;
				struct video_state *old_vsp = vsp;

				vsp->vt_status &= ~VT_WAIT_REL;

				switch_screen(new_screen,
					      vsp->vt_status & VT_GRAFX,
					      vs[new_screen].vt_status
					      & VT_GRAFX);

				opri = spltty();
				if(old_vsp->vt_status & VT_WAIT_ACT)
				{
					old_vsp->vt_status &= ~VT_WAIT_ACT;
					wakeup(&old_vsp->smode);
				}
				if(vsp->vt_status & VT_WAIT_ACT)
				{
					vsp->vt_status &= ~VT_WAIT_ACT;
					wakeup(&vsp->smode);
				}
				splx(opri);

				if(vsp->smode.mode == VT_PROCESS) {
					/*
					 * if the new vt is also in process
					 * mode, we have to wait until its
					 * controlling process acknowledged
					 * the switch
					 */
					vsp->vt_status
						|= VT_WAIT_ACK;
					if(vsp->smode.acqsig) {
						PROC_LOCK(vsp->proc);
						psignal(vsp->proc,
							vsp->smode.acqsig);
						PROC_UNLOCK(vsp->proc);
					}
				}
				else
				{
					/* we are committed */
					vt_switch_pending = 0;

					/* XXX */
					if(pcvt_is_console)
						cnavailable(pcvt_consptr, TRUE);
				}
				return 0;
			}
			break;

		case VT_ACKACQ:
			/* new vts controlling process acknowledged */
			if(vsp->vt_status & VT_WAIT_ACK) {
				vt_switch_pending = 0;
				vsp->vt_status &= ~VT_WAIT_ACK;

				/* XXX */
				if(pcvt_is_console)
					cnavailable(pcvt_consptr, FALSE);

				return 0;
			}
			break;
		}
		return EINVAL;	/* end case VT_RELDISP */


	case VT_OPENQRY:
		/* return free vt */
		for(i = 0; i < PCVT_NSCREENS; i++)
			if(!vs[i].openf) {
				*(int *)data = i + 1;
				return 0;
			}
		return EAGAIN;

	case VT_GETACTIVE:
		*(int *)data = current_video_screen + 1;
		return 0;

	case VT_ACTIVATE:
		return vgapage(*(int *)data - 1);

	case VT_WAITACTIVE:
		/* sleep until vt switch happened */
		i = *(int *)data - 1;

		if(i != -1
		   && (i < 0 || i >= PCVT_NSCREENS))
			return EINVAL;

		if(i != -1 && current_video_screen == i)
			return 0;

		if(i == -1)
			i = minor(dev);

		{
			int x = spltty();
			error = 0;
			while (current_video_screen != i &&
			       (error == 0 || error == ERESTART))
			{
				vs[i].vt_status |= VT_WAIT_ACT;
				error = tsleep(&vs[i].smode,
					       PZERO | PCATCH, "waitvt", 0);
			}
			splx(x);
		}
		return error;

	case KDENABIO:
		/* grant the process IO access; only allowed if euid == 0 */
		/* and insecure */
	{
		struct trapframe *fp = td->td_frame;

		error = suser(td);
		if (error != 0)
			return (error);
		error = securelevel_gt(td->td_ucred, 0);
		if (error != 0)
			return (error);

		fp->tf_eflags |= PSL_IOPL;

		return 0;
	}

	case KDDISABIO:
		/* abandon IO access permission */
	{
		struct trapframe *fp = td->td_frame;
		fp->tf_eflags &= ~PSL_IOPL;
		return 0;
	}

	case KDSETMODE:
	{
		struct video_state *vsx = &vs[minor(dev)];
		int haschanged = 0;

		if(adaptor_type != VGA_ADAPTOR
		   && adaptor_type != MDA_ADAPTOR)
			/* X will only run on those adaptors */
			return (EINVAL);

		/* set text/graphics mode of current vt */
		switch(*(int *)data)
		{
		case KD_TEXT:
			haschanged = (vsx->vt_status & VT_GRAFX) != 0;
			vsx->vt_status &= ~VT_GRAFX;
			if(haschanged && vsx == vsp)
				switch_screen(current_video_screen, 1, 0);
			return 0;

		case KD_GRAPHICS:
			/* xxx It might be a good idea to require that
			   the vt be in process controlled mode here,
			   and that the calling process is the owner */
			haschanged = (vsx->vt_status & VT_GRAFX) == 0;
			vsx->vt_status |= VT_GRAFX;
			if(haschanged && vsx == vsp)
				switch_screen(current_video_screen, 0, 1);
			return 0;

		}
		return EINVAL;	/* end case KDSETMODE */
	}

	case KDSETRAD:
		/* set keyboard repeat and delay */
		return kbdioctl(dev, KBDSTPMAT, data, flag);

	case KDSKBMODE:
		switch(*(int *)data)
		{
		case K_RAW:

#if PCVT_SCANSET > 1
			/* put keyboard to return ancient PC scan codes */
			kbd_emulate_pc(1);
#endif /* PCVT_SCANSET > 1 */

			pcvt_kbd_raw = 1;
			shift_down = meta_down = altgr_down = ctrl_down = 0;
			return 0;

		case K_XLATE:

#if PCVT_SCANSET > 1
			kbd_emulate_pc(0);
#endif /* PCVT_SCANSET > 1 */

			pcvt_kbd_raw = 0;
			return 0;
		}
		return EINVAL;	/* end KDSKBMODE */

	case KDMKTONE:
		/* ring the speaker */
		if(data)
		{
			int duration = *(int *)data >> 16;
			int pitch = *(int *)data & 0xffff;

			sysbeep(pitch, duration * hz / 3000);
		}
		else
		{
 			sysbeep(PCVT_SYSBEEPF / 1493, hz / 4);
 		}
		return 0;

	case KDSETLED:
		/* set kbd LED status */
		/* unfortunately, the LED definitions between pcvt and */
		/* USL differ some way :-( */
		i = *(int *)data;
		j = (i & LED_CAP? KBD_CAPSLOCK: 0)
			+ (i & LED_NUM? KBD_NUMLOCK: 0)
			+ (i & LED_SCR? KBD_SCROLLLOCK: 0);
		return kbdioctl(dev, KBDSLOCK, (caddr_t)&j, flag);

	case KDGETLED:
		/* get kbd LED status */
		if((error = kbdioctl(dev, KBDGLOCK, (caddr_t)&j, flag)))
			return error;
		i = (j & KBD_CAPSLOCK? LED_CAP: 0)
			+ (j & KBD_NUMLOCK? LED_NUM: 0)
			+ (j & KBD_SCROLLLOCK? LED_SCR: 0);
		*(int *)data = i;
		return 0;

	case GIO_KEYMAP:
		get_usl_keymap((keymap_t *)data);
		return 0;
	}			/* end case cmd */

	return -1;		/* inappropriate usl_vt_compat ioctl */
}
#endif /* XSERVER */

/* ------------------------- E O F ------------------------------------------*/
