/* mach32info.c prints out some info about your mach32card		*/

/* Please report the info it produces if the mach32driver of svgalib	*/
/* works not like expected.						*/

/* This tool is part of svgalib. Although it's output maybe useful to   */
/* debug Xfree86 Mach32 Servers, I am NOT related to Xfree86!!          */
/* PLEASE DO NOT SEND ME (MICHAEL WELLER) ANY XFREE86 BUG REPORTS!!!    */
/* Thanx in advance.                                                    */

/* This tool is free software; you can redistribute it and/or		*/
/* modify it without any restrictions. This tool is distributed	*/
/* in the hope that it will be useful, but without any warranty.	*/

/* Copyright 1994 by 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)
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

/* Some stuff for the ATI VGA */
#define ATIPORT         0x1ce
#define ATIOFF          0x80
#define ATISEL(reg)     (ATIOFF+reg)
/* Ports we use: */
#define SUBSYS_CNTL     0x42E8
#define GE_STAT         0x9AE8
#define CONF_STAT1      0x12EE
#define CONF_STAT2      0x16EE
#define MISC_OPTIONS    0x36EE
#define MEM_CFG         0x5EEE
#define MEM_BNDRY       0x42EE
#define SCRATCH_PAD_0   0x52EE
#define DESTX_DIASTP    0x8EE8
#define R_SRC_X         0xDAEE
#define R_EXT_GE_CONF   0x8EEE
#define CHIP_ID		0xFAEE
#define MAX_WAITSTATES	0x6AEE
#define LOCAL_CNTL	0x32EE
#define R_MISC_CNTL	0x92EE
#define PCI_CNTL	0x22EE
#define DISP_STATUS	0x2E8
#define DISP_CNTL	0x22E8
#define CLOCK_SEL	0x4AEE
#define H_DISP		0x06E8
#define H_TOTAL		0x02E8
#define H_SYNC_WID	0x0EE8
#define H_SYNC_STRT	0x0AE8
#define V_DISP		0x16E8
#define V_SYNC_STRT	0x1AE8
#define V_SYNC_WID	0x1EE8
#define V_TOTAL		0x12E8
#define	R_H_TOTAL	0xB2EE
#define	R_H_SYNC_STRT	0xB6EE
#define	R_H_SYNC_WID	0xBAEE
#define	R_V_TOTAL	0xC2EE
#define	R_V_DISP	0xC6EE
#define	R_V_SYNC_STRT	0xCAEE
#define	R_V_SYNC_WID	0xD2EE



/* Bit masks: */
#define GE_BUSY         0x0200

/* Chip_id's */
#define ATI68800_3	('A'*256+'A')
#define ATI68800_6	('X'*256+'X')
#define ATI68800_6HX	('H'*256+'X')
#define ATI68800LX	('L'*256+'X')
#define ATI68800AX	('A'*256+'X')

static inline void port_out( int value, int port )
{
__asm__ volatile ("outb %0,%1"
: : "a" ((unsigned char)value), "d" ((unsigned short)port));
}

static inline void port_outw( int value, int port ) {
__asm__ volatile("outw %0,%1"
: : "a" ((unsigned short)value), "d" ((unsigned short)port));
}

static inline int port_in( int port )
{
unsigned char value;
__asm__ volatile ("inb %1,%0"
	: "=a" (value)
	: "d" ((unsigned short)port));
return value;
}

static inline int port_inw( int port )
{
unsigned short value;
__asm__ volatile ("inw %1,%0"
	: "=a" (value)
	: "d" ((unsigned short)port));
return value;
}

#define inb port_in
#define inw port_inw
#define outb(port, value) port_out(value, port)
#define outw(port, value) port_outw(value, port)

int force=0,chip_id,bus;
unsigned short eeprom[128];
char *pel_width[]={"  4bpp","  8bpp","  16bpp","  24bpp"};
char *bpp16mode[]={"  5-5-5","  5-6-5","  6-5-5","  6-6-4"};
char *bpp24mode[]={"  RGB","  RGBa","  BGR","  aBGR"};
char *bustype[]={"  16-bit ISA","  EISA","  16-bit MicroChannel",
		"  32-bit MicroChannel","  LocalBus SX, 386SX",
		"  LocalBus 1/2, 386DX",
		"  LocalBus 1/2, 486DX","  PCI"};
char *memtype3[]={"  256Kx4 DRAM", "  256Kx4 VRAM, 512 bit serial transfer",
		"  256Kx4 VRAM, 256 bit serial transfer", "  256Kx16 DRAM",
		"  invalid", "  invalid", "  invalid", "  invalid"};
char *memtype6[]={"  256Kx4 DRAM", "  256Kx4 VRAM, 512 bit serial transfer",
		"  256Kx16 VRAM, 256 bit serial transfer", "  256Kx16 DRAM",
		"  256Kx4 Graphics DRAM",
		"  256Kx4 VRAM, 512 bit split transfer",
		"  256Kx16 VRAM, 256 bit split transfer",
		"  invalid"};
char *dactype[]={"  ATI-68830 (Type 0)", "  SC-11483 (Type 1)",
		 "  ATI-68875 (Type 2)", "  Bt-476 (Type 3)",
		 "  Bt-481 (Type 4)", "  ATI-68860 (Type 5)",
		 "  Unknown type 6", "  Unknown type 7"};
char *localbus[]={"  reserved","  LOCAL#2","  LOCAL#3","  LOCAL#1"};
char *aperture[]={"  memory aperture disabled","  1 MB memory aperture",
		  "  4 MB memory aperture","  reserved"};
char *mono_color[]={"  white","  green","  amber"," reserved"};
char *videomonames[]={"lores color - secondary",
		      "(hires) color - secondary",
		      "monochrome - secondary",
		      "lores color - primary",
		      "hires color - primary",
		      "monochrome - primary"};
char *clockdiv[]={"  1","  2","  reserved","  reserved"};
char *transwid[]={"  auto select","  16 bit","  8 bit","  8 bit hostdata/16 bit other"};
char *vgabound[]={"  shared","  256 KB","  512 KB","  1 MB"};
char *maxpix[]={"  8 bpp","  16 bpp","  24 bpp","  reserved"};
static int mach32_clocks[16];

void puttable(int table);

void usage(void)
{
  	fputs("Usage: mach32info {info|force}\n"
"    prints out almost all the info about your mach32 card from configuration\n"
"    registers and Mach32 EEPROM.  It also measures the Mach32 clocks.  A\n"
"    completely idle system is required when these measurements are being\n"
"    performed.  During these measurements, the video signals will be screwed up\n"
"    for about 3-4 seconds.\n"
"*   If your monitor does not switch off when getting a video signal it can't\n"
"    stand (fixed freq. monitors) better switch it off before starting\n"
"    mach32info.  Your computer will beep when it is finished probing.\n"
"      You can redirect the 'stdout' of 'mach32info' to some file for viewing\n"
"    the results easier.  Do not redirect 'stderr' as you won't hear the beep.\n"
"*   The 'force' option disables the sanity check that tries to detect the\n"
"    presence of the mach32.  Do not use this option unless you are really,\n"
"    really sure that you have a Mach32 compatible vga card installed.\n"
"*   This tool is part of svgalib. Although it's output maybe useful to debug\n"
"    Xfree86 Mach32 Servers, I am NOT related to Xfree86!  PLEASE DO NOT SEND\n"
"    ME (MICHAEL WELLER) ANY XFREE86 BUG REPORTS!  Thanx in advance.\n"
"*   Note that this tool comes WITHOUT ANY WARRANTY! Use it at your OWN risk!\n"
"*   Warning, this tool does not check for VC changes etc.. Just let it run in\n"
"    its own virtual console and don't try to fool it.\n"
"Please report any problems with running 'mach32info' or with config-\n"
"uring the 'svgalib' mach32 driver to 'eowmob@exp-math.uni-essen.de'.\n"
"Include the results from running this test with your report.\n",
	stderr);
exit(2);
}

static void mach32_i_bltwait()
{
int i;

for (i=0; i < 100000; i++)
        if(!(inw(GE_STAT) & (GE_BUSY | 1)))
                break;
if(i>=100000)
	puts("GE idled out");
}

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

tmp = inw(SCRATCH_PAD_0);
outw(SCRATCH_PAD_0, 0x5555);
mach32_i_bltwait();
if(inw(SCRATCH_PAD_0) == 0x5555)
        {
        outw(SCRATCH_PAD_0, 0x2a2a);
        mach32_i_bltwait();
        if(inw(SCRATCH_PAD_0) == 0x2a2a)
                {
                /* Aha.. 8514/a detected.. */
                result=1;
                }
        }
outw(SCRATCH_PAD_0,tmp);
if(!result)
        goto quit;
/* Now ensure it is not a plain 8514/a: */
result=0;
outw(DESTX_DIASTP, 0xaaaa);
mach32_i_bltwait();
if(inw(R_SRC_X)==0x02aa)
        {
        outw(DESTX_DIASTP, 0x5555);
        mach32_i_bltwait();
        if(inw(R_SRC_X)==0x0555)
                result=1;
        }
quit:
return result;
}

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

for(i=0;i<16;i++)
        dummy++;        /*Dummy is volatile..*/
}

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

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

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

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

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

void putflag(char *str,int flag)
{
int i;

i=72-strlen(str)-10;
printf("   %s  ",str);
while(i-- >0)
	putchar('.');
puts(flag?".  enabled":"  disabled");
}

void putint(char *str,char *format,int value)
{
char buffer[128];
int i;

sprintf(buffer,format,value);
i=72-strlen(str)-strlen(buffer);
printf("   %s  ",str);
while(i-- >0)
	putchar('.');
puts(buffer);
}

void putstr(char *str,char *strval)
{
putint(str,strval,0);
}

unsigned short putword(int word)
{
printf("\n EEPROM Word %02xh:\t%04x\n",word,eeprom[word]);
return eeprom[word];
}

char *offset(char *buffer,int word)
{
int tab;

word>>=8;
if((word<0x0d)||(word>0x67))
	{
	illegal:
	sprintf(buffer,"  %02xh words (no table there)",word);
	}
else	{
	tab=word-0x0d;
	if(tab%(0x1c-0x0d))
		goto illegal;
	sprintf(buffer,"  %02xh words (table %d)",word,tab/(0x1c-0x0d)+1);
	}
return buffer;
}

char *hsyncstr(int pixels,int clock, double fclock)
{
static char buffer[50];

if(!clock)
	sprintf(buffer,"  %d pixels",pixels);
else	sprintf(buffer,"  %d pixels, %.3f us",
		pixels,pixels/fclock);
return buffer;
}

char *vsyncstr(int lines,int clock, double lilen)
{
static char buffer[50];

if(!clock)
	sprintf(buffer,"  %d lines",lines);
else	sprintf(buffer,"  %d lines, %.3f ms",
		lines,lines/lilen);
return buffer;
}

/* Shamelessly 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);

puts(
"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." );
#if 0
puts("\n(Enter Y<Return> to continue, any other text to bail out)");

if(getchar()!='Y')
	exit(0);
if(getchar()!='\n')
	exit(0);
#endif

htotndisp=inw(R_H_TOTAL);
hstrt=inb(R_H_SYNC_STRT);
hsync=inb(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);

outb(DISP_CNTL,0x63);

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

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

	usleep(50000);

	count = 0;
	loop = 200000;

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

	outb(DISP_CNTL,0x63);
	}

outw(CLOCK_SEL,clck);

outw(H_DISP,htotndisp);
outb(H_SYNC_STRT,hstrt);
outb(H_SYNC_WID,hsync);
outw(V_DISP,vdisp);
outw(V_TOTAL,vtotal);
outw(V_SYNC_STRT,vstrt);
outw(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;
}

int main(int argc, char *argv[])
{
char *ptr,buffer[40];
int i,j,lastfound,mask,index,flag;

memset(eeprom,0,sizeof(unsigned short)*(size_t)256);

if(argc!=2)
	usage();
if(strcmp(argv[1],"info"))
	{
	if(strcmp(argv[1],"force"))
		usage();
	force=1;
	}
if(iopl(3)<0)
	{
	fputs("mach32info needs to be run as root!\n",stderr);
	exit(1);
	}
if(!force)	
	{
	if(mach32_test())
		puts("Mach32 succesful detected.");
	else	{
		fputs("Sorry, no Mach32 detected.\n",stderr);
		exit(1);
		}
	}
else	puts("Mach32 autodetection skipped.");

puts("\nThis tool is part of svgalib. Although this output maybe useful\n"
"to debug Xfree86 Mach32 Servers, I am NOT related to Xfree86!!\n"
"PLEASE DO NOT SEND ME (MICHAEL WELLER) ANY XFREE86 BUG REPORTS!!!\n"
"Thanx in advance.\n");

mach32_scan_clocks();

puts("\nResulting clocks command for your libvga.config should be:\n");
fputs("clocks",stdout);
for(i=0;i<16;i++)
        printf(" %3d",mach32_clocks[i]);

fputs("\a",stderr);
fflush(stderr);
puts("\n\nParsing for chip id...");
lastfound=inw(CHIP_ID)&0x3ff;
flag=0;
for(i=0;i<10240;i++)
	{
	j=inw(CHIP_ID)&0x3ff;
	index=(j>>4);
	mask=1<<(j&15);
	if(!(eeprom[index]&mask))
		printf("\tfound id: %c%c\n",
			0x41+((j>>5)&0x1f),0x41+(j&0x1f));
	eeprom[index]|=mask;
	if(lastfound!=j)
		flag=1;
	}
/* Build chip_id from last found id: */
chip_id=(j&0x1f)+((j<<3)&0x1f00);
chip_id+=ATI68800_3;

switch(chip_id)
	{
	case ATI68800_3:
		ptr="ATI68800-3 (guessed)";
		break;
	case ATI68800_6:
		ptr="ATI68800-6";
		break;
	case ATI68800_6HX:
		ptr="ATI68800-6 (HX-id)";
		break;
	case ATI68800LX:
		ptr="ATI68800LX";
		break;
	case ATI68800AX:
		ptr="ATI68800AX";
		break;
	default:
		ptr="Unknown (assuming ATI68800-3)";
		chip_id=ATI68800_3;
		flag=1;
		break;
	}
printf("Chipset: %s, Class: %d, Revision: %d\n",
		 ptr, (j >> 10) & 3, (j >> 12) & 15);
if (flag) {
	puts(
"WARNING! Strange chipset id! Please report all output of this utility\n"
"together with exact type of your card / type printed on your videochips\n"
"to me, Michael Weller, eowmob@exp-math.uni-essen.de.  Alternate\n"
"email-addresses are in the source of this utility and in 'README.mach32'.\n"
		);
	}
j=inw(MAX_WAITSTATES);
if(chip_id==ATI68800AX)
	{
	printf("\nAPERTURE_CNTL:\t\t%04x\n",j);
	putflag("Zero waitstates for PCI aperture",j&0x400);
	putflag("Fifo read ahead for PCI aperture",j&0x800);
	putflag("Pixel stream 1 SCLK delay",j&0x1000);
	putflag("Decrement burst",j&0x2000);
	putstr("Direction of burst",(j&0x4000)?
		"Increments burst":"Decrements burst");
	putflag("Bus timeout on burst read/writes",!(j&0x8000));
	}
else	{
	printf("\nMAX_WAITSTATES:\t\t%04x\n",j);
	putint("Max. I/O waitstates","  %d",4*(j&15));
	putint("BIOS-ROM waitstates","  %d",(j>>4)&15);
	putflag("Linedraw optimizations",j&0x100);
	}
j=inw(MISC_OPTIONS);
printf("\nMISC_OPTIONS:\t\t%04x\n",j);
putflag("Waitstates if FIFO is half full",j&0x0001);
putstr("Host data I/O size", (j & 0x0002) ? "8-bit" : "16-bit");
putint("Memory size","  %d KB",(1<<((j>>2)&3))*512);
putflag("VGA-controller",!(j&0x0010));
putflag("16-bit 8514 I/O cycles",j&0x0020);
putflag("Local RAMDAC",!(j&0x0040));
putflag("VRAM-serial/DRAM-memory(bits 63:0) data delay latch",j&0x0080);
putflag("Test-mode",j&0x0100);
putflag("Non ATI68800-3: Block-write",j&0x0400);
putflag("Non ATI68800-3: 64-bit Draw",j&0x0800);
putflag("Latch video memory read data",j&0x1000);
putflag("Memory data delay latch(bits 63:0)",j&0x2000);
putflag("Memory data latch full clock pulse",j&0x4000);


j=inw(R_EXT_GE_CONF);
printf("\nR_EXT_GE_CONF:\t\t%04x\n",j);
putint("Monitor alias id","  %d",j&7);
putflag("Monitor alias",j&0x0008);
putstr("Pixel width",pel_width[(j>>4)&3]);
putstr("16 bit per plane organization",bpp16mode[(j>>6)&3]);
putflag("Multiplex pixels",j&0x0100);
putstr("24 bit per plane organization",bpp24mode[(j>>9)&3]);
putstr("Reserved (11)",(j&0x0800)?"  1":"  0");
putint("Extended RAMDAC address","  %d",(j>>12)&3);
putflag("8 bit RAMDAC operation",j&0x4000);
putstr("Reserved (15)",(j&0x8000)?"  1":"  0");

j=inw(CONF_STAT1);
printf("\nCONF_STAT1:\t\t%04x\n",j);
putflag("VGA circuitry",!(j&0x0001));
putstr("Bus Type",bustype[bus=((j>>1)&7)]);
putstr("Memory Type",(chip_id==ATI68800_3)?memtype3[(j>>4)&7]:
				memtype6[(j>>4)&7]);
putflag("Chip",!(j&0x0080));
putflag("Delay memory write for tests",(j&0x0100));
putstr("RAMDAC Type",dactype[(j>>9)&7]);
putflag("Internal MicroChannel address decode",!(j&0x1000));
putint("Controller id (0 if unsupported)","  %d",(j>>13)&7);

j=inw(CONF_STAT2);
printf("\nCONF_STAT2:\t\t%04x\n",j);
if (chip_id == ATI68800_3 )
	putflag("ATI68800-3: 2 clock sequencer timing", j & 0x0001);
else	putstr("Reserved (0)", (j&0x0001) ? "  1" : "  0");
putflag("Memory address range FE0000-FFFFFF",!(j&0x0002));
if (!bus)
	putflag("16-bit ISA Bus (ISA cards only)", (j & 0x0004));
else	putstr("Reserved (2)", (j&0x0004) ? "  1" : "  0");
putflag("Korean character font support",(j&0x0008));
putstr("Local Bus signal (Local Bus only)",localbus[(j>>4)&3]);
putflag("Local Bus 2 (non multiplexed) configuration",(j&0x0040));
putflag("Read data 1 clk after RDY (Local Bus only)",(j&0x0080));
putflag("Local decode of RAMDAC write (Local Bus only)",!(j&0x0100));
putflag("1 clk RDY delay for write (Local Bus only)",!(j&0x0200));
putstr("BIOS EPROM at",(j&0x0400)?"  C000:0-C7FF:F":"  E000:0-E7FF:F");
switch(bus)
	{
	case 1:
		putflag("Enable POS register function (EISA)",(j&0x0800));
		break;
	case 4:
	case 5:
	case 6:
		putflag("Local decode of 102h register (Local Bus only)",
			!(j & 0x0800) );
		break;
	default:
		putstr("Reserved (11)", (j&0x0800)?"  1":"  0");
		break;
	}
putflag("VESA compliant RDY format (Local Bus only)",!(j&0x1000));
putflag("Non ATI68800-3: 4 GB aperture address",(j&0x2000));
putstr("Non ATI68800-3: Memory support in LBus 2 config",
	(j&0x4000)?"  2MB DRAM":"  1MB DRAM");
putstr("Reserved (15)",(j&0x8000)?"  1":"  0");

j=inw(MEM_BNDRY);
printf("\nMEM_BNDRY:\t\t%04x\n",j);
putint("Video memory partition (VGA <, Mach32 >=)","  %d KB",(j&15)*256);
putflag("Video memory partition write protection",j&0x0010);
putint("Reserved (15:5)","  %03xh",(j>>5));


j=inw(MEM_CFG);
printf("\nMEM_CFG:\t\t%04x\n",j);
putstr("Memory aperture",aperture[j&3]);
putint("Memory aperture page (for 1MB aperture)","  %d",(j>>2)&3);
if( (bus==7) || ( ((bus==5)||(bus==6)) && (inw(CONF_STAT2)&0x2000) ) )
	putint("Memory aperture location (0-4 GB)","  %d MB",j>>4);
else	{
	putint("Reserved (7:4)","  %x",(j>>4)&0xf);
	putint("Memory aperture location (0-128 MB)","  %d MB",j>>8);
	}

j=inw(LOCAL_CNTL);
printf("\nLOCAL_CNTL:\t\t%04x\n",j);
putflag("6 clock non page cycle",j&0x0001);
putflag("7 clock non page cycle",j&0x0002);
putflag("1/2 memory clock CAS precharge time",j&0x0004);
putflag("RAMDAC clocked on positive clock edge",j&0x0008);
putflag("FIFO testing",j&0x0010);
if(chip_id==ATI68800_3)
	putint("Filtering of 1 clock IOW low or high pulse","  %d",(j>>5)&3);
else	{
	putflag("Memory mapped registers",j&0x0020);
	putflag("Local Bus BIOS ROM decode",j&0x0040);
	}
putint("ROM wait states","  %d",(j>>7)&7);
putint("Memory read wait states","  %d",(j>>10)&3);
if(chip_id==ATI68800AX)
	putint("Additional I/O waitstates","  %d",(j>>12)&15);
else	putint("Minimum Local Bus waistates","  %d",(j>>12)&15);

j=inw(R_MISC_CNTL);
printf("\nR_MISC_CNTL:\t\t%04x\n",j);
putint("Reserved (3:0)","  %x",j&15);
putint("ROM page select","  %d KB",(j>>3)&0x1e);
putint("Blank adjust (delays BLANK_1_PCLK for RAMDAC type 2)","  %d",(j>>8)&3);
putint("Pixel data skew from PCLK (pixel delay)","  %d",(j>>10)&3);
putint("Reserved (15:12)","  %x",(j>>12)&15);

j=inw(PCI_CNTL);
printf("\nPCI_CNTL:\t\t%04x\n",j);
putint("RAMDAC read/write waitstates","  %d",j&7);
putflag("Target abort cycle",j&0x0004);
putflag("PCI RAMDAC delay",j&0x0010);
putflag("Snooping on DAC read",j&0x0020);
putflag("0 waitstates on aperture burst write",j&0x0040);
putflag("Fast memory mapped I/O read/write",j&0x0080);
putint("Reserved (15:8)","  %02x",(j>>8)&0xff);

fputs("\nReading in EEPROM... (some screen flicker will occur)",stdout);
fflush(stdout);
for(i=0;i<128;i++)
	eeprom[i]=mach32_eeget(i);
puts(" ...done.\n");
fputs("EEPROM contents:",stdout);
for(i=0;i<128;i++)
	{
	if(i&7)	putchar(' ');
	else	fputs("\n   ",stdout);
	printf(" %02x-%04x",i,eeprom[i]);
	}	
puts("\n\nDecoded info out of EEPROM:");
putword(0);
putint("EEPROM write counter","  %d",eeprom[0]);
putword(1);
switch(eeprom[1]&0xff)
	{
	case 0x00:
		ptr="  disabled";
		break;
	case 0x08:
		ptr="  secondary address";
		break;
	case 0x18:
		ptr="  primary address";
		break;
	default:
		ptr="  reserved";
	}
putstr("Mouse address select",ptr);
switch((eeprom[1]>>8)&0xff)
	{
	case 0x20:
		ptr="  IRQ 5";
		break;
	case 0x28:
		ptr="  IRQ 4";
		break;
	case 0x30:
		ptr="  IRQ 3";
		break;
	case 0x38:
		ptr="  IRQ 2";
		break;
	default:
		ptr="  reserved";
	}
putstr("Mouse interrupt handler select",ptr);
j=putword(2);
switch((j>>8)&0xff)
	{
	case 0x03:
	case 0x05:
	case 0x07:
	case 0x09:
	case 0x0b:
	case 0x12:
	case 0x13:
	case 0x15:
	case 0x17:
	case 0x19:
	case 0x1b:
		sprintf(ptr=buffer,"  %cGA %s",(j&0x1000)?'E':'V',
			videomonames[(((j>>8)&0xf)-1)>>1]);
		break;
	case 0x20:
		ptr="  CGA";
		break;
	case 0x30:
		ptr="  Hercules 720x348";
		break;
	case 0x40:
		ptr="  Hercules 640x400";
		break;
	default:
		ptr="  reserved";
	}
putstr("Power up video mode",ptr);
putstr("Monochrome color",mono_color[(j>>6)&3]);
putflag("Dual monitor",j&0x0020);
putstr("Power up font",(j&0x0010)?"  8x16 or 9x16":"  8x14 or 9x14");
putint("VGA Bus I/O","  %d bits",(j&0x0008)+8);
putflag("0 waitstates RAM read/write",j&0x0004);
putflag("0 waitstates ROM read",j&0x0002);
putflag("ROM 16 bit",j&0x0001);
j=putword(3);
putflag("Scrolling fix",j&0x8000);
putflag("Korean BIOS support",j&0x4000);
putint("Reserved (13:4)","  %03xh",(j>>4)&0x3ff);
putint("EEPROM table revision","  %d",j&15);
j=putword(4);
putint("Custom monitor indices","  %04x",j);
j=putword(5);
putstr("Host data transfer width",transwid[(j>>14)&3]);
putint("Monitor code","  %02xh",(j>>8)&0x3f);
putint("Reserved (7)","  %d",(j>>7)&1);
putstr("VGA boundary",vgabound[(j>>4)&3]);
putflag("Monitor alias",j&0x0008);
putint("Monitor alias setting","  %d",j&0x0007);
j=putword(6);
putint("Memory aperture location","  %d MB",(j>>4));
j&=15;
putstr("Memory aperture size",aperture[(j>3)?3:j]);
j=putword(7);
putstr("Offset to 640x480 mode table",offset(buffer,j));
putint("Reserved (7:2)","  %02xh",(j>>2)&0x3f);
putflag("Use stored params for 640x480",j&2);
putflag("640x480 72Hz",j&1);
j=putword(8);
putstr("Offset to 800x600 mode table",offset(buffer,j));
putflag("Use stored params for 800x600",j&0x80);
putint("Reserved (6)","  %d",(j>>6)&1);
putflag("800x600 72Hz",j&0x20);
putflag("800x600 70Hz",j&0x10);
putflag("800x600 60Hz",j&8);
putflag("800x600 56Hz",j&4);
putflag("800x600 89Hz Interlaced",j&2);
putflag("800x600 95Hz Interlaced",j&1);
j=putword(9);
putstr("Offset to 1024x768 mode table",offset(buffer,j));
putflag("Use stored params for 1024x768",j&0x80);
putint("Reserved (6:5)","  %d",(j>>5)&3);
putflag("1024x768 66Hz",j&0x10);
putflag("1024x768 72Hz",j&8);
putflag("1024x768 70Hz",j&4);
putflag("1024x768 60Hz",j&2);
putflag("1024x768 87Hz Interlaced",j&1);
j=putword(10);
putstr("Offset to 1280x1024 mode table",offset(buffer,j));
putflag("Use stored params for 1280x1024",j&0x80);
putint("Reserved (6:2)","  %02xh",(j>>2)&0x1f);
putflag("1280x1024 95Hz Interlaced",j&2);
putflag("1280x1024 87Hz Interlaced",j&1);
j=putword(11);
putstr("Offset to alternate mode table",offset(buffer,j));
putflag("Use stored params for alternate",j&0x80);
putint("Reserved (6:2)","  %02xh",(j>>2)&0x1f);
putflag("1152x900",j&2);
putflag("1120x760",j&1);
for(j=0;j<7;j++)
	puttable(j);
puts( "\n EEPROM Words 76h-7dh:  reserved." );
j=putword(0x7e);
putint("Reserved (15)","  %d",j>>15);
putflag("VGA circuitry",j&0x4000);
putint("Memory size","  %d KB",1<< ( ((j>>11)&7) + 8 ) );
putstr("DAC type",dactype[(j>>8)&7]);
putint("Reserved (7:0)","  %02xh",j&0xff);
j=putword(0x7f);
putint("EEPROM Checksum","  %04x",j);
j=0;
for(i=0;i<=0x7f;)
	j+=eeprom[i++];
printf("\nEEPROM contents sum up to %04x:%04x.\n",j>>16,j&0xffff);
if( ! (j & 0xffff) )
	{
	puts("ATI style checksum.");
	}
else	{
	j-= (eeprom[0x7f]<<1)-1;
	if( ! (j & 0xffff) )
		puts("AST style checksum.");
	else puts(
"WARNING! Strange EEPROM checksum!\n"
"Be sure that:\n"
"1. You installed the Mach32 correctly with the ATI install tool from\n"
"   DOS (yuck!).\n"
"2. Wrote the proper config to the EEPROM with it.\n"
"3. DOS bios reads out the Mach32 EEPROM with out problems and obeys\n"
"   all settings (for example, power up video mode).\n"
"If you can't get a correct checksum, read the section \"EEPROM woes\"\n"
"in \"README.mach32\" of your svgalib distribution.\n"
		);
	}
return 0;
}

void puttable(int table)
{
int i;
int clock;
char buffer[80];

unsigned short *tab;

tab=eeprom+(table*15+0xd);
printf("\n EEPROM Words %02xh-%02xh:\tCRT Parameter table %d",table*15+0xd,
	(table+1)*15+0xc,table+1);
if(tab[10]&0x3f00)
	puts(":");
else	{
	puts("  .....................  invalid");
	return;
	}
table=tab[0];
putstr("Vertical sync polarity",(table&0x8000)?"  -":"  +");
putstr("Horizontal sync polarity",(table&0x4000)?"  -":"  +");
putflag("Interlace",table&0x2000);
putflag("Multiplex pixels",table&0x1000);
i=(table>>9)&7;
putstr("Maximum pixel depth",maxpix[(i>3)?3:i]);
putstr("Parameter type",(table&0x0100)?"  8514/Mach32":"  VGA");
putstr("Dotclock select",(table&0x0080)?"  user supplied":"  default");
putstr("Usage of CRTC parameters",(table&0x0040)?"  all"
	:"  sync polarities only");
putint("Dotclock chip select","  #%d",table&15);
clock=mach32_clocks[table&15];
putstr("Dotclock divide by",clockdiv[(table>>4)&3]);
if(!(table&0x20))
	if(table&0x10)
		clock/=2;
if(clock)
	putint("Pixel clock (approximate value)","  %d MHz",(int)(clock+0.5));
else	putstr("Pixel clock","  (sorry, don't know the frequency)");
if(table&0x0100)
	{ /*8514/Mach32*/
	double fclock,lilen;
	int xpels=((tab[3]&0xff)+1)<<3,
	    ypels=tab[6],
	    xtotal=((tab[3]>>8)+1)<<3,
	    ytotal=tab[5],
	    xstart=((tab[4]>>8)+1)<<3,
	    ystart=tab[7],xsync=(tab[4]&0x1f)*8,
	    ysync=(tab[8]>>8)&0x1f;
	puts("  Mach32 / 8514/A CRT parameters:");
	putint("Video fifo 16bpp","  %d",tab[2]&0xff);
	putint("Video fifo 24bpp","  %d",tab[2]>>8);
	putint("H_TOTAL","  %d",tab[3]>>8);
	putint("H_DISP","  %d",tab[3]&0xff);
	putint("H_SYNC_STRT","  %d",tab[4]>>8);
	putint("H_SYNC_WID","  %02xh",tab[4]&0xff);
	putint("V_TOTAL","  %xh",tab[5]);
	putint("V_DISP","  %xh",tab[6]);
	putint("V_SYNC_STRT","  %xh",tab[7]);
	putint("V_SYNC_WID","  %02xh",tab[8]>>8);
	putint("DISP_CNTL","  %02xh",tab[8]&0xff);
	putint("CLOCK_SEL","  %xh",tab[9]);
	clock=mach32_clocks[(tab[9]>>2)&15];
	if(!(tab[9]&0x40))
		clock*=2;
	puts("  Resulting video timings:");
	if(clock)
		{
		sprintf(buffer,"  %.1f MHz",fclock=((double)clock)/2);
		}
	else	{
		sprintf(buffer,"  #%d, don't know clock frequency, so no timings",
			(tab[9]>>2)&15);
		fclock=0;
		}
	putstr("Pixel clock",buffer);
	switch(tab[8]&0x6)
		{
		case 0:
			ypels=((ypels>>2)&~1)|(ypels&1);
			ytotal=((ytotal>>2)&~1)|(ytotal&1);
			ystart=((ystart>>2)&~1)|(ystart&1);
			break;
		case 2:
			ypels=((ypels>>1)&0xFFFC)|(ypels&3);
			ytotal=((ytotal>>1)&0xFFFC)|(ytotal&3);
			ystart=((ystart>>1)&0xFFFC)|(ystart&3);
			break;
		default:
			puts("  Unknown DISP_CNTL, vertical values are probably wrong.");
		}
	ypels++;
	ytotal++;
	ystart++;
	sprintf(buffer,"  %d x %d%s",xpels,ypels,(tab[8]&0x10)?", Interlaced":
								"" );
	putstr("Resolution",buffer);
	if(clock)
		{
		sprintf(buffer,"  %.3f KHz",lilen=(fclock*1e3)/xtotal);
		putstr("Horizontal frequency",buffer);
		sprintf(buffer,"  %.2f Hz",(lilen*1000)/ytotal);
		putstr("Vertical frequency",buffer);
		}
	else	lilen=0;
	putstr("Horizontal sync polarity",(tab[4]&0x20)?"  -":"  +");
	putstr("Horizontal sync width",hsyncstr(xsync,clock,fclock));
	putstr("Horizontal front porch",hsyncstr(xstart-xpels,clock,fclock));
	putstr("Horizontal back porch",hsyncstr(xtotal-xsync-xstart,
							clock,fclock));
	putstr("Horizontal active time",hsyncstr(xpels,clock,fclock));
	putstr("Horizontal blank time",hsyncstr(xtotal-xpels,clock,fclock));
	putstr("Vertical sync polarity",(tab[8]&0x2000)?"  -":"  +");
	putstr("Vertical sync width",vsyncstr(ysync,clock,lilen));
	putstr("Vertical front porch",vsyncstr(ystart-ypels,clock,lilen));
	putstr("Vertical back porch",vsyncstr(ytotal-ysync-ystart,
							clock,lilen));
	putstr("Vertical active time",vsyncstr(ypels,clock,lilen));
	putstr("Vertical blank time",vsyncstr(ytotal-ypels,clock,lilen));
	}
else	{ /*VGA mode*/
	puts("  VGA CRT parameters:");
	putint("VIDEO_MODE_SEL_1","  %02xh",tab[1]>>8);
	putint("VIDEO_MODE_SEL_2","  %02xh",tab[1]&0xff);
	putint("VIDEO_MODE_SEL_3","  %02xh",tab[2]>>8);
	putint("VIDEO_MODE_SEL_4","  %02xh",tab[2]&0xff);
	putint("H_TOTAL (CRT00)","  %02xh",tab[3]>>8);
	putint("V_TOTAL (CRT06)","  %02xh",tab[3]&0xff);
	putint("H_RETRACE_START (CRT04)","  %02xh",tab[4]>>8);
	putint("H_RETRACE_END (CRT05)","  %02xh",tab[4]&0xff);
	putint("V_RETRACE_START (CRT10)","  %02xh",tab[5]>>8);
	putint("V_RETRACE_END (CRT11)","  %02xh",tab[5]&0xff);
	putint("H_BLANK_START (CRT02)","  %02xh",tab[6]>>8);
	putint("H_BLANK_END (CRT03)","  %02xh",tab[6]&0xff);
	putint("V_BLANK_START (CRT15)","  %02xh",tab[7]>>8);
	putint("V_BLANK_END (CRT16)","  %02xh",tab[7]&0xff);
	putint("CRT_OVERFLOW (CRT07)","  %02xh",tab[8]>>8);
	putint("MAX_SCANLINE (CRT09)","  %02xh",tab[8]&0xff);
	putint("V_DISPLAYED (CRT12)","  %02xh",tab[9]>>8);
	putint("CRT_MODE (CRT17)","  %02xh",tab[9]&0xff);
	puts(
"  Resulting video timings  .........................  not implemented for VGA"
		);
	}
table=tab[10];
puts("  Additional mode flags:");
putflag("Pixel clock divide by 2",table&0x8000);
putflag("Multiplex (MUX flag)",table&0x4000);
putint("Size of mode table","  %d words",(table>>8)&0x3f);
putstr("Offset to alternate table",offset(buffer,(table<<8)&0xff00));
putint("Horizontal overscan","  %d",tab[11]);
putint("Vertival overscan","  %d",tab[12]);
putint("Overscan color blue","  %d",tab[13]>>8);
putint("Overscan color index 8bpp","  %d",tab[13]&0xff);
putint("Overscan color red","  %d",tab[14]>>8);
putint("Overscan color green","  %d",tab[14]&0xff);
}
