/*
 * This is a sample program to query a video adapters capabilities
 * conforming to Vesa Super VGA BIOS Extension Standard #VS891001
 *
 * This program is compiled under Microsoft C version 5.0 (or equivalent).
 * Any memory model may be used.
 * (Note the "%Fs" format to display the (far) OEM string)
 *
 * The command used to compile this is:
 *		cl /AS /Os /Gs /G0 /Zp vesatest.c
 *
 * Program written by Mark D. Niemiec of Tecmar Inc.
 * This program is in the public domain.
 */
#include <stdio.h>

typedef unsigned char uchar;
typedef unsigned int uint;

#define	lobyte(n)	((n) & 0xFF)
#define	hibyte(n)	((n) >> 8 & 0xFF)



/*
 * VGA BIOS extension functions
 */
#define	SUPERVGA		0x4F

#define	GetSuperVGAInfo		0x00
#define	GetModeInfo		0x01



/*
 * VGA information block returned by GetSuperVGAInfo call
 */
struct VgaInfoBlock {
	char	VESASignature[4];	/* 0: 4 signature bytes:  must be 'VESA' */
	uint	VESAVersion;		/* 4: VESA version number */
	char	far *OEMStringPtr;	/* 6: Pointer to OEM string */
	long	Capabilities;		/* 10: Capabilities of the video environment */
	int	far *VideoModePtr;	/* 14: list of supported Super VGA modes */
	char	VIBfiller[256-18];	/* 18: fill to 256 bytes */
};

struct VgaInfoBlock far vib;
/* NOTE: Since 'vib' is a static variable, it is pre-initialized to zero */



/*
 * Mode information returned by GetModeInfo call
 */
struct ModeInfoBlock {
	uint	ModeAttributes;		/* 0: mode attributes */
	uchar	WinAAttributes;		/* 2: Window A attributes */
	uchar	WinBAttributes;		/* 3: Window B attributes */
	uint	WinGranularity;		/* 4: Window granularity */
	uint	WinSize;		/* 6: Window size */
	uint	WinASegment;		/* 8: Window A start segment */
	uint	WinBSegment;		/* 10: Window B start segment */
	uint	WinFuncPtr[2];		/* 12: (far) pointer to window function */
	uint	BytesPerScanLine;	/* 14: bytes per scan line */

	uint	XResolution;		/* 16: horizontal resolution */
	uint	YResolution;		/* 18: vertical resolution */
	uchar	XCharSize;		/* 19: character cell width */
	uchar	YCharSize;		/* 20: character cell height */
	uchar	NumberOfPlanes;		/* 21: number of memory planes */
	uchar	BitsPerPixel;		/* 22: bits per pixel */
	uchar	NumberOfBanks;		/* 23: number of banks */
	uchar	MemoryModel;		/* 24: memory model type */
	uchar	BankSize;		/* 25: bank size in KB */

	char	MIBfiller[256-26];	/* 26: fill to 256 bytes */
};

struct ModeInfoBlock far mib;
/* NOTE: Since 'mib' is a static variable, it is pre-initialized to zero */



/*
 * Known memory models
 */
uchar *models[] = {
	"Text",				/* 0 = text */
	"CGA",				/* 1 = CGA graphics */
	"Herc",				/* 2 = Hercules graphics */
	"EGA",				/* 3 = EGA/VGA planar graphics */
	"VGA",				/* 4 = VGA packed-pixel graphics */
	"256"				/* 5 = non-chain-4 256 color */
};					/* 6-F = VESA TBD; 10-FF = OEM */



/*
 * Column headings
 */
char headings[] = "\n\
Mode Attr Window  WindowA WindowB Mapping  Bytes Resolution Char BPP Banks Type\n\
          Grn Siz Seg att Seg att Function /line            Cell   Planes\n\n";



/*
 * Main procedure
 */
void
main(int argc, char **argv)
{
	extern void queryvesa(void);

	queryvesa();
	exit(0);
}



/*
 * Inquire about VESA information
 */
void
queryvesa(void)
{
	extern void vesainfo(void);
	extern int same(char far *, char *, int);
	int status;

	status = int10(SUPERVGA<<8|GetSuperVGAInfo, 0, 0, 0, &vib);

	if(lobyte(status) != SUPERVGA) {
		printf("VGA BIOS extensions not installed\n");
	} else if(hibyte(status) != 0) {
		printf("Get Super VGA Information call returned error %d\n",
		  hibyte(status));
	} else if(!same(vib.VESASignature,"VESA",4)) {
		printf("VESA signature was expected, '%4.4s' was found\n",
		  vib.VESASignature);
	} else {
		vesainfo();
	}
}



/*
 * See if two strings (one far and one normal) are equal
 */
int
same(char far *p, char *q, int n)
{
	while(--n >= 0) {
		if(*p++ != *q++)
			return(0);
	}
	return(1);
}



/*
 * Display VESA information
 */
void
vesainfo(void)
{
	extern void querymode(int);
	int far *modes;

	printf("Super VGA BIOS extension version %d.%02d is installed\n",
	  hibyte(vib.VESAVersion),
	  lobyte(vib.VESAVersion));
	printf(
	  vib.OEMStringPtr==NULL ?
	    "No OEM String is present" : "OEM string is '%Fs'\n",
	  vib.OEMStringPtr);
	printf("Device capabilities are %lX\n",
	  vib.Capabilities);

	printf(headings);

	for(modes=vib.VideoModePtr; *modes!=-1; )
		querymode(*modes++);
}



/*
 * Inquire about mode information
 */
void
querymode(int mode)
{
	extern void dispmode(void), dispextmode(void);
	int status;

	printf("%04X", mode);
	status = int10(SUPERVGA<<8|GetModeInfo, 0, mode, 0,
	  (struct ModeInfoBlock far *)&mib);

	if(lobyte(status) != SUPERVGA) {
		printf(": Get Mode Information call is not available\n");
	} else if(hibyte(status) != 0) {
		printf(": Get Mode Information call returned error %d\n",
		  hibyte(status));
	} else {
		dispmode();
		if(mib.ModeAttributes & 0x02)
			dispextmode();
	}

	printf("\n");
}



/*
 * Display mandatory mode information
 */
void
dispmode(void)
{
	printf(" %s%s%s%s",
	  mib.ModeAttributes&0x01 ? "I" : " ",		/* initializable */
	  mib.ModeAttributes&0x04 ? "B" : " ",		/* BIOS fns. */
	  mib.ModeAttributes&0x08 ? "C" : "M",		/* color/mono */
	  mib.ModeAttributes&0x10 ? "G" : "T");		/* graphics/text */
	printf("%4d%4d",
	  mib.WinGranularity,				/* window granularity */
	  mib.WinSize);					/* window size */
	printf(
	  mib.WinAAttributes&0x01 ? " %04X %s%s" : "        ",
	  mib.WinASegment,				/* window A segment */
	  mib.WinAAttributes&0x02 ? "R" : " ",		/* window A readable */
	  mib.WinAAttributes&0x04 ? "W" : " ");		/* window A writable */
	printf(
	  mib.WinBAttributes&0x01 ? " %04X %s%s" : "        ",
	  mib.WinBSegment,				/* window B segment */
	  mib.WinBAttributes&0x02 ? "R" : " ",		/* window B readable */
	  mib.WinBAttributes&0x04 ? "W" : " ");		/* window B writable */
	printf(" %04X:%04X",
	  mib.WinFuncPtr[1], mib.WinFuncPtr[0]);	/* mapping function */
	printf("%5d",
	  mib.BytesPerScanLine);			/* bytes/scan line */
}



/*
 * Display extended mode information
 */
void
dispextmode(void)
{
	printf("%5dx%-4d",
	  mib.XResolution,				/* screen width */
	  mib.YResolution);				/* screen height */
	printf("%3dx%-2d",
	  mib.XCharSize,				/* character width */
	  mib.YCharSize);				/* character height */
	printf("%2d",
	  mib.BitsPerPixel);				/* bits/pixel */
	printf("%2d",
	  mib.NumberOfPlanes);				/* number of planes */
	printf(
	  mib.BankSize!=0 ? "%2dx%-3d" : "%2d    ",
	  mib.NumberOfBanks,				/* number of banks */
	  mib.BankSize);				/* bank size */

	if(mib.MemoryModel < 6)
		printf(" %s", models[mib.MemoryModel]);	/* model type */
	else
		printf(" %02X", mib.MemoryModel);	/* model number */
}



/*
 * Call video BIOS
 * (Note:  es and di may be passed separately, or together via a far pointer)
 */
#include <dos.h>

int10(int ax, int bx, int cx, int dx, int di, int es)
{
	union REGS r;
	struct SREGS s;

	r.x.ax = ax;
	r.x.bx = bx;
	r.x.cx = cx;
	r.x.dx = dx;
	r.x.di = di;
	s.es = es;
	int86x(0x10, &r, &r, &s);
	return(r.x.ax);
}
