Vesa or Mastercard ?

VESA BIOS EXTENSION 2.0
Basic Trainer
by dlt, May 1998

INTRODUCTION

This trainer/tutorial is aimed at getting you started with VBE2; that is, setting up an appropriate graphics mode, drawing to the screen, and some other basic functions that you will probably need. If you are interested in anything more advanced or in-depth, I suggest you get the VBE2 spec:

ftp://ftp.scitechsoft.com/devel/docs/vbe20-11.exe

What might also be helpful is the DPMI spec; 0.9 will do as well as 1.0.

ftp://x2ftp.oulu.fi/pub/msdos/programming/specs/dpmispec.arj (0.9)
or
ftp://x2ftp.oulu.fi/pub/msdos/programming/specs/dpmi100.zip (1.0)

As you will discover while reading this document, I use DJGPP, and I like it a lot :-) Many thanks to DJ Delorie & co. Point yourself to:

http://www.delorie.com/

REQUIREMENTS
1. A compiler which produces protected mode source. This is not the same as a protected mode compiler; Turbo Pascal 7 users may as well stop reading this now, and go get DJGPP.

2. A very basic understanding of the differences between protected mode and real mode.

3. Reading lessons help, as my science teacher is wont to say...

SETTING UP A VIDEO MODE

This is probably the first thing you will want to do. But before you can set up a video mode using VBE2, you need to establish that VBE2 functions are in fact installed on your system. For this you need a data structure.

      VBE_INFO_STRUC
         VBE_Signature      DWORD
         Version            WORD
         OEM_String         DWORD
         Capabilities       DWORD
         VideoModeList      DWORD
         TotalMemory        WORD
         OEMSoftwareRev     WORD
         OEMVendorName      DWORD
         OEMProductName     DWORD
         OEMProductRev      DWORD
         Reserved           222 bytes
         OEM_Data           256 bytes
      VBE_INFO_STRUC ENDS

A couple of these may need explaining:

OEM_* are seg:off pointers to null-terminated strings.

VideoModeList points to an array of words representing the VBE2 display modes supported by the video card. This list is terminated by a 0xffff.

TotalMemory is in 64k chunks, so you need to multiply by 65536 to get the number of bytes of video memory actually available.

Version is a binary-coded decimal. In other words, the most significant byte contains the major version number (the number before the decimal point) and the least significant byte contains the minor version number.

Capabilities is a set of flags indicating what the video card can do. You probably won't need to look at this.

You'll need to set this structure up in whatever form is appropriate to your compiler. Note that for many C compilers, an int is 4 bytes big, in other words a signed dword. You need to use the type short to store words. Also beware of #pragma pack() and __PACKED__ conventions (look these up in your compiler's documentation).

Now that you have the structure, you need to do three things:

1. Set VBE_Signature = "VBE2" - this indicates to the VBE that we want the structure for VBE2 as opposed to earlier versions (yes, the structure is different).

2. Call the real-mode interrupt 0x10, with: AX = 0x4f00 ES:DI = pointer to the (real mode) address of the VBE_INFO structure (if you don't know how to call a real-mode interrupt, then have a look at the section "CALLING REAL-MODE INTERRUPTS" at the end)

3. Check that a) VBE_Signature is now equal to "VESA" b) VBE_Version >= 0x0200

If all of this works okay, then VBE2 functions are installed.

To set up a particular screen mode is relatively easy. Again, you call real- mode interrupt 0x10, with AX = 0x4f02, and BX = the video mode you want. To get back to text mode, do the same thing with BX = 3 (this is exactly the same as a normal BIOS "set video mode" call).

LINEAR FRAME BUFFERS

This is probably the reason you wanted to use VBE2 in the first place, right? For those who are in the dark, having a linear frame buffer is a neat little protected mode gimmick whereby you can access all of the video card's memory directly. Yup, that's right, no messing around with write-planes and small windows into video memory... you get to play with the whole lot at once. You needn't worry about the mechanics of how this works. Just pretend that the video card's memory has been tacked onto the end of the "normal" memory in your system. All you really have to do is find out the address where it begins. This is unfortunately a less-than-simple process.

     STEP 1:
         Find out where the linear frame buffer is going to be for your
         particular video mode.  Another (large) structure is required.  This is
         it:
            Mode_Attributes         WORD
            Window_A_Attributes     BYTE
            Window_B_Attributes     BYTE
            Window_Granularity      WORD
            Window_Size             WORD
            Window_A_Segment        WORD
            Window_B_Segment        WORD
            Window_Function         DWORD (POINTER)
            Bytes_Per_Scanline      WORD
            Horiz_Resolution        WORD
            Vert_Resolution         WORD
            Horiz_Char_Size         BYTE            ; for text
            Vert_Char_Size          BYTE
            Number_Planes           BYTE
            Bytes_Per_Pixel         BYTE
            Number_Banks            BYTE
            Memory_Model            BYTE            ; arrangement of data
            Bank_Size               BYTE
            Number_Pages            BYTE
            Reserved                BYTE
            Color_Data              9 BYTES
            Linear_Framebuffer      DWORD (POINTER)
            Offscreen_Offset        DWORD
            Offscreen_Size          WORD
            Reserved                206 BYTES

I won't go into the details of this structure. What we're interested in is that Linear_Framebuffer entry. What it is, is a *PHYSICAL* pointer to the start of the buffer. The reason I emphasize PHYSICAL is because its physical address is not neccessarily the same thing as the address you will use when accessing it (not that the physical address has much to do with reality either...).

STEP 2:
Ask your DPMI server (the program that's in charge of protected mode) to convert the physical address into something you can work with. This step is very much dependant on the compiler you are using. Usually it will be a function called "physical_address_mapping" or suchlike. For those of you using assembler the function is:

            DPMI (int 31h) function 0x0800
             AX    = 0x0800
             SI:DI = 32-bit size (length) of requested block
             BX:CX = 32-bit physical address of start of block

Here is the DJGPP code I use. Look in DPMI.H or the equivalent for your compiler and you should find equivalent structures and functions:

            __dpmi_meminfo ms;
            ms.size = vbe2info->TotalMemory*65536l;
            ms.address = (unsigned int)modeinfo->FrameBuffer;
            __dpmi_physical_address_mapping(&ms);
            bufferptr=ms.address;

STEP 3:
Decide how you're going to access the memory. There are generally two methods. The first is that you allocate a selector with which you can address the video memory. This is a big pain, and will slow you down because of the overrides (eg. fs:[edi] instead of just [edi]). The advantage is that it will work on *all* protected mode servers (including Windows NT and Linux DOSEMU). However, you are unlikely to be running a program which uses VBE2 linear framebuffering under a secure operating system such as the two mentioned. In most cases, you can use near pointers. The trick is to set the upper limit of your DS selector to 0x000000ff. Then you can access the video memory using DS, like so:

            videobuffer = bufferptr - ds_linear_base

where ds_linear_base is the linear address of the start of your data segment. This should be provided to you by your compiler or whatever DPMI routines you are using. In DJGPP, you can use the variable __djgpp_conventional_base, but you have to ADD it to bufferptr in the example above, not subtract it (assume it's already been negated for you).

Setting the limit of the DS selector can be done with DPMI function 0x0008; some compilers provide functions which do this for you. DJGPP's is called __djgpp_nearptr_enable.

STEP 4:
You now have direct access to the video memory, where everything is stored linearly. If you have 1 byte/pixel and 640 pixels/row, then you can write to a pixel with

            videobuffer[y*640+x] = colour

which obviously makes relatively simple work of complex algorithms.

PANNING, PALETTES, PAGE FLIPPING

The good news is that you've got past the hard bit. The rest is fairly trivial. Here are a few other things you might want to do with VBE2:

     PANNING
         Use the Get/Set Display Start function.
           Real-mode interrupt 0x10
            AX = 0x4F07
            BH = 0
            BL = 0x00 to set the display start
                 0x80 to set it during a retrace (can be useful)
                 0x01 to get the current display start (not so useful)
            CX = X offset (first pixel in scanline)
            DX = Y offset (first scanline)

When the amount of available video memory is more than twice the amount required for a screenful of information (Xres * Yres * BperPix), then you can have multiple pages. The easiest way is probably to flip between YOfs=0 and YOfs=YRes using the above function.

CHANGING THE LOGICAL SCANLINE WIDTH
This is helpful when setting up a large virtual page over which to scroll, and also in calculating pixel addresses (for example, by using a multiplication table or bit-shifting).

           Real-mode interrupt 0x10
            AX = 0x4F06
            BX = as for get/set display start
            CX = number of pixels per logical scanline

PALETTE FUNCTIONS
VBE2 supplies palette manipulation functions, but unless you really, really need 8-bit control over your video DAC, it's easier to use the old VGA registers:

            port[0x3c7] = read index
            port[0x3c8] = write index
            port[0x3c9] = r,g,b   (write/read them one-by-one)

ANY QUESTIONS?

You can contact me via

e-mail: dturner@global.co.za
irc: dlt on ZAnet (try 196.31.1.7)
web: have a look at http://www.surf.to/demos/ in the
groups section; I should be there somewhere.

APPENDIX: CALLING REAL-MODE INTERRUPTS

Generally:

         Use DPMI (int 31h) function 0x0300:
           AX = 0x0300
           BL = real mode interrupt number
           BH = set bit one if the interrupt you are calling is an IRQ handler
           CX = number of words on stack to pass to interrupt
           ES:EDI -> real mode call structure
         The real mode call structure:

             R_EDI           DWORD
             R_ESI           DWORD
             R_EBP           DWORD
             resvd           DWORD
             R_EBX           DWORD
             R_EDX           DWORD
             R_ECX           DWORD
             R_EAX           DWORD
             R_FLAGS         WORD
             R_ES            WORD
             R_DS            WORD
             R_FS            WORD
             R_GS            WORD
             R_IP            WORD
             R_CS            WORD
             R_SP            WORD
             R_SS            WORD

Tran's PMODE:
As I recall, he used int 33h with the interrupt number pushed onto the stack. So, set up the registers as if you were in real mode, push the interrupt number as an immediate, and call interrupt 33h.

DJGPP (and similarly for some other Cs):

         Use the structure __dpmi_regs (defined in dpmi.h), and pass this to
         the function __dpmi_int().
         For example:

           __dpmi_regs r;
           r.x.ax=0x4f01;
           r.x.bx=3;
           __dpmi_int(0x10,&r);

I believe some Cs have carried over their real-mode calling conventions; so you will have things like UNION REGS r; dosint(0x10,&r); (if I remember rightly :-))

Borland Pascal 7.0:

I have absolutely no idea. Use the general DPMI function. All I can suggest is that you don't use Borland Pascal's protected mode functions. Unless you happen to love 16-bit code. Go get DJGPP; it's C and it's free .

And that should be enough to get you started.