Undocumented VflatD

The virtual flat framebuffer driver, or VflatD, was introduced in Windows 95 in order to ease development of display drivers. It was surprisingly poorly documented and the sample drivers did not illustrate its use very well.

A short backgrounder may be in order. Traditional VGA adapters accessed all of their 256KB of video memory through a 64KB aperture at physical address A0000h. Because of the unusual four-plane memory organization, the 64KB aperture was enough for covering four times 64KB, or 256KB. A distinguishing characteristic of Super VGA adapters was that they sported more than 256KB of video memory; perhaps 512KB, perhaps 1MB, perhaps more.

However, accessing that memory was not easy. The 64KB aperture was no longer sufficient to address all video memory. Linear framebuffer access was not always available, in part because it was difficult to use from 16-bit software, in part because mapping a megabyte or more in the 16-megabyte ISA address space was problematic. Super VGA adapters therefore supported bank switching, i.e. controlling which part of the larger framebuffer the 64KB aperture at A0000h accesses. The simplest case was a single read-write “window” which could be positioned with 64KB granularity.

Using banking was quite painful for software, because all drawing algorithms had to be able to switch banks of whatever they were doing. Drawing to a linear framebuffer (a contiguous memory area larger than 64KB) was much simpler, but did not work with Super VGA adapters which only supported banking. To ease the pain, Microsoft shipped VflatD, a virtual device driver (VxD) which emulated a linear framebuffer on top of a banked Super VGA adapter.

The mechanism was relatively simple. VflatD allocated linear address space large enough to cover the actual video memory size. However, this space was not backed with physical memory. Instead, VflatD installed a page fault handler covering the entire area. On the first access (which caused a fault), VflatD calculated the bank number corresponding to the address, selected the desired bank, mapped 64KB at A0000h to a block of linear memory, and removed the page fault handler from that 64KB area.

As long as software was accessing only the memory area corresponding to the currently mapped bank, no page faults were incurred and memory accesses were executed at full speed (not necessarily fast—merely as fast as the hardware allowed). As soon as software attempted to accessed memory outside the currently mapped area, VflatD would select and map a different bank and adjust the mappings. This approach performed reasonably well and greatly simplified the drawing algorithms.

Every driver for a banked Super VGA adapter was expected to use VflatD. For minidrivers utilizing the DIB engine, VflatD was a requirement (for banked modes). Unfortunately, comparing the DDK documentation and actual sample drivers reveals surprisingly large holes.

The documentation did not substantially change since the original Windows 95 DDK release until the end of the Windows 9x line. The DDK documentation isn’t bad per se, but it refers to a file called vflatd.inc which Microsoft never actually shipped (or it was really, really well hidden). As a consequence, the symbolic constants used in documentation can’t be converted to numeric values.

The DDK sample drivers (MINI, FRAMEBUF, S3V) do use VflatD, but in a manner which appears to contradict the documentation. However, careful analysis of the VflatD module shows that the sample code is (as one might expect) correct and the documentation is misleading.

The VflatD documentation provides details for a function called VflatD_Create_Virtual_Frame_Buffer. It is implied that that’s what drivers should be using. However, the sample drivers use the VflatD_Get_Video_Base function instead, even though it’s been declared obsolete ever since the initial Windows 95 DDK! It doesn’t help that the DDK does not provide vflatd.inc, and the drivers therefore declare their own GET_VIDEO_SEL constant to obscure matters further.

VflatD_Get_Video_Base is in fact a less flexible variant of VflatD_Create_Virtual_Frame_Buffer with several restrictions. The bank is assumed to be 64KB at A0000h and the BankSize and BankLocation (EBX/ESI) arguments are therefore not used. The size of the framebuffer is passed (in EAX), but is specified in pages rather than bytes. Aside from these differences, the functions behave the same. For a typical Super VGA, the obsolete VflatD_Get_Video_Base does the job and is slightly easier to call.

For reference, here are the VflatD function IDs which were presumably defined in the missing vflatd.inc:

VflatD_Query                                0
VflatD_Get_Video_Base                       1
VflatD_Reset                                2
VflatD_Create_Virtual_Frame_Buffer          3
VflatD_Create_Physical_Frame_Buffer         4
VflatD_Begin_Linear_Access                  5
VflatD_End_Linear_Access                    6
VflatD_Query_Bank_Info                      7

The above numbers were obtained by disassembling the debug version of VFLATD.VXD for Windows Me and cross-referencing the code with the symbols from the accompanying VFLATD.SYM.

It is apparent that the GET_VIDEO_SEL macro with value of 1 used by DDK sample drivers is in fact a gratuitous alias for VflatD_Get_Video_Base which is mentioned in the DDK documentation.

This entry was posted in Documentation, VGA, Windows. Bookmark the permalink.

2 Responses to Undocumented VflatD

  1. xavery says:

    I managed to get ahold of vflatd.inc in a Windows 2000 source leak that can still be found on the Internet. Unfortunately, the contents are very disappointing and not useful at all.


    WIN31 equ 1 ; support Windows 3.1

    VflatD_Chicago_ID equ 011Fh ; OEM #8 Device #31
    VflatD_Windows_ID equ (0440h + 29d) ; MMSYS OEM# device 30

    if WIN31
    VflatD_Device_ID equ VflatD_Windows_ID
    else
    VflatD_Device_ID equ VflatD_Chicago_ID
    endif

    VflatD_Version equ (0100h + 50) ; 1.50

    VflatD_Get_Version equ 0
    VflatD_Get_Sel equ 1
    VflatD_Reset equ 2

    There’s also another file with the same name, which says that it’s a “Virtual Flat Device Exported Services” description. Here you go :


    IFDEF VMM_TRUE
    Begin_Service_Table VFLATD

    VFLATD_Service VFLATD_Get_Version, LOCAL
    VFLATD_Service VFLATD_Unmap_Flat_Selector, LOCAL

    End_Service_Table VFLATD
    ENDIF

    VflatD_Chicago_ID equ 011Fh ; OEM #8 Device #31
    VflatD_Windows_ID equ (0440h + 29d) ; MMSYS OEM# device 30

    ifdef WIN31
    VflatD_Device_ID equ VflatD_Windows_ID
    VflatD_Version equ 0160h ; 1.60
    else
    VflatD_Device_ID equ VflatD_Chicago_ID
    VflatD_Version equ 0200h ; 2.00
    endif

    ;
    ; PM API defines
    ;
    VflatD_Query equ 0
    VflatD_Get_Version equ 0
    VflatD_Get_Sel equ 1
    VflatD_Reset equ 2
    VflatD_Create_Virtual_Frame_Buffer equ 3
    VflatD_Create_Physical_Frame_Buffer equ 4

    ;******************************************************************************
    ;
    ; VFLATD_PM_API_Query
    ;
    ; DESCRIPTION:
    ; Query VFlatD info
    ;
    ; ENTRY:
    ; Client_DX = 0
    ;
    ; EXIT:
    ; Client_EAX = VFlatD version
    ; Client_EBX = selector to the frame buffer
    ; Client_ECX = size of frame buffer
    ; Client_EDX = Bank size + flags?
    ;
    ;==============================================================================

    ;******************************************************************************
    ;
    ; VFLATD_PM_API_Get_Video_Base
    ;
    ; DESCRIPTION:
    ; Return a GDT selector to the flat video buffer
    ;
    ; ENTRY:
    ; Client_DX = 1
    ; Client_AX = # of pages of video memory
    ; Client_CX = size of bank switch code
    ; Client_ES:DI -> bank switch code
    ;
    ; EXIT:
    ; Client_AX = Selector to flat video buffer
    ; Client_EDX = Linear base of flat video buffer
    ; Client carry flag clear
    ;
    ;==============================================================================

    ;******************************************************************************
    ;
    ; VFLATD_PM_API_Create_Physical_Frame_Buffer
    ;
    ; DESCRIPTION:
    ; Return a GDT selector to a flat video buffer
    ;
    ; ENTRY:
    ; Client_DL = 4
    ; Client_DH = flags
    ; Client_EAX = physical base of frame buffer
    ; Client_ECX = size of frame buffer (in bytes)
    ;
    ; EXIT:
    ; Client_AX = Selector to flat video buffer
    ; Client_EDX = Linear base of flat video buffer
    ; Client carry flag clear
    ;
    ;==============================================================================

    ;******************************************************************************
    ;
    ;******************************************************************************
    ;
    ; VFLATD_PM_API_Create_Virtual_Frame_Buffer
    ;
    ; DESCRIPTION:
    ; Return a GDT selector to the flat video buffer
    ;
    ; ENTRY:
    ; Client_DL = 3
    ; Client_DH = flags
    ; Client_EAX = size of frame buffer (in bytes)
    ; Client_EBX = size of a bank (in bytes)
    ; Client_ESI = location of bank
    ; Client_CX = size of bank switch code
    ; Client_ES:DI = ptr to bank switch code
    ;
    ; EXIT:
    ; Client_AX = Selector to flat video buffer
    ; Client_EDX = Linear base of flat video buffer
    ; Client carry flag clear
    ;
    ;==============================================================================

    There are also some assembly sources available under the names vflat.asm and vflatd.asm. I haven’t checked them out, though.

  2. Michal Necasek says:

    I wonder if this was in Windows 2000 purely for Windows 3.1 compatibility.

Leave a Reply

Your email address will not be published. Required fields are marked *