/* VGAlib version 1.2 - (c) 1993 Tommy Frandsen 		   */
/*								   */
/* This library is free software; you can redistribute it and/or   */
/* modify it without any restrictions. This library is distributed */
/* in the hope that it will be useful, but without any warranty.   */

/* ATI Mach32 driver (C) 1994 Michael Weller			   */
/* eowmob@exp-math.uni-essen.de mat42b@vm.hrz.uni-essen.de	   */
/* eowmob@pollux.exp-math.uni-essen.de mat42b@de0hrz1a.bitnet      */
/* Works only if Mach32 onboard VGA is enabled.			   */
/* Reads in eeprom. 						   */
/* There is a dirty hack in here to raise the linewidth for        */
/* 800x600 to 832 to keep my mem happy.. (and even though it's a   */
/* VRAM card... probably has to do something with mempages....     */
/* I change it by tweaking the info array.. so watch out.          */
/* The Number of additional pixels to append is set below, it has  */
/* to be a multiple of 8. This seems only to be needed for 16/24bpp*/

#define PIXTWEAK 32

#define DAC_SAFETY /*reminder for people with DAC!=2 */

/*Pure experimental and probably specific to my card (VRAM 68800-3)*/
#define MAXCLK16 2000 /*This effectly switches this off.. seems not
			  to be needed with PIXTWEAK>32 */
#define MAXCLK24 49

/*Stack preallocate:*/
#define PREALLOC 16

/*#define DEBUG*/
/*#define DEBUG_KEY*/ /*Wait for keypress at some locations*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/mman.h>

#include "mach32.h"
#include "vga.h"
#include "libvga.h"
#include "driver.h"

/*Internal mixup commands:*/
#define CMD_MSK 0xf000
#define CMD_ADD 0x0000
#define CMD_DEL 0x1000
#define CMD_MOD 0x2000
#define CMD_CPY 0x3000
#define CMD_MOV 0x4000

#define ATIPORT		0x1ce
#define ATIOFF		0x80
#define ATISEL(reg)	(ATIOFF+reg)
/* Ports we use: */
#define SCRATCH_PAD_0	0x52EE
#define DESTX_DIASTP    0x8EE8
#define R_SRC_X		0xDAEE
#define GE_STAT		0x9AE8
#define CONF_STAT1	0x12EE
#define CONF_STAT2	0x16EE
#define MISC_OPTIONS	0x36EE
#define MEM_CFG		0x5EEE
#define MEM_BNDRY	0x42EE
#define LOCAL_CNTL	0x32EE
#define CHIP_ID		0xFAEE
#define EXT_GE_CONF	0x7AEE
#define R_EXT_GE_CONF	0x8EEE
#define DISP_CNTL	0x22E8
#define CLOCK_SEL	0x4AEE
#define GE_PITCH	0x76EE
#define GE_OFFSET_HI	0x72EE
#define GE_OFFSET_LO	0x6EEE
#define CRT_PITCH	0x26EE
#define CRT_OFFSET_HI	0x2EEE
#define CRT_OFFSET_LO	0x2AEE
#define H_DISP		0x06E8
#define H_TOTAL		0x02E8
#define H_SYNC_WID	0x0EE8
#define H_SYNC_STRT	0x0AE8
#define V_DISP		0x16E8
#define V_SYNC_STRT	0x1AE8
#define V_SYNC_WID	0x1EE8
#define V_TOTAL		0x12E8
#define SHADOW_SET	0x5AEE
#define SHADOW_CTL	0x46EE
#define MISC_CTL	0x7EEE
#define R_MISC_CTL	0x92EE

#define DAC_W_INDEX	0x02EC
#define DAC_DATA	0x02ED
#define DAC_MASK	0x02EA
#define DAC_R_INDEX	0x02EB

#define DAC0		0x02EC
#define DAC1		0x02ED
#define DAC2		0x02EA
#define DAC3		0x02EB

/* Bit masks: */
#define GE_BUSY		0x0200
#define ONLY_8514	1
#define LOCAL_BUS_CONF2	(1<<6)
#define BUS_TYPE	0x000E
#define PCI		0x000E
#define Z4GBYTE		(1<<13)

/* ATI-EXT regs to save (ensure lock regs are set latest): */
static unsigned char mach32_eregs[]={
	0x00,0x01,0x02,0x03,0x04,0x05,0x06,
	0x20,0x23,0x24,0x25,0x26,0x2d,0x2e,
	0x30,0x31,0x32,0x33,0x35,0x36,0x37,
	0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
	0x2b,0x34,0x38};
/* Mach32 regs to save.. read, write address alternating: */
static unsigned short mach32_acc_regs[]={
	0xB2EE, 0x06E8,		/* H_DISP(ALT H_TOTAL) */
	0xC2EE,	0x12E8,		/* V_TOTAL */
	0xC6EE, 0x16E8,		/* V_DISP */
	0xCAEE, 0x1AE8,		/* V_SYNC_STRT */
	0xD2EE, 0x1EE8,		/* V_SYNC_WID */
	0x4AEE, 0x4AEE,		/* CLOCK_SEL */
	0x96EE, 0x96EE,		/* BRES_COUNT */
	0x86E8, 0x86E8,		/* CUR_X */
	0x82E8, 0x82E8,		/* CUR_Y */
	0x22EE,	0x22EE,		/* DAC_CONT(PCI) */
	0xF2EE,	0xF2EE,		/* DEST_COLOR_CMP_MASK */
	0x92E8,	0x92E8,		/* ERR_TERM */
	0xA2EE,	0xA2EE,		/* LINEDRAW_OPT */
	0x32EE,	0x32EE,		/* LOCAL_CNTL */
	0x6AEE,	0x6AEE,		/* MAX_WAITSTATES / MISC_CONT(PCI) */
	0x36EE,	0x36EE,		/* MISC_OPTIONS */
	0x82EE,	0x82EE,		/* PATT_DATA_INDEX */
	0x8EEE,	0x7AEE,		/* EXT_GE_CONFIG */
	0xB6EE, 0x0AE8,		/* H_SYNC_STRT */
	0xBAEE, 0x0EE8,		/* H_SYNC_WID */
	0x92EE, 0x7EEE,		/* MISC_CNTL */
	0xDAEE, 0x8EE8,		/* SRC_X */
	0xDEEE, 0x8AE8,		/* SRC_Y */
	0x52EE, 0x52EE,		/* SCRATCH0 */
	0x56EE, 0x56EE,		/* SCRATCH1 */
	0x42EE, 0x42EE,		/* MEM_BNDRY */
	0x5EEE, 0x5EEE,		/* MEM_CFG */
	};

#define PAGE_UNKNOWN 0
#define PAGE_DIFF    1
#define PAGE_BOTH    2

#define DAC_MODE8    0
#define DAC_MODEMUX  1
#define DAC_MODE555  2
#define DAC_MODE565  3
#define DAC_MODERGB  4
#define DAC_SEMICLK  0x80

/*DAC_MODETABLES:   */
static const unsigned char mach32_dac1[4]={0x00,0x00,0xa2,0xc2};

static const unsigned char mach32_dac4[4]={0x00,0x00,0xa8,0xe8};

static const unsigned char mach32_dac5[5]={0x00,0x00,0x20,0x21,0x40};

typedef struct	{
		unsigned char vfifo16,vfifo24;
		unsigned char h_disp, h_total, h_sync_wid, h_sync_strt;
		unsigned short v_total, v_disp, v_sync_strt;
		unsigned char disp_cntl, v_sync_wid;
		unsigned short clock_sel, flags, mask, offset;
		} mode_entry;

/*sizeof mode_entry in shorts:*/
#define SOMOD_SH ((sizeof(mode_entry)+sizeof(short)-1)/sizeof(short))

/*I put the appropriate VFIFO values from my selfdefined modes in... raise them if you get
  strange screen flickering.. (but they have to be <=0xf !!!!)*/
static const mode_entry predef_modes[]={
		{0x0, 0x0, 0x4f, 0x63, 0x2c, 0x52, 0x418, 0x3bf, 0x3d6, 0x23, 0x22, 0x50, 0, 0x0000, 0},
		{0x0, 0x0, 0x4f, 0x69, 0x25, 0x52, 0x40b, 0x3bf, 0x3d0, 0x23, 0x23, 0x24, 0, 0x0001, 7},
		{0x9, 0xe, 0x63, 0x84, 0x10, 0x6e, 0x580, 0x4ab, 0x4c2, 0x33, 0x2c, 0x7c, 0, 0x003f, 8},
		{0x9, 0xe, 0x63, 0x84, 0x10, 0x6d, 0x580, 0x4ab, 0x4c2, 0x33, 0x0c, 0x0c, 0, 0x003d, 8},
		{0x9, 0xe, 0x63, 0x7f, 0x09, 0x66, 0x4e0, 0x4ab, 0x4b0, 0x23, 0x02, 0x0c, 0, 0x003c, 8},
		{0x9, 0xe, 0x63, 0x83, 0x10, 0x68, 0x4e3, 0x4ab, 0x4b3, 0x23, 0x04, 0x30, 0, 0x0038, 8},
		{0x9, 0xe, 0x63, 0x7d, 0x12, 0x64, 0x4f3, 0x4ab, 0x4c0, 0x23, 0x2c, 0x1c, 0, 0x0030, 8},
		{0x9, 0xe, 0x63, 0x82, 0x0f, 0x6a, 0x531, 0x4ab, 0x4f8, 0x23, 0x06, 0x10, 0, 0x0020, 8},
		{0xd, 0xe, 0x7f, 0x9d, 0x16, 0x81, 0x668, 0x5ff, 0x600, 0x33, 0x08, 0x1c, 0, 0x0001, 9},
		{0xd, 0xe, 0x7f, 0xa7, 0x31, 0x82, 0x649, 0x5ff, 0x602, 0x23, 0x26, 0x3c, 0, 0x0003, 9},
		{0xd, 0xe, 0x7f, 0xad, 0x16, 0x85, 0x65b, 0x5ff, 0x60b, 0x23, 0x04, 0x38, 0, 0x0013, 9},
		{0xd, 0xe, 0x7f, 0xa5, 0x31, 0x83, 0x649, 0x5ff, 0x602, 0x23, 0x26, 0x38, 0, 0x0017, 9},
		{0xd, 0xe, 0x7f, 0xa0, 0x31, 0x82, 0x649, 0x5ff, 0x602, 0x23, 0x26, 0x38, 0, 0x001f, 9},
		{0xe, 0xe, 0x9f, 0xc7, 0x0a, 0xa9, 0x8f8, 0x7ff, 0x861, 0x33, 0x0a, 0x2c, 0, 0x0001, 10},
		{0xe, 0xe, 0x9f, 0xc7, 0x0a, 0xa9, 0x838, 0x7ff, 0x811, 0x33, 0x0a, 0x2c, 0, 0x0003, 10},
		};

#define NUM_MODES (sizeof(predef_modes)/sizeof(mode_entry))
#ifndef MAX_MODES
#define MAX_MODES 54 /* 10+4+15+3+1+5+16*/
#endif
#define STDVGA_MODES 10 

static const mode_entry *mach32_modes[MAX_MODES];

static int mach32_clocks[32]={	/* At least these should be ok for all Mach32 (out of my docs) */
	  0,   0,  92,  36,  50,  56,   0,  45,
	  0,  32, 110,  80,  40,  45,  75,  65, 
	  0,   0,  46,  18,  25,  22,   0,  22,
	  0,  16,  55,  40,  20,  22,  37,  32};

static unsigned char mach32_mmask[2][8]={
	{/* General supported modes..*/ 3,3,7,1,7,7,1,1},
	{/* Modes above 80Mhz..      */ 0,0,1,0,0,7,0,0}};

static float mach32_monitor[6]={31.5, 35.1, 35.5, 37.9, 48.3, 56.0};

static int mach32_memory,mach32_dac,mach32_pagemode=PAGE_UNKNOWN,mach32_dacmode=DAC_MODE8;
static int mach32_8bitmode=0,mach32_chiptype,mach32_maxclk16=MAXCLK16,mach32_maxclk24=MAXCLK24;
static int mixup_alloc=0,mixup_ptr=0;
static unsigned short *mach32_eeprom, mach32_disp_shadow=0,mach32_strictness=0,*mach32_modemixup;
static char *eeprom_fname=NULL;
static volatile char *mach32_aperture=NULL;
static volatile int dummy;

static void mach32_blankadj(int adj);
static void mach32_modfill(const mode_entry * mode, int modemask, int forcein);
static int mach32_test(void);
static int mach32_init(int force, int chiptype, int memory);
static void mach32_i_bltwait(void);
static int mach32_bltwait(void);
static void mach32_wait(void);
static void mach32_experm(void);
static int mach32_eeclock(register int ati33);
static void mach32_eekeyout(register int ati33, register int offset, register int mask);
static int mach32_eeget(int offset);
static int mach32_saveregs(unsigned char regs[]);
static int mach32_setregs(unsigned char regs[]);
static int mach32_unlock(void);
static int mach32_lock(void);
static int mach32_sav_dac(int offset,unsigned char regs[]);
static int mach32_set_dac(int dac_mode, int clock_intended,int xres);
static int mach32_setpage(int page);
static int mach32_setrdpage(int page);
static int mach32_setwrpage(int page);
static void mach32_setappage(int page);
static int mach32_setdisplaystart(int address);
static int mach32_setlogicalwidth(int width);
static int mach32_modeavailable(int mode);
static int mach32_getmodeinfo(int mode,vga_modeinfo *modeinfo);
static int mach32_setmode(int mode, int previous);
static void mach32_readconfig(void);
static void mach32_final_modefixup(void);

static int nothing() { return 0; }


int (*mach32_chipsetfunctions[])() = {
	mach32_saveregs,/* saveregs */
	mach32_setregs,/* setregs */
	mach32_unlock,	/* unlock */
	mach32_lock,	/* lock */
	mach32_test,
	mach32_init,
	mach32_setpage,	/* setpage */
	mach32_setrdpage,    /* setrdpage */
	mach32_setwrpage,	/* setwrpage */
	mach32_setmode,	/* setmode */
	mach32_modeavailable, /* modeavailable */
	mach32_setdisplaystart, /* setdisplaystart */
	mach32_setlogicalwidth, /* setlogicalwidth */
	mach32_getmodeinfo, /* getmodeinfo */
	nothing, /* bitblt */
	nothing, /* imageblt */
	nothing, /* fillblt */
	nothing, /* hlinelistblt */
	mach32_bltwait, /* bltwait */
};

static int mach32_setdisplaystart(int address)
{
unsigned short tmp;

#ifdef DEBUG
printf("mach32_setdisplaystart(%x)\n",address);
#endif
tmp=address>>2;
outw(CRT_OFFSET_LO,tmp);
outw(GE_OFFSET_LO,tmp);
tmp=0xff&(address>>18);
outw(CRT_OFFSET_HI,tmp);
outw(GE_OFFSET_HI,tmp);

return vga_chipsetfunctions[CHIPSET_SETDISPLAYSTART](address);
}

static int mach32_setlogicalwidth(int width)
{
register int mywidth;
#ifdef DEBUG
printf("mach32_setlogicalwidth(%d)\n",width);
#endif
/*Unfortunately the Mach32 expect this value in Pixels not bytes:*/
switch(mach32_dacmode&0x7f)
	{
	case DAC_MODE555:
	case DAC_MODE565:
		mywidth=width>>1;
		break;
	case DAC_MODERGB:
		mywidth=width/3;
		break;
	default:
		mywidth=width;
		break;
	}
mywidth=(mywidth>>3)&0xff;
#ifdef DEBUG
printf("mach32_setlogicalwidth: Mach32 width to %d pels.\n",mywidth*8);
#endif
outw(CRT_PITCH,mywidth);
outw(GE_PITCH,mywidth);

return vga_chipsetfunctions[CHIPSET_SETLOGICALWIDTH](width);
}


static int mach32_setpage(int page)
{
register unsigned short tmp;

#ifdef DEBUG
printf("mach32_setpage(%d)\n",page);
#endif
if(mach32_pagemode!=PAGE_BOTH)
	{
	outb(ATIPORT,ATISEL(0x3E));
	tmp=inb(ATIPORT+1)&0xf7;
	tmp=(tmp<<8)|ATISEL(0x3E);
	outw(ATIPORT,tmp);
	mach32_pagemode=PAGE_BOTH;
	}
tmp=(page<<9)&0x1e00;
outw(ATIPORT,ATISEL(0x32)|tmp);
outb(ATIPORT,ATISEL(0x2e));
tmp=(inb(ATIPORT+1)&0xfc)|((page>>4)&3);
outw(ATIPORT,ATISEL(0x2e)|(tmp<<8));
return 0;
}

static int mach32_setwrpage(int page)
{
register unsigned short tmp;
if(mach32_pagemode!=PAGE_DIFF)
	{
	outb(ATIPORT,ATISEL(0x3E));
	outw(ATIPORT,(ATISEL(0x3E)<<8)|(inb(ATIPORT+1)|8));
	mach32_pagemode=PAGE_DIFF;
	}
outb(ATIPORT,ATISEL(0x32));
tmp=inb(ATIPORT+1)&0xe1;
outw(ATIPORT,(ATISEL(0x32)<<8)|((page<<1)&0x1e));
outb(ATIPORT,ATISEL(0x2e));
tmp=inb(ATIPORT+1)&0xfc;
outw(ATIPORT,(ATISEL(0x2e)<<8)|tmp|((page>>4)&3));
return 0;
}

static int mach32_setrdpage(int page)
{
register unsigned short tmp;
if(mach32_pagemode!=PAGE_DIFF)
	{
	outb(ATIPORT,ATISEL(0x3E));
	outw(ATIPORT,(ATISEL(0x3E)<<8)|(inb(ATIPORT+1)|8));
	mach32_pagemode=PAGE_DIFF;
	}
outb(ATIPORT,ATISEL(0x32));
tmp=inb(ATIPORT+1)&0x1e;
if(page&8)
	tmp|=1;
outw(ATIPORT,(ATISEL(0x32)<<8)|tmp|((page<<5)&0xe0));
outb(ATIPORT,ATISEL(0x2e));
tmp=inb(ATIPORT+1)&0xf3;
outw(ATIPORT,(ATISEL(0x2e)<<8)|tmp|((page>>2)&0xc));
return 0;
}

static void mach32_setappage(int page)
{
outw(MEM_CFG,(inw(MEM_CFG)&0xfff3)|(0xc&(page<<2)));
}

static int mach32_sav_dac(int offset,unsigned char regs[])
{
/* I was unable to read out the actual DAC_config, so
we just save which mode we are in: */

regs[offset]=mach32_dacmode;
#ifdef DEBUG
printf("mach32_sav_dac(%d,...): Dac:%d, dac_mode:%d\n",offset,mach32_dac,
		regs[offset]);
#endif
return offset+1;
}

static inline void clean_clocks(void)
{
outw(CLOCK_SEL,(inw(CLOCK_SEL)&~0x7f)|0x11);
}

static int mach32_set_dac(int dac_mode, int clock_intended,int xres)
{
unsigned short act_ge_conf;
const unsigned char *dac_reg;


act_ge_conf=inw(R_EXT_GE_CONF)&0x8fff;

#ifdef DEBUG
printf("mach32_set_dac(%d,%d): Dac:%d\n",dac_mode, clock_intended,mach32_dac);
#endif
mach32_dacmode=dac_mode;
dac_mode&=0x7f;
mach32_blankadj((mach32_dac==MACH32_ATI68830)?0x4:0xc);
switch(mach32_dac)
	{
	case MACH32_SC11483:
		dac_reg=mach32_dac1;
		goto dac1_4;
	case MACH32_ATI6871:
		/*This one is a nightmare: */
		clean_clocks();
		outw(EXT_GE_CONF,0x201a);

		switch(dac_mode)
			{

			default:
			case DAC_MODE8:
			case DAC_MODEMUX:
				outb(DAC1,0x00);
				outb(DAC2,0x30);
				outb(DAC3,0x2d);
#ifdef DEBUG
				puts("DAC2: 0x00 0x30 0x2d (8bpp)");
#endif
				if(dac_mode!=DAC_MODEMUX)
					break;
				outb(DAC2,0x09);
				outb(DAC3,0x1d);
				outb(DAC1,0x01);
				mach32_blankadj(1);
#ifdef DEBUG
				puts("DAC2: 0x01 0x09 0x1d (8bpp MUX)");
#endif
				break;
			case DAC_MODE555:
			case DAC_MODE565:
			case DAC_MODERGB:
				mach32_blankadj(1);
				outb(DAC1,0x01);
				if((!(clock_intended&0xc0))&&(!(mach32_dacmode&DAC_SEMICLK)))
					{
					outb(DAC2,0x00);
#ifdef DEBUG
				puts("DAC2: 0x01 0x00 0x0d (16/24bpp)");
#endif
					}
				else	{
					clock_intended&=0xff3f;
					outb(DAC2,0x08);
					mach32_dacmode|=DAC_SEMICLK;
					/*if(xres==640)*/
						mach32_blankadj(2);
#ifdef DEBUG
				puts("DAC2: 0x01 0x08 0x0d (16/24bpp)");
#endif
					}
				outb(DAC3,0x0d);
				act_ge_conf|=0x4000;
				break;
			}
		break;
	case MACH32_BT481:
		dac_reg=mach32_dac4;
		dac1_4:
		if(dac_mode<=DAC_MODE565)
			{
			clean_clocks();
			outw(EXT_GE_CONF,0x101a);
			outb(DAC2,dac_reg[dac_mode]);
			}
		break;
	case MACH32_ATI68860:
		clean_clocks();
		outw(EXT_GE_CONF,0x301a);
		outb(DAC0,mach32_dac5[dac_mode]);
		break;
	}
if(dac_mode<DAC_MODE555)
	{
	/* 8bpp mode */
	if(mach32_8bitmode)
		act_ge_conf|=0x4000;
	}
/*Dac RS2,3 to 0 */
act_ge_conf&=0xcfff;
outw(EXT_GE_CONF,act_ge_conf);
/*Set appropriate DAC_MASK:*/
switch(dac_mode)
	{
	case DAC_MODE8:
	case DAC_MODEMUX:
		outb(DAC_MASK,0xff);
		break;
	default:
		outb(DAC_MASK,0x00);
		break;
	}
return clock_intended;
}

static int mach32_saveregs(unsigned char regs[])
{
int i,retval;
unsigned short tmp;

for(i=0;i<sizeof(mach32_eregs);i++)
	{
	outb(ATIPORT,ATISEL(mach32_eregs[i]));
	regs[EXT+i]=inb(ATIPORT+1);
	}
regs[EXT+i]=mach32_disp_shadow; /* This is for DISP_CNTL */
retval=i+EXT+1;
for(i=0;i < ( sizeof(mach32_acc_regs) / sizeof(unsigned short) )  ; i+=2 )
	{
	tmp=inw(mach32_acc_regs[i]);
	regs[retval++]=tmp;
	regs[retval++]=(tmp>>8);
	}
retval=mach32_sav_dac(retval,regs);
#ifdef DEBUG
printf("mach32_saveregs: retval=%d\n",retval);
#endif
return retval-EXT;
}

static int mach32_setregs(unsigned char regs[])
{
int i,offset,retval,clock_intended=0;
unsigned short tmp;

offset=EXT+sizeof(mach32_eregs)+( sizeof(mach32_acc_regs) / sizeof(unsigned short) )+1;

/*Find out which clock we want to use:*/
for(i = ( sizeof(mach32_acc_regs) / sizeof(unsigned short) ) - 1, retval=offset; i>=0 ;
    i-=2, retval-=2 )
	if(mach32_acc_regs[i]==CLOCK_SEL)
		{
		clock_intended=((unsigned short)regs[--retval])<<8;
		clock_intended|=regs[--retval];
		break;
		}

#ifdef DEBUG
printf("mach32_setregs: offset=%d, clock_intended=%d\n",offset,clock_intended);
#endif

retval=offset+1;
mach32_set_dac(regs[offset],clock_intended,0);/*We can savely call with a fake xres coz
						blank adjust will be restored afterwards
						anyways...*/

for(i = ( sizeof(mach32_acc_regs) / sizeof(unsigned short) ) - 1 ; i>=0  ; i-=2 )
	{
	tmp=((unsigned short)regs[--offset])<<8;
	tmp|=regs[--offset];
	outw(mach32_acc_regs[i],tmp);
	}
mach32_disp_shadow=regs[--offset]&~0x60;
outb(DISP_CNTL,mach32_disp_shadow|0x40); /* Mach32 CRT reset */
if(inw(CLOCK_SEL)&1)	/* If in non VGA mode */
	outw(DISP_CNTL,mach32_disp_shadow|0x20); /* Mach32 CRT enabled */

for(i=sizeof(mach32_eregs)-1;i>=0;i--)
	{
	outb(ATIPORT,ATISEL(mach32_eregs[i]));
	outb(ATIPORT+1,regs[--offset]);
	}

return retval-EXT;
}

static int mach32_unlock(void)
{
unsigned short oldval;

#ifdef DEBUG
puts("mach32_unlock");
#endif /* DEBUG */
outb(ATIPORT,ATISEL(0x2b));
oldval=inb(ATIPORT+1)&~0x18;  /* Unlock DAC, Dbl Scan. */
outw(ATIPORT,ATISEL(0x2b)|(oldval<<8));

outb(ATIPORT,ATISEL(0x34));
oldval=inb(ATIPORT+1)&~0xfc; /* Unlock Crt9[0:4,7], Vtiming, Cursr start/end,
				CRT0-7,8[0-6],CRT14[0-4]. but disable ignore of CRT11[7] */
outw(ATIPORT,ATISEL(0x34)|(oldval<<8));

outb(ATIPORT,ATISEL(0x38)); /* Unlock ATTR00-0f, ATTR11, whole vga, port 3c2 */
oldval=inb(ATIPORT+1)&~0x0f;
outw(ATIPORT,ATISEL(0x38)|(oldval<<8));

/* Unlock vga-memory too:*/

outw(MEM_BNDRY,0);

/* Unlock Mach32 CRT Shadowregisters... this one made me crazy...
   Thanx to the Xfree sources I finally figured it out....*/
outw(SHADOW_SET, 1);
outw(SHADOW_CTL, 0);
outw(SHADOW_SET, 2);
outw(SHADOW_CTL, 0);
outw(SHADOW_SET, 0);

return 0;
}

static int mach32_lock(void)
{
unsigned short oldval;

#ifdef DEBUG
puts("mach32_lock");
#endif /* DEBUG */
/* I'm really not sure if calling this function would be a good idea */
/* Actually it is not called in svgalib */
outb(ATIPORT,ATISEL(0x2b));
oldval=inb(ATIPORT+1)|0x18;  /* Lock DAC, Dbl Scan. */
outw(ATIPORT,ATISEL(0x2b)|(oldval<<8));

outb(ATIPORT,ATISEL(0x34));
oldval=inb(ATIPORT+1)|0xfc; /* Lock Crt9[0:4,7], Vtiming, Cursr start/end,
				CRT0-7,8[0-6],CRT14[0-4]. but disable ignore of CRT11[7] */
outw(ATIPORT,ATISEL(0x34)|(oldval<<8));

outb(ATIPORT,ATISEL(0x38)); /* Lock ATTR00-0f, ATTR11, whole vga, port 3c2 */
oldval=inb(ATIPORT+1)|0x0f;
outw(ATIPORT,ATISEL(0x38)|(oldval<<8));

outw(SHADOW_SET, 1);
outw(SHADOW_CTL, 0x7f);
outw(SHADOW_SET, 2);
outw(SHADOW_CTL, 0x7f);
outw(SHADOW_SET, 0);

return 0;
}

static void mach32_experm(void)
{
printf("svgalib(mach32): Cannot get I/O permissions.\n");
exit(-1);
}

static int mach32_bltwait(void)
{
while(inw(GE_STAT) & (GE_BUSY | 1));
CRITICAL = 0; /* Obviously we're idle */
return 0;
}

static void mach32_i_bltwait()
{
int i;

for (i=0; i < 100000; i++)
	if(!(inw(GE_STAT) & (GE_BUSY | 1)))
		break;
}

static int mach32_test()
{
int result=0;
short tmp;

if(iopl(3)<0)
	mach32_experm();
/* Har, har.. now we can switch off interrupts to crash the system... ;-)=) */
/* (But actually we need only access to extended io-ports...) */

tmp = inw(SCRATCH_PAD_0);
outw(SCRATCH_PAD_0, 0x5555);
mach32_i_bltwait();
if(inw(SCRATCH_PAD_0) == 0x5555)
	{
	outw(SCRATCH_PAD_0, 0x2a2a);
	mach32_i_bltwait();
	if(inw(SCRATCH_PAD_0 == 0x2a2a))
		{
		/* Aha.. 8514/a detected.. */
		result=1;
		}
	}
outw(SCRATCH_PAD_0,tmp);
if(!result)
	goto quit;
/* Now ensure it is not a plain 8514/a: */
result=0;
outw(DESTX_DIASTP, 0xaaaa);
mach32_i_bltwait();
if(inw(R_SRC_X)==0x02aa)
	{
	outw(DESTX_DIASTP, 0x5555);
	mach32_i_bltwait();
	if(inw(R_SRC_X)==0x0555)
		result=1;
	}
if(!result)
	goto quit;
result=0;
/* Ok, now we have a Mach32. Unfortunately we need also the VGA to be enabled: */
if(inw(CONF_STAT1)&ONLY_8514)
	{
	if (__svgalib_driver_report)
		puts("Mach32 detected, but unusable with VGA disabled.\nSorry.\n");
	goto quit;	/*VGA circuitry disabled.*/
	}
result=1;
quit:
if(result)
	mach32_init(0,0,0);
else	iopl(0); /* safety */
#ifdef DEBUG
printf("mach32_test: returning %d.\n",result);
#endif /* DEBUG */
return result;
}

static void mach32_wait()
{
/* Wait for at least 22 us.. (got that out of a BIOS disassemble on my 486/50 ;-) ) ... */
register int i;
for(i=0;i<16;i++)
	dummy++;	/*Dummy is volatile..*/
}

static int mach32_eeclock(register int ati33)
{
outw(ATIPORT,ati33|=0x200);	/* clock on */
mach32_wait();
outw(ATIPORT,ati33&= ~0x200); /* clock off */
mach32_wait();
return ati33;
}

static void mach32_eekeyout(register int ati33, register int offset, register int mask)
{
do	{
	if(mask&offset)
		ati33|= 0x100;
	else	ati33&=~0x100;
	outw(ATIPORT,ati33);
	mach32_eeclock(ati33);
	}
while(mask>>=1);
}

static int mach32_eeget(int offset)
{
register int ati33;
register int result,i;

/* get current ATI33 */
outb(ATIPORT,ATISEL(0x33));
ati33=((int)inw(ATIPORT+1))<<8;
ati33|=ATISEL(0x33);
/* prepare offset.. cut and add header and trailer */
offset=(0x600|(offset&0x7f))<<1;

/* enable eeprom sequence */
ati33=mach32_eeclock(ati33);
/*input to zero..*/
outw(ATIPORT,ati33&=~0x100);
/*enable to one*/
outw(ATIPORT,ati33|= 0x400);
mach32_eeclock(ati33);
/*select to one*/
outw(ATIPORT,ati33|= 0x800);
mach32_eeclock(ati33);
mach32_eekeyout(ati33,offset,0x800);
for(i=0,result=0;i<16;i++)
	{
	result<<=1;
	outb(ATIPORT,ATISEL(0x37));
	if(inb(ATIPORT+1)&0x8)
		result|=1;
	mach32_eeclock(ati33);
	}
/*deselect...*/
outw(ATIPORT,ati33&=~0x800);
mach32_eeclock(ati33);
/*disable...*/
outw(ATIPORT,ati33&=~0x400);
mach32_eeclock(ati33);
return result;
}

static int mach32_max_mask(int x)
{
if(x<=0x63)
	return 0x7;
if(x<=0x7f)
	return 0x3;
return 1;
}

static int mach32_init(int force, int chiptype, int memory)
{
static int max2msk[]={1,3,7,7};
static const short mem_conf[]={512,1024,2048,4096};
static const short mem_aper[]={0,
                         MACH32_APERTURE,
                         MACH32_APERTURE|MACH32_APERTURE_4M,
                         0};
int i;
unsigned short *ptr,mem_cfg,cfg1,cfg2;
unsigned long apertloc;


if(iopl(3)<0)
	mach32_experm();

mach32_readconfig();
/* Ensure we are idle. (Probable dead lock if no 8514 installed) */
mach32_bltwait();

/*get main config registers*/
cfg1=inw(CONF_STAT1);
cfg2=inw(CONF_STAT2);
mem_cfg=inw(MEM_CFG);
mach32_dac=(cfg1>>9)&7;
mach32_chiptype=mem_aper[mem_cfg&3];
if(!force)
	mach32_memory=mem_conf[(inw(MISC_OPTIONS)>>2)&3];
else	{
	mach32_memory=memory;
	if(chiptype&MACH32_FORCEDAC)
		mach32_dac=chiptype&0xf;
	if(chiptype&MACH32_FORCE_APERTURE)
		{
		mach32_chiptype=(chiptype&(MACH32_APERTURE|MACH32_APERTURE_4M));
		if(mach32_chiptype&MACH32_APERTURE_4M)
			mach32_chiptype|=MACH32_APERTURE;
		}
	}
if(mach32_dac>5)
	{
	printf("mach32: Warning, unknown DAC type: %d. Assuming stupid 8bpp DAC rated at 80Mhz!\n",
		mach32_dac);
	/* Assumption is already done in all tables used... */
	}
#ifdef DAC_SAFETY
if(mach32_dac!=2)
	{
	char *cptr;
	/* Safety notification */
	cptr=getenv("SVGALIB_MACH32");
	if(cptr==NULL)
		{
		messexit:
		puts("\aWarning!! The mach32 driver of svgalib was never tried with a DAC type\n"
		       "!=2. Nevertheless, I put every knowledge I got from the DOCs in this\n"
		       "driver, so it should work. If you think you can stand this risk\n"
		       "(for example, your monitor will just switch off if he can't stand the\n"
		       "video signal) then set the environment variable SVGALIB_MACH32 to\n"
		       "ILLTRYIT or --- of course --- recompile svgalib after removing the\n"
		       "#define DAC_SAFETY in mach32.c\n\n"
		       "To reduce possibility of damage to your card you should check that no\n"
		       "video mode above 80Mhz dotclock is used with dacs !=2 or 5. To check\n"
		       "recompile with DEBUG defined in mach32.c and look at the output.\n"
		       "In any case, that is, if it works or not (and what went wrong),\n"
		       "please tell me about:\n"
		       "Michael Weller, eowmob@exp-math.uni-essen.de,\n"
		       "eowmob@pollux.exp-math.uni-essen.de, mat42b@vm.hrz.uni-essen.de, or\n"
		       "mat42b@de0hrz1a.bitnet.\n");
		exit(-1);
		}
	if(strcmp(cptr,"ILLTRYIT"))
		goto messexit;
	}
#endif
/* get 2 eeprom buffers */
if(mach32_eeprom==NULL)
	{
	unsigned short sum=0;	/* sum is !=0 if we succesfull read in an eeprom file.. */

	if((mach32_eeprom=malloc(sizeof(short)*2*128))==NULL)
		{
		printf("svgalib: mach32: Fatal error,\n"
			"not enough memory for EEPROM image (512 Bytes needed)\n");
		/* svgamode not yet set.. */
		exit(-1);
		}
	/* Do we have it already in a file ?*/
	if(eeprom_fname!=NULL)
		{
		FILE *fd;

		fd=fopen(eeprom_fname,"rb");
		if(fd==NULL)
			{
			readerr:
			printf("mach32: Warning, can't access EEPROM file >%s<\nError was %d - %s\n",
				eeprom_fname,errno,strerror(errno));
			goto read_eeprom;
			}
		if(1!=fread(mach32_eeprom,sizeof(short)*129,1,fd))
			{
			i=errno;
			fclose(fd);
			errno=i;
			goto readerr;
			}
		if(fclose(fd))
			goto readerr;
		/*Checksum..*/
		for(i=0,sum=0,ptr=mach32_eeprom;i<128;i++)
			sum+= *ptr++;
		if(sum == *ptr)
			{
			sum=1; /*Mark succesful read...*/
			goto skip_read;	/* Use the read in file... */
			}
		sum=0;	/*Mark unsuccesful read..*/
		printf("mach32: Warning, EEPROM file >%s< corrupted.\n",eeprom_fname);
		}		
	
	/* Read in eeprom */
	read_eeprom:
	for(i=0,ptr=mach32_eeprom;i<128;i++)
		*ptr++ = mach32_eeget(i);
	skip_read:
	if((eeprom_fname!=NULL)&&(!sum)) /* create file if not there or unsuccesful */
		{
		FILE *fd;

		/*Calc valid checksum..*/
		for(i=0,sum=0,ptr=mach32_eeprom;i<128;i++)
			sum+= *ptr++;

		fd=fopen(eeprom_fname,"wb");
		if(fd==NULL)
			{
			writerr:
			printf("mach32: Warning, can't create new EEPROM file >%s<\nError was %d - %s\n",
				eeprom_fname,errno,strerror(errno));
			goto finish_w_eeprom;
			}
		if(1!=fwrite(mach32_eeprom,sizeof(short)*128,1,fd))
			{
			fwri_err:
			i=errno;
			fclose(fd);
			errno=i;
			goto writerr;
			}
		if(1!=fwrite(&sum,sizeof(short),1,fd))
			goto fwri_err;
		if(fclose(fd))
			goto writerr;
		printf("mach32: Notice, new EEPROM file >%s< succesful created.\n",
			eeprom_fname);
		finish_w_eeprom:
		}
	#ifdef DEBUG
	printf("ATI-EEPROM contents:");
	for(i=0;i<128;i++)
		{
		if(i&7)
			putchar(' ');
		else	puts("");
		printf("%02x - %04x",i,mach32_eeprom[i]);
		}
	#ifdef DEBUG_KEY
	fputs("\n(Hit Return)",stdout);
	while(getchar()!='\n');
	#else
	putchar('\n');
	#endif /* DEBUG_KEY */
	#endif /* DEBUG */
	}
/*create a scratch copy.*/
memcpy(mach32_eeprom+128,mach32_eeprom,(size_t)(128*sizeof(short)));
/* Access aperture */
if((mach32_chiptype&MACH32_APERTURE)&&(mach32_aperture==NULL))
	{
	if( ((cfg1&BUS_TYPE)==PCI) || ((cfg2&Z4GBYTE)&& !(cfg2&LOCAL_BUS_CONF2)) )
		{
		/* 4GB address range... */
		apertloc=((unsigned long)(mem_cfg&0xfff0))<<16;
		}
	else	{
		/* 128MB address range... */
		apertloc=((unsigned long)(mem_cfg&0xff00))<<12;
		}
#ifdef DEBUG
	printf("mach32: Physical aperture starts at %08lx.\n", apertloc);
#endif
	mach32_aperture=(char *)mmap((caddr_t)0x40000000,
		(mach32_chiptype&MACH32_APERTURE_4M)?4194304:1048576,
		PROT_READ|PROT_WRITE,
		MAP_SHARED|MAP_FIXED,
		__svgalib_mem_fd,
		apertloc);
	/* I trust the comment about memory waste in vgamisc.c...*/
	if(((long)mach32_aperture)<0)
		{
		i=errno;
		printf("mach32: Mmaping of aperture failed.\nError was %d",i);
		errno=i;
		perror(" - ");
		exit(-1);
		}
#ifdef DEBUG
	printf("mach32: Aperture mapped to %08lx.\n", (long)mach32_aperture);
#endif
	}
chipsetfunctions=mach32_chipsetfunctions;

/*Populating database:*/
#ifdef DEBUG
		puts("Populating mode table:");
#endif
for(i=0;i<MAX_MODES;i++)
	mach32_modes[i]=NULL;
for(i=0;i<NUM_MODES;i++)
	{
	if((predef_modes[i].offset==0)||
	    (mach32_eeprom[predef_modes[i].offset]&predef_modes[i].mask))
		mach32_modfill(predef_modes+i,mach32_max_mask(predef_modes[i].h_disp),-1);
	}

if(mach32_eeprom[7]&2)
	{
	i=(mach32_eeprom[7]>>8)&0xff;
	if((mach32_eeprom[i]&0x140)==0x140)
		mach32_modfill((mode_entry *)(mach32_eeprom+i+2),
			max2msk[3&(mach32_eeprom[i]>>9)],-1);
	}
if(mach32_eeprom[8]&0x80)
	{
	i=(mach32_eeprom[8]>>8)&0xff;
	if((mach32_eeprom[i]&0x140)==0x140)
		mach32_modfill((mode_entry *)(mach32_eeprom+i+2),
			max2msk[3&(mach32_eeprom[i]>>9)],-1);
	}
if(mach32_eeprom[9]&0x80)
	{
	i=(mach32_eeprom[9]>>8)&0xff;
	if((mach32_eeprom[i]&0x140)==0x140)
		mach32_modfill((mode_entry *)(mach32_eeprom+i+2),
			max2msk[3&(mach32_eeprom[i]>>9)],-1);
	}
if(mach32_eeprom[10]&0x80)
	{
	i=(mach32_eeprom[10]>>8)&0xff;
	if((mach32_eeprom[i]&0x140)==0x140)
		mach32_modfill((mode_entry *)(mach32_eeprom+i+2),
			max2msk[3&(mach32_eeprom[i]>>9)],-1);
	}
#ifdef DEBUG
		puts("Squeeze in run-time config:");
#endif
	mach32_final_modefixup();
if(__svgalib_driver_report)
	printf("Using Mach32 driver (%s aperture, %dK memory, DAC-Type %d, %sforced).\n",
		(mach32_chiptype&MACH32_APERTURE)?((mach32_chiptype&MACH32_APERTURE_4M)?"4M":"1M"):"no",
		mach32_memory,mach32_dac,force?"":"not ");

return 0;
}

static inline int col2msk(struct info *iptr)
{
switch(iptr->colors)
	{
	case 1<<24:
		if(iptr->bytesperpixel==3)
			return 4;
		else	return 0;
	case 1<<15:
	case 1<<16:
		if(iptr->bytesperpixel==2)
			return 2;
		else	return 0;
	case 256:
		if(iptr->bytesperpixel==1)
			return 1;
		else	return 0;
	}
return 0;
}

static inline int col2bypp(struct info *iptr)
{
return iptr->bytesperpixel;
}

#ifdef DEBUG
static int mach32_log2(unsigned n)
{
int res= -1;

while(n){
	res++;
	n>>=1;
	}
return res;
}
#endif

static void mach32_modfill(const mode_entry * mode, int modemask, int forcein)
{
register int i;
register struct info *iptr;
register unsigned wid,hei;

float horz,vert,n_horz,n_vert;
int clock,n_clock,tmp;

clock=mach32_clocks[n_clock=0x1f&(mode->clock_sel>>2)];
horz=(1e3*clock)/(float)(mode->h_total*8+8);
hei=mode->v_total;
if(!(mode->disp_cntl&0x6))
	hei=((hei>>2)&0xfffe)|(hei&1);
else	hei=((hei>>1)&0xfffc)|(hei&3);
hei++;
vert=(horz*1000)/hei;

wid=mode->h_disp*8+8;
hei=mode->v_disp;
if(!(mode->disp_cntl&0x6))
	hei=((hei>>2)&0xfffe)|(hei&1);
else	hei=((hei>>1)&0xfffc)|(hei&3);
hei++;

/*Maskout impossible colorsettings for high clock rates:*/
modemask&=mach32_mmask[(clock>80)?1:0][mach32_dac];

/*Rate down clocks exceeding mem bandwidth*/
if(clock>mach32_maxclk16)
	modemask&=~2;
if(clock>mach32_maxclk24)
	modemask&=~4;
#if 0
/* ATI6871 needs clockdiv=1 for 16 and 24bpp */
if((n_clock>=16)&&(mach32_dac==MACH32_ATI6871))
	modemask&=1;
#endif

if(forcein>=0)
	{
	/*Well hack the next loop to run exactly one time for i=forcein..*/
	/*I know it's dirty...*/
	i=forcein;
	iptr=infotable+i;
	goto jumpin;	/*Show that to a Pascal coder and he falls down dead..*/
	}
for(i=0,iptr=infotable;(i<MAX_MODES)&&(forcein<0);i++,iptr++)
	{
	jumpin:
	if((iptr->xdim!=wid)||(iptr->ydim!=hei)|| !(modemask&col2msk(iptr)))
		continue;
#ifdef DEBUG
	printf("%4ux%4ux%2u: ",
		wid,hei,mach32_log2(iptr->colors));
	if(clock)
		printf("%3d MHz Clock, %6.3f KHz horz., %6.3f Hz vert.,\n\t %s, VFIFO(16-%d,24-%d)",
				clock,horz,vert,
				(mode->disp_cntl&0x10)?"Interlaced":"Non-Interlaced",
				mode->vfifo16,mode->vfifo24);
	else	fputs(" unknown Clock, horz., and vert.",stdout);
#endif
	if(iptr->xbytes*iptr->ydim >mach32_memory*1024)
		{
#ifdef DEBUG
		puts(" (not enough memory)");
#endif
		continue;
		}
	if(__svgalib_monitortype<6)
		if(mach32_monitor[__svgalib_monitortype]<horz)
			{
#ifdef DEBUG
			puts(" (rejected, monitor safety)");
#endif
			continue;
			}
	if((mach32_modes[i]==NULL)||(forcein>=0))
		{
#ifdef DEBUG
		puts(" (accepted)");
#endif
		fillin:
		mach32_modes[i]=mode;
		continue;
		}
	if((mach32_modes[i]->disp_cntl^mode->disp_cntl)&0x10)
		{
		if(mode->disp_cntl&0x10)
			{
#ifdef DEBUG
			puts(" (rejected, is interlaced)");
#endif
			continue;
			}
		else	{
#ifdef DEBUG
			puts(" (preferred, is non-interlaced)");
#endif
			goto fillin;
			}
		}
	n_clock=mach32_clocks[0x1f&(mach32_modes[i]->clock_sel>>2)];
	n_horz=(1e3*n_clock)/(float)(mach32_modes[i]->h_total*8+8);
	tmp=mach32_modes[i]->v_total;
	if(!(mach32_modes[i]->disp_cntl&0x6))
		tmp=((tmp>>2)&0xfffe)|(tmp&1);
	else	tmp=((tmp>>1)&0xfffc)|(tmp&3);
	tmp++;
	n_vert=(n_horz*1000)/tmp;
	if(n_vert<vert)
		{
#ifdef DEBUG
		puts(" (higher V_SYNC preferred)");
#endif
		goto fillin;
		}
#ifdef DEBUG
	puts(" (rejected)");
#endif
	}
}

static int mach32_modeavailable(int mode)
{
if(mode>MAX_MODES)
	return 0;
if(mach32_modes[mode]==NULL)
	return vga_chipsetfunctions[CHIPSET_MODEAVAILABLE](mode);
return SVGADRV;
}

static int mach32_getmodeinfo(int mode,vga_modeinfo *modeinfo)
{
if(mode>MAX_MODES)
	return 0;
if(mach32_modes[mode]==NULL)
	return 0;
modeinfo->flags|=(HAVE_RWPAGE|EXT_INFO_AVAILABLE);
if(mach32_modes[mode]->disp_cntl&0x10)
	modeinfo->flags|=IS_INTERLACED;

if(modeinfo->colors==256)
	modeinfo->linewidth_unit=8;
else if(modeinfo->colors==(1<<24))
	modeinfo->linewidth_unit=(64*3);
else	modeinfo->linewidth_unit=(64*2);

modeinfo->bytesperpixel=infotable[mode].bytesperpixel;
modeinfo->linewidth=infotable[mode].xbytes;
modeinfo->maxlogicalwidth=0xff*8*modeinfo->bytesperpixel;
modeinfo->maxpixels=(mach32_memory*1024)/modeinfo->bytesperpixel;
modeinfo->startaddressrange=0xfffffffc&(mach32_memory*1024-1);
modeinfo->haveblit=0;

modeinfo->chiptype=mach32_dac|(mach32_chiptype&(MACH32_APERTURE_4M|MACH32_APERTURE));
if(mach32_chiptype&MACH32_APERTURE_4M)
	modeinfo->aperture_size=(4<<10);
else if(mach32_chiptype&MACH32_APERTURE)
	modeinfo->aperture_size=(1<<10);
else modeinfo->aperture_size=0;
modeinfo->memory=mach32_memory;
if(modeinfo->aperture_size>=mach32_memory)
	modeinfo->flags|=CAPABLE_LINEAR;
modeinfo->linear_aperture=(char *)mach32_aperture;
modeinfo->set_aperture_page=mach32_setappage;
modeinfo->extensions=(mach32_eeprom+128);

return 1;
}

static void mach32_blankadj(int adj)
{
#ifdef DEBUG
printf("mach32_blankadj(%d)\n",adj);
#endif
outb(MISC_CTL+1,(inb(R_MISC_CTL+1)&0xf0)|adj);
}

static int mach32_setmode(int mode, int previous)
{
register const mode_entry *mptr;
int type,clock,clock_intended;
unsigned short act_ge_conf;

type=mach32_modeavailable(mode);
if(!type)
	return 1;
if(type!=SVGADRV)
	{
	#ifdef DEBUG
	printf("mach32_setmode: standard mode:%d\n",mode);
	#endif
	/*Set std dac-mode here*/
	mach32_set_dac(DAC_MODE8,0,0);
	/* Ensure we are in VGA-mode: */
	outw(CLOCK_SEL,(inw(CLOCK_SEL)&~0x7f)|0x10); /* slow clock and disable
								Mach CRT */
	outw(DISP_CNTL,mach32_disp_shadow|0x40); /* Mach32 CRT reset */
	return vga_chipsetfunctions[CHIPSET_SETMODE](mode);
	/* vga_setmode will ensure vga is on...*/
	}
/*switch off vga for better mem access and to fake setpalette.....*/
/*vga_screenoff(); Actually vga_setmode did this already.... and vga_screenon
is already disabled..*/

#ifdef DEBUG
printf("mach32_setmode: Setting vga in 8bpp graphmode.\n");
#endif
/*Ripped out from Xfree.. mach32 Server..*/
outw(SEQ_I, 4 | (0x0a << 8));
/* Enable write access to all memory maps */
outw(SEQ_I, 2 | (0x0f << 8));
/* Set linear addressing mode */
outw(ATIPORT, ATISEL(0x36) | (0x05 << 8));
/* Set the VGA display buffer to 0xa0000 */
outw(GRA_I, 6 | (0x05 << 8));
/*These are mine...someone turned the VGA to erase on write....(Eeeek!)*/
outw(GRA_I, 1 | (0x00 << 8));
outw(GRA_I, 3 | (0x00 << 8));
outw(GRA_I, 5 | (0x00 << 8));
#ifdef DEBUG
printf("mach32_setmode: extended mode:%d\n",mode);
#endif

mptr=mach32_modes[mode];

mach32_disp_shadow=mptr->disp_cntl&0x7f;
outw(DISP_CNTL,mach32_disp_shadow|0x60); /* Mach32 CRT reset */
/*set dac: */

clock_intended=mptr->clock_sel;
clock=mach32_clocks[0x1f&(clock_intended>>2)];

switch(infotable[mode].colors)
	{
	default:	/* 256 */
		clock_intended=mach32_set_dac((clock>80)?DAC_MODEMUX:DAC_MODE8,
					clock_intended,infotable[mode].xdim);
		act_ge_conf=inw(R_EXT_GE_CONF)&0xf80f;
		if(clock>80)
			act_ge_conf|=0x0110;
		else	act_ge_conf|=0x0010;
		break;
	case 1<<15:
		clock_intended=mach32_set_dac(DAC_MODE555,clock_intended,infotable[mode].xdim);
		act_ge_conf=inw(R_EXT_GE_CONF)&0xf80f;
		act_ge_conf|=0x0020;
		/* Force VFIFO in clock_sel to zero:*/
		clock_intended&=0xf0ff;
		clock_intended|=((mptr->vfifo16)<<8);
		break;
	case 1<<16:
		clock_intended=mach32_set_dac(DAC_MODE565,clock_intended,infotable[mode].xdim);
		act_ge_conf=inw(R_EXT_GE_CONF)&0xf80f;
		act_ge_conf|=0x0060;
		/* Force VFIFO in clock_sel to zero:*/
		clock_intended&=0xf0ff;
		clock_intended|=((mptr->vfifo16)<<8);
		break;
	case 1<<24:
		clock_intended=mach32_set_dac(DAC_MODERGB,clock_intended,infotable[mode].xdim);
		act_ge_conf=inw(R_EXT_GE_CONF)&0xf80f;
		act_ge_conf|=0x0030;
		/* Force VFIFO in clock_sel to zero:*/
		clock_intended&=0xf0ff;
		clock_intended|=((mptr->vfifo24)<<8);
		break;
	}
outw(EXT_GE_CONF,act_ge_conf);
/*Set clock and disable VGA... those pinheads from ATI have the clock
  settings in all their tables with the VGA enabled....Gee..*/
clock_intended|=1;

#ifdef DEBUG
printf("GE_CONF:%hx\n",act_ge_conf);
printf("Setting mode:\th_disp: %x\th_total: %x\th_sync_wid: %x\th_sync_strt: %x\t"
"v_disp: %x\tv_total: %x\tv_sync_wid: %x\tv_sync_strt: %x\tclock_sel: %x\tdisp_ctl: %x\n",
mptr->h_disp,mptr->h_total,mptr->h_sync_wid,mptr->h_sync_strt,mptr->v_disp,mptr->v_total,
mptr->v_sync_wid,mptr->v_sync_strt,clock_intended,mach32_disp_shadow);
#endif
outb(H_TOTAL,mptr->h_total);
outw(H_DISP,mptr->h_disp);
outb(H_SYNC_STRT,mptr->h_sync_strt);
outb(H_SYNC_WID,mptr->h_sync_wid);
outw(V_TOTAL,mptr->v_total);
outw(V_DISP,mptr->v_disp);
outw(V_SYNC_STRT,mptr->v_sync_strt);
outw(V_SYNC_WID,mptr->v_sync_wid);

#ifdef DEBUG
	outw(CLOCK_SEL,clock_intended);
#else
	outw(CLOCK_SEL,clock_intended&~1); /*Screen does not show up yet..
					    will be fired up by final vga_screenon
					    called from setmode (still time to
					    erase the screen.. ;) */
#endif

mach32_setlogicalwidth(infotable[mode].xbytes);
mach32_setdisplaystart(0);

outw(DISP_CNTL,mach32_disp_shadow|0x20); /* Mach32 CRT enable */

#if defined(DEBUG_KEY)&&defined(DEBUG)
	fputs("\n(Hit Return)",stdout);
	while(getchar()!='\n');
#endif
return 0;
}

static short *push(int num)
{
short *ptr;

if(mixup_ptr+num>=mixup_alloc)
	{
	if(mach32_modemixup==NULL)
		mach32_modemixup=malloc(PREALLOC*sizeof(short));
	else	mach32_modemixup=realloc(mach32_modemixup,(mixup_alloc+=PREALLOC)*sizeof(short));
	if(mach32_modemixup==NULL)
		{
		puts("mach32-config: Fatal error: Out of memory.");
		exit(-1);
		}
	}
ptr=mach32_modemixup+mixup_ptr;
mixup_ptr+=num;
return ptr;
}

static int isnumber(char *str)
{
if(str==NULL)
	return 0;
return strlen(str)==strspn(str,"0123456789");
}

static int parsemode(char *str,int linelength,int create)
{
char tmpstr[5], *ptr;
int i;
unsigned width,height,colors, bytesperpixel;

if(!str)
	return -1;
for(ptr=str;*ptr;ptr++)
	*ptr=tolower(*ptr);
if(sscanf(str,"%ux%ux%4s",&width,&height,tmpstr)!=3)
	return -1;
if(!strcmp(tmpstr,"256")) {
	colors=256;
	bytesperpixel = 1;
}
else if(!strcmp(tmpstr,"32k")) {
	colors=(1<<15);
	bytesperpixel = 2;
}
else if(!strcmp(tmpstr,"64k")) {
	colors=(1<<16);
	bytesperpixel = 2;
}
else if(!strcmp(tmpstr,"16m")) {
	colors=(1<<24);
	bytesperpixel = 3;
}
else	{
	printf("mach32-config: mode %s unsupported, only 256, 32K, 64K, 16M colors allowed.\n",tmpstr);
	return -2;
	}
width=(width+7)&~7;
if(linelength<width)
	linelength=width;
else	linelength=(linelength+7)&~7;

linelength *= bytesperpixel;

for(i=0;i<MAX_MODES;i++)
	{
	if((infotable[i].xdim==width)&&(infotable[i].ydim==height)&&
			(infotable[i].colors==colors)&&(infotable[i].bytesperpixel==bytesperpixel))
		{
		infotable[i].xbytes=linelength;
		return i;
		}
	}
if(!create)
	return -2;
if((i=__vga_addmode(width,height,colors,linelength,bytesperpixel))<0)
	{
	printf("mach32-config: no more dynamic modes, %s ignored.\n",tmpstr);
	return -2;
	}
return i;
}

static inline unsigned int mach32_skip2(unsigned int vtime)
{
return ((vtime<<1)&0xfff8)|(vtime&3)|((vtime&0x80)>>5);
}

static char *mach32_procopt(int command,int mode) {
mode_entry *mptr;
char *ptr;
int i,currptr,flag;

switch(command)
	{
	case 0:
		ptr=strtok(NULL," ");
		if(!strcmp(ptr,"6"))
			{
			mach32_8bitmode=0;
			break;
			}
		else if(!strcmp(ptr,"8"))
			{
			mach32_8bitmode=1;
			break;
			}
		else	{
			puts("mach32-config: illegal/unsupported dacwidth command.\nUsage: dacwidth {6|8}.");
			return ptr;
			}
	case 1:
		ptr=strtok(NULL," ");
		if(ptr==NULL)
			{
			puts("mach32-config: mach32eeprom command expects a parameter.");
			return ptr;
			}
		if(!mode)
			{
			puts("mach32-config: mach32eeprom command denied.");
			break;
			}
		if(eeprom_fname!=NULL)
			free(eeprom_fname);
		eeprom_fname=strdup(ptr);
		if(eeprom_fname==NULL)
			{
			puts("mach32-config: Fatal error: out of memory.");
			exit(-1);
			}
		break;
	case 2:
		for(;;)	{
			ptr=strtok(NULL," ");
			i=parsemode(ptr,0,0);
			if(i== -1)
				break;
			if(i>=0)
				*push(1)=CMD_DEL+i;
			}
		break;
	case 3:	/* Define a new mode.. */
		currptr=mixup_ptr;
		flag=0;
		for(;;)	{
			ptr=strtok(NULL," ");
			i=parsemode(ptr,0,1);
			if(i== -1)
				break;
			if(i>=0)
				{
				*push(1)=CMD_ADD+i;
				flag=1;
				}
			}
		if(!flag)
			{
			puts("mach32-config: Invalid define command, no valid vga-mode given");
			ex_inv_mod:
			mixup_ptr=currptr;
			return ptr;
			}
		*push(1)=CMD_MOD;
		mptr=(mode_entry *)push(SOMOD_SH);
		/* Read the mode in, this is almost like Xconfig.. */
		/*The clock is the only thing that may differ:*/
		if(ptr==NULL)
			{
			inv_clk:
			puts("mach32-config: Invalid define command, clock is invalid");
			goto ex_inv_mod;
			}
		if(*ptr==':')	/*No. of clock given*/
			{
			if(!isnumber(ptr+1))
				goto inv_clk;
			i=atoi(ptr+1);
			}
		else	{ /* search clock in table */
			if(!isnumber(ptr))
				goto inv_clk;
			flag=atoi(ptr);
			for(i=0;i<32;i++)
				if(flag==mach32_clocks[i])
					break;
			}
		if(i>31)
			goto inv_clk;
		mptr->clock_sel=(i<<2);
#ifdef DEBUG
printf("Constructed clock_sel is: %d\n",mptr->clock_sel);
#endif
		mptr->disp_cntl=0x23; /* Assume non interlaced*/
		/* The rest is straight forward: */
		ptr=strtok(NULL," ");
		if(!isnumber(ptr))
			{
			inv_time:
			puts("mach32-config: Invalid define command, timing is invalid");
			goto ex_inv_mod;
			}
		mptr->h_disp=(atoi(ptr)>>3)-1;
		ptr=strtok(NULL," ");
		if(!isnumber(ptr))
			goto inv_time;
		i=atoi(ptr);
		mptr->h_sync_strt=(i>>3)-1;
		ptr=strtok(NULL," ");
		if(!isnumber(ptr))
			goto inv_time;
		mptr->h_sync_wid=((atoi(ptr)-i)>>3);
		if(mptr->h_sync_wid>0x1f)
			mptr->h_sync_wid=0x1f;
		ptr=strtok(NULL," ");
		if(!isnumber(ptr))
			goto inv_time;
		mptr->h_total=(atoi(ptr)>>3)-1;

		ptr=strtok(NULL," ");
		if(!isnumber(ptr))
			goto inv_time;
		mptr->v_disp=mach32_skip2(atoi(ptr)-1);
		ptr=strtok(NULL," ");
		if(!isnumber(ptr))
			goto inv_time;
		i=atoi(ptr);
		mptr->v_sync_strt=mach32_skip2(i-1);
		ptr=strtok(NULL," ");
		if(!isnumber(ptr))
			goto inv_time;
		mptr->v_sync_wid=atoi(ptr)-i;
		if(mptr->v_sync_wid>0x1f)
			mptr->v_sync_wid=0x1f;
		ptr=strtok(NULL," ");
		if(!isnumber(ptr))
			goto inv_time;
		mptr->v_total=mach32_skip2(atoi(ptr)-1);
		for(;;)	{	/* Parse for additional goodies */
			ptr=strtok(NULL," ");
			if(!strcasecmp(ptr,"interlace"))
				mptr->disp_cntl|=0x10;
			else if(!strcasecmp(ptr,"+hsync"))
				mptr->h_sync_wid&=~0x20;
			else if(!strcasecmp(ptr,"-hsync"))
				mptr->h_sync_wid|=0x20;
			else if(!strcasecmp(ptr,"+vsync"))
				mptr->v_sync_wid&=~0x20;
			else if(!strcasecmp(ptr,"-vsync"))
				mptr->v_sync_wid|=0x20;
			else	break;
			}
		/*final fixup*/
		if(mptr->h_disp>0x4f)	/* x>640 */
			{
			mptr->vfifo24=0xe;
			if(mptr->h_disp>0x7f)
				mptr->vfifo16=0xe;
			else if(mptr->h_disp>0x63)
				mptr->vfifo16=0xd;
			else	mptr->vfifo16=0x9;
			}
		else	{
			mptr->vfifo16=0;
			mptr->vfifo24=0;
			}
		return ptr;	
	case 4:
		ptr=strtok(NULL," ");
		if(!isnumber(ptr))
			{
			puts("mach32-config: illegal setlinelength command.\n
				Usage: setlinelength integer modes...");
			return ptr;
			}
		i=atoi(ptr);
		do	ptr=strtok(NULL," ");
		while(parsemode(ptr,i,0) != -1);
		break;
	case 5:
		ptr=strtok(NULL," ");
		if(!isnumber(ptr))
			{
			ilmaxclk:
			puts("mach32-config: illegal maxclock16 or maxclock24 command.\n"
				"Usage: maxclock16 integer or maxclock24 integer");
			return ptr;
			}
		mach32_maxclk16=atoi(ptr);
		break;
	case 6:
		ptr=strtok(NULL," ");
		if(!isnumber(ptr))
			goto ilmaxclk;
		mach32_maxclk24=atoi(ptr);
		break;
	case 7:
		if(!mode)
			{
			puts("mach32-config: clocks command denied.");
			}
		for(i=0;i<16;i++)
			{
			ptr=strtok(NULL," ");
			if(!isnumber(ptr))
				{
				puts("mach32-config: illegal clocks command.\n"
					"Usage: clocks integer integer ...\n"
					"16 clocks have to be specified.\n"
					"specify 0 for unsupported clocks\n"
					"(padding clocks with 0's)");
				if(mode){
					while(i<16)
						mach32_clocks[i++]=0;
					/*Set halfed clocks:*/
					for(i=0;i<16;i++)
						mach32_clocks[i+16]=mach32_clocks[i]/2;
					}
				return ptr;
				}
			if(mode)
				mach32_clocks[i]=atoi(ptr);
			}
		break;
	case 8:
		mach32_strictness=0;
		break;
	case 9:
		mach32_strictness=1;
		break;
	case 10:
		mach32_strictness=2;
		break;
	}
return strtok(NULL," ");
}

static char *mach32_conf_commands[]={
	"dacwidth","mach32eeprom","inhibit","define","setlinelength",
	"maxclock16","maxclock24","clocks","variablelinelength","strictlinelength",
	"duplicatelinelength",
        NULL};

static void mach32_readconfig(void)
{
static int conf_read=0;
int i,j,limit,newxbytes;

if(conf_read)
	return;
conf_read=1;

/*Hack info table.. see comment at start of file*/
for(i=0;i<MAX_MODES;i++)
	{
	if(infotable[i].xdim==800)
		{
		if(infotable[i].xbytes==1600)
			infotable[i].xbytes+=2*PIXTWEAK;
		else if(infotable[i].xbytes==2400)
			infotable[i].xbytes+=3*PIXTWEAK;
		}
	}
__svgalib_read_options(mach32_conf_commands,mach32_procopt);
if(mach32_strictness) /* Do something about the modes where we redefined xbytes.. */
	{
	for(j=STDVGA_MODES;j<MAX_MODES;j++) /*Find first free dynamic mode*/
		if(!infotable[j].xdim)
			break;
	j=limit=GLASTMODE;
	for(i=STDVGA_MODES;i<limit;i++) /* leave the non SVGA modes alone */
		{
		if(!col2bypp(infotable+i))
			continue;    /* Don't deal with non mach32-modes */
		if(infotable[i].xbytes!=infotable[i].xdim*col2bypp(infotable+i)) /* mode was redefined */
			{
			newxbytes=infotable[i].xbytes;
			/*Let infotable[i] look like a virgin again:*/
			infotable[i].xbytes=infotable[i].xdim*col2bypp(infotable+i);
			j=__vga_addmode(infotable[i].xdim,infotable[i].ydim,infotable[i].colors,
						newxbytes, infotable[i].bytesperpixel);
			if(j<0)
				{
				puts("mach32: Out of dynamic modes for redefinition of modes with\n"
				     "        non-standard linelength. Use inhibit in case of problems.\n");
				goto giveup;
				}
			if(mach32_strictness==1)
				*push(1)=CMD_MOV+i; /* later move this mode up to the newly defined.. */
			else	*push(1)=CMD_CPY+i; /* Nope, copy instead of move.. */
			*push(1)=j;		/* The destination mode. */
#ifdef 1
			printf("Redefining mode %d to %d:%dx%dx%d\n",i,j,
					infotable[i].xdim,infotable[i].ydim,infotable[i].colors);
#endif
			}
		}
	}
giveup:
*push(1)=CMD_MSK; /* End of fixup commands */
mach32_modemixup=realloc(mach32_modemixup,mixup_ptr*sizeof(short));
if(mach32_modemixup==NULL)
	{
	puts("mach32-config: Fatal: queue shrink failed.");
	exit(-1);
	}
}

static void mach32_final_modefixup(void)
{
int i;
const mode_entry *oldentry;
short *ptr,*ptm;


for(ptr=mach32_modemixup;;ptr++)
	switch(*ptr&CMD_MSK)
		{
		case CMD_MSK:	/* end marker.. */
			return;
		case CMD_DEL:	/* any mode found here is invalid. */
			mach32_modes[*ptr&~CMD_MSK]=NULL;
			break;
		case CMD_MOD:	/* skip embedded mode table */
			ptr+=SOMOD_SH;		
			break;
		case CMD_MOV:	/* move from this position to the one in next command short. */
			i=(*ptr++)&~CMD_MSK;
			mach32_modes[*ptr&~CMD_MSK]=mach32_modes[i];
			/* erase old entry... */
			mach32_modes[i]=NULL;
			break;
		case CMD_CPY:	/*Same as above, but without erase..*/
			i=(*ptr++)&~CMD_MSK;
			mach32_modes[*ptr&~CMD_MSK]=mach32_modes[i];
			break;
		case CMD_ADD:	/* Add next mode in supplied position.. */
			/*First find the mode table..*/
			ptm=ptr+1;
			while((*ptm&CMD_MSK)!=CMD_MOD)
				ptm++;
			/* one more to skip modemarker.. */
			ptm++;
			/*Override any entry we had already for this mode..  (if the mode is acceptable)*/
			i= *ptr&~CMD_MSK;
			oldentry=mach32_modes[i];
#ifdef DEBUG
puts("Calling mach32_modfill to add new mode.");
#endif
			mach32_modfill((mode_entry *)ptm,  ~0, /* will be further restricted by modfill*/
					 i );
			if(mach32_modes[i]==oldentry)
				{
				printf("mach32: Setting of mode %dx%dx%d failed.\n"
					"Mode can't be realized with your hardware\n"
					"(or due to configured restrictions ;-) ), sorry.\n",
					infotable[i].xdim,infotable[i].ydim,infotable[i].colors);
				}
			break;
		}
}
