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!
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;
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.
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.
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.