VESA v1.2 thru v2.0 Programming

Created : Nov 23/96
Updates : Nov 24/96
Updates : Nov 28/96
Updates : Sept 24/97

Table of Contents

Notice : First a little note, do not get VESA VBE (Video BIOS Extensions) confused with VESA local bus. They are two totaly different things. VESA VBE is a device-independant way of using your video card, wether it be ISA, EISA or PCI or VLB. The VESA local bus (VLB) is a type of slot.

VESA functions are all called by loading registers as needed, loading ah=4fh and calling INT 10h in real mode. There are advanced funcs that can be called in PMODE which will be discussed later.
This tutor is intended for using VESA VBE 2.0+ but will show when functions require VBE 2.0. It also does not explain in detail things you need not worry about. If you want more information then consult the VBE20.ZIP file in my Files Page.
Func 0h : Get VBE information
in : ax    = 4f00h
     es:di = ptr to buffer for VbeInfoBlock
out: ax    = VBE status

) The buffer must be 256 bytes for VBE 1.x.
) If the 1st four bytes in the buffer are 'VBE2' then the buffer must be 512
 bytes and will be filled in with VESA 2.0 info.
) There are bugs in VBE 1.x that will copy more that 256 bytes of info to
 the buffer so even if you plan on using just VBE 1.x make your buffer is 512
 bytes just in case.
Func 1h : Get VBE mode information
in : ax    = 4f01h
     cx    = mode #
     es:di = ptr to buffer for ModeInfoBlock
out: ax    = VBE status

) The info block contains info such as the modes x,y and colour depth.  What
 the windows size is, if it supports linear mode and/or banking mode.  etc...

Func 2h : Set VBE mode
in : ax    = 4f02h
     bx    = bit 0-8 : mode # (0h thru 1ffh)
             bit 9-13 : Reserved (must be 0)
             bit 14 : =1 use linear/flat frame buffer model (VBE 2.0+ only)
                      =0 use windowed/banking frame buffer model
             bit 15 : =1 clear display memory
                      =0 don't clear memory
out: ax    = VBE status

) Under VBE 1.x you could assume a video mode such as 010fh to always be
 video mode (320x200x24bit), but under VBE 2.0 this is no longer true!!!
 Under VBE 2.0 BIOS's are suppose to use 1 byte video modes (for good
 reasons) but they should still support the 2 byte modes.  So therefore
 when using VBE you should check every mode avail until you find one that
 suits the x,y and colour depth you need.  Use func #0 to find avail modes
 and func #1 to get info on each mode.
) If bit 14 in bx is set (use linear mode) and the video mode does not
 support linear mode then this bit is simply ignored and banking mode is
 used.  It is up to you to make sure the mode supports linear mode.  Func #1
 can be used to determine if the mode support linear mode.

Func 3h : Get current VBE mode
in : ax    = 4f03h
out: ax    = VBE status
     bx    = Curret VBE mode (same as given to func #2)

) The value returned in bx will be EXACTLY as given in func #2 included bit
 14 and 15.

Func 4h : Save/restore state
in : ax    = 4f04h
     dl    = 0 - get buffer size required for state info
             1 - Save state
             2 - Restore state
     cx    = bit 0 : save/restore controller hardware state
             bit 1 : save/restore BIOS data state
             bit 2 : save/restore DAC state
             bit 3 : save/restore Register state
     es:bx = ptr to buffer (if dl<>0h)
out :ax    = VBE status
     bx    = # of 64byte blocks required for state buffer (if dl==0h)

) DO NOT USE THIS FUNC.  Although this function looks so powerful it's full
 of too many bugs.  Many BIOS's do not support it and many VBE Extenders
 (such as UNIVBE or Display Doctor) do not either.  It's too risky using it.

Func 5h : VBE display-window control
in : ax    = 4f05h
     bh    = 0 - set memory window
             1 - get memory window
     bl    = 0 - window A
             1 - window B
     dx    = window # in VRAM in window-granularity units (if bh==0)
out: ax    = VBE status
     dx    = window # in window-granularity units

) This is used in windowed/banking mode to make the windows point to certain
 areas of the VRAM.  This func should not be used in linear buffering mode.
 Most of the time (but not always) window A is at 0a0000h (linear address)
 in system RAM and the window is 64K in size.
 But not always, the Window segments and size can be found by func #1.

Func 6h : VBE set/get logical scan-line length
in : ax    = 4f06h
     bl    = 0 - set scan-line length in pixels
             1 - get scan-line length
             2 - set scan-line length in bytes  (VBE 2.0+ only)
             3 - get maximum scan-line length   (VBE 2.0+ only)
     cx    = desired pixels or bytes (only if bl==0 or bl==2)
out: ax    = VBE status
     bx    = bytes per scan line
     cx    = actual pixels/scan line (truncated to nearest pixel)
     dx    = max # of scan lines

) This is used to change the logical scan line length.  This can be equal
 to or bigger than the physical scan line length.
 Physical scan line length = x * y * bytesperpixel

Func 7h : VBE set/get display start
in : ax    = 4f07h
     bh    = 0 (reserved and must be 0)
     bl    = 0 - Set display start
             1 - get display start  (VBE 2.0+ only)
             80h - set display start during vertical retrace (VBE 2.0+ only)
     cx    = 1st displayed pixel in scan line (if bl==0 or bl==80h)
     dx    = 1st displayed scan line (if bl==0 or bl==80h)
out: ax    = VBE status

) This is used to set/get the starting point in VRAM where the video card
 should consider as the top left most corner of the output screen.  Good for
 scrolling and shaking the screen.

Func 8h : VBE set/get palette format
in : ax    = 4f08h
     bl    = 0 - set DAC palette format
             1 - get DAC palette format
     bh    = desired # of bits of colour per primary (if bl==0)
out: ax    = VBE status
     bh    = curret # of bits of colour per primary

) This applies to 256 colour modes only
) bh may be from 6 to 8
) Each time you set a mode this is reset to 6.

Func 9h : VBE set/get palette data  (VBE 2.0+ only)
in : ax    = 4f09h
     bl    = 0 - set palette data
             1 - get palette data
             2 - set secondary palette data
             3 - get secondary palette data
             80h - set palette during vertical retrace with blank bit on
     cx    = number of palette registers to set/get
     dx    = 1st palette register to update
     es:di = buffer for palette
out  ax    = VBE status

) I do not know how the secondary palette is used.
) The buffer is as follows:
 1 byte for alignment (ignored)
 1 byte for red
 1 byte for green
 1 byte for blue

Func 0ah : VBE get protected mode interface (VBE 2.0+ only)
in : ax    = 4f01h
     bl    = 0 (return PMODE table)
out: ax    = VBE status
     es:di = real mode seg:off of table
     cx    = size of table (including code)

) The table returned contains the address of a PMODE functions that can be
 used to call func # 5,7 and 9.  This table/code and everything in the
 buffer is relocatable so you could copy it to your code segment and use it
 from there for greater speed.
) Make sure to check the status (ax) register to check if this func is
) The table is as follows:
 Offset Size Description
 ------ ---- -----------
   00h  word Offset in table of Protected mode code for Function 5
   02h  word Offset in table of Protected mode code for Function 7 
   04h  word Offset in table of Protected mode code for Function 9
   06h  word Offset in table of Ports and Memory Locations that the
              application may need I/O privilege for.
              (Optional:  if unsupported this must be 0000h)
   ?    ?    Variable remainder of Table including Code
) The Ports and Memory sub-table is as follows:

  port dw ?,?,?... ; last entry is 0ffffh
  memory dd ? (32bit linear address)
  memsize dw ?
  ;last entry is 0ffffh

     Example 1. For Port/Index combination 3DE/Fh and Memory locations
     DE800-DEA00h (length = 200h) the table would look like this:
          DE 03 DF 03 FF FF 00 E8 0D 00 00 02 FF FF

     Example 2. For only the ports it would look like:
          DE 03 DF 03 FF FF FF FF

     Example 3. For only the memory locations it would look like
          FF FF 00 E8 0D 00 00 02 FF FF

) The code must be called in 32bit PMODE only with a 32bit stack and enough
 room for it's operation.  You must make sure that certain RAM is accessable
 and IO ports as described in the Ports and Memory locations table.  I'm
 not sure how it access this RAM and the PAL LIB that comes with DOS32
 ignores the PMODE functions if any memory is required.  I'm not sure if
 it can be used under PMODE/W which has a base of zero.  Something cool
 to find out.

Appendix A : Tables

VBE status :
  AL == 4Fh: Function is supported
  AL != 4Fh: Function is not supported
  AH == 00h: Function call successful
  AH == 01h: Function call failed
  AH == 02h: Software supports this function, but the hardware does not
  AH == 03h: Function call invalid in current video mode

VBEInfoBlock struct  ;used in func #0
     VbeSignature        db   'VESA'    ; VBE Signature (or VBE2)
     VbeVersion          dw   ?         ; VBE Version
     OemStringPtr        dd   ?         ; Pointer to OEM String
     Capabilities        db   4 dup (?) ; Capabilities of graphics cont.
     VideoModePtr        dd   ?         ; Pointer to Video Mode List
     TotalMemory         dw   ?         ; Number of 64kb memory blocks
  ; Following is VBE 2.0+ only
     OemSoftwareRev      dw   ?         ; VBE implementation Software revision
     OemVendorNamePtr    dd   ?         ; Pointer to Vendor Name String
     OemProductNamePtr   dd   ?         ; Pointer to Product Name String
     OemProductRevPtr    dd   ?         ; Pointer to Product Revision String
     _Reserved_          db   222 dup (?); Reserved for VBE implementation
                                        ; scratch area
     OemData             db   256 dup (?); Data Area for OEM Strings
VBEInfoBlock ends

) All pointers are real mode seg:off types.
) The version word is BCD coded. VBE 2.0 is 200h and VBE 1.2 is 102h.
) Under VBE 1.x the buffer is 256 bytes.
) If VbeSignature is 'VBE2' before the call then then VBE2.0 info is
 returned and the buffer must be 512 bytes.
) VbeSignature will always be set to 'VESA' upon return (including VBE 2.0)
) Remember that some VBE 1.x BIOS's are buggy so you should always
 use 512 bytes. (I believe some VBE 1.x BIOSs filled in 265 bytes or so...)
) Capabilities is as follows:
 bit 0 = 0     DAC is fixed width, with 6 bits per primary colour
       = 1     DAC width is switchable to 8 bits per primary colour
 bit 1 = 0     Controller is VGA compatible
       = 1     Controller is not VGA compatible
 bit 2 = 0     Normal RAMDAC operation
       = 1     When programming large blocks of information to the
               RAMDAC use blank bit in Function 09h. i.e. RAMDAC
               recommends programming during blank period only.
 bits 3-31  =  Reserved
) When ever you set a mode the DAC is always set to 6 bits/primary colour
) VideoModePtr is a seg:off pointing to a list of video modes the BIOS 'may'
 support.  Each mode is 16bits and the list ends with 0ffffh.  You must
 make sure the mode is available with func #1.
) TotalMemory is the # of 64K blocks totally available. (ie: 256k = 4)

VBEModeInfoBlock struct  ;used in func #1
     ModeAttributes      dw ?      ; mode attributes (flags)
     WinAAttributes      db ?      ; window A attributes
     WinBAttributes      db ?      ; window B attributes
     WinGranularity      dw ?      ; window granularity
     WinSize             dw ?      ; window size
     WinASegment         dw ?      ; window A start segment
     WinBSegment         dw ?      ; window B start segment
     WinFuncPtr          dd ?      ; pointer to window function
     BytesPerScanLine    dw ?      ; bytes per scan line

  ; Following is available if bit 1 of ModeAttributes is set
  ; Always true under VBE 1.2+
     XResolution         dw ?      ; horizontal resolution in pixels or chars
     YResolution         dw ?      ; vertical resolution in pixels or chars
     XCharSize           db ?      ; character cell width in pixels
     YCharSize           db ?      ; character cell height in pixels
     NumberOfPlanes      db ?      ; number of memory planes
     BitsPerPixel        db ?      ; bits per pixel
     NumberOfBanks       db ?      ; number of banks
     MemoryModel         db ?      ; memory model type
     BankSize            db ?      ; bank size in KB
     NumberOfImagePages  db ?      ; number of images
     _Reserved           db ?      ; reserved for page function

  ; Direct Colour fields (required for direct/6 and YUV/7 memory models)
     RedMaskSize         db ?      ; size of direct colour red mask in bits
     RedFieldPosition    db ?      ; bit position of lsb of red mask
     GreenMaskSize       db ?      ; size of direct colour green mask in bits
     GreenFieldPosition  db ?      ; bit position of lsb of green mask
     BlueMaskSize        db ?      ; size of direct colour blue mask in bits
     BlueFieldPosition   db ?      ; bit position of lsb of blue mask
     RsvdMaskSize        db ?      ; size of direct colour reserved mask in bits
     RsvdFieldPosition   db ?      ; bit position of lsb of reserved mask
     DirectColorModeInfo db ?      ; direct colour mode attributes

  ; Following is VBE 2.0+ only
     PhysBasePtr         dd ?      ; physical address for flat frame buffer
     OffScreenMemOffset  dd ?      ; pointer to start of off screen memory
     OffScreenMemSize    dw ?      ; amount of off screen memory in 1k units
     __Reserved          db 206 dup (?)  ; remainder of ModeInfoBlock
VBEModeInfoBlock ends

) ModeAttributes is as follows:  (bits set if true)
  bit 0 : hareware supports mode (must be checked)
  bit 1 : VBE 1.2 extended info available (could be set in VBE 1.0 or 1.1)
  bit 2 : TTY Output functions supported by BIOS
    (this is rarely set - I said before to make sure this was set but that's wrong)
  bit 3 : colour  (else monochrome)
  bit 4 : grafix  (else text)
  bit 5 : NOT VGA compatibility
  bit 6 : windowed/banking mode available
  bit 7 : linear frame mode available
  bit 8-15 : Reserved
- When bit 2 is set then the following TTY functions are allowed in the mode
   01   Set Cursor Size
   02   Set Cursor Position
   06   Scroll TTY window up or Blank Window
   07   Scroll TTY window down or Blank Window
   09   Write character and attribute at cursor position
   0A   Write character only at cursor position
   0E   Write character and advance cursor  
- If bit 3 is clear (monochrome) then the CRTC is at 3B4h instead of 3D4h.
- If bit 5 is clear (VGA compat) then you may use standard VGA registers. 

) WinGranularity : this is really weird but very important.  It applies
 to banking mode only.  Basically when this equals 64, the banks do not
 overlap.  If this equals 32 then the banks overlap 50% of each other, and
 if it is 16 then they overlap 75% of each other, etc...
 Here's some code to show how banking should work.
) MemoryModel could be:
  00h  =    Text mode
  01h  =    CGA graphics
  02h  =    Hercules graphics
  03h  =    Planar
  04h  =    Packed pixel
  05h  =    Non-chain 4, 256 colour
  06h  =    Direct Colour
  07h  =    YUV
  08h-0Fh =      Reserved, to be defined by VESA
  10h-FFh =      To be defined by OEM
 Colour formats:
     VBE Version 1.1 and earlier defined Direct Colour graphics modes with
     pixel formats 1:5:5:5, 8:8:8, and 8:8:8:8  as a Packed Pixel model
     with 16, 24, and 32 bits per pixel, respectively. In VBE Version 1.2
     and later, the Direct Colour modes use the Direct Colour memory model
     and use the MaskSize and FieldPosition fields of the ModeInfoBlock to
     describe the pixel format.
) BytesperScanLine indicates how many bytes are used in VRAM for each
 physical scan line.  This logical scan line size can be equal to or greater
 than the physical one.  Therefore you can not assume an image is a
 continuous stream of data in VRAM.  It could have extra (ignored) data at
 the end of each scan line.  But this data could be visiable if you use
 func #7 (set/get display start)
) NumberofBanks indicates how many banks you can switch to in banking mode.
 This should be 1 in modes 0dh-13h.
) Banksize (or WinSize) is size of banking window size in KBs.
 This should be 0 in modes 0dh-13h which don't have scanlines.
) NumberofimagePages :(this -1) indicates how many full images can be loaded
 into VRAM and then each could be displayed one at a time. (good for
 flicker-free animations).  ie: 0 means 1 image, 1 means 2 images, etc.
) OffScreenMemOffset & Size indicate what VRAM you are allowed to access out
 side of the current active VRAM.  Some VRAM is used by the BIOS for holding
 info and if you trash it the system can become very unstable. The offset is
 a 32bit offset into VRAM and the size is in 1KB units.
) PhysBasePtr is a 32bit linear address to access VRAM in linear frame mode.
 Will be 0 if linear mode is not available.
) Win (A/B) Attributes is as follows:
   bit 0 : 0 =  Single non-relocatable window only
           1 =  Relocatable window(s) are supported
   bit 1 : 0 =  Window is not readable
           1 =  Window is readable
   bit 2 : 0 =  Window is not writeable
           1 =  Window is writeable
   bit 3-7   =  Reserved
) WinFuncPtr can be called to do func #5
  Just load regs as if calling func #5 (only bx and dx need be loaded).
  This is faster than calling INT 10h for func #5 but using the PMODE
  functions is even faster when in PMODE.

Appendix B : Logical/physical scan lines

A physical scan line is exactly what you see on the video screen. But a logical scan line may be much longer or it could be the exact same length. Here is what the video memory what look if the logical lines were longer.
|                 |         |
|                 |         |
|                 |         |
|                 |         |
|                 |         |
|                 |         |
\ physical length /
\------- logical length ----/
And the physical "visable" area can be shifted left and right to perform fast scrolling thru the use of VESA func #7.
I myself never use this scrolling though.

Appendix C : Banking vs. Linear Frame Buffer

When the VGA card was first introduced it used a small 64k memory mapped region at 0a0000h (0a000h:0h). This was fine for the 320 x 200 x 256 video mode, but as higher resoltions were developed more then 64k was needed to access the video cards memory. So what happened is that banking was created and now the 64k of memory mapped address space owned by video cards is a "window" into the video cards memory. And this "window" can be moved around to point to any bank within the video cards memory. So the video memory is usually broken up into 64k blocks and the 64k segment at 0a0000h can be remapped to point to any of the 64k blocks of memory in the video card by using VESA func #5. Note that each bank of the video card may not be 64k (an example is given below to show how to handle this). Scan lines may be divided across banks making copying to VRAM very difficult and slow. There is also another problem to consider, when some video modes are setup the logical scan line could be larger than the phyical scan line by default. So it's a good idea to set the logical scan line to the phyical size.
When the VLB and PCI buses were invented a larger address bus was available and some video cards now take advantage of it. With these newer cards a certain range (way above 1MB) is reserved for the video card and this memory range will be a "whole" window into the video memory. This range is not blocked up like in banking mode. This region could lie anywhere in the physical memory map. Note: This region is called a "linear frame buffer" (LFB) and this is a "physical address" which you must convert to a "linear address" using DPMI func #800h to properly access this area.

Appendix D : Examples

  ;init first
    int bankshift;  //global data for setbank() function
    while (( 64 >> bankshift) != wingranularity) bankshift++;
  ;to set bank use the following
    setbank (int banknum) {
      struct REGS in,out;
      if (banknum==current_banknum) return;;
      in.x.dx=banknum << bankshift;
      int86(10h,&in,&out); //set Window A"5;"
      in.x.dx="banknum" << bankshift;
      int86(10h,&in,&out); //set Window B
Last Notes:

You can not just use any VRAM you feel like. See ModeInfoBlock to see why.

If you found this tutorial confusing then get VBE20.ZIP and you'll get your socks knocked off! It's a book...

Sources :
1) Dr. Dobbs Journal : July, 95
2) VBE20.TXT : SciTech Labs (can be found in my Files Page)

Copyright © 1995-2005 Nexus Systems Privacy Logo