Mail Archives: djgpp/2002/10/25/11:15:25
Having a bit of trouble trying to use VESA under Windows XP.
I've compiled the code below which is mainly taken from:
"Guide: VESA graphics modes"
http://www.delorie.com/djgpp/doc/ug/graphics/vesa.html
using DJGPP v2.03
The executable produced runs fine on Win 95 (not tried 98),
showing a uniform field of random pixels across the whole display.
On XP, however, the display becomes garbled, the pixel fields
split into several bands at 640 x 480, higher modes do nothing.
(Monitor flashes up an 'invalid signal' warning at 1024 x 768).
Does anyone have any tips? I don't have a lot of experience with
C or DJGPP and am a little puzzled.
I appreciate any help that might be forthcoming.
Here's my code, which I was using to try and figure things out..
/* assembled from "Guide: VESA graphics modes" at: */
/* http://www.delorie.com/djgpp/doc/ug/graphics/vesa.html */
#include <stdio.h>
#include <dos.h>
#include <math.h>
#include <pc.h>
#include <keys.h>
#include <sys/farptr.h>
#include <go32.h>
#include <dpmi.h>
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define NUM_COLORS 256
int i;
int x;
int y;
int color;
/* make sure that a VESA driver is present, and retrieve a copy of the
VESA information structure */
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;
/* call VESA function 0x4F00 to fill it with information about the
current driver. */
/*Because VESA was designed as a real mode API for use by 16 bit
programs, */
/*this data must be transferred via a buffer in conventional memory
with the */
/*dosmemput() and dosmemget() functions: see the DPMI chapter for
details of this. */
/*The function below will copy the VESA driver information into a
global VESA_INFO */
/*structure, returning zero on success or -1 if anything goes wrong
(ie. no driver is available). */
VESA_INFO vesa_info;
int get_vesa_info()
{
__dpmi_regs r;
long dosbuf;
int c;
/* use the conventional memory transfer buffer */
dosbuf = __tb & 0xFFFFF;
/* initialize the buffer to zero */
for (c=0; c<sizeof(VESA_INFO); c++)
_farpokeb(_dos_ds, dosbuf+c, 0);
dosmemput("VBE2", 4, dosbuf);
/* call the VESA function */
r.x.ax = 0x4F00;
r.x.di = dosbuf & 0xF;
r.x.es = (dosbuf>>4) & 0xFFFF;
__dpmi_int(0x10, &r);
/* quit if there was an error */
if (r.h.ah)
return -1;
/* copy the resulting data into our structure */
dosmemget(dosbuf, sizeof(VESA_INFO), &vesa_info);
/* check that we got the right magic marker value */
if (strncmp(vesa_info.VESASignature, "VESA", 4) != 0)
return -1;
/* it worked! */
return 0;
}
/*Information about a particular mode can be obtained in a similar way
to the main VESA information block,
but using function 0x4F01 with a different structure, eg: */
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;
MODE_INFO mode_info;
int get_mode_info(int mode)
{
__dpmi_regs r;
long dosbuf;
int c;
/* use the conventional memory transfer buffer */
dosbuf = __tb & 0xFFFFF;
/* initialize the buffer to zero */
for (c=0; c<sizeof(MODE_INFO); c++)
_farpokeb(_dos_ds, dosbuf+c, 0);
/* call the VESA function */
r.x.ax = 0x4F01;
r.x.di = dosbuf & 0xF;
r.x.es = (dosbuf>>4) & 0xFFFF;
r.x.cx = mode;
__dpmi_int(0x10, &r);
/* quit if there was an error */
if (r.h.ah)
return -1;
/* copy the resulting data into our structure */
dosmemget(dosbuf, sizeof(MODE_INFO), &mode_info);
/* it worked! */
return 0;
}
/*
information can easily be obtained from the main VESA information
block.
This contains a list of all the possible modes that are supported by
the driver,
so you can write a little routine that will loop through all these
modes,
retrieving information about each one in turn until it finds the one
that
you are looking for. For example:
*/
int find_vesa_mode(int w, int h)
{
int mode_list[256];
int number_of_modes;
long mode_ptr;
int c;
/* check that the VESA driver exists, and get information about
it */
if (get_vesa_info() != 0)
return 0;
/* convert the mode list pointer from seg:offset to a linear
address */
mode_ptr = ((vesa_info.VideoModePtr & 0xFFFF0000) >> 12) +
(vesa_info.VideoModePtr & 0xFFFF);
number_of_modes = 0;
/* read the list of available modes */
while (_farpeekw(_dos_ds, mode_ptr) != 0xFFFF) {
mode_list[number_of_modes] = _farpeekw(_dos_ds, mode_ptr);
number_of_modes++;
mode_ptr += 2;
}
/* scan through the list of modes looking for the one that we
want */
for (c=0; c<number_of_modes; c++) {
/* get information about this mode */
if (get_mode_info(mode_list[c]) != 0)
continue;
/* check the flags field to make sure this is a color graphics mode,
* and that it is supported by the current hardware */
if ((mode_info.ModeAttributes & 0x19) != 0x19)
continue;
/* check that this mode is the right size */
if ((mode_info.XResolution != w) || (mode_info.YResolution != h))
continue;
/* check that there is only one color plane */
if (mode_info.NumberOfPlanes != 1)
continue;
/* check that it is a packed-pixel mode (other values are used for
* different memory layouts, eg. 6 for a truecolor resolution) */
if (mode_info.MemoryModel != 4)
continue;
/* check that this is an 8-bit (256 color) mode */
if (mode_info.BitsPerPixel != 8)
continue;
/* if it passed all those checks, this must be the mode we want! */
return mode_list[c];
}
/* oh dear, there was no mode matching the one we wanted! */
return 0;
}
/* ready to actually select a VESA graphics mode and start
drawing things onto the screen. This is done by calling function
0x4F02 with a
mode number in the BX register, eg: */
int set_vesa_mode(int w, int h)
{
__dpmi_regs r;
int mode_number;
/* find the number for this mode */
mode_number = find_vesa_mode(w, h);
if (!mode_number)
return -1;
/* call the VESA mode set function */
r.x.ax = 0x4F02;
r.x.bx = mode_number;
__dpmi_int(0x10, &r);
if (r.h.ah)
return -1;
/* it worked! */
return 0;
}
/* The SVGA video memory is located at physical address 0xA0000, the
same as in mode 13h,
but there is one small problem with this: there simply isn't enough
room for it all
to fit there! The original DOS memory map only included space for 64k
of video memory
between 0xA0000 and 0xB0000, which is fine for a 320x200 resolution
but nowhere near
enough for a 640x480 screen (that takes up 300k of framebuffer space,
and higher
resolutions need even more). The SVGA hardware designers solved this
problem by
using a banked memory architecture, where the 64k VGA memory region is
treated as
a sliding window onto the larger expanse of real video memory inside
your card.
To access an arbitrary location on the SVGA screen you must first call
VESA function
0x4F05 to tell it which bank you want to use, and then write to a
memory location
within that bank. You can set the bank with the function: */
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);
}
/* Using this, a simple putpixel function can be implemented as: */
void putpixel_vesa_640x480(int x, int y, int color)
{
int address = y*640+x;
int bank_size = mode_info.WinGranularity*640;
int bank_number = address/bank_size;
int bank_offset = address%bank_size;
set_vesa_bank(bank_number);
_farpokeb(_dos_ds, 0xA0000+bank_offset, color);
}
main()
{
printf("Test routine for figuring out VESA graphics in Win 9x/XP, 640
by 480 res and up.\n");
printf("Compiling using DJGPP v2.03\n");
printf("\nPress a key to begin.\n\n");
getkey();
if (get_vesa_info() == -1)
{
printf("\nFailed to get VESA info.\nPress a key to exit.\n");
getkey();
exit(-1);
}
else
{
if (get_vesa_info() == 0)
{
printf("\nFound VESA info.\nPress a key to continue.\n");
getkey();
if (find_vesa_mode(640, 480) != 0)
{
printf("\nFound 640 by 480 mode.\nPress a key for
demo...\n");
printf("\nYou should see a uniform field of pixels, and an
exit text message.\n");
printf("\nIf no exit message appears, just hit any key to get
out....\n");
getkey();
set_vesa_mode(640, 480);
for(i=0;i<10000;i++)
{
x=rand()%SCREEN_HEIGHT;
y=rand()%SCREEN_WIDTH;
color=rand()%NUM_COLORS;
putpixel_vesa_640x480(x,y,color);
}
printf("\nOK. Press a key to exit\n");
getkey();
exit(-1);
}
else
{
printf("\nFailed to find 640 by 480 mode.\nPress a key to
exit.\n");
getkey();
exit(-1);
}
}
}
}
- Raw text -