VESA Graphics Programming

Here you can learn how to use 640*480 resolution graphics. Intermediate programming knowledge is assumed. We recommend users to go through the tutorial in sequence and check all the examples.

What's VESA?

VESA is a standard which provides programmers with a single method for using many different video cards. With VESA you need not worry about the specific hardware in different cards, you only need to understand the VESA functions. Anyone old enough will remember having to install a VESA driver for their video card, these days it's implemented in hardware though - so that's another thing you need not worry about. We highly recommend that anyone attempting this tutorial gets a copy of the official VESA 2.0 specification. This tutorial should get you up and running even if you don't understand a word of it though!

Vesa Information Structures

To begin with you need 2 structures to store information returned to you by the video card. Just copy ours. VESA_INFO will store information about your video card. MODE_INFO will hold information about the graphics mode you are using. The use of many fields should be fairly obvious:

typedef struct VESA_INFO
{
	unsigned char  VESASignature[4]     __attribute__((packed));
	unsigned short VESAVersion          __attribute__((packed));
	unsigned long  OEMStringPtr         __attribute__((packed));
	unsigned char  Capabilities[4]      __attribute__((packed));
	unsigned long  VideoModePtr         __attribute__((packed));
	unsigned short TotalMemory          __attribute__((packed));
	unsigned short OemSoftwareRev       __attribute__((packed));
	unsigned long  OemVendorNamePtr     __attribute__((packed));
	unsigned long  OemProductNamePtr    __attribute__((packed));
	unsigned long  OemProductRevPtr     __attribute__((packed));
	unsigned char  Reserved[222]        __attribute__((packed));
	unsigned char  OemData[256]         __attribute__((packed));
}VESA_INFO;
typedef struct MODE_INFO
{
	unsigned short ModeAttributes       __attribute__((packed));
	unsigned char  WinAAttributes       __attribute__((packed));
	unsigned char  WinBAttributes       __attribute__((packed));
	unsigned short WinGranularity       __attribute__((packed));
	unsigned short WinSize              __attribute__((packed));
	unsigned short WinASegment          __attribute__((packed));
	unsigned short WinBSegment          __attribute__((packed));
	unsigned long  WinFuncPtr           __attribute__((packed));
	unsigned short BytesPerScanLine     __attribute__((packed));
	unsigned short XResolution          __attribute__((packed));
	unsigned short YResolution          __attribute__((packed));
	unsigned char  XCharSize            __attribute__((packed));
	unsigned char  YCharSize            __attribute__((packed));
	unsigned char  NumberOfPlanes       __attribute__((packed));
	unsigned char  BitsPerPixel         __attribute__((packed));
	unsigned char  NumberOfBanks        __attribute__((packed));
	unsigned char  MemoryModel          __attribute__((packed));
	unsigned char  BankSize             __attribute__((packed));
	unsigned char  NumberOfImagePages   __attribute__((packed));
	unsigned char  Reserved_page        __attribute__((packed));
	unsigned char  RedMaskSize          __attribute__((packed));
	unsigned char  RedMaskPos           __attribute__((packed));
	unsigned char  GreenMaskSize        __attribute__((packed));
	unsigned char  GreenMaskPos         __attribute__((packed));
	unsigned char  BlueMaskSize         __attribute__((packed));
	unsigned char  BlueMaskPos          __attribute__((packed));
	unsigned char  ReservedMaskSize     __attribute__((packed));
	unsigned char  ReservedMaskPos      __attribute__((packed));
	unsigned char  DirectColorModeInfo  __attribute__((packed));
	unsigned long  PhysBasePtr          __attribute__((packed));
	unsigned long  OffScreenMemOffset   __attribute__((packed));
	unsigned short OffScreenMemSize     __attribute__((packed));
	unsigned char  Reserved[206]        __attribute__((packed));
}MODE_INFO;

Testing for VESA

We will use the VESA_INFO structure when we first check for valid VESA information with this function:

VESA_INFO vesa_info;
MODE_INFO mode_info;
char* double_buffer;

int get_vesa_info()
{
	__dpmi_regs r;
	long dosbuf;
	int c;
	dosbuf = __tb & 0xFFFFF;
	for (c = 0; c < sizeof(VESA_INFO); c++)
		_farpokeb(_dos_ds, dosbuf + c, 0);
	dosmemput("VBE2", 4, dosbuf);
	r.x.ax = 0x4F00;
	r.x.di = dosbuf & 0xF;
	r.x.es = (dosbuf >> 4) & 0xFFFF;
	__dpmi_int(0x10, &r);
	if (r.h.ah)
		return -1;
	dosmemget(dosbuf, sizeof(VESA_INFO), &vesa_info);
	if (strncmp(vesa_info.VESASignature, "VESA", 4) != 0)
		return -1;
	return 0;
}

Here we are using the dos interrupt 10 Hex. All VESA functions are called though this combined with a value loaded into the AX register. This function returns zero if valid VESA info is found.

Setting VESA modes

Once you have validated VESA compatibility you are ready to set and check for the various resolutions available:

int get_vesa_mode()
{
	__dpmi_regs r;
	r.x.ax = 0x4F03;
	__dpmi_int(0x10, &r);
	if (r.h.ah)
		return -1;
	return r.x.bx;
}

int set_vesa_mode(int mode)
{
	__dpmi_regs r;
	r.x.ax = 0x4F02;
	r.x.bx = mode;
	__dpmi_int(0x10, &r);
	if (r.h.ah)
		return -1;
	return 0;
}

The following get_mode_info function can be used in conjunction with these to get specific details about a mode:

int get_mode_info()
{
	__dpmi_regs r;
	long dosbuf;
	int c;
	dosbuf = __tb & 0xFFFFF;
	for (c = 0; c < sizeof(MODE_INFO); c++)
		_farpokeb(_dos_ds, dosbuf + c, 0);
	r.x.ax = 0x4F01;
	r.x.di = dosbuf & 0xF;
	r.x.es = (dosbuf >> 4) & 0xFFFF;
	r.x.cx = 257;
	__dpmi_int(0x10, &r);
	if (r.h.ah)
		return 0;
	dosmemget(dosbuf, sizeof(MODE_INFO), &mode_info);
	return 1;
}

It may be worth going through this example which includes calls to all the code so far.

Drawing

Initially you may draw to a buffer as in the VGA examples earlier in this tutorial. When we come to display that buffer there are new considerations. The video memory for high resolutions is split into a number of banks (the reason for this lies in limitations of PC architecture). The 640*480 buffer will have to be copied in (probably 4) chunks. The following code will do this:

void copy_to_vesa_screen(char* memory_buffer)
{
	int bank_number = 0;
	int todo = 307200;
	int copy_size;
	while (todo > 0)
	{
		set_vesa_bank(bank_number);
		if (todo > 65536)
			copy_size = 65536;
		else
			copy_size = todo;
		dosmemput(memory_buffer, copy_size, 0xA0000);
		todo -= copy_size;
		memory_buffer += copy_size;
		bank_number++;
	}
}

Note the use of set_vesa_bank function detailed here:

void set_vesa_bank(int bank_number)
{
	__dpmi_regs r;
	r.x.ax = 0x4F05;
	r.x.bx = 0;
	r.x.dx = bank_number;
	__dpmi_int(0x10, &r);
}

We now have all the tools to display pictures in 640 * 480 resolutions. Have a look at this example which includes setting the palette and using a bitmap.