#include <stdio.h>
#include <dos.h>

/*
 * VESA BIOS extended function number and return code
 */
#define VESA_FUNC	0x4f00
#define VESA_SUCCESS	0x004f

/*
 * VESA BIOS sub-function numbers
 */
#define VESA_VGA_INFO	0
#define VESA_MODE_INFO  1
#define VESA_SET_MODE	2
#define VESA_GET_MODE	3
#define VESA_VGA_STATE  4
#define VESA_PAGE_CTL	5

#define ushort  unsigned short

/*
 * The VGA info structure
 */
typedef struct {
    char       VESASignature[4];	/* should be "VESA" */
    short      VESAVersion;		/* VESA version number */
    char  far *OEMStringPtr;		/* Pointer to OEM string */
    long       Capabilities;		/* capabilities of the video env */
    short far *VideoModePtr;		/* ptr to supported Super VGA modes */
} VgaInfoBlock;

/*
 * The mode info structure
 */
typedef struct {
    short      ModeAttributes;		/* mode attributes */
    char       WinAAttributes;		/* Window A attributes */
    char       WinBAttributes;		/* Window B attributes */
    short      WinGranularity;		/* window granularity */
    short      WinSize;			/* window size */
    ushort     WinASegment;		/* Window A start segment */
    ushort     WinBSegment;		/* Window B start segment */
    void far   (*WinFuncPtr)();		/* pointer to window function */
    short      BytesPerScanLine;	/* bytes per scan line */
    /* extended information */
    /* optional information */
    short      XResolution;		/* horizontal resolution */
    short      YResolution;		/* vertical resolution */
    char       XCharSize;		/* character cell width */
    char       YCharSize;		/* character cell height */
    char       NumberOfPlanes;		/* number of memory planes */
    char       BitsPerPixel;		/* bits per pixel */
    char       NumberOfBanks;		/* number of banks */
    char       MemoryModel;		/* memory model type */
    char       BankSize;		/* bank size in K */
} ModeInfoBlock;

/*
 * MODE attribute bits
 */
#define MODE_SUPPORTED  1		/* Mode supported in hardware */
#define MODE_EXTINFO	2		/* Extended information available */
#define MODE_SUPBIOS	4		/* Outp functions supported by BIOS */
#define MODE_ISCOLOR	8		/* Monochrome/color mode */
#define MODE_ISGRAPHICS 16		/* Mode type (0: text, 1:graphics) */

/*
 * Window attribute bits
 */
#define WIN_SUPPORTED	1		/* Window supported */
#define WIN_READABLE	2		/* Window readable */
#define WIN_WRITABLE	4		/* Window writable */

/*
 * MemoryModel values
 */
#define MODEL_TEXT	0		/* 00h = Text mode */
#define MODEL_CGA	1		/* 01h = CGA graphics */
#define MODEL_HERC	2		/* 02h = Hercules graphics */
#define MODEL_4PLANE	3		/* 03h = 4-plane planar */
#define MODEL_PACKED	4		/* 04h = Packed pixel */
#define MODEL_256_NC	5		/* 05h = Non-chain 4, 256 color */
/* 06h-0Fh = Reserved, to be defined by VESA */
/* 10h-FFh = To be defined by OEM	     */


int getinfo(VgaInfoBlock *vgainfo)
{
	_ES = FP_SEG(vgainfo);
	_DI = FP_OFF(vgainfo);
	_AX = VESA_FUNC + VESA_VGA_INFO;
	geninterrupt(0x10);
	if(_AX != VESA_SUCCESS) return(0);
	if(vgainfo->VESASignature[0] != 'V') return(0);
	if(vgainfo->VESASignature[1] != 'E') return(0);
	if(vgainfo->VESASignature[2] != 'S') return(0);
	if(vgainfo->VESASignature[3] != 'A') return(0);
	return(1);
}

int getmodeinfo(int mode,ModeInfoBlock *modeinfo)
{
	_ES = FP_SEG(modeinfo);
	_DI = FP_OFF(modeinfo);
	_CX = mode;
	_AX = VESA_FUNC + VESA_MODE_INFO;
	geninterrupt(0x10);
	if(_AX != VESA_SUCCESS) return(0);
	return(modeinfo->ModeAttributes & MODE_SUPPORTED);
}


void printinfo(VgaInfoBlock *vgainfo)
{
	char  far *sp = vgainfo->OEMStringPtr;
	short far *mp = vgainfo->VideoModePtr;

	printf("VESASignature: \"%c%c%c%c\"\n",
	    vgainfo->VESASignature[0],
	    vgainfo->VESASignature[1],
	    vgainfo->VESASignature[2],
	    vgainfo->VESASignature[3]
	);
	printf("VESAVersion:\t%d\n",vgainfo->VESAVersion);
	printf("OEMStringPtr:\t\"");
	while(*sp != '\0') putchar(*sp++);
	printf("\"\nCapabilities:\t%ld\n",vgainfo->Capabilities);
	printf("VideoModePtr:\t%Fp\n",mp);
	printf("Video Modes:\t");
	while(*mp != (-1)) printf("0x%x ",*mp++);
	printf("\n\n");
}

#define DEFBIT(bit)	{ bit, #bit }
typedef struct {
    int  bit;
    char *name;
} bitdef;

bitdef modeattrbits[] = {
    DEFBIT(MODE_SUPPORTED),
    DEFBIT(MODE_EXTINFO),
    DEFBIT(MODE_SUPBIOS),
    DEFBIT(MODE_ISCOLOR),
    DEFBIT(MODE_ISGRAPHICS),
    { 0 }
};

bitdef winattrbits[] = {
    DEFBIT(WIN_SUPPORTED),
    DEFBIT(WIN_READABLE),
    DEFBIT(WIN_WRITABLE),
    { 0 }
};

bitdef memorymodels[] = {
    DEFBIT(MODEL_TEXT),
    DEFBIT(MODEL_CGA),
    DEFBIT(MODEL_HERC),
    DEFBIT(MODEL_4PLANE),
    DEFBIT(MODEL_PACKED),
    DEFBIT(MODEL_256_NC),
    { 0 }
};

void printbits(int value,bitdef *def)
{
	int prev = 0;

	while(def->bit != 0) {
	    if(value & def->bit) {
		if(prev) printf(" | ");
		printf(def->name);
		prev = 1;
	    }
	    def++;
	}
	if(!prev) printf("0");
	printf("\n");
}

char *getmodelname(int model)
{
	static char temp[50];

	if(model < 0) return(sprintf(temp,"Invalid model [%d]",model),temp);
	if(model <= MODEL_256_NC) return(memorymodels[model].name);
	if(model <= 15) return(sprintf(temp,"VESA model [%0x2x]",model),temp);
	return(sprintf(temp,"OEM model [%0x2x]",model),temp);
}

void printmodeinfo(int mode,ModeInfoBlock *modeinfo)
{
	printf("Mode 0x%x is supported\n",mode);
	printf("  ModeAttributes:\t");
	printbits(modeinfo->ModeAttributes,modeattrbits);
	printf("  WinAAttributes:\t");
	printbits(modeinfo->WinAAttributes,winattrbits);
	printf("  WinBAttributes:\t");
	printbits(modeinfo->WinBAttributes,winattrbits);
	printf("  WinGranularity:\t%d\n",modeinfo->WinGranularity);
	printf("  WinSize:\t\t%d\n",modeinfo->WinSize);
	printf("  WinASegment:\t\t0x%04x\n",modeinfo->WinASegment);
	printf("  WinBSegment:\t\t0x%04x\n",modeinfo->WinBSegment);
	printf("  WinFuncPtr:\t\t\%Fp\n",modeinfo->WinFuncPtr);
	printf("  BytesPerScanLine:\t%d\n",modeinfo->BytesPerScanLine);
	if(!(modeinfo->ModeAttributes & MODE_EXTINFO)) return;
	printf("  XResolution:\t\t%d\n",modeinfo->XResolution);
	printf("  YResolution:\t\t%d\n",modeinfo->YResolution);
	printf("  XCharSize:\t\t%d\n",modeinfo->XCharSize);
	printf("  YCharSize:\t\t%d\n",modeinfo->YCharSize);
	printf("  NumberOfPlanes:\t%d\n",modeinfo->NumberOfPlanes);
	printf("  BitsPerPixel:\t\t%d\n",modeinfo->BitsPerPixel);
	printf("  NumberOfBanks:\t%d\n",modeinfo->NumberOfBanks);
	printf("  MemoryModel:\t\t%d (%s)\n",modeinfo->MemoryModel,getmodelname(modeinfo->MemoryModel));
	printf("  BankSize:\t\t%d\n",modeinfo->BankSize);
}

void main(void)
{
	VgaInfoBlock  vgainfo;
	ModeInfoBlock modeinfo;
	short far *modeptr;
	int mode;

	if(getinfo(&vgainfo)) {
	    printinfo(&vgainfo);
	    modeptr = vgainfo.VideoModePtr;
	    while((mode = *modeptr++) != (-1)) {
		if(getmodeinfo(mode,&modeinfo)) printmodeinfo(mode,&modeinfo);
		else printf("Mode 0x%x IS NOT SUPPORTED!\n",mode);
	    }
	}
	else printf("VESA BIOS extensions not found\n");
	exit(0);
}

