/* 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) 1995 Michael Weller                       */
/* eowmob@exp-math.uni-essen.de mat42b@aixrs1.hrz.uni-essen.de     */
/* eowmob@pollux.exp-math.uni-essen.de                             */

/*
 * MICHAEL WELLER DISCLAIMS ALL WARRANTIES WITH REGARD
 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS, IN NO EVENT SHALL MICHAEL WELLER BE LIABLE
 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

/* This tool contains one routine out of Xfree86, therefore I repeat    */
/* its copyright here: (Actually it is longer than the copied code)     */

/*
 * Copyright 1992 by Orest Zborowski <obz@Kodak.com>
 * Copyright 1993 by David Wexelblat <dwex@goblin.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the names of Orest Zborowski and David Wexelblat 
 * not be used in advertising or publicity pertaining to distribution of 
 * the software without specific, written prior permission.  Orest Zborowski
 * and David Wexelblat make no representations about the suitability of this 
 * software for any purpose.  It is provided "as is" without express or 
 * implied warranty.
 *
 * OREST ZBOROWSKI AND DAVID WEXELBLAT DISCLAIMS ALL WARRANTIES WITH REGARD 
 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 
 * FITNESS, IN NO EVENT SHALL OREST ZBOROWSKI OR DAVID WEXELBLAT BE LIABLE 
 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
 * Copyright 1993 by Kevin E. Martin, Chapel Hill, North Carolina.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Thomas Roell not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Thomas Roell makes no representations
 * about the suitability of this software for any purpose.  It is provided
 * "as is" without express or implied warranty.
 *
 * THOMAS ROELL, KEVIN E. MARTIN, AND RICKARD E. FAITH DISCLAIM ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE AUTHORS
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Thomas Roell, roell@informatik.tu-muenchen.de
 *
 * Rewritten for the 8514/A by Kevin E. Martin (martin@cs.unc.edu)
 * Modified for the Mach-8 by Rickard E. Faith (faith@cs.unc.edu)
 * Rewritten for the Mach32 by Kevin E. Martin (martin@cs.unc.edu)
 *
 */

/* 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 */
/* Ok,later I found that the number of pixels has to be a multiple */
/* of 64 at least... somewhere in the  ATI docs, always choosing a */
/* multiple of 128 was suggested....                               */
/* So set the multiple to be used below..                          */

#define PIXALIGN 64

#define DAC_SAFETY 0x1d		/*reminder for people with DAC!=4,3,2,0 */
			/*set bits for well known DACTYPES */

/*
 * Sync allowance in 1/1000ths. 10 (1%) corresponds to a 315 Hz
 * deviation at 31.5 kHz, 1 Hz at 100 Hz
 */
#define SYNC_ALLOWANCE 10

#define SUPP_32BPP		/*Accept 32BPP modes */
/*#define USE_RGBa *//* ifdef(SUPP_32BPP) use RGBa format (R first in memory),
		      otherwise aBGR is used */
/*Pure experimental and probably specific to my card (VRAM 68800-3) */
#define MAXCLK8  2000		/*This effectly switches this off.. seems not
				   to be needed with PIXTWEAK>32 */
#define MAXCLK16 2000		/*This effectly switches this off.. seems not
				   to be needed with PIXTWEAK>32 */
#define MAXCLK24 49
#define MAXCLK32 39

/*Clock values to be set on original ATI Mach32 to emulate vga compatible timings */
/*Bit 4 is ATI3e:4, bits 3-2 are ATI39:1-0, bits 1-0 are ATI38:7-6 */
#define SVGA_CLOCK 0x09

/*And here are minimum Vfifo values.. just guessed values (valid settings are 0..15): */
#define VFIFO8	6
#define VFIFO16	9
#define VFIFO24	14
#define VFIFO32	14

/*Wait count for busywait loops */
#define BUSYWAIT 10000000	/* Around 1-2 sec on my 486-50 this should be enough for
				   any graphics command to complete, even on a fast machine.
				   however after that slower code is called to ensure a minimum
				   wait of another: */
#define ADDIWAIT 5		/* seconds. */

/*#define FAST_MEMCPY *//* Use a very fast inline memcpy instead of the standard libc one */
			/* Seems not to make a big difference for the Mach32 */
			/* Even more seems to be MUCH slower than the libc memcpy, even */
			/* though this uses a function call. Probably just rep movsb is */
			/* fastest. So => do not use this feature. */

#define MODESWITCHDELAY	50000	/* used to wait for clocks to stabilize */

/*#define DEBUG */
/*#define DEBUG_KEY *//*Wait for keypress at some locations */
/*#define EXPERIMENTAL *//*Experimental flags */
/*#define FAKEBGR *//* Use BGR instead of RGB, this is for debugging only. Leave it alone! */

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

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

#define OFF_ALLOWANCE(a) ((a) * (1.0f - (SYNC_ALLOWANCE/1000.0f)))
#define ADD_ALLOWANCE(a) ((a) * (1.0f + (SYNC_ALLOWANCE/1000.0f)))

/*List preallocate(for internal info table mixup commands): */
#define PREALLOC 16

/*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: */
/* moved to 8514a.h - Stephen Lee */
#define VGA_DAC_MASK	0x3C6
#define DISP_STATUS	0x2E8

#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 ISA		0x0000
#define Z4GBYTE		(1<<13)

/* ATI-EXT regs to save (ensure lock regs are set latest, that is they are listed first here!): */
static const unsigned char mach32_eregs[] =
{
	/* Lock regs: */
    0x38, 0x34, 0x2e, 0x2b,
	/* All other extended regs. */
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
    0x20, 0x23, 0x24, 0x25, 0x26, 0x27, 0x2c,
    0x2d, 0x30, 0x31, 0x32, 0x33, 0x35, 0x36,
    0x37, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
    0x3f};

/* Extended ATI VGA regs that have to be reset for plain vga-modes */
/* First num is ATI reg, 2nd has bits set where to clear bits in the */
/* ATI reg. Note: the most important clocksel regs 0x38 & 0x39 are */
/* handled manually */
static const unsigned char mach32_ati_ext[] =
{
    0x05, 0x80,			/* VGA std blink rate */
    0x23, 0x18,			/* clear ext CRTC bits */
    0x26, 0xc1,			/* no skew of disp enab, dashedunderl */
    0x27, 0xf1,
    0x2c, 0x41,
    0x2d, 0xff,			/* several ext bits */
    0x2e, 0xef,			/* ext page pointers */
    0x30, 0x65,
    0x31, 0x7b,
    0x32, 0xff,
    0x33, 0xc0,
    0x34, 0x03,
    0x35, 0xf7,
    0x36, 0xf7,
    0x3d, 0x0d};


/* Mach32 regs to save.. read, write address alternating: */
static const 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 */
};

/* Some internal flags */
#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_MODE32B  5
#define DAC_SEMICLK  0x80

#define EMU_POSS     1
#define EMU_OVER     2

#define EEPROM_USE_CHKSUM	1
#define EEPROM_USE_MEMCFG	2
#define EEPROM_USE_TIMING	4
#define EEPROM_UPDATE		8

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

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

static const unsigned char mach32_dac5[6] =
{0x00, 0x00, 0x20, 0x21, 0x40,
#ifdef USE_RGBa
 0x61
#else
 0x60
#endif
};

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;

typedef enum {
    R_UNKNOWN, R_STANDARD, R_EXTENDED
} accelstate;

/*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[] =
{
    {0x9, 0xe, 0x4f, 0x63, 0x2c, 0x52, 0x418, 0x3bf, 0x3d6, 0x23, 0x22, 0x50, 0, 0x0000, 0},
    {0x9, 0xe, 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))

static const mode_entry **mach32_modes = NULL;

static int mach32_clocks[32] =
{				/* init to zero for safety */
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0};

static signed char mach32_clock_by2[32], mach32_clock_by3[32];

/*Bitmask is 1 256-col, 2 15-16 bpp, 4 24 bpp, 8 32bpp */
static unsigned char mach32_mmask[2][8] =
{
    { /* General supported modes.. */ 3, 3, 0xf, 1, 0x7, 0xf, 1, 1},
    { /* Modes above 80Mhz..      */ 0, 0, 1, 0, 0, 0xf, 0, 0}};

/* Modes that ATI mentions for it's DACS.. this is for safety and the internal
   predefined modes.. I think the above restrictions should be enough.. However
   I don't want to risk the life of your DAC so.. */
static unsigned char dacmo640[] =
{0x3, 0x3, 0xf, 0x1, 0x7, 0xf, 0x1, 0x1};
static unsigned char dacmo800[] =
{0x3, 0x3, 0xf, 0x1, 0x3, 0xf, 0x1, 0x1};
static unsigned char dacmo1024[] =
{0x3, 0x1, 0x3, 0x1, 0x1, 0xf, 0x1, 0x1};
static unsigned char dacmo1280[] =
{0x0, 0x0, 0x1, 0x0, 0x0, 0xf, 0x0, 0x0};

/* several globals.. mostly to record current state of the driver */
static char vfifo8 = VFIFO8, vfifo16 = VFIFO16, vfifo24 = VFIFO24, vfifo32 = VFIFO32,
 mach32_apsiz = 0;
static char dac_override = 127, svga_clock = SVGA_CLOCK, mach32_ast = 0;
static char *eeprom_fname = NULL;
static char emuimage = EMU_POSS | EMU_OVER;	/* Never set EMU_OVER w/o EMU_POSS !!! */
static char eeprom_option = EEPROM_USE_CHKSUM | EEPROM_USE_MEMCFG | EEPROM_USE_TIMING;

static unsigned short *mach32_eeprom, mach32_disp_shadow = 0, mach32_strictness = 0,
*mach32_modemixup;
static unsigned short mach32_apadd = 1, mach32_memcfg;

static unsigned short mach32_ge_pitch = 0, mach32_ge_off_h = 0, mach32_ge_off_l = 0;
static unsigned short mach32_bkgd_alu_fn = 7, mach32_frgd_alu_fn = 7;
static unsigned short mach32_frgd_col = 0xFFFF, mach32_bkgd_col = 0;
static unsigned short mach32_polyfill = 0, mach32_rop = ROP_COPY;

static int mach32_memory, mach32_dac, mach32_pagemode = PAGE_UNKNOWN,
 mach32_dacmode = DAC_MODE8;
static int mach32_chiptype, mixup_alloc = 0, mixup_ptr = 0, clocks_set = 0,
 ext_settings = 0;
static int mach32_maxclk8 = MAXCLK8, mach32_maxclk16 = MAXCLK16, mach32_maxclk24 = MAXCLK24;
static int mach32_maxclk32 = MAXCLK32, latchopt = ~0, bladj = -1;
static int pos_ext_settings = VGA_CLUT8;
static int acc_supp = HAVE_BLITWAIT | HAVE_FILLBLIT | HAVE_IMAGEBLIT | HAVE_BITBLIT | HAVE_HLINELISTBLIT;
static int accel_supp = ACCELFLAG_SETFGCOLOR | ACCELFLAG_SETBGCOLOR |
		    ACCELFLAG_SETTRANSPARENCY | ACCELFLAG_SETRASTEROP |
		    ACCELFLAG_FILLBOX | ACCELFLAG_SCREENCOPY |
		    ACCELFLAG_DRAWLINE | ACCELFLAG_PUTIMAGE |
		    ACCELFLAG_DRAWHLINELIST | ACCELFLAG_PUTBITMAP |
		    ACCELFLAG_SCREENCOPYMONO | ACCELFLAG_SETMODE |
		    ACCELFLAG_POLYLINE | ACCELFLAG_POLYHLINE |
		    ACCELFLAG_POLYFILLMODE | ACCELFLAG_SYNC;

static int palcurind = -1;
static int palsetget = -1;

static accelstate mach32_accelstate = R_UNKNOWN;

#ifdef DEBUG
static char verbose = 1;
#else
static char verbose = 0;
#endif
#ifdef EXPERIMENTAL
static unsigned mach32_experimental = 0;
#endif


static volatile char *mach32_aperture = NULL;
static volatile int dummy;

static signed char mach32_search_clk(int clk);
static void mach32_setstate(accelstate which);
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 void 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 void mach32_setregs(const unsigned char regs[], int mode);
static void mach32_unlock(void);
static void 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 void mach32_setpage(int page);
static void mach32_setrdpage(int page);
static void mach32_setwrpage(int page);
static void mach32_setappage(int page);
static void mach32_setdisplaystart(int address);
static void mach32_setlogicalwidth(int width);
static int mach32_modeavailable(int mode);
static void mach32_getmodeinfo(int mode, vga_modeinfo * modeinfo);
static int mach32_setmode(int mode, int previous);
static void mach32_bitblt(int srcaddr, int destaddr, int w, int h, int pitch);
static void mach32_fillblt(int destaddr, int w, int h, int pitch, int c);
static void mach32_imageblt(void *srcaddr, int destaddr, int w, int h, int pitch);
static void mach32_memimageblt(void *srcaddr, int destaddr, int w, int h, int pitch);
static void mach32_hlinelistblt(int ymin, int n, int *xmin, int *xmax, int pitch, int c);
static void mach32_readconfig(void);
static void mach32_final_modefixup(void);
static int mach32_ext_set(unsigned what, va_list params);
static int mach32_accel(unsigned operation, va_list params);
static void mach32_ge_reset(void);
static void slow_queue(unsigned short mask);

static void mach32_savepalette(unsigned char *red, unsigned char *green, unsigned char *blue);
static void mach32_restorepalette(const unsigned char *red, const unsigned char *green,
				  const unsigned char *blue);
static int  mach32_setpalette(int index, int red, int green, int blue);
static void mach32_getpalette(int index, int *red, int *green, int *blue);
static int mach32_screenon(void);
static void mach32_waitretrace(void);

DriverSpecs __svgalib_mach32_driverspecs =
{
    mach32_saveregs,		/* saveregs */
    mach32_setregs,		/* setregs */
    mach32_unlock,		/* unlock */
    mach32_lock,		/* lock */
    mach32_test,
    mach32_init,
    mach32_setpage,		/* __svgalib_setpage */
    mach32_setrdpage,		/* __svgalib_setrdpage */
    mach32_setwrpage,		/* __svgalib_setwrpage */
    mach32_setmode,		/* setmode */
    mach32_modeavailable,	/* modeavailable */
    mach32_setdisplaystart,	/* setdisplaystart */
    mach32_setlogicalwidth,	/* setlogicalwidth */
    mach32_getmodeinfo,		/* getmodeinfo */
    mach32_bitblt,		/* bitblt */
    mach32_imageblt,		/* imageblt */
    mach32_fillblt,		/* fillblt */
    mach32_hlinelistblt,	/* hlinelistblt */
    mach32_bltwait,		/* bltwait */
    mach32_ext_set,
    mach32_accel,
    0,				/* linear -- mach32 driver handles it differently */
    NULL,			/* Accelspecs */
    NULL,			/* Emulation */
};

static Emulation mach32_vgaemul = {
    mach32_savepalette,
    mach32_restorepalette,
    mach32_setpalette,
    mach32_getpalette,
    NULL, /* void (*savefont)(void); */
    NULL, /* void (*restorefont)(void); */
    NULL, /* int (*screenoff)(void); */
    mach32_screenon,
    mach32_waitretrace,
};

#ifdef FAST_MEMCPY
/* This memcpy is immediately derived from memset as given in speedtest.c in this
   same svgalib package written by Harm Hanemaayer */

static inline void *
 fast_memcpy(void *dest, void *source, size_t count)
{
    __asm__(
	       "cld\n\t"
	       "cmpl $12,%%edx\n\t"
	       "jl 1f\n\t"	/* if (count >= 12) */

	       "movl %%edx,%%ecx\n\t"
	       "negl %%ecx\n\t"
	       "andl $3,%%ecx\n\t"	/* (-s % 4) */
	       "subl %%ecx,%%edx\n\t"	/* count -= (-s % 4) */
	       "rep ; movsb\n\t"	/* align to longword boundary */

	       "movl %%edx,%%ecx\n\t"
	       "shrl $2,%%ecx\n\t"

    /* Doing any loop unrolling here proved to SLOW!!! down
       the copy on my system, at least it didn't show any speedup. */

	       "rep ; movsl\n\t"	/* copy remaining longwords */

	       "andl $3,%%edx\n"	/* copy last few bytes */
	       "1:\tmovl %%edx,%%ecx\n\t"	/* <= 12 entry point */
  "rep ; movsb\n\t":
  /* no outputs */ :
    /* inputs */
    /* SI= source address */ "S"(source),
    /* DI= destination address */ "D"(dest),
  /* CX= words to transfer */ "d"(count):
    /* eax, edx, esi, edi, ecx destructed: */ "%eax", "%edx", "%esi", "%edi", "%ecx");
    return dest;
}
#else
#define fast_memcpy memcpy
#endif

static inline int max(int a, int b)
{
    return (a > b) ? a : b;
}

static inline void checkqueue(int n)
{				/* This checks for at least n free queue positions */
/*Prepare mask: */
    unsigned short mask = (unsigned short) (0xffff0000 >> n);
    int i = BUSYWAIT;

    while (i--)
	if (!(inw(EXT_FIFO_STATUS) & mask))
	    return;

    slow_queue(mask);
}

static void slow_queue(unsigned short mask)
{
    clock_t clk_time;

    clk_time = clock();

    do {
	if (!(inw(EXT_FIFO_STATUS) & mask))
	    return;
    }
    while ((clock() - clk_time) < (CLOCKS_PER_SEC * ADDIWAIT));

    mach32_ge_reset();		/* Give up */
}

static void mach32_ge_reset(void)
{				/* This is intended as a safety bailout if we locked
				   up the card. */
    int queue_stat, ioerr, ge_stat;
    int i = 1000000;
    volatile int dummy;

/* Provide diagnostics: */
    ioerr = inw(SUBSYS_STAT);
    queue_stat = inw(EXT_FIFO_STATUS);
    ge_stat = inw(GE_STAT);


    port_outw_r(SUBSYS_CNTL, 0x800f);	/*Reset GE, disable all IRQ, reset all IRQ state bits */
    while (i--)
	dummy++;
    port_outw_r(SUBSYS_CNTL, 0x400f);	/*Continue normal operation */
/* Better reconfigure all used registers */
    mach32_accelstate = R_UNKNOWN;
    CRITICAL = 0;		/* Obviously we are idle */
    fprintf(stderr,"\asvgalib: mach32: Warning! GE_Engine timed out, draw command\n"
	 "was probably corrupted! If you have a very fast machine (10*Pentium)\n"
	 "raise BUSYWAIT and ADDIWAIT in mach32.c, may also be a driver/card bug,\n"
	 "so report detailed info to me (Michael Weller).\nBUT:\n"
	 "This reaction is normal when svgalib is killed in a very critical operation\n"
	 "by a fatal signal like INT (pressing ^C). In this situation this reset just\n"
	 "guarantees that you can continue working on the console, so in this case\n"
	 "PLEASE don't bloat my mailbox with bug reports. Thx, Michael.\n");
    fprintf(stderr,"POST-Mortem:\n\tSubsys stat: %04x - %sIOerror (is usually a queue overrun)\n"
	   "\tGE stat    : %04x - engine %s, %sdata ready for host.\n\tQueue stat : %04x\n",
	   ioerr, (ioerr & 4) ? "" : "no ", ge_stat,
	   (ge_stat & GE_BUSY) ? "busy" : "idle", (ge_stat & 0x100) ? "" : "no ", queue_stat);
}

static void mach32_setstate(accelstate which)
{				/* Set GE registers to values assumed elsewhere */
    mach32_accelstate = which;

    checkqueue(10);
    /*Effectively switch off hardware clipping: */
    port_outw_r(MULTI_FUNC_CNTL, 0x1000 | (0xfff & -1));	/*TOP */
    port_outw_r(MULTI_FUNC_CNTL, 0x2000 | (0xfff & -1));	/*LEFT */
    port_outw_r(MULTI_FUNC_CNTL, 0x3000 | 1535);	/*BOTTOM */
    port_outw_r(MULTI_FUNC_CNTL, 0x4000 | 1535);	/*RIGHT */
    /*Same for ATI EXT commands: */
    port_outw_r(EXT_SCISSOR_T, 0xfff & -512);	/*TOP */
    port_outw_r(EXT_SCISSOR_L, 0xfff & -512);	/*LEFT */
    port_outw_r(EXT_SCISSOR_B, 1535);	/*BOTTOM */
    port_outw_r(EXT_SCISSOR_R, 1535);	/*RIGHT */
    port_outw_r(MULTI_FUNC_CNTL, 0xa000);
    port_outw_r(DP_CONFIG, 0x3291);

    checkqueue(4);
    port_outw_r(DEST_CMP_FN, 0);
    if (which == R_STANDARD) {
	checkqueue(2);
	port_outw_r(BKGD_MIX, 0x0027);	/* Not used .. however ensure PIX_TRANS is not used */
	port_outw_r(ALU_FG_FN, 7);
    } else {
	checkqueue(5);
	port_outw_r(GE_OFFSET_LO, mach32_ge_off_l);
	port_outw_r(GE_OFFSET_HI, mach32_ge_off_h);
	port_outw_r(GE_PITCH, mach32_ge_pitch);
	port_outw_r(BKGD_MIX, mach32_bkgd_alu_fn);
	port_outw_r(FRGD_MIX, mach32_frgd_alu_fn | 0x20);
	checkqueue(6);
	port_outw_r(ALU_BG_FN, mach32_bkgd_alu_fn);
	port_outw_r(ALU_FG_FN, mach32_frgd_alu_fn);
        port_outw_r(FRGD_COLOR, mach32_frgd_col);
        port_outw_r(BKGD_COLOR, mach32_bkgd_col);
	port_outw_r(RD_MASK, (infotable[CM].colors == 32768) ? 0x7fff : 0xffff);
	port_outw_r(LINEDRAW_OPT, 0);
    }
}

static void mach32_setdisplaystart(int address)
{
#ifdef DEBUG
    fprintf(stderr,"mach32_setdisplaystart(%x)\n", address);
#endif
    mach32_ge_off_l = address >> 2;
    port_outw_r(CRT_OFFSET_LO, mach32_ge_off_l);
    mach32_ge_off_h = 0xff & (address >> 18);
    port_outw_r(CRT_OFFSET_HI, mach32_ge_off_h);
    if (mach32_accelstate == R_EXTENDED) {
	checkqueue(2);
	port_outw_r(GE_OFFSET_LO, mach32_ge_off_l);
	port_outw_r(GE_OFFSET_HI, mach32_ge_off_h);
    }
    __svgalib_vga_driverspecs.setdisplaystart(address);
}

static void mach32_setlogicalwidth(int width)
{
    register int mywidth;
#ifdef DEBUG
    fprintf(stderr,"mach32_setlogicalwidth(%d)\n", width);
#endif
    if (infotable[CM].bytesperpixel) {	/* always >= 1 for Mach32 modes */
	/*Unfortunately the Mach32 expects this value in Pixels not bytes: */
	mywidth = width / (infotable[CM].bytesperpixel);
	mywidth = (mywidth >> 3) & 0xff;
#ifdef DEBUG
	fprintf(stderr,"mach32_setlogicalwidth: Mach32 width to %d pels.\n", mywidth * 8);
#endif
	port_outw_r(CRT_PITCH, mywidth);
	mach32_ge_pitch = mywidth;
	if (mach32_accelstate == R_EXTENDED) {
	    checkqueue(1);
	    port_outw_r(GE_PITCH, mywidth);
	}
    }
    __svgalib_vga_driverspecs.setlogicalwidth(width);
}


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

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

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

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

static void mach32_setappage(int page)
{
    port_outw_r(MEM_CFG, (mach32_memcfg | (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
    fprintf(stderr,"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)
{
    port_outw_r(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
    fprintf(stderr,"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;
#ifdef DEBUG
	fputs("DAC1: ", stderr);
#endif
	if (dac_mode <= DAC_MODE565)
	    goto dac1_4;
	break;
    case MACH32_ATI6871:
	/*This one is a nightmare: */
	clean_clocks();
	port_outw_r(EXT_GE_CONF, 0x201a);

	switch (dac_mode) {

	default:
	case DAC_MODE8:
	case DAC_MODEMUX:
	    port_out_r(DAC1, 0x00);
	    port_out_r(DAC2, 0x30);
	    port_out_r(DAC3, 0x2d);
#ifdef DEBUG
	    fprintf(stderr,"DAC2: 0x00 0x30 0x2d (8bpp)\n");
#endif
	    if (dac_mode != DAC_MODEMUX)
		break;
	    port_out_r(DAC2, 0x09);
	    port_out_r(DAC3, 0x1d);
	    port_out_r(DAC1, 0x01);
	    mach32_blankadj(1);
#ifdef DEBUG
	    fprintf(stderr"DAC2: 0x01 0x09 0x1d (8bpp MUX)\n");
#endif
	    break;
	case DAC_MODE555:
	case DAC_MODE565:
	case DAC_MODERGB:
	    mach32_blankadj(1);
	case DAC_MODE32B:
	    port_out_r(DAC1, 0x01);
	    if ((!(clock_intended & 0xc0)) && (!(mach32_dacmode & DAC_SEMICLK))) {
		port_out_r(DAC2, 0x00);
#ifdef DEBUG
		fprintf(stderr,"DAC2: 0x01 0x00 0x0d (16/24bpp)\n");
#endif
	    } else {
		clock_intended &= 0xff3f;
		port_out_r(DAC2, 0x08);
		mach32_dacmode |= DAC_SEMICLK;
		if (xres <= 640)
		    mach32_blankadj(2);
#ifdef DEBUG
		fprintf(stderr,"DAC2: 0x01 0x08 0x0d (16/24bpp)\n");
#endif
	    }
	    port_out_r(DAC3, 0x0d);
	    act_ge_conf |= 0x4000;
	    if (dac_mode == DAC_MODE32B)
		mach32_blankadj(3);
	    break;
	}
	if (bladj >= 0)
	    mach32_blankadj(bladj);
	break;
    case MACH32_BT481:
	dac_reg = mach32_dac4;
#ifdef DEBUG
	fputs("DAC4: ", stderr);
#endif
      dac1_4:
	if (dac_mode <= DAC_MODERGB) {
	    clean_clocks();
	    port_outw_r(EXT_GE_CONF, 0x101a);
#ifdef DEBUG
	    fprintf(stderr,"%02x\n", dac_reg[dac_mode]);
#endif
	    port_out_r(DAC2, dac_reg[dac_mode]);
	}
	break;
    case MACH32_ATI68860:
	clean_clocks();
	port_outw_r(EXT_GE_CONF, 0x301a);
#ifdef DEBUG
	fprintf(stderr,"DAC5: %02x\n", mach32_dac5[dac_mode]);
#endif
	port_out_r(DAC0, mach32_dac5[dac_mode]);
	break;
    }
/*Dac RS2,3 to 0 */
    act_ge_conf &= 0x8fff;
    if ((dac_mode == DAC_MODE8) || (dac_mode == DAC_MODEMUX))
	if (ext_settings & VGA_CLUT8)
	    act_ge_conf |= 0x4000;
    port_outw_r(EXT_GE_CONF, act_ge_conf);
/*Set appropriate DAC_MASK: */
    switch (dac_mode) {
    case DAC_MODE8:
    case DAC_MODEMUX:
#ifdef DEBUG
	fprintf(stderr,"DAC-Mask to 0xff\n");
#endif
	port_out_r(DAC_MASK, 0xff);
	break;
    default:
	switch (mach32_dac) {
	case MACH32_ATI6871:
	case MACH32_BT481:
#ifdef DEBUG
	    fprintf(stderr,"DAC-Mask to 0x00\n");
#endif
	    port_out_r(DAC_MASK, 0x00);
	    break;
	}
#ifdef DEBUG
	fprintf(stderr,"VGA-DAC-Mask to 0x0f\n");
#endif
	port_out_r(VGA_DAC_MASK, 0x0f);
	break;
    }
    return clock_intended;
}

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

    mach32_bltwait();		/* Ensure noone draws in the screen */

    for (i = 0; i < sizeof(mach32_eregs); i++) {
	port_out_r(ATIPORT, ATISEL(mach32_eregs[i]));
	regs[EXT + i] = port_in(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
    fprintf(stderr,"mach32_saveregs: retval=%d\n", retval);
#endif
    return retval - EXT;
}

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

    mach32_bltwait();		/* Ensure noone draws in the screen */
    mach32_accelstate = R_UNKNOWN;	/* Accel registers need to be reset */

    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
    fprintf(stderr,"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];
	/* restore only appage in MEM_CFG... otherwise badly interaction
	   with X that may change MEM_CFG and does not only not restore it's
	   original value but also insist on it not being changed on VC
	   change... =:-o */
	if (mach32_acc_regs[i] == MEM_CFG)
	    tmp = (inw(MEM_CFG) & ~0xc) | (tmp & 0xc);
	if (mach32_ast && (mach32_acc_regs[i] == MISC_CTL))
	    continue;
        if (mach32_acc_regs[i] == H_DISP) {
	    /* H_TOTAL (alt) in H_DISP broken for some chipsets... */
	    port_out_r(H_TOTAL, tmp >> 8);
	    port_out_r(H_DISP, tmp);
	} else {
	    port_outw_r(mach32_acc_regs[i], tmp);
	}
    }
    mach32_disp_shadow = regs[--offset] & ~0x60;
    port_out_r(DISP_CNTL, mach32_disp_shadow | 0x40);		/* Mach32 CRT reset */
    if (inw(CLOCK_SEL) & 1)	/* If in non VGA mode */
	port_outw_r(DISP_CNTL, mach32_disp_shadow | 0x20);	/* Mach32 CRT enabled */

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

}

static void mach32_unlock(void)
{
    unsigned short oldval;

#ifdef DEBUG
    fprintf(stderr,"mach32_unlock\n");
#endif				/* DEBUG */
    port_out_r(ATIPORT, ATISEL(0x2e));
    oldval = port_in(ATIPORT + 1) & ~0x10;	/* Unlock CPUCLK Select */
    port_outw_r(ATIPORT, ATISEL(0x2e) | (oldval << 8));

    port_out_r(ATIPORT, ATISEL(0x2b));
    oldval = port_in(ATIPORT + 1) & ~0x18;	/* Unlock DAC, Dbl Scan. */
    port_outw_r(ATIPORT, ATISEL(0x2b) | (oldval << 8));

    port_out_r(ATIPORT, ATISEL(0x34));
    oldval = port_in(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] */
    port_outw_r(ATIPORT, ATISEL(0x34) | (oldval << 8));

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

/* Unlock vga-memory too: */

    port_outw_r(MEM_BNDRY, 0);

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

static void mach32_lock(void)
{
    unsigned short oldval;

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

    port_out_r(ATIPORT, ATISEL(0x34));
    oldval = port_in(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] */
    port_outw_r(ATIPORT, ATISEL(0x34) | (oldval << 8));

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

    port_outw_r(SHADOW_SET, 1);
    port_outw_r(SHADOW_CTL, 0x7f);
    port_outw_r(SHADOW_SET, 2);
    port_outw_r(SHADOW_CTL, 0x7f);
    port_outw_r(SHADOW_SET, 0);
}

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

static void mach32_bltwait(void)
{
    int i = BUSYWAIT;
    clock_t clk_time;

    checkqueue(16);		/* Ensure nothing in the queue */

    while (i--)
	if (!(inw(GE_STAT) & (GE_BUSY | 1))) {
	  done:
	    /* Check additional stats */
	    if (inw(SUBSYS_STAT) & 4)
		goto failure;
	    CRITICAL = 0;	/* Obviously we're idle */
	    return;
	}
    clk_time = clock();

    do {
	if (!(inw(GE_STAT) & (GE_BUSY | 1)))
	    goto done;
    }
    while ((clock() - clk_time) < (CLOCKS_PER_SEC * ADDIWAIT));

  failure:
    mach32_ge_reset();
}

static void mach32_i_bltwait(void)
{
    int i;

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

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

/* 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);
    port_outw_r(SCRATCH_PAD_0, 0x5555);
    mach32_i_bltwait();
    if (inw(SCRATCH_PAD_0) == 0x5555) {
	port_outw_r(SCRATCH_PAD_0, 0x2a2a);
	mach32_i_bltwait();
	if (inw(SCRATCH_PAD_0) == 0x2a2a) {
	    /* Aha.. 8514/a detected.. */
	    result = 1;
	}
    }
    port_outw_r(SCRATCH_PAD_0, tmp);
    if (!result)
	goto quit;
/* Now ensure it is not a plain 8514/a: */
    result = 0;
    port_outw_r(DESTX_DIASTP, 0xaaaa);
    mach32_i_bltwait();
    if (inw(R_SRC_X) == 0x02aa) {
	port_outw_r(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)
	    fprintf(stderr,"Mach32 detected, but unusable with VGA disabled.\nSorry.\n");
	goto quit;		/*VGA circuitry disabled. */
    }
    result = 1;
  quit:
    if (result)
	mach32_init(0, 0, 0);
#ifdef DEBUG
    fprintf(stderr,"mach32_test: returning %d.\n", result);
#endif				/* DEBUG */
    return result;
}

static void mach32_wait(void)
{
/* 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)
{
    port_outw_r(ATIPORT, ati33 |= 0x200);	/* clock on */
    mach32_wait();
    port_outw_r(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;
	port_outw_r(ATIPORT, ati33);
	mach32_eeclock(ati33);
    }
    while (mask >>= 1);
}

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

/* get current ATI33 */
    port_out_r(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.. */
    port_outw_r(ATIPORT, ati33 &= ~0x100);
/*enable to one */
    port_outw_r(ATIPORT, ati33 |= 0x400);
    mach32_eeclock(ati33);
/*select to one */
    port_outw_r(ATIPORT, ati33 |= 0x800);
    mach32_eeclock(ati33);
    mach32_eekeyout(ati33, offset, 0x800);
    for (i = 0, result = 0; i < 16; i++) {
	result <<= 1;
	port_out_r(ATIPORT, ATISEL(0x37));
	if (port_in(ATIPORT + 1) & 0x8)
	    result |= 1;
	mach32_eeclock(ati33);
    }
/*deselect... */
    port_outw_r(ATIPORT, ati33 &= ~0x800);
    mach32_eeclock(ati33);
/*disable... */
    port_outw_r(ATIPORT, ati33 &= ~0x400);
    mach32_eeclock(ati33);
    return result;
}

static int mach32_max_mask(int x)
{
    if (x <= 0x4f)
	return dacmo640[mach32_dac];
    if (x <= 0x63)
	return dacmo800[mach32_dac];
    if (x <= 0x7f)
	return dacmo1024[mach32_dac];
    return dacmo1280[mach32_dac];
}

static int mach32_max_vfifo16(int x)
{
    int retval;

    if (x <= 0x63)
	retval = vfifo16;
    else if (x <= 0x7f)
	retval = vfifo16 + 4;
    else
	retval = vfifo16 + 5;
    return (retval < 15) ? retval : 15;
}

/* Shameless ripped out of Xfree2.1 (with slight changes) : */

static void mach32_scan_clocks(void)
{
    const int knownind = 7;
    const double knownfreq = 44.9;

    char hstrt, hsync;
    int htotndisp, vdisp, vtotal, vstrt, vsync, clck, i;

    int count, saved_nice, loop;
    double scale;

    saved_nice = nice(0);
    nice(-20 - saved_nice);

    fprintf(stderr,"Warning, about to measure clocks. Wait until system is completely idle!\n"
	 "Any activity will disturb measuring, and therefor hinder correct driver\n"
	 "function. Test will need about 3-4 seconds.\n"
	 "If your monitor doesn't switch off when it gets signals it can't sync on\n"
	 "better switch it off now before continuing. I'll beep when you can safely\n"
	 "switch it back on.\n");
#if 1
    fprintf(stderr,"\n(Enter Y<Return> to continue, any other text to bail out)\n");

    if (getchar() != 'Y') {
      bailout:
	fprintf(stderr,"\aBailed out.\n");
	exit(0);
    }
    if (getchar() != '\n')
	goto bailout;
#endif

    htotndisp = inw(R_H_TOTAL);
    hstrt = port_in(R_H_SYNC_STRT);
    hsync = port_in(R_H_SYNC_WID);
    vdisp = inw(R_V_DISP);
    vtotal = inw(R_V_TOTAL);
    vstrt = inw(R_V_SYNC_STRT);
    vsync = inw(R_V_SYNC_WID);
    clck = inw(CLOCK_SEL);

    port_out_r(DISP_CNTL, 0x63);

    port_out_r(H_TOTAL, 0x63);
    port_out_r(H_DISP, 0x4f);
    port_out_r(H_SYNC_STRT, 0x52);
    port_out_r(H_SYNC_WID, 0x2c);
    port_outw_r(V_TOTAL, 0x418);
    port_outw_r(V_DISP, 0x3bf);
    port_outw_r(V_SYNC_STRT, 0x3d6);
    port_outw_r(V_SYNC_WID, 0x22);

    for (i = 0; i < 16; i++) {
	port_outw_r(CLOCK_SEL, (i << 2) | 0xac1);
	port_out_r(DISP_CNTL, 0x23);

	usleep(MODESWITCHDELAY);

	count = 0;
	loop = 200000;

	while (!(port_in(DISP_STATUS) & 2))
	    if (loop-- == 0)
		goto done;
	while (port_in(DISP_STATUS) & 2)
	    if (loop-- == 0)
		goto done;
	while (!(port_in(DISP_STATUS) & 2))
	    if (loop-- == 0)
		goto done;

	for (loop = 0; loop < 5; loop++) {
	    while (!(port_in(DISP_STATUS) & 2))
		count++;
	    while ((port_in(DISP_STATUS) & 2))
		count++;
	}
      done:
	mach32_clocks[i] = count;

	port_out_r(DISP_CNTL, 0x63);
    }

    port_outw_r(CLOCK_SEL, clck);

    port_out_r(H_TOTAL, htotndisp >> 8);
    port_out_r(H_DISP, htotndisp);
    port_out_r(H_SYNC_STRT, hstrt);
    port_out_r(H_SYNC_WID, hsync);
    port_outw_r(V_DISP, vdisp);
    port_outw_r(V_TOTAL, vtotal);
    port_outw_r(V_SYNC_STRT, vstrt);
    port_outw_r(V_SYNC_WID, vsync);
    nice(20 + saved_nice);

/*Recalculation: */
    scale = ((double) mach32_clocks[knownind]) * knownfreq;
    for (i = 0; i < 16; i++) {
	if (i == knownind)
	    continue;
	if (mach32_clocks[i])
	    mach32_clocks[i] = 0.5 + scale / ((double) mach32_clocks[i]);
    }
    mach32_clocks[knownind] = knownfreq + 0.5;
}

static signed char mach32_search_clk(int clk)
{
    int i;

    if (!clk)
	return 0;
    if (clk > 80)		/* Actually filter out the too high clocks here already */
	return 0;
    for (i = 0; i < 32; i++)
	if (abs(clk - mach32_clocks[i]) <= 1)
	    return i + 1;
    return 0;
}

static unsigned short dac14_clock_adjust(unsigned short clock, signed char *clock_map)
{
    unsigned short clock_ind;

    if ((mach32_dac != MACH32_SC11483) && (mach32_dac != MACH32_BT481))
	return clock;
    clock_ind = 0x1f & (clock >> 2);
    clock &= 0xff83;
    if (!clock_map[clock_ind]) {
	vga_setmode(TEXT);
	fprintf(stderr,"svgalib-mach32: Panic, internal error: DAC1/4, invalid clock for >8bpp.\n");
	exit(1);
    }
    return clock | ((clock_map[clock_ind] - 1) << 2);
}

static int mach32_init(int force, int chiptype, int memory)
{
    char messbuf[12], eeapert = 0;
    static int max2msk[] =
    {1, 3, 0xf, 0xf};
    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, cfg1, cfg2;
    unsigned int apertloc = 0;

#ifdef EXPERIMENTAL
    if (getenv("EXPERIMENTAL") != NULL) {
	mach32_experimental = atoi(getenv("EXPERIMENTAL"));
	fprintf(stderr,"mach32: EXPERIMENTAL set to: %04x\n", mach32_experimental);
    } else {
	fprintf(stderr,"mach32: EXPERIMENTAL defaults to: %04x\n", mach32_experimental);
    }
#endif

    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);

/* get 2 eeprom buffers */
    if (mach32_eeprom == NULL) {
	unsigned short sum, crea_eeprom = 1;

	if ((mach32_eeprom = malloc(sizeof(short) * 2 * 128)) == NULL) {
	    fprintf(stderr,"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) && !(eeprom_option & EEPROM_UPDATE)) {
	    FILE *fd;

	    fd = fopen(eeprom_fname, "rb");
	    if (fd == NULL) {
	      readerr:
		fprintf(stderr,"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) {
		crea_eeprom = 0;	/*Mark succesful read... */
		goto skip_read;	/* Use the read in file... */
	    }
	    sum = 0;		/*Mark unsuccesful read.. */
	    fprintf(stderr,"mach32: Warning, EEPROM file >%s< corrupted.\n", eeprom_fname);
	}
	/* Read in eeprom */
      read_eeprom:
	if (!(eeprom_option & (EEPROM_USE_CHKSUM |
			       EEPROM_USE_MEMCFG | EEPROM_USE_TIMING))) {
	    /* No need to bother reading the EEPROM */
	    goto skip_read;
	}
	for (i = 0, ptr = mach32_eeprom; i < 128; i++)
	    *ptr++ = mach32_eeget(i);
	if (eeprom_option & EEPROM_USE_CHKSUM) {
	    for (i = 0, sum = 0, ptr = mach32_eeprom; i < 128; i++)
		sum += *ptr++;
	    sum &= 0xffff;
	    if (sum) {
		/*Hmm.. no ATI checksum... try AST: */
		sum -= (mach32_eeprom[0x7f] << 1) - 1;
	    }
	} else
	    sum = 0;
	if (sum & 0xffff) {
	    fprintf(stderr,"mach32: Warning, Illegal checksum in Mach32 EEPROM.\n"
	     "Check configuration of your card and read README.mach32,\n"
		 "esp. the Mach32 Eeprom Woe chapter.\n"
		 "Ignoring contents...\n");
	    eeprom_option &= ~(EEPROM_USE_MEMCFG | EEPROM_USE_TIMING);
	}
      skip_read:

	if ((eeprom_fname != NULL) && (crea_eeprom || (eeprom_option & EEPROM_UPDATE)))
	    /* create file if not there or unsuccesful or requested */
	{
	    FILE *fd;

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

	    /*  Ensure that this fopen gets a file descriptor greater
	     *  than 2, else problems can occur with stdio functions
	     *  under certain strange conditions:  */
            if ((fcntl(0,F_GETFD) == -1) && (open("/dev/null", O_RDONLY) == -1)){
	        perror("/dev/null");
	        exit(1);
            }
            if ((fcntl(1,F_GETFD) == -1) && (open("/dev/null", O_WRONLY) == -1)){
	        perror("/dev/null");
	        exit(1);
            }
            if ((fcntl(2,F_GETFD) == -1) && (open("/dev/null", O_WRONLY) == -1)){
	        perror("/dev/null");
	        exit(1);
            }
	    fd = fopen(eeprom_fname, "wb");
	    if (fd == NULL) {
	      writerr:
		fprintf(stderr,"mach32: Warning, can't create new EEPROM file >%s<\nError was %d - %s\n",
		       eeprom_fname, errno, strerror(errno));
		crea_eeprom = 0;
		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;
	    fprintf(stderr,"mach32: Notice: new EEPROM file >%s< succesful created.\n",
		   eeprom_fname);
	}
	finish_w_eeprom:
	/* Change eeprom contents if requested: */
	if (!(eeprom_option & EEPROM_USE_MEMCFG))
	    mach32_eeprom[6] = 0;
	if (!(eeprom_option & EEPROM_USE_TIMING)) {
	    /* Consider ALL standard modes, but no user defined */
	    mach32_eeprom[0x07] = 0x01;		/*  640 x  480 */
	    mach32_eeprom[0x08] = 0x3f;		/*  640 x  480 */
	    mach32_eeprom[0x09] = 0x1f;		/*  640 x  480 */
	    mach32_eeprom[0x0A] = 0x03;		/*  640 x  480 */
	    mach32_eeprom[0x0B] = 0x00;		/*  no 1152x900 or 1120x750 */
	}
	if (!clocks_set) {
	    char linebuffer[512];

	    fprintf(stderr,"mach32: Warning, no clocks defined.. please have a look at\n"
		 "README.Mach32 and README.config for details.\n");
	    mach32_scan_clocks();
	    fputs("\a\nResulting clocks command for your libvga.config should be:\n\n"
		  "clocks", stderr);
	    for (i = 0; i < 16; i++)
		fprintf(stderr," %3d", mach32_clocks[i]);
	    fputs("\n\nPlease edit \""
		  SVGALIB_CONFIG_FILE
		  "\" appropriately.\n"
		  "Or shall I try to do it for you? You have to have write access to the\n"
		  "config file to do it! (y/n) ", stderr);
	    i = getchar();
	    if (i != '\n')
		while (getchar() != '\n');
	    if ((i != 'y') && (i != 'Y')) {
		fprintf(stderr,"Ok, then do it yourself.\n");
		exit(0);
	    }
	    /* Toast setuid settings coz of security breach of system().. */

	    setreuid(getuid(), getuid());
	    setuid(getuid());
	    setgid(getgid());

	    strcpy(linebuffer, "rm -f "
		   SVGALIB_CONFIG_FILE
		   ".bak; mv "
		   SVGALIB_CONFIG_FILE
		   " "
		   SVGALIB_CONFIG_FILE
		   ".bak; echo clocks");
	    for (i = 0; i < 16; i++)
		sprintf(strchr(linebuffer, 0), " %d", mach32_clocks[i]);
	    strcat(linebuffer, " | cat - "
		   SVGALIB_CONFIG_FILE
		   ".bak >"
		   SVGALIB_CONFIG_FILE);
	    if (system(linebuffer))
		fprintf(stderr,"Failed (at least partial, maybe there was just no config file yet).\n");
	    else
		fprintf(stderr,"Ok. (But you should check it anyway)\n");
	    exit(0);
	}
#ifdef DEBUG
	fprintf(stderr,"ATI-EEPROM contents:");
	for (i = 0; i < 128; i++) {
	    if (i & 7)
		putc(' ',stderr);
	    else
		fprintf(stderr,"");
	    fprintf(stderr,"%02x - %04x", i, mach32_eeprom[i]);
	}
#ifdef DEBUG_KEY
	fputs("\n(Hit Return)", stderr);
	while (getchar() != '\n');
#else
	putc('\n',stderr);
#endif				/* DEBUG_KEY */
#endif				/* DEBUG */
    }
/*create a scratch copy. */
    memcpy(mach32_eeprom + 128, mach32_eeprom, (size_t) (128 * sizeof(short)));

/*Get current memcfg */
    mach32_memcfg = inw(MEM_CFG) & 0xfff3;

/*If aperture not currently enabled but configured in EEPROM or setup already,
   then setup a faked setuplinear, switched off with setuplinear 0 0 */

    if ((!(mach32_memcfg & 3)) && (((mach32_eeprom[6] + 1) & 3) > 1) && (!mach32_apsiz)) {
	/*create setup from eeprom: */
	mach32_apsiz = (mach32_eeprom[6] & 3);
	mach32_apadd = (mach32_eeprom[6] >> 4);
	eeapert = 1;
    }
/*setup up memory aperture if requested: */

    if (mach32_apsiz) {
	unsigned new_memcfg;

	if (!mach32_apadd)
	    mach32_memcfg = 0;	/*disable any aperture */
	else {
	    if (((cfg1 & BUS_TYPE) == PCI) || ((cfg2 & Z4GBYTE) && !(cfg2 & LOCAL_BUS_CONF2))) {
		/* 4GB address range... */
		new_memcfg = (mach32_apadd << 4) & 0xfff0;
		i = 4096;
	    } else {
		/* 128MB address range... */
		new_memcfg = (mach32_apadd << 8) & 0xff00;
		i = 128;
	    }
	    new_memcfg |= mach32_apsiz;
	    if ((cfg1 & BUS_TYPE) == ISA)
		i = 16;
	    i -= mach32_apadd;
	    i -= (mach32_apsiz == 1) ? 1 : 4;
	    if (i < 0) {
		fprintf(stderr,"svgalib-mach32: Dangerous warning:\n"
		     "\tsetuplinear setting exceeds phys. address range of card!\n"
		     "\tSetting ignored.\n");
		mach32_apsiz = 0;
	    } else {
		/* will be actually set by setappage(0) call in setmode */
		mach32_memcfg = new_memcfg;
	    }
	}
    }
    if (dac_override > 5)
	mach32_dac = (cfg1 >> 9) & 7;
    else
	mach32_dac = dac_override;
    mach32_chiptype = mem_aper[mach32_memcfg & 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) {
	fprintf(stderr,"mach32: Warning, unknown DAC type: %d. Assuming stupid 8bpp DAC rated at 80Mhz!\n",
	       mach32_dac);
	/* Assumption is already done in all tables used... */
    }
    if (!(DAC_SAFETY & (1 << mach32_dac))) {
	char *cptr;
	/* Safety notification */
	cptr = getenv("SVGALIB_MACH32");
	if (cptr == NULL) {
	  messexit:
	    fprintf(stderr,"\aWarning!! The mach32 driver of svgalib was never tried with a DAC type\n"
		 "!=4,3,2,0. 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 changing 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, or mat42b@aixrs1.hrz.uni-essen.de.\n.\n");
	    exit(-1);
	}
	if (strcmp(cptr, "ILLTRYIT"))
	    goto messexit;
    }
    if (mach32_dac != 2)
	pos_ext_settings &= ~VGA_CLUT8;


/* 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 int) (mach32_memcfg & 0xfff0)) << 16;
	} else {
	    /* 128MB address range... */
	    apertloc = ((unsigned int) (mach32_memcfg & 0xff00)) << 12;
	}
#ifdef DEBUG
	fprintf(stderr,"mach32: Physical aperture starts at %08x.\n", apertloc);
#endif
	mach32_aperture = (char *) mmap(NULL,
	      (mach32_chiptype & MACH32_APERTURE_4M) ? 4194304 : 1048576,
					PROT_READ | PROT_WRITE,
					MAP_SHARED,
					__svgalib_mem_fd,
					apertloc);
	/* I trust the comment about memory waste in vgamisc.c... */
	if (mach32_aperture == MAP_FAILED) {
	    i = errno;
	    fprintf(stderr,"mach32: Mmaping of aperture failed.\nError was %d", i);
	    errno = i;
	    perror(" - ");
	    exit(-1);
	}
#ifdef DEBUG
	fprintf(stderr,"mach32: Aperture mapped to %p.\n", mach32_aperture);
#endif
    }
    __svgalib_driverspecs = &__svgalib_mach32_driverspecs;

    __svgalib_banked_mem_base=0xa0000;
    __svgalib_banked_mem_size=0x10000;
/*    __svgalib_linear_mem_base=MACH32_APERTURE;
    __svgalib_linear_mem_sizemach32_memory*0x400;  */

/* Check if imageblit emulation possible: */
    i = 0;
    if (mach32_chiptype & MACH32_APERTURE_4M)
	i = (4 << 10);
    else if (mach32_chiptype & MACH32_APERTURE)
	i = (1 << 10);
    if (i < mach32_memory) {	/*aperture to small */
	emuimage &= ~(EMU_OVER | EMU_POSS);
    }
    if (!mach32_modes) {
	mach32_modes = malloc(__svgalib_max_modes * sizeof(mode_entry *));
	if (!mach32_modes) {
	    fprintf(stderr,"mach32: No memory for dynamic mode table.\n");
	    exit(1);
	}
    }
/*Calculate the double and triple clocks */
    for (i = 0; i < 32; i++)
	mach32_clock_by2[i] = mach32_search_clk(mach32_clocks[i] << 1);
    for (i = 0; i < 32; i++)
	mach32_clock_by3[i] = mach32_search_clk(mach32_clocks[i] * 3);

#ifdef DEBUG
    fprintf(stderr,"15/16bpp DAC1/4 clock_maps:\n");
    for (i = 0; i < 32; i++) {
	if (!(i & 7))
	    putc('\n',stderr);
	if (mach32_clock_by2[i])
	    fprintf(stderr,"%3d->%3d  ", mach32_clocks[i], mach32_clocks[mach32_clock_by2[i] - 1]);
	else
	    fprintf(stderr,"%3d->  -  ", mach32_clocks[i]);
    }
    fprintf(stderr,"\n24bpp DAC1/4 clock_maps:\n");
    for (i = 0; i < 32; i++) {
	if (!(i & 7))
	    putc('\n',stderr);
	if (mach32_clock_by3[i])
	    fprintf(stderr,"%3d->%3d  ", mach32_clocks[i], mach32_clocks[mach32_clock_by3[i] - 1]);
	else
	    fprintf(stderr,"%3d->  -  ", mach32_clocks[i]);
    }
    putc('\n',stderr);
#endif

/*Populating database: */
    if (verbose)
	fprintf(stderr,"Populating mode table:\n");

    for (i = 0; i < __svgalib_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);
    }
    if (verbose)
	fprintf(stderr,"Squeeze in run-time config:\n");
    mach32_final_modefixup();

    if (__svgalib_driver_report) {
	sprintf(messbuf, " at %uM", apertloc >> 20);
	fprintf(stderr,"Using Mach32 driver 2.1 (%s apert%s (%s), %dK mem, DAC %d%s%s).\n",
	       (mach32_chiptype & MACH32_APERTURE) ?
	       ((mach32_chiptype & MACH32_APERTURE_4M) ? "4M" : "1M")
	       : "no",
	       (mach32_chiptype & MACH32_APERTURE) ? messbuf : "",
	    mach32_apsiz ? (eeapert ? "EEPROM" : "setup") : "autodetect",
	    mach32_memory, mach32_dac, (dac_override < 6) ? "(set)" : "",
	       force ? ", forced" : "");
    }
    return 0;
}

static inline int col2msk(struct vgainfo *iptr)
{
    switch (iptr->colors) {
    case 1 << 24:
	if (iptr->bytesperpixel == 3)
	    return 4;
#ifdef SUPP_32BPP
	if (iptr->bytesperpixel == 4)
	    return 8;
#endif
	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 vgainfo *iptr)
{
    return iptr->bytesperpixel;
}

static int mach32_log2(struct vgainfo *iptr)
{
    int res = -1, n = iptr->colors;

    while (n) {
	res++;
	n >>= 1;
    }
    if ((res == 24) && (iptr->bytesperpixel == 4))
	return 32;
    return res;
}

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

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

    clock = mach32_clocks[n_clock = 0x1f & (mode->clock_sel >> 2)];
    if (!clock) {
	if (verbose)
	    fprintf(stderr,"%s","Illegal clock #%d of unknown frequency! (rejected)\n");
	return;
    }
    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: */
    i = modemask;
    modemask &= mach32_mmask[(clock > 80) ? 1 : 0][mach32_dac];
    if (verbose && (i != modemask)) {
	fprintf(stderr,"Modemask (32bpp,24bpp,16bpp,8bpp) %x cut down to %x by DAC restrictions.\n", i, modemask);
    }
/*Check if needed multiples are available */
    if ((mach32_dac == MACH32_BT481) || (mach32_dac == MACH32_SC11483)) {
	if ((modemask & 2) && (!mach32_clock_by2[n_clock])) {
	    modemask &= ~2;
	    if (verbose)
		fprintf(stderr,"DAC1/4: There is no clock with two times the requested\n"
		       "\tfreq. of %dMHz => no 15 or 16 bpp.\n", clock);
	}
	if ((mach32_dac == MACH32_BT481) && (modemask & 4) && (!mach32_clock_by3[n_clock])) {
	    modemask &= ~4;
	    if (verbose)
		fprintf(stderr,"DAC4: There is no clock with three times the requested\n"
		       "\tfreq. of %dMHz => no 24bpp.\n", clock);
	}
    }
    i = modemask;
/*Rate down clocks exceeding mem bandwidth */
    if (clock > mach32_maxclk8)
	modemask &= ~1;
    if (clock > mach32_maxclk16)
	modemask &= ~2;
    if (clock > mach32_maxclk24)
	modemask &= ~4;
    if (clock > mach32_maxclk32)
	modemask &= ~8;

    if (verbose && (i != modemask))
	fprintf(stderr,"Modemask (32bpp,24bpp,16bpp,8bpp) %x cut down to %x by maxclocks.\n", i, modemask);

    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 < __svgalib_max_modes) && (forcein < 0); i++, iptr++) {
      jumpin:
	if ((iptr->xdim != wid) || (iptr->ydim != hei) || !(modemask & col2msk(iptr)))
	    continue;
	cmpvert = (mode->disp_cntl & 0x10) ? 2.0 * vert  : vert;
	if (verbose) {
	    fprintf(stderr,"%4ux%4ux%2u: ",
		   wid, hei, mach32_log2(iptr));
	    fprintf(stderr,"%3d MHz Clock, %6.3f KHz horz., %6.3f Hz vert.,\n\t%s, VFIFO(16-%d,24-%d)",
		   clock, horz, cmpvert,
	      (mode->disp_cntl & 0x10) ? "Interlaced" : "Non-Interlaced",
		   mode->vfifo16, mode->vfifo24);
	}
	if (iptr->xbytes * iptr->ydim > mach32_memory * 1024) {
	    if (verbose)
		fprintf(stderr," (not enough memory)\n");
	    continue;
	}
	if (__svgalib_horizsync.max < OFF_ALLOWANCE(horz * 1000)) {
	    if (verbose)
		fprintf(stderr," (rejected, hsync too high)\n");
	    continue;
	}
	if (__svgalib_horizsync.min > ADD_ALLOWANCE(horz * 1000)) {
	    if (verbose)
		fprintf(stderr," (rejected, hsync too low)\n");
	    continue;
	}
	if (__svgalib_vertrefresh.max < OFF_ALLOWANCE(cmpvert)) {
	    if (verbose)
		fprintf(stderr," (rejected, vsync too high)\n");
	    continue;
	}
	if (__svgalib_vertrefresh.min > ADD_ALLOWANCE(cmpvert)) {
	    if (verbose)
		fprintf(stderr," (rejected, vsync too low)\n");
	    continue;
	}
	if ((mach32_modes[i] == NULL) || (forcein >= 0)) {
	    if (verbose)
		fprintf(stderr," (accepted)\n");
	  fillin:
	    mach32_modes[i] = mode;
	    continue;
	}
	if ((mach32_modes[i]->disp_cntl ^ mode->disp_cntl) & 0x10) {
	    if (mode->disp_cntl & 0x10) {
		if (verbose)
		    fprintf(stderr," (rejected, is interlaced)\n");
		continue;
	    } else {
		if (verbose)
		    fprintf(stderr," (preferred, is non-interlaced)\n");
		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++;
	/* Note: both modes are either interlaced or not, hence we directly compare vsyncs */
	n_vert = (n_horz * 1000) / tmp;
	if (n_vert < vert) {
	    if (verbose)
		fprintf(stderr," (higher V_SYNC preferred)\n");
	    goto fillin;
	}
	if (verbose)
	    fprintf(stderr," (rejected, have a better one already)\n");
    }
}

static int mach32_modeavailable(int mode)
{
    if (mode >= __svgalib_max_modes)
	return 0;
    if (mach32_modes[mode] == NULL)
	return __svgalib_vga_driverspecs.modeavailable(mode);
    return SVGADRV;
}

static void mach32_getmodeinfo(int mode, vga_modeinfo * modeinfo)
{
    if (mode >= __svgalib_max_modes)
	return;
    modeinfo->flags |= HAVE_EXT_SET;
    modeinfo->haveblit = 0;

    if (mach32_modes[mode] == NULL)
	return;

    if (infotable[mode].bytesperpixel <= 2)
	modeinfo->haveblit = acc_supp;
    else if (emuimage & EMU_POSS) {	/* imageblt can be emulated with memory aperture */
	modeinfo->haveblit = acc_supp & HAVE_IMAGEBLIT;
    }
    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 = (PIXALIGN * infotable[mode].bytesperpixel);
	if ((infotable[mode].bytesperpixel == 4) ||
#ifdef FAKEBGR
	    ((infotable[mode].bytesperpixel == 3) && (mach32_dac == MACH32_ATI6871)))
#else
	    ((infotable[mode].bytesperpixel == 3) && (mach32_dac == MACH32_BT481)))
#endif
	    modeinfo->flags |= RGB_MISORDERED;
    } else
	modeinfo->linewidth_unit = (PIXALIGN * 2);

    modeinfo->bytesperpixel = infotable[mode].bytesperpixel;
    modeinfo->linewidth = infotable[mode].xbytes;
    modeinfo->maxlogicalwidth = 0xff * 8 * modeinfo->bytesperpixel;

/* This is obsolete.. modeinfo->bytesperpixel should always be > 0 here */
    if (modeinfo->bytesperpixel > 0) {
	modeinfo->maxpixels = (mach32_memory * 1024) / modeinfo->bytesperpixel;
    } else {
	modeinfo->maxpixels = (mach32_memory * 1024);
    }

    modeinfo->startaddressrange = 0xfffffffc & (mach32_memory * 1024 - 1);

    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);
}

static void mach32_blankadj(int adj)
{
    if(mach32_ast)
	return;
#ifdef DEBUG
    fprintf(stderr,"mach32_blankadj(%d)\n", adj);
#endif
    port_out_r(MISC_CTL + 1, (port_in(R_MISC_CTL + 1) & 0xf0) | adj);
}

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

    type = mach32_modeavailable(mode);
    if (!type)
	return 1;

#ifdef DEBUG
    fprintf(stderr,"mach32_setmode: %d -> %d\n", previous, mode);
#endif

    if (mach32_modeavailable(previous) == SVGADRV)
	mach32_bltwait();	/* Ensure noone draws in the screen */

    mach32_accelstate = R_UNKNOWN;	/* Accel registers need to be reset */

#ifdef DEBUG
    fprintf(stderr,"mach32_setmode: accel set.\n");
#endif

    if (type != SVGADRV) {
	int i;
	const unsigned char *ptr;
	unsigned int val;

#ifdef DEBUG
	fprintf(stderr,"mach32_setmode: standard mode:%d\n", mode);
#endif

	__svgalib_mach32_driverspecs.emul = NULL;

	/*if imageblit works at all, it'll be this one: */
	__svgalib_mach32_driverspecs.imageblt = mach32_memimageblt;

	/*Set std dac-mode */
	mach32_set_dac(DAC_MODE8, 0, 0);

	/* Ensure we are in VGA-mode: */
	port_outw_r(CLOCK_SEL, (inw(CLOCK_SEL) & ~0x7f) | 0x10);	/* slow clock and disable
								   Mach CRT */
	port_outw_r(DISP_CNTL, mach32_disp_shadow | 0x40);	/* Mach32 CRT reset */

	/* Force all extended ati regs to std. vga mode. This is needed for
	   correct function when using non standard console text modes. */

	/* Stop vga sequencer: */
	port_out_r(SEQ_I, 0x00);
	port_out_r(SEQ_D, 0x00);

	/* Handle the important reg 0x38: clock-diff by from bits 0+1 svga_clock, no herc emul */

	port_out_r(ATIPORT, ATISEL(0x38));
	val = port_in(ATIPORT + 1);
	if (svga_clock >= 0)
	    val = (val & 0x3f) | ((svga_clock << 6) & 0xc0);
	port_outw_r(ATIPORT, ATISEL(0x38) | (val << 8));

	/* Handle the important reg 0x39: Select some clock */

	port_out_r(ATIPORT, ATISEL(0x39));
	val = port_in(ATIPORT + 1);
	if (svga_clock >= 0)
	    val = (val & 0xfc) | ((svga_clock >> 2) & 0x03);
	port_outw_r(ATIPORT, ATISEL(0x39) | (val << 8));

	/* Register 3e, reset several bits and set clock as needed */
	port_out_r(ATIPORT, ATISEL(0x3e));
	val = port_in(ATIPORT + 1) & 0x30;
	if (svga_clock >= 0)
	    val = (val & 0xef) | (svga_clock & 0x10);
	port_outw_r(ATIPORT, ATISEL(0x3e) | (val << 8));

	/* reset several remaining extended bits: */
	for (i = 0, ptr = mach32_ati_ext; i < (sizeof(mach32_ati_ext) / 2); i++, ptr += 2) {
	    port_out_r(ATIPORT, ATISEL(*ptr));
	    val = port_in(ATIPORT + 1) & ~ptr[1];
	    port_outw_r(ATIPORT, ATISEL(*ptr) | (val << 8));
	}

	/* sequencer is reenabled by vga setmode */
	return __svgalib_vga_driverspecs.setmode(mode, previous);
	/* vga_setmode ensured vga is notblanked... */
    } else {
	/* handle the selection of imageblit */
	if ((infotable[mode].colors != (1 << 24))	/* mode is supported by engine */
	    &&(!(emuimage & EMU_OVER))	/* and should not be overridden */
	    )
	    __svgalib_mach32_driverspecs.imageblt = mach32_imageblt;
	else
	    __svgalib_mach32_driverspecs.imageblt = mach32_memimageblt;
    }

    __svgalib_mach32_driverspecs.emul = &mach32_vgaemul;

#ifdef DEBUG
    fprintf(stderr,"mach32_setmode: Setting vga in 8bpp graphmode.\n");
#endif

/*Ripped out from Xfree.. mach32 Server.. */
    port_outw_r(SEQ_I, 4 | (0x0a << 8));
/* Enable write access to all memory maps */
    port_outw_r(SEQ_I, 2 | (0x0f << 8));
/* Set linear addressing mode */
    port_outw_r(ATIPORT, ATISEL(0x36) | (0x05 << 8));
/* Set the VGA display buffer to 0xa0000 */
    port_outw_r(GRA_I, 6 | (0x05 << 8));
/*These are mine...someone turned the VGA to erase on write....(Eeeek!) */
    port_outw_r(GRA_I, 1 | (0x00 << 8));
    port_outw_r(GRA_I, 3 | (0x00 << 8));
    port_outw_r(GRA_I, 5 | (0x00 << 8));
    port_outw_r(GRA_I, 7 | (0x00 << 8));
    port_outw_r(GRA_I, 8 | (0xff << 8));
/*Switch on any videomemory latches we have: */
    port_outw_r(MISC_OPTIONS, (inw(MISC_OPTIONS) & 0x8f7f) | (latchopt & 0x7080));
#ifdef DEBUG
    fprintf(stderr,"mach32_setmode: extended mode:%d\n", mode);
#endif

    mptr = mach32_modes[mode];

    mach32_disp_shadow = mptr->disp_cntl & 0x7f;
    port_outw_r(DISP_CNTL, mach32_disp_shadow | 0x60);		/* Mach32 CRT reset */
    port_outw_r(MULTI_FUNC_CNTL, 0x5006);	/* Linear memory layout */
/*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;
	/* Force VFIFO in clock_sel to zero: */
	clock_intended &= 0xf0ff;
	clock_intended |= (vfifo8 << 8);
	break;
    case 1 << 15:
	clock_intended = mach32_set_dac(DAC_MODE555,
		    dac14_clock_adjust(clock_intended, mach32_clock_by2),
					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;
	vfifo = max(mptr->vfifo16, mach32_max_vfifo16(infotable[mode].xdim));
	clock_intended |= (vfifo << 8);
	break;
    case 1 << 16:
	clock_intended = mach32_set_dac(DAC_MODE565,
		    dac14_clock_adjust(clock_intended, mach32_clock_by2),
					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;
	vfifo = max(mptr->vfifo16, mach32_max_vfifo16(infotable[mode].xdim));
	clock_intended |= (vfifo << 8);
	break;
    case 1 << 24:
	clock_intended = mach32_set_dac(
	(infotable[mode].bytesperpixel == 3) ? DAC_MODERGB : DAC_MODE32B,
		    dac14_clock_adjust(clock_intended, mach32_clock_by3),
					   infotable[mode].xdim);
	act_ge_conf = inw(R_EXT_GE_CONF) & 0xf80f;
	if (infotable[mode].bytesperpixel == 3) {
	    act_ge_conf |= 0x0030;
#ifdef FAKEBGR
	    if (mach32_dac == MACH32_ATI6871)
		act_ge_conf |= 0x0400;
#endif
	    vfifo = max(mptr->vfifo24, vfifo24);
	}
#ifdef SUPP_32BPP
	else {
#ifdef USE_RGBa
	    act_ge_conf |= 0x0630;
#else
	    act_ge_conf |= 0x0230;
#endif
	    vfifo = max(mptr->vfifo24, vfifo32);
	}
#endif
	/* Force VFIFO in clock_sel to zero: */
	clock_intended &= 0xf0ff;
	clock_intended |= (vfifo << 8);
	break;
    }
    port_outw_r(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
    fprintf(stderr,"GE_CONF:%hx\n", act_ge_conf);
    fprintf(stderr,"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
    port_out_r(H_DISP, mptr->h_disp);
    port_out_r(H_TOTAL, mptr->h_total);
    port_out_r(H_SYNC_STRT, mptr->h_sync_strt);
    port_out_r(H_SYNC_WID, mptr->h_sync_wid);
    port_outw_r(V_TOTAL, mptr->v_total);
    port_outw_r(V_DISP, mptr->v_disp);
    port_outw_r(V_SYNC_STRT, mptr->v_sync_strt);
    port_outw_r(V_SYNC_WID, mptr->v_sync_wid);

#ifdef DEBUG
    port_outw_r(CLOCK_SEL, clock_intended);
#else
    port_outw_r(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);
    mach32_setappage(0);	/* This sets up a requested mem aperture too.. */

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

#if defined(DEBUG_KEY)&&defined(DEBUG)
    fputs("\n(Hit Return)", stderr);
    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) {
	    fprintf(stderr,"mach32-config: Fatal error: Out of memory.\n");
	    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[6], *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%5s", &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;
    }
#ifdef SUPP_32BPP
    else if (!strcmp(tmpstr, "16m32")) {
	colors = (1 << 24);
	bytesperpixel = 4;
    } else if (!strcmp(tmpstr, "16m4")) {	/* compatibility */
	colors = (1 << 24);
	bytesperpixel = 4;
    }
#endif
    else {
	fprintf(stderr,"mach32-config: mode %s unsupported, only 256, 32K, 64K, 16M, 16M32 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 < __svgalib_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 = __svgalib_addmode(width, height, colors, linelength, bytesperpixel)) < 0) {
	fprintf(stderr,"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_conf_commands[] =
{
    "mach32eeprom", "inhibit", "define", "setlinelength", "maxclock16",
    "maxclock24", "clocks", "variablelinelength", "strictlinelength", "duplicatelinelength",
    "maxclock8", "maxclock32", "verbose", "quiet", "vfifo8",
    "latch", "blank", "vfifo16", "vfifo24", "vfifo32",
    "setuplinear", "blit", "ramdac", "svgaclock", "vendor", "misc_ctl",
    NULL};

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

    switch (command) {
    case 0:
	{
	    int position = 0;	/* We are handling the POSITION-th parameter */

	    if (!mode) {
	      access_denied:
		fprintf(stderr,"mach32-config: %s command access denied.",
		       mach32_conf_commands[command]);
		break;
	    }
	    for (;;) {
		position++;
		ptr = strtok(NULL, " ");
		if (ptr == NULL) {
		    if (position <= 1)
			fprintf(stderr,"mach32-config: mach32eeprom "
			     "command expects a parameter.\n");
		    return ptr;
		}
		if (!strcasecmp(ptr, "compatible")) {
		    eeprom_option |= EEPROM_USE_CHKSUM |
			EEPROM_USE_MEMCFG | EEPROM_USE_TIMING;
		} else if (!strcasecmp(ptr, "ignore")) {
		    eeprom_option &= ~(EEPROM_USE_CHKSUM |
				  EEPROM_USE_MEMCFG | EEPROM_USE_TIMING);
		} else if (!strcasecmp(ptr, "useaperture")) {
		    eeprom_option |= EEPROM_USE_MEMCFG;
		} else if (!strcasecmp(ptr, "usetimings")) {
		    eeprom_option |= EEPROM_USE_TIMING;
		} else if (!strcasecmp(ptr, "nofile")) {
		    eeprom_fname = NULL;
		} else if (!strcasecmp(ptr, "updatefile")) {
		    eeprom_option |= EEPROM_UPDATE;
		} else if (!strcasecmp(ptr, "keepfile")) {
		    eeprom_option &= ~EEPROM_UPDATE;
		} else if (!strcasecmp(ptr, "file")) {
		    ptr = strtok(NULL, " ");
		    if (ptr == NULL) {
			fprintf(stderr,"mach32-config: mach32eeprom file subcommand "
			     "expects a parameter.\n");
			return ptr;
		    }
		    goto fileparam;
		} else if (position == 1) {
		  fileparam:
		    if (*ptr == '/') {
			if (eeprom_fname != NULL)
			    free(eeprom_fname);
			eeprom_fname = strdup(ptr);
			if (eeprom_fname == NULL) {
			    fprintf(stderr,"mach32-config: Fatal error: out of memory.\n");
			    exit(-1);
			}
		    } else {
			fprintf(stderr,"mach32-config: mach32eeprom: filename has "
			     "to start with a / !!\n");
		    }
		} else
		    return ptr;
	    }
	}
	break;
    case 1:
	for (;;) {
	    ptr = strtok(NULL, " ");
	    i = parsemode(ptr, 0, 0);
	    if (i == -1)
		return ptr;
	    if (i >= 0)
		*push(1) = CMD_DEL + i;
	}
	break;
    case 2:			/* 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) {
	    fprintf(stderr,"mach32-config: Invalid define command, no valid vga-mode given\n");
	  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:
	    fprintf(stderr,"mach32-config: Invalid define command, clock is invalid\n");
	    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
	fprintf(stderr,"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:
	    fprintf(stderr,"mach32-config: Invalid define command, timing is invalid\n");
	    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 (!ptr)
		break;
	    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 3:
	ptr = strtok(NULL, " ");
	if (!isnumber(ptr)) {
	    fprintf(stderr,"mach32-config: illegal setlinelength command.\n"
		 "Usage: setlinelength integer modes...\n");
	    return ptr;
	}
	i = atoi(ptr);
	do
	    ptr = strtok(NULL, " ");
	while (parsemode(ptr, i, 0) != -1);
	return ptr;
    case 4:
	ptr = strtok(NULL, " ");

	if (!mode) {
	  maxclk_deny:
	    fprintf(stderr,"Don't use the maxclock's commands out of the environment variable.\n");
	    return ptr;
	}
	if (!isnumber(ptr)) {
	  ilmaxclk:
	    fprintf(stderr,"mach32-config: illegal maxclock16 or maxclock24 command.\n"
		 "Usage: maxclock16 integer or maxclock24 integer\n");
	    return ptr;
	}
	mach32_maxclk16 = atoi(ptr);
	break;
    case 5:
	ptr = strtok(NULL, " ");
	if (!mode)
	    goto maxclk_deny;
	if (!isnumber(ptr))
	    goto ilmaxclk;
	mach32_maxclk24 = atoi(ptr);
	break;
    case 6:
	if (!mode)
	    goto access_denied;
	for (i = 0; i < 16; i++) {
	    ptr = strtok(NULL, " ");
	    clocks_set = 1;
	    if (!isnumber(ptr)) {
		fprintf(stderr,"mach32-config: illegal clocks command.\n"
		     "Usage: clocks integer integer ...\n"
		     "16 clocks have to be specified.\n"
		     "specify 0 for unsupported clocks\n"
		     "(using 0's for the mistyped clocks)\n");
		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);
	}
	/*Set halfed clocks: */
	for (i = 0; i < 16; i++)
	    mach32_clocks[i + 16] = mach32_clocks[i] / 2;
	break;
    case 7:
	mach32_strictness = 0;
	break;
    case 8:
	mach32_strictness = 1;
	break;
    case 9:
	mach32_strictness = 2;
	break;
    case 10:
	ptr = strtok(NULL, " ");
	if (!mode)
	    goto maxclk_deny;
	if (!isnumber(ptr))
	    goto ilmaxclk;
	mach32_maxclk8 = atoi(ptr);
	break;
    case 11:
	ptr = strtok(NULL, " ");
	if (!mode)
	    goto maxclk_deny;
	if (!isnumber(ptr))
	    goto ilmaxclk;
	mach32_maxclk32 = atoi(ptr);
	break;
    case 12:
	verbose = 1;
	break;
    case 13:
#ifndef DEBUG
	verbose = 0;
#endif
	break;
    case 14:
	ptr = strtok(NULL, " ");
	if (!mode) {
	  tweak_deny:
	    fprintf(stderr,"The vfifo, latch, blank commands are not allowed out of the environment.\n");
	    return ptr;
	}
	if (!isnumber(ptr)) {
	  ilvfi:
	    fprintf(stderr,"Illegal vfifo command\n");
	    return ptr;
	}
	vfifo8 = atoi(ptr) & 0xf;
	break;
    case 15:
	ptr = strtok(NULL, " ");
	if (!mode)
	    goto tweak_deny;
	if (!isnumber(ptr)) {
	    fprintf(stderr,"Illegal latch command\n");
	    return ptr;
	}
	latchopt = atoi(ptr);
	break;
    case 16:
	ptr = strtok(NULL, " ");
	if (!mode)
	    goto tweak_deny;
	if (!isnumber(ptr)) {
	    fprintf(stderr,"Illegal blank command\n");
	    return ptr;
	}
	bladj = atoi(ptr) & 0xf;
	break;
    case 17:
	ptr = strtok(NULL, " ");
	if (!mode)
	    goto tweak_deny;
	if (!isnumber(ptr))
	    goto ilvfi;
	vfifo16 = atoi(ptr) & 0xf;
	break;
    case 18:
	ptr = strtok(NULL, " ");
	if (!mode)
	    goto tweak_deny;
	if (!isnumber(ptr))
	    goto ilvfi;
	vfifo24 = atoi(ptr) & 0xf;
	break;
    case 19:
	ptr = strtok(NULL, " ");
	if (!mode)
	    goto tweak_deny;
	if (!isnumber(ptr))
	    goto ilvfi;
	vfifo32 = atoi(ptr) & 0xf;
	break;
    case 20:
	ptr = strtok(NULL, " ");
	if (!isnumber(ptr)) {
	  ilsetupli:
	    fprintf(stderr,"Illegal setuplinear command.\n"
		 "usage: setuplinear address size\n"
		 "where size = 1 or 4 and both are in MB\n");
	    return ptr;
	}
	i = atoi(ptr);
	ptr = strtok(NULL, " ");
	if (!strcmp(ptr, "4"))
	    flag = 1;
	else if (!strcmp(ptr, "1"))
	    flag = 0;
	else if (!strcmp(ptr, "0")) {
	    flag = 0;
	    i = 0;
	    goto setuplin;
	} else
	    goto ilsetupli;
	if ((i & ~0xfff) || (!i)) {
	    fprintf(stderr,"setuplinear: address out of range\n");
	    return ptr;
	}
      setuplin:
	if (!mode) {
	    fprintf(stderr,"setuplinear config option strictly disallowed from the environment.\n");
	    return ptr;
	}
	mach32_apsiz = flag + 1;
	mach32_apadd = i;
	break;
    case 21:
	for (;;) {
	    ptr = strtok(NULL, " ");
	    if (!ptr)
		break;
	    if (!strcasecmp(ptr, "bit"))
		acc_supp |= HAVE_BITBLIT;
	    else if (!strcasecmp(ptr, "nobit"))
		acc_supp &= ~HAVE_BITBLIT;
	    else if (!strcasecmp(ptr, "fill"))
		acc_supp |= HAVE_FILLBLIT;
	    else if (!strcasecmp(ptr, "nofill"))
		acc_supp &= ~HAVE_FILLBLIT;
	    else if (!strcasecmp(ptr, "image")) {
		acc_supp |= HAVE_IMAGEBLIT;
		emuimage &= ~EMU_OVER;
	    } else if (!strcasecmp(ptr, "noimage")) {
		acc_supp &= ~HAVE_IMAGEBLIT;
	    } else if (!strcasecmp(ptr, "memimage")) {
		acc_supp |= HAVE_IMAGEBLIT;
		emuimage |= EMU_OVER | EMU_POSS;
	    } else if (!strcasecmp(ptr, "nomemimage")) {
		emuimage &= ~(EMU_OVER | EMU_POSS);
	    } else if (!strcasecmp(ptr, "hlinelist")) {
		acc_supp |= HAVE_HLINELISTBLIT;
	    } else if (!strcasecmp(ptr, "nohlinelist")) {
		acc_supp &= ~HAVE_HLINELISTBLIT;
	    } else if (!strcasecmp(ptr, "settrans")) {
		accel_supp |= ACCELFLAG_SETTRANSPARENCY;
	    } else if (!strcasecmp(ptr, "nosettrans")) {
		accel_supp &= ~ACCELFLAG_SETTRANSPARENCY;
	    } else if (!strcasecmp(ptr, "setrop")) {
		accel_supp |= ACCELFLAG_SETRASTEROP;
	    } else if (!strcasecmp(ptr, "nosetrop")) {
		accel_supp &= ~ACCELFLAG_SETRASTEROP;
	    } else if (!strcasecmp(ptr, "fillbox")) {
		accel_supp |= ACCELFLAG_FILLBOX;
	    } else if (!strcasecmp(ptr, "nofillbox")) {
		accel_supp &= ~ACCELFLAG_FILLBOX;
	    } else if (!strcasecmp(ptr, "screencopy")) {
		accel_supp |= ACCELFLAG_SCREENCOPY;
	    } else if (!strcasecmp(ptr, "noscreencopy")) {
		accel_supp &= ~ACCELFLAG_SCREENCOPY;
	    } else if (!strcasecmp(ptr, "drawline")) {
		accel_supp |= ACCELFLAG_DRAWLINE;
	    } else if (!strcasecmp(ptr, "nodrawline")) {
		accel_supp &= ~ACCELFLAG_DRAWLINE;
	    } else if (!strcasecmp(ptr, "putimage")) {
		accel_supp |= ACCELFLAG_PUTIMAGE;
		emuimage &= ~EMU_OVER;
	    } else if (!strcasecmp(ptr, "noputimage")) {
		accel_supp &= ~ACCELFLAG_PUTIMAGE;
	    } else if (!strcasecmp(ptr, "drawhlinelist")) {
		accel_supp |= ACCELFLAG_DRAWHLINELIST;
	    } else if (!strcasecmp(ptr, "nodrawhlinelist")) {
		accel_supp &= ~ACCELFLAG_DRAWHLINELIST;
	    } else if (!strcasecmp(ptr, "putbitmap")) {
		accel_supp |= ACCELFLAG_PUTBITMAP;
	    } else if (!strcasecmp(ptr, "noputbitmap")) {
		accel_supp &= ~ACCELFLAG_PUTBITMAP;
	    } else if (!strcasecmp(ptr, "screencopymono")) {
		accel_supp |= ACCELFLAG_SCREENCOPYMONO;
	    } else if (!strcasecmp(ptr, "noscreencopymono")) {
		accel_supp &= ~ACCELFLAG_SCREENCOPYMONO;
	    } else if (!strcasecmp(ptr, "setmode")) {
		accel_supp |= ACCELFLAG_SETMODE | ACCELFLAG_SYNC;
	    } else if (!strcasecmp(ptr, "nosetmode")) {
		accel_supp &= ~(ACCELFLAG_SETMODE | ACCELFLAG_SYNC);
	    } else if (!strcasecmp(ptr, "polyline")) {
		accel_supp |= ACCELFLAG_POLYLINE;
	    } else if (!strcasecmp(ptr, "nopolyline")) {
		accel_supp &= ~ACCELFLAG_POLYLINE;
	    } else if (!strcasecmp(ptr, "polyhline")) {
		accel_supp |= ACCELFLAG_POLYHLINE;
	    } else if (!strcasecmp(ptr, "nopolyhline")) {
		accel_supp &= ~ACCELFLAG_POLYHLINE;
	    } else if (!strcasecmp(ptr, "polyfillmode")) {
		accel_supp |= ACCELFLAG_POLYFILLMODE;
	    } else if (!strcasecmp(ptr, "nopolyfillmode")) {
		accel_supp &= ~ACCELFLAG_POLYFILLMODE;
	    } else
		return ptr;
	}
	break;
    case 22:
	ptr = strtok(NULL, " ");
	if (!mode)
	    goto access_denied;
	if (!ptr)
	    goto invpar;
	else if (!strcasecmp(ptr, "auto"))
	    dac_override = 127;
	else if (!strcasecmp(ptr, "dumb"))
	    dac_override = MACH32_BT476;
	else if ((ptr[0] >= '0') && (ptr[0] <= '5') && (ptr[1] == 0))
	    dac_override = *ptr - '0';
	else {
	  invpar:
	    fprintf(stderr,"Invalid parameter:  \"%s %s\"\n",
		   mach32_conf_commands[command], ptr);
	}
	break;
    case 23:
	ptr = strtok(NULL, " ");
	if (!mode)
	    goto access_denied;
	if (!ptr)
	    goto invpar;
	else if (!strcasecmp(ptr, "keep"))
	    svga_clock = (-1);
	else {
	    if (!isnumber(ptr))
		goto invpar;
	    i = atoi(ptr);
	    if ((i < 0) || (i > 0x1f))
		goto invpar;
	    svga_clock = i;
	}
	break;
    case 24:
	ptr = strtok(NULL, " ");
	if (!mode)
	    goto access_denied;
	if (!ptr)
	    goto invpar;
	else if (!strcasecmp(ptr, "ati")) {
	    dac_override = 127;
	    svga_clock = SVGA_CLOCK;
	    eeprom_option |= EEPROM_USE_CHKSUM |
		EEPROM_USE_MEMCFG | EEPROM_USE_TIMING;
	    mach32_ast = 0;
	} else if (!strcasecmp(ptr, "dell")) {
	    dac_override = MACH32_BT476;
	    svga_clock = 0x00;
	    mach32_ast = 0;
	    eeprom_option &= ~(EEPROM_USE_CHKSUM |
			       EEPROM_USE_MEMCFG | EEPROM_USE_TIMING);
	} else if (!strcasecmp(ptr, "ast")) {
	    dac_override = 127;
	    svga_clock = SVGA_CLOCK;
	    mach32_ast = 1;
	    eeprom_option &= ~(EEPROM_USE_CHKSUM |
			       EEPROM_USE_MEMCFG | EEPROM_USE_TIMING);
	} else
	    goto invpar;
	break;
    case 25:
	ptr = strtok(NULL, " ");
	if (!mode)
	    goto access_denied;
	if (!ptr)
	    goto invpar;
	else if (!strcasecmp(ptr, "keep-off")) {
	    mach32_ast = 1;
	} else if (!strcasecmp(ptr, "use")) {
	    mach32_ast = 0;
	} else
	    goto invpar;
	break;
    }
    return strtok(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 < __svgalib_max_modes; i++) {
	if (infotable[i].bytesperpixel < 2)
	    continue;		/* Not needed for those.. also excludes STDVGA non MACH32 modes */
	j = infotable[i].xdim % PIXALIGN;
	if (j)
	    infotable[i].xbytes += infotable[i].bytesperpixel * (PIXALIGN - j);
    }
    __svgalib_read_options(mach32_conf_commands, mach32_procopt);
    if (mach32_strictness) {	/* Do something about the modes where we redefined xbytes.. */
	limit = GLASTMODE;
	for (i = G640x480x256; 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 = __svgalib_addmode(infotable[i].xdim, infotable[i].ydim, infotable[i].colors,
				  newxbytes, infotable[i].bytesperpixel);
		if (j < 0) {
		    fprintf(stderr,"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 */
		fprintf(stderr,"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) {
	fprintf(stderr,"mach32-config: Fatal: queue shrink failed.\n");
	exit(-1);
    }
}

static char *
 colstr(struct vgainfo *mode)
{
    static char str[4];
    if (mode->colors <= 256) {
	sprintf(str, "%d", mode->colors);
	return str;
    }
    switch (mode->colors) {
    case 1 << 15:
	return "32K";
    case 1 << 16:
	return "64K";
    case 1 << 24:
	if (mode->bytesperpixel == 3)
	    return "16M";
	if (mode->bytesperpixel == 4)
	    return "16M32";
    }
    return "?";
}

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
	    fprintf(stderr,"Calling mach32_modfill to add new mode.\n");
#endif
	    mach32_modfill((mode_entry *) ptm, col2msk(infotable + i), i);
	    if (mach32_modes[i] == oldentry) {
		fprintf(stderr,"mach32: Setting of mode %dx%dx%s failed.\n"
		       "Mode cannot be realized with your hardware\n"
		     "(or due to configured restrictions ;-) ), sorry.\n"
		       "specify verbose in config file or SVGALIB_CONFIG to get\n"
		       "detailed information\n",
		       infotable[i].xdim, infotable[i].ydim, colstr(infotable + i));
	    }
	    break;
	}
}

static int mach32_canaccel(void)
{
    if (CM >= __svgalib_max_modes)
	return 0;
    if (mach32_modes[CM] == NULL)
	return 0;
    return (infotable[CM].bytesperpixel <= 2);
}

static int mach32_ext_set(unsigned what, va_list params)
{
    int param2, old_values, retval;

    switch (what) {
    case VGA_EXT_AVAILABLE:
	param2 = va_arg(params, int);
	switch (param2) {
	case VGA_AVAIL_SET:
	    return VGA_EXT_AVAILABLE | VGA_EXT_SET | VGA_EXT_CLEAR | VGA_EXT_RESET;
	case VGA_AVAIL_ACCEL:
	    if (mach32_canaccel()) {
	    	retval = ACCELFLAG_SETFGCOLOR | ACCELFLAG_SETBGCOLOR |
		    ACCELFLAG_SETTRANSPARENCY | ACCELFLAG_SETRASTEROP |
		    ACCELFLAG_FILLBOX | ACCELFLAG_SCREENCOPY |
		    ACCELFLAG_DRAWLINE | ACCELFLAG_PUTIMAGE |
		    ACCELFLAG_DRAWHLINELIST | ACCELFLAG_PUTBITMAP |
		    ACCELFLAG_SCREENCOPYMONO | ACCELFLAG_SETMODE |
		    ACCELFLAG_POLYLINE | ACCELFLAG_POLYHLINE |
		    ACCELFLAG_POLYFILLMODE | ACCELFLAG_SYNC;
		retval = accel_supp & retval;
		if (! (retval & ~(ACCELFLAG_SETFGCOLOR | ACCELFLAG_SETBGCOLOR |
				  ACCELFLAG_SETTRANSPARENCY | ACCELFLAG_SETRASTEROP |
				  ACCELFLAG_POLYFILLMODE | ACCELFLAG_SYNC |
				  ACCELFLAG_SETMODE)) )
		     return 0; /* No real operations left at all */
		else
		     return retval;
	    } else if ((emuimage & EMU_POSS) && (CM < __svgalib_max_modes) &&
		       (mach32_modes[CM] != NULL)) {
		return ACCELFLAG_PUTIMAGE; /* putimage can be emulated with memory aperture */
	    } else
		return 0;
	case VGA_AVAIL_FLAGS:
	    if (((mach32_dacmode & 0x7f) == DAC_MODE8) ||
		((mach32_dacmode & 0x7f) == DAC_MODEMUX))
		return pos_ext_settings;
	    else
		return pos_ext_settings & ~VGA_CLUT8;
	case VGA_AVAIL_ROP:
	    if (mach32_canaccel() && (accel_supp & ACCELFLAG_SETRASTEROP)) {
	    	retval = ACCELFLAG_FILLBOX | ACCELFLAG_SCREENCOPY |
		    ACCELFLAG_PUTIMAGE | ACCELFLAG_DRAWLINE |
		    ACCELFLAG_SETFGCOLOR | ACCELFLAG_SETBGCOLOR |
		    ACCELFLAG_PUTBITMAP | ACCELFLAG_SCREENCOPYMONO |
		    ACCELFLAG_POLYLINE | ACCELFLAG_POLYHLINE |
		    ACCELFLAG_POLYFILLMODE | ACCELFLAG_DRAWHLINELIST;
		return accel_supp & retval;
	    } else
		return 0;
	case VGA_AVAIL_TRANSPARENCY:
	    if (mach32_canaccel() && (accel_supp & ACCELFLAG_SETTRANSPARENCY)) {
	        retval = ACCELFLAG_PUTBITMAP | ACCELFLAG_SCREENCOPYMONO | ACCELFLAG_POLYFILLMODE;
		return accel_supp & retval;
	    } else
		return 0;
	case VGA_AVAIL_ROPMODES:
	    if (mach32_canaccel() && (accel_supp & ACCELFLAG_SETRASTEROP))
		return (1 << ROP_COPY) | (1 << ROP_OR) | (1 << ROP_AND) |
			(1 << ROP_XOR) | (1 << ROP_INVERT);
	    else
		return 0;
	case VGA_AVAIL_TRANSMODES:
	    if (mach32_canaccel() && (accel_supp & ACCELFLAG_SETTRANSPARENCY))
		return (1 << ENABLE_BITMAP_TRANSPARENCY);
	    else
		return 0;
	}
	return 0;
    case VGA_EXT_SET:
	old_values = ext_settings;
	ext_settings |= (va_arg(params, int)) & pos_ext_settings;
      params_changed:
	if (((old_values ^ ext_settings) & pos_ext_settings) &&
	    (((mach32_dacmode & 0x7f) == DAC_MODE8) ||
	     ((mach32_dacmode & 0x7f) == DAC_MODEMUX))) {
	    CRITICAL = 1;
	    vga_lockvc();
	    if (ext_settings & VGA_CLUT8)
		port_outw_r(EXT_GE_CONF, inw(R_EXT_GE_CONF) | 0x4000);
	    else
		port_outw_r(EXT_GE_CONF, inw(R_EXT_GE_CONF) & 0xbfff);
	    vga_unlockvc();
	    CRITICAL = 0;
	}
	return old_values;
    case VGA_EXT_CLEAR:
	old_values = ext_settings;
	ext_settings &= ~((va_arg(params, int)) & pos_ext_settings);
	goto params_changed;
    case VGA_EXT_RESET:
	old_values = ext_settings;
	ext_settings = (va_arg(params, int)) & pos_ext_settings;
	goto params_changed;
    default:
	return 0;
    }
}

static void mach32_bitblt(int srcaddr, int destaddr, int w, int h, int pitch)
{
    int base, src_x, des_x, adj_w, forw_cpy;

#ifdef DEBUG
    fprintf(stderr,"mach32_bitblt(%xh,%xh,%d,%d,%d)\n", srcaddr, destaddr, w, h, pitch);
#endif

    base = (forw_cpy = (srcaddr > destaddr)) ? destaddr : srcaddr;

    base &= ~3;
    srcaddr -= base;
    destaddr -= base;

/*calculate resulting x values */
    src_x = srcaddr % pitch;
    des_x = destaddr % pitch;

/*width in bytes */
    adj_w = (infotable[CM].bytesperpixel == 2) ? w << 1 : w;

#ifdef DEBUG
    fprintf(stderr,"   base=%xh,src=%xh,des=%xh,src_x=%d,des_x=%d,adj_w=%d\n", base, srcaddr,
	   destaddr, src_x, des_x, adj_w);
#endif

/*Currentsetting invalid? */
    if (((src_x + adj_w) > pitch) || ((des_x + adj_w) > pitch)) {
	int adj;
	/* try to adjust: */
	adj = des_x - src_x;
	if (adj < 0)
	    adj = -adj;
	adj = pitch - (adj & ~3);
	srcaddr += adj;;
	destaddr += adj;
	base -= adj;
	if ((src_x += adj) >= pitch)
	    src_x -= pitch;
	if ((des_x += adj) >= pitch)
	    des_x -= pitch;

#ifdef DEBUG
	fprintf(stderr,"   src=%xh,des=%xh,src_x=%d,des_x=%d,adj=%d\n", srcaddr, destaddr, src_x, des_x, adj);
#endif
	/*Still invalid? */
	if (((src_x + adj_w) > pitch) || ((des_x + adj_w) > pitch))
	    goto f_ugh;
    }
/*reduce addresses to y values: */
    srcaddr /= pitch;
    destaddr /= pitch;

#ifdef DEBUG
    fprintf(stderr,"   >>src=%xh,des=%xh,base=%xh\n", srcaddr, destaddr, base);
#endif

    if ((srcaddr > (1535 - h)) || (destaddr > (1535 - h)))
	goto f_ugh;
/*Reduce values to pixels */
    if (infotable[CM].bytesperpixel == 2) {
	if ((src_x & 1) || (des_x & 1) || (pitch & 0xfffff00f)) {
	  f_ugh:
	    fprintf(stderr,"\asvgalib: mach32: mach32_bitblt can't emulate Cirrus.\n");
	    return;
	}
	src_x >>= 1;
	des_x >>= 1;
	pitch >>= 4;
    } else {
	if (pitch & 0xfffff807)
	    goto f_ugh;
	pitch >>= 3;
    }

/*y in range is not checked. */

/* Do not disturb us: */
    CRITICAL = 1;
    vga_lockvc();

/* Ensure std GE config to emulate simple Cirrus */
    if (mach32_accelstate != R_STANDARD)
	mach32_setstate(R_STANDARD);

    checkqueue(5);		/* Need FIFO room for commands */

/* set GE base close to destination: */
    port_outw_r(GE_OFFSET_LO, (unsigned short) (base >> 2));
    port_outw_r(GE_OFFSET_HI, (unsigned short) (base >> 18));
    port_outw_r(GE_PITCH, (unsigned char) pitch);
    port_outw_r(DP_CONFIG, 0x6011);	/* Replace with VRAM blit source */
    checkqueue(10);		/* Need new FIFO room for commands */
/*start coords: */
#ifdef DEBUG
    fprintf(stderr,"   ##x=%x,y=%x,xd=%x,yd=%x\n", src_x, srcaddr, des_x, destaddr);
#endif
    if (forw_cpy) {
	/* cpy from lower to higher addr, x, y */
	/* Use Mach32 extended blit. */
	port_outw_r(SRC_X, src_x);
	port_outw_r(SRC_X_START, src_x);
	port_outw_r(SRC_Y, srcaddr);
	port_outw_r(SRC_X_END, 0x7ff & (src_x + w));
	port_outw_r(SRC_Y_DIR, 1);
	port_outw_r(CUR_X, des_x);
	port_outw_r(DEST_X_START, des_x);
	port_outw_r(CUR_Y, destaddr);
	port_outw_r(DEST_X_END, 0x7ff & (des_x + w));
	port_outw_r(DEST_Y_END, 0x7ff & (destaddr + h));
    } else {
	/* rev_cpy, adj start coords. */
	/* Use Mach32 extended blit. */
	port_outw_r(SRC_X, 0x7ff & (src_x + w));
	port_outw_r(SRC_X_START, 0x7ff & (src_x + w));
	port_outw_r(SRC_Y, 0x7ff & (srcaddr + h - 1));
	port_outw_r(SRC_X_END, src_x);
	port_outw_r(SRC_Y_DIR, 0);
	port_outw_r(CUR_X, 0x7ff & (des_x + w));
	port_outw_r(DEST_X_START, 0x7ff & (des_x + w));
	port_outw_r(CUR_Y, 0x7ff & (destaddr + h - 1));
	port_outw_r(DEST_X_END, des_x);
	port_outw_r(DEST_Y_END, 0x7ff & (destaddr - 1));
    }
/* Wait then unlock vc */
    mach32_bltwait();		/* resets critical as well */
    port_outw_r(DP_CONFIG, 0x3291);	/* reset to standard config.. otherwise dramatical speed down */
#ifdef DEBUG
    fprintf(stderr,"   **x=%xh,y=%xh\n", (int) inw(CUR_X), (int) inw(CUR_Y));
#endif

    vga_unlockvc();
}

static void mach32_fillblt(int destaddr, int w, int h, int pitch, int c)
{
#ifdef DEBUG
    fprintf(stderr,"mach32_fillblt(%xh,%d,%d,%d,%d)\n", destaddr, w, h, pitch, c);
#endif

/* Do not disturb us: */
    CRITICAL = 1;
    vga_lockvc();

/* Ensure std GE config to emulate simple Cirrus */
    if (mach32_accelstate != R_STANDARD)
	mach32_setstate(R_STANDARD);

    checkqueue(5);		/* Need FIFO room for commands */
/* set GE base close to destination: */
    port_outw_r(GE_OFFSET_LO, (unsigned short) (destaddr >> 2));
    port_outw_r(GE_OFFSET_HI, (unsigned short) (destaddr >> 18));
    destaddr &= 3;		/* remaining x offset */

    if (infotable[CM].bytesperpixel == 2) {
	if ((destaddr & 1) || (pitch & 0xfffff00f)) {
	  ugh:
	    fprintf(stderr,"\asvgalib: mach32: mach32_fillblt can't emulate Cirrus.\n");
	    CRITICAL = 0;
	    vga_unlockvc();
	    return;
	}
	destaddr >>= 1;
	pitch >>= 4;
    } else {
	if (pitch & 0xfffff807)
	    goto ugh;
	pitch >>= 3;
    }
    port_outw_r(GE_PITCH, (unsigned char) pitch);
    port_outw_r(FRGD_COLOR, (unsigned short) c);
    port_outw_r(FRGD_MIX, 0x27);	/* replace with FRGD_COLOR */
    checkqueue(5);		/* Need new FIFO room for commands */
/*start coords: */
    port_outw_r(CUR_X, destaddr);
    port_outw_r(CUR_Y, 0);
/*width+height */
    port_outw_r(MAJ_AXIS_PCNT, w - 1);
    port_outw_r(MULTI_FUNC_CNTL, 0x7ff & (h - 1));
/*kick it off */
    port_outw_r(CMD, 0x50B3);		/*Rectangle fill with horz lines,pos x+y dir,draw last pel */

/* Wait then unlock vc */
    mach32_bltwait();		/* resets critical as well */
    vga_unlockvc();
}

static void mach32_hlinelistblt(int ymin, int n, int *xmin, int *xmax, int pitch, int c)
{
#ifdef DEBUG
    fprintf(stderr,"mach32_hlinelistblt(%d,%d,%08xh,%08xh,%d,%d)\n", ymin, n, (unsigned) xmin, (unsigned) xmax, pitch, c);
#endif

/* Do not disturb us: */
    CRITICAL = 1;
    vga_lockvc();

/* Ensure std GE config to emulate simple Cirrus */
    if (mach32_accelstate != R_STANDARD)
	mach32_setstate(R_STANDARD);

    checkqueue(5);		/* Need FIFO room for commands */
    /* This is how I understand the Cirrus-code. Since we have to be compatible: */
    port_outw_r(GE_OFFSET_LO, 0);
    port_outw_r(GE_OFFSET_HI, 0);

    if (infotable[CM].bytesperpixel == 2) {
	if (pitch & 0xfffff00f) {
	  ugh:
	    fprintf(stderr,"\asvgalib: mach32: mach32_hlinelistblt can't emulate Cirrus.\n");
	    CRITICAL = 0;
	    vga_unlockvc();
	    return;
	}
	pitch >>= 4;
    } else {
	if (pitch & 0xfffff807)
	    goto ugh;
	pitch >>= 3;
    }
    port_outw_r(GE_PITCH, (unsigned char) pitch);
    port_outw_r(FRGD_COLOR, (unsigned short) c);

/*DP_CONFIG is replace with foreground already... */
/*And now close your eyes and enjoy.. this is exactly what a Mach32 is built for.. */

    checkqueue(12);		/* Need new FIFO room for commands */

/*I think we can trust the hardware handshake from now on. */
/*Sigh.. no we can't.. queue overruns in 16bpp */

    while (n-- > 0) {
	port_outw_r(CUR_X, 0x7ff & (*xmin));
	port_outw_r(CUR_Y, 0x7ff & (ymin++));
	if (*xmin <= *xmax)
	    port_outw_r(SCAN_TO_X, (0x7ff & (*xmax + 1)));
	xmin++;
	xmax++;

	if (!(n & 3))
	    checkqueue(12);
    }

/* Wait then unlock vc */
    mach32_bltwait();		/* resets critical as well */
    vga_unlockvc();
}

static void mach32_imageblt(void *srcaddr, int destaddr, int w, int h, int pitch)
{
    unsigned count;		/* 16bit words needed to transfer */

/* Do not disturb us: */
    CRITICAL = 1;
    vga_lockvc();

#ifdef DEBUG
    fprintf(stderr,"mach32_imageblt(%xh,%xh,%d,%d,%d)\n", (int) srcaddr, destaddr, w, h, pitch);
#endif

/* Ensure std GE config to emulate simple Cirrus */
    if (mach32_accelstate != R_STANDARD)
	mach32_setstate(R_STANDARD);

    checkqueue(4);		/* Need FIFO room for commands */
/* set GE base close to destination: */
    port_outw_r(GE_OFFSET_LO, (unsigned short) (destaddr >> 2));
    port_outw_r(GE_OFFSET_HI, (unsigned short) (destaddr >> 18));
    destaddr &= 3;		/* remaining x offset */

    count = w * h;
    if (infotable[CM].bytesperpixel == 2) {
	if ((destaddr & 1) || (pitch & 0xfffff00f)) {
	  ugh:
	    fprintf(stderr,"\asvgalib: mach32: mach32_imageblt can't emulate Cirrus.\n");

	    CRITICAL = 0;
	    vga_unlockvc();
	    return;
	}
	destaddr >>= 1;
	pitch >>= 4;
    } else {
	if (pitch & 0xfffff807)
	    goto ugh;
	pitch >>= 3;
	count = (count + 1) >> 1;	/* two pixels transferred in one blow */
    }

    if (!count) {
	CRITICAL = 0;
	vga_unlockvc();
	return;			/* safety bailout */
    }
    port_outw_r(GE_PITCH, (unsigned char) pitch);
    port_outw_r(FRGD_MIX, 0x47);	/* replace with value of PIX_TRANS */
    checkqueue(6);		/* Need new FIFO room for commands, one more than actually needed to ensure
				   correct hostdata fifo operation. */
/*start coords: */
    port_outw_r(CUR_X, destaddr);
    port_outw_r(CUR_Y, 0);
/*width+height */
    port_outw_r(MAJ_AXIS_PCNT, w - 1);
    port_outw_r(MULTI_FUNC_CNTL, 0x7ff & (h - 1));
/*kick it off */
    port_outw_r(CMD, 0x53B1);		/*Rectangle fill with horz lines,pos x+y dir,draw last pel */
    /*(set also wait for MSB in the hope that it will ensure the correct
       thing if we write out an odd number of bytes) */

/* Do the transfer: All handshake is done in hardware by waitstates. Note that the first
   word can be transferred w/o problems.. after that automatic handshake by waitstates is enabled
   by the ATI on it's own.
   The outs is regarding to my assembler docs not only the most simple but also by far the
   fastest way to do this. Loop unrolling will gain nothing, so it is not done.. */

#ifdef __alpha__
    fprintf(stderr,"mach32_imageblt: not done yet\n");
#else
    asm("cld\n"			/* Increasing addresses.. */
	"	movw $0xe2e8,%%dx\n"	/* PIX_TRANS port */
	"	rep\n"		/* repeat.. count is in ECX */
  "	outsw\n":		/* Blast data out in 16bit pieces */
  /* no outputs */ :
    /* inputs */
    /* SI= source address */ "S"(srcaddr),
  /* CX= words to transfer */ "c"(count):
    /* dx destructed: */ "%dx"
	);
#endif

    checkqueue(2);
/*Sigh, it seems that DP_CONFIG gets affected by the 8514/A settings. so reset to standard. */
    port_outw_r(DP_CONFIG, 0x3291);

/* The bltwait is more intended for a final checkup if everything worked ok and we
   didn't lock up the Mach32 fifo */

/* Wait then unlock vc */
    mach32_bltwait();		/* resets critical as well */
    vga_unlockvc();
}

static void mach32_memimageblt(void *srcaddr, int destaddr, int w, int h, int pitch)
{
    char *source = srcaddr, *dest = (char * /*discard volatile */ ) mach32_aperture + destaddr;

#ifdef DEBUG
    fprintf(stderr,"mach32_memimageblt(%xh,%xh,%d,%d,%d)\n", (int) srcaddr, destaddr, w, h, pitch);
    fprintf(stderr,"\tmemtrans: %xh -> %xh\n", (int) source, (int) dest);
#endif

    if (!infotable[CM].bytesperpixel)
	return;

    w *= infotable[CM].bytesperpixel;

    if (w == pitch) {
	fast_memcpy(dest, source, w * h);
    } else {
	while (h--) {
	    fast_memcpy(dest, source, w);
	    dest += pitch;
	    source += w;
	}
    }
#ifdef DEBUG
    fprintf(stderr,"mach32_mach32_imageblt ended.\n");
#endif
}

static unsigned char mach32ropdefs[] = { 0x7, 0xB, 0xC, 0x5, 0x0 };

static void enter_accel(void)
{
    if (!CRITICAL) {
	CRITICAL = 1;
	vga_lockvc();
    }
    if (mach32_accelstate != R_EXTENDED)
	mach32_setstate(R_EXTENDED);
}

static unsigned short *mk_bitrot(void)
{
    unsigned short *bitrot, *ptr;
    int i = 0;    

    ptr = bitrot = malloc(sizeof(unsigned short) * (1 << 16));
    if (!bitrot) {
	fprintf(stderr,"svgalib: mach32: Not enough memory for PUTBITMAP rotation table.\n");
	return NULL;
    }
    for (i = 0; i < 16; i++)
	*ptr++ = ((i & 1) << 15) | ((i & 2) << 13) | ((i & 4) << 11) | ((i & 8) << 9);
    for (; i < 256; i++)
	*ptr++ = bitrot[i & 15] | (bitrot[i >> 4] >> 4);
    for (; i < (1 << 16); i++)
	*ptr++ = bitrot[i & 255] | (bitrot[i >> 8] >> 8);
    return bitrot;
}

static int mach32_accel(unsigned operation, va_list params)
{
    static int accel_blockmode = BLITS_SYNC;
    static unsigned short *bitrot = NULL;
    unsigned short x, y, x2, y2, w, h;

    switch(operation) {
	case ACCEL_SETRASTEROP:
	    enter_accel();
	    x = va_arg(params, int);
	    if (x <= sizeof(mach32ropdefs)) {
		mach32_rop = x;
		checkqueue(4); /* could check for 2 + 2 but this is probably not worth it */
		mach32_frgd_alu_fn = mach32ropdefs[x];
		port_outw_r(FRGD_MIX, mach32_frgd_alu_fn | 0x20);
		port_outw_r(ALU_FG_FN, mach32_frgd_alu_fn);
		if (mach32_bkgd_alu_fn != 0x3) {
		    mach32_bkgd_alu_fn = mach32_frgd_alu_fn;
		    port_outw_r(BKGD_MIX, mach32_bkgd_alu_fn);
		    port_outw_r(ALU_BG_FN, mach32_bkgd_alu_fn);
		}
	    }
	    break;
	case ACCEL_SETTRANSPARENCY:
	    enter_accel();
	    x = va_arg(params, int);
	    if (x == ENABLE_BITMAP_TRANSPARENCY) {
		if (mach32_bkgd_alu_fn != 0x3) {
	    	    checkqueue(2);
		    mach32_bkgd_alu_fn = 0x3;
		    port_outw_r(BKGD_MIX, mach32_bkgd_alu_fn);
		    port_outw_r(ALU_BG_FN, mach32_bkgd_alu_fn);
		}
	    } else if (x == DISABLE_BITMAP_TRANSPARENCY) {
		if (mach32_bkgd_alu_fn == 0x3) {
	    	    checkqueue(2);
		    mach32_bkgd_alu_fn = mach32_frgd_alu_fn;
		    port_outw_r(BKGD_MIX, mach32_bkgd_alu_fn);
		    port_outw_r(ALU_BG_FN, mach32_bkgd_alu_fn);
		}
	    }
	    break;
	case ACCEL_SETFGCOLOR:
	    enter_accel();
	    mach32_frgd_col = va_arg(params, int);
	    checkqueue(1);
	    port_outw_r(FRGD_COLOR, mach32_frgd_col);
	    break;
	case ACCEL_SETBGCOLOR:
	    enter_accel();
	    mach32_bkgd_col = va_arg(params, int);
	    checkqueue(1);
	    port_outw_r(BKGD_COLOR, mach32_bkgd_col);
	    break;
	case ACCEL_SETMODE:
	    x = va_arg(params, int);
	    switch(x) {
		case BLITS_IN_BACKGROUND:
		case BLITS_SYNC:
		    accel_blockmode = x;
		    break;
	    }
	    break;
	case ACCEL_SYNC:
	    if (CRITICAL) {
	        mach32_bltwait();
	        vga_unlockvc();
	    }
	    return 0;
	default:
	    return -1;
	case ACCEL_FILLBOX:
	    enter_accel();
	    checkqueue(5);	/* Need new FIFO room for commands */
		/*start coords: */
	    port_outw_r(CUR_X, (unsigned short)va_arg(params, int));
	    port_outw_r(CUR_Y, (unsigned short)va_arg(params, int));
		/*width+height */
	    port_outw_r(MAJ_AXIS_PCNT, (unsigned short)(va_arg(params, int) - 1));
	    port_outw_r(MULTI_FUNC_CNTL, (unsigned short)(0x7ff & (va_arg(params, int) - 1)));
		/*kick it off */
	    port_outw_r(CMD, 0x50B3);	/*Rectangle fill with horz lines,pos x+y dir,draw last pel */
	    break;
	case ACCEL_SCREENCOPY:
	    enter_accel();
	    checkqueue(1);
	    port_outw_r(DP_CONFIG, 0x6011);	/* Replace with VRAM blit source */
	    x = va_arg(params, int);
	    y = va_arg(params, int);
	    x2 = va_arg(params, int);
	    y2 = va_arg(params, int);
	    w = va_arg(params, int);
	    h = va_arg(params, int);
	    checkqueue(10);		/* Need new FIFO room for commands */
	    if ((y > y2) || ((y == y2) && (x >= x2))) {
		/* cpy from lower to higher addr, x, y */
		/* Use Mach32 extended blit. */
		port_outw_r(SRC_X, x & 0x7ff);
		port_outw_r(SRC_X_START, x & 0x7ff);
		port_outw_r(SRC_Y, y & 0x7ff);
		port_outw_r(SRC_X_END, 0x7ff & (x + w));
		port_outw_r(SRC_Y_DIR, 1);
		port_outw_r(CUR_X, x2 & 0x7ff);
		port_outw_r(DEST_X_START, x2 & 0x7ff);
		port_outw_r(CUR_Y, y2 & 0x7ff);
		port_outw_r(DEST_X_END, 0x7ff & (x2 + w));
		port_outw_r(DEST_Y_END, 0x7ff & (y2 + h));
	    } else {
		/* rev_cpy, adj start coords. */
		/* Use Mach32 extended blit. */
		port_outw_r(SRC_X, 0x7ff & (x + w));
		port_outw_r(SRC_X_START, 0x7ff & (x + w));
		port_outw_r(SRC_Y, 0x7ff & (y + h - 1));
		port_outw_r(SRC_X_END, x & 0x7ff);
		port_outw_r(SRC_Y_DIR, 0);
		port_outw_r(CUR_X, 0x7ff & (x2 + w));
		port_outw_r(DEST_X_START, 0x7ff & (x2 + w));
		port_outw_r(CUR_Y, 0x7ff & (y2 + h - 1));
		port_outw_r(DEST_X_END, x2 & 0x7ff);
		port_outw_r(DEST_Y_END, 0x7ff & (y2 - 1));
	    }
	    checkqueue(1);
	    port_outw_r(DP_CONFIG, 0x3291);
	    break;
	case ACCEL_DRAWHLINELIST:
	    {
	    int *xmin, *xmax;

	    enter_accel();
	    y = va_arg(params, int);
	    h = va_arg(params, int);
	    xmin = va_arg(params, int *);
	    xmax = va_arg(params, int *);
	    checkqueue(12);
	    while (h-- > 0) {
		x = *xmin++;
		x2 =  *xmax++;
		if (x > x2) {
		    x = x2;
		    x2 = xmin[-1];
		}
		port_outw_r(CUR_X, x);
		port_outw_r(CUR_Y, y++);
		port_outw_r(SCAN_TO_X, 0x7ff & (x2 + 1));

		if (!(h & 3))
		    checkqueue(12);
	    }

	    }
	    break;
	case ACCEL_PUTIMAGE:
	    {
	    unsigned char *data;
	    unsigned int count;

	    enter_accel();
	    if (((emuimage & EMU_OVER) || infotable[CM].colors == (1 << 24)) &&
		    mach32_rop == ROP_COPY) {
		/* Ensure the engine is idle */
	        mach32_bltwait();
		/* Emulate putimage: */
		x = va_arg(params, int);
		y = va_arg(params, int);
		w = va_arg(params, int);
		h = va_arg(params, int);
		data = va_arg(params, unsigned char *);
		mach32_memimageblt(data,
		    (((int)mach32_ge_off_l) << 2) +
		    (((int)mach32_ge_off_h) << 18) +
		    infotable[CM].xbytes * y +
		    infotable[CM].bytesperpixel * x,
		    w, h, infotable[CM].bytesperpixel * (int)(mach32_ge_pitch << 3));
		break;
	    }
	    checkqueue(12);
	    port_outw_r(FRGD_MIX, mach32_frgd_alu_fn | 0x40);
	    port_outw_r(CUR_X, va_arg(params, int));
	    port_outw_r(CUR_Y, va_arg(params, int));
	    w = va_arg(params, int);
	    h = va_arg(params, int);
	    count = w * h;
	    port_outw_r(MAJ_AXIS_PCNT, w - 1);
	    port_outw_r(MULTI_FUNC_CNTL, 0x7ff & (h - 1));
	    data = va_arg(params, unsigned char *);
	    port_outw_r(CMD, 0x53B1);
#ifdef __alpha__
	    fprintf(stderr,"mach32_ACCEL_PUTIMAGE: not done yet\n");
#else
    	    if (infotable[CM].bytesperpixel == 2) {
		asm("cld\n"
		    "movw $0xe2e8, %%dx\n"
		    "rep\n"
	  	    "outsw\n": : "S"(data), "c"(count): "%dx"
		);
	    } else {
		if (count > 1)
			asm("cld\n"
			    "movw $0xe2e8, %%dx\n"
			    "rep\n"
	  		    "outsw\n": : "S"(data), "c"(count >> 1): "%dx"
			);
		if (count & 1)
		    port_outw_r(PIX_TRANS, ((unsigned short)data[count - 1]));
	    }
#endif
	    checkqueue(2);
	    port_outw_r(FRGD_MIX, mach32_frgd_alu_fn | 0x20);
	    port_outw_r(DP_CONFIG, 0x3291);
	    }
	    break;
	case ACCEL_DRAWLINE:
	    enter_accel();
	    checkqueue(6);
	    port_outw_r(LINEDRAW_OPT, mach32_polyfill);
	    port_outw_r(LINEDRAW_INDEX, 0);
	    port_outw_r(LINEDRAW, va_arg(params, int));
	    port_outw_r(LINEDRAW, va_arg(params, int));
	    port_outw_r(LINEDRAW, va_arg(params, int));
	    port_outw_r(LINEDRAW, va_arg(params, int));
	    break;
	case ACCEL_PUTBITMAP:
	    {
	    unsigned short *data;
	    unsigned int count;

	    if (!bitrot)
		bitrot = mk_bitrot();
	    if (!bitrot)
		break;
	
	    enter_accel();
	    checkqueue(1);
	    port_outw_r(DP_CONFIG, 0x2255);
	    x = va_arg(params, int);
	    y = va_arg(params, int);
	    w = va_arg(params, int);
	    h = va_arg(params, int);
	    checkqueue(10);
	    /* cpy from lower to higher addr, x, y */
	    /* Use Mach32 extended blit for packed bitmap. */
	    port_outw_r(EXT_SCISSOR_R, x + w - 1);
	    w = (w + 31) & ~31;
	    port_outw_r(CUR_X, x & 0x7ff);
	    port_outw_r(DEST_X_START, x & 0x7ff);
	    port_outw_r(CUR_Y, y & 0x7ff);
	    port_outw_r(DEST_X_END, 0x7ff & (x + w));
	    port_outw_r(DEST_Y_END, 0x7ff & (y + h));
	    count = (w * h) / 16;
	    data = va_arg(params, unsigned short *);
	    checkqueue(12);
	    while(count--)
		port_outw_r(PIX_TRANS, bitrot[*data++]);
	    checkqueue(2);
	    port_outw_r(DP_CONFIG, 0x3291);
	    port_outw_r(EXT_SCISSOR_R, 1535);
	    }
	    break;
	case ACCEL_SCREENCOPYMONO:
	    enter_accel();
	    checkqueue(1);
	    if (mach32_polyfill)
		port_outw_r(DP_CONFIG, 0x3217);
	    else
		port_outw_r(DP_CONFIG, 0x3275);
	    x = va_arg(params, int);
	    y = va_arg(params, int);
	    x2 = va_arg(params, int);
	    y2 = va_arg(params, int);
	    w = va_arg(params, int);
	    h = va_arg(params, int);
	    checkqueue(10);
	    /* cpy from lower to higher addr, x, y */
	    port_outw_r(SRC_X, x & 0x7ff);
	    port_outw_r(SRC_X_START, x & 0x7ff);
	    port_outw_r(SRC_Y, y & 0x7ff);
	    port_outw_r(SRC_X_END, 0x7ff & (x + w));
	    port_outw_r(SRC_Y_DIR, 1);
	    port_outw_r(CUR_X, x2 & 0x7ff);
	    port_outw_r(DEST_X_START, x2 & 0x7ff);
	    port_outw_r(CUR_Y, y2 & 0x7ff);
	    port_outw_r(DEST_X_END, 0x7ff & (x2 + w));
	    port_outw_r(DEST_Y_END, 0x7ff & (y2 + h));
	    checkqueue(1);
	    port_outw_r(DP_CONFIG, 0x3291);
	    break;
	case ACCEL_SETOFFSET:
	    {
	    unsigned int address;

	    address = va_arg(params, unsigned int);
	    enter_accel();
	    checkqueue(2);
	    mach32_ge_off_l = address >> 2;
	    port_outw_r(GE_OFFSET_LO, mach32_ge_off_l);
	    mach32_ge_off_h = 0xff & (address >> 18);
	    port_outw_r(GE_OFFSET_HI, mach32_ge_off_h);
	    }
	    break;
	case ACCEL_POLYLINE:
	    {
	    unsigned short *data;
	    int count, flag;

	    enter_accel();
	    flag = va_arg(params, int);
	    count = va_arg(params, int) << 1;
	    data = va_arg(params, unsigned short *);

	    checkqueue(5);
	    if (flag & ACCEL_START) {
		port_outw_r(LINEDRAW_OPT, 0x4 | mach32_polyfill);
	        port_outw_r(LINEDRAW_INDEX, 0);
	    }
	    if (flag & ACCEL_END) {
		count -= 2;
	    }
	    if (count > 0)
		asm("cld\n"
		    "movw $0xfeee, %%dx\n"
		    "rep\n"
	  	    "outsw\n": : "S"(data), "c"(count): "%dx"
		);
	    if (flag & ACCEL_END) {
	    	checkqueue(3);
		port_outw_r(LINEDRAW_OPT, mach32_polyfill);
	        port_outw_r(LINEDRAW, data[count++]);
	        port_outw_r(LINEDRAW, data[count]);
	    }
	    }
	    break;
	case ACCEL_POLYHLINE:
	    {
	    unsigned short *data;
	    int flag;
	    static int loc_y;

	    enter_accel();
	    flag = va_arg(params, int);
	    y = va_arg(params, int);
	    h = va_arg(params, int);
	    data = va_arg(params, unsigned short *);
	    checkqueue(12);

	    if (flag & ACCEL_START) {
		loc_y = y;
	    }
	    while (h--) {
		w = (*data++) - 1;
		port_outw_r(CUR_X, *data++);
		port_outw_r(CUR_Y, loc_y++);
		for(; w > 1; w -= 2) {
		    port_outw_r(SCAN_TO_X, (*data++) + 1);
		    port_outw_r(SCAN_TO_X, (*data++));
		}
		port_outw_r(SCAN_TO_X, (*data++) + 1);
		if (!(h & 3))
		    checkqueue(12);
	    }
	    }
	    break;
	case ACCEL_POLYFILLMODE:
	    {
	    if (va_arg(params, int))
		mach32_polyfill = 2;
	    else
		mach32_polyfill = 0;
	    }
	    return 0;
    }
    if (accel_blockmode == BLITS_SYNC) {
	mach32_bltwait();	/* resets critical as well */
	vga_unlockvc();
    }
    return 0;
}

static void mach32_savepalette(unsigned char *red, unsigned char *green, unsigned char *blue) {
    int i;

    port_out(0, PEL8514_IR);
    for (i = 0; i < 256; i++) {
	__svgalib_delay();
	*(red++) = port_in(PEL8514_D);
	__svgalib_delay();
	*(green++) = port_in(PEL8514_D);
	__svgalib_delay();
	*(blue++) = port_in(PEL8514_D);
    }
}

static void mach32_restorepalette(const unsigned char *red, const unsigned char *green,
				  const unsigned char *blue) {
    int i;

    port_out(0, PEL8514_IW);
    for (i = 0; i < 256; i++) {
	__svgalib_delay();
	port_out(*(red++), PEL8514_D);
	__svgalib_delay();
	port_out(*(green++), PEL8514_D);
	__svgalib_delay();
	port_out(*(blue++), PEL8514_D);
    }
}

static int mach32_setpalette(int index, int red, int green, int blue) {
    if ((index != palcurind) || (palsetget != 1)) {
	port_out(index, PEL8514_IW);
	palcurind = index + 1;
	palsetget = 1;
    } else {
	palcurind++;
    }
    __svgalib_delay();
    port_out(red, PEL8514_D);
    __svgalib_delay();
    port_out(green, PEL8514_D);
    __svgalib_delay();
    port_out(blue, PEL8514_D);
    return 0;
}

static void mach32_getpalette(int index, int *red, int *green, int *blue) {
    if ((index != palcurind) || (palsetget != 0)) {
	port_out(index, PEL8514_IR);
	palcurind = index + 1;
	palsetget = 0;
    } else {
	palcurind++;
    }
    __svgalib_delay();
    *red = (int) port_in(PEL8514_D);
    __svgalib_delay();
    *green = (int) port_in(PEL8514_D);
    __svgalib_delay();
    *blue = (int) port_in(PEL8514_D);
}

static int mach32_screenon(void) {
    /* Force Mach32 to ATI mode (used by setmode) */
    port_outw_r(0x4AEE, inw(0x4AEE) | 1);
    return 0;
}

static void mach32_waitretrace(void) {
    unsigned char csync;

    csync = port_in(0xd2ee) >> 3;       /* Current sync polarity in bit 2,
                                       0-pos, 1-neg  */
    /*Wait until in displayed area.. */
    while ((port_in(0x2e8) ^ csync) & 2);
    /*Wait until V_SYNC */
    csync = ~csync;
    while ((port_in(0x2e8) ^ csync) & 2);
}
