Skip to content
FreeDOSDeep Dive Published Updated 8 min readViews unavailable

VESA VBE on FreeDOS: Discover Modes Before Touching the Framebuffer

Probe VESA VBE capabilities, validate a mode, choose banked or linear access, and restore display state without hard-coding adapter assumptions.

VESA BIOS Extensions provide a standardized software interface for graphics modes and framebuffer layouts beyond the original VGA services. On DOS, VBE Core calls are made through INT 10h with AH set to 4Fh. They let a program ask the installed video BIOS which modes exist, inspect their attributes, select a mode, and query the current state instead of assuming that one numeric mode has the same meaning on every adapter.

VBE is a negotiation and metadata interface, not a guarantee that every listed mode is usable by every application. The controller’s BIOS, available memory, execution mode, framebuffer model, and display environment constrain the result. A program must check function status and mode attributes, then use the returned pitch, memory model, and address semantics. It must not treat a physical framebuffer address as an ordinary far pointer or write bytes using a guessed width.

Begin with the controller information call

The VBE 3.0 Core Functions specification defines Function 00h, called with AX=4F00h, to return a VbeInfoBlock into a caller-provided ES:DI buffer. For VBE 2.0 and later, the caller presets the signature field with the four bytes VBE2 and provides a 512-byte information block; on success the implementation returns the signature VESA and fills version, OEM, capability, and supported-mode-list information. Older VBE 1.x layouts use a smaller block, so the buffer must match the compatibility target.

Use the VBE completion status, not only the absence of a crash. The return value has AL=4Fh for a recognized function and AH=00h for success. A non-4Fh AL means the function is not supported; nonzero AH represents a failure or unavailable configuration. Treat any nonzero AH as failure and retain the returned registers for diagnostics.

The supported-mode pointer is a far pointer in the VbeInfoBlock. It refers to a zero-terminated mode list, with the terminator represented by FFFFh. The list is evidence of candidate identifiers, not proof that a mode will be usable with the program’s memory or rendering assumptions. Do not copy a mode number from another machine or a forum post and skip discovery.

Validate every candidate with Function 01h

Call Function 01h with AX=4F01h, CX set to one listed mode number, and ES:DI pointing to a 256-byte ModeInfoBlock. Check the same status convention. Inspect the supported and graphics-mode attributes, resolution, planes, bits per pixel, memory model, scan-line length, and framebuffer fields relevant to the renderer. The mode information is a fixed-size binary structure; do not pack it using a compiler’s default layout unless you have verified every byte offset against the specification.

A candidate is acceptable only if its attributes and memory model match the rendering code. A 320 by 200 mode at 8 bits per pixel is not compatible with code that assumes a 16-bit RGB565 pixel. A packed-pixel mode is not interchangeable with a planar or banked mode. A reported width is not necessarily the number of bytes per row: padding, alignment, and programmable logical scan-line length affect pitch.

When the mode list contains a familiar resolution, still inspect the mode attributes and actual parameters. A mode can be listed but unavailable in the current configuration or unsupported by the current adapter state. The VBE specification explicitly requires an application to verify modes returned by Function 00h using Function 01h. This is why enumeration and validation belong in the program, rather than in a static table maintained by the operator.

Choose banked or linear framebuffer access explicitly

The VBE mode-setting call Function 02h takes the desired mode in BX. Bit 14 selects the framebuffer model: clear requests the banked/windowed model; set requests the linear or flat framebuffer model. That request is valid only when the mode and implementation support the requested capability. Function 01h should be used first to confirm the mode’s attributes.

In a banked mode, software accesses a window and changes the selected bank to reach other portions of video memory. The ModeInfoBlock reports window granularity and size plus a window segment. Treat these as separate values: granularity describes how far a bank change advances, while window size describes the accessible aperture. Calculating a bank number as if those values were always identical causes gaps, overlap, or wrong offsets.

In a linear-framebuffer mode, the mode block provides a physical base address. The VBE specification warns that this address is physical and cannot be used directly by a protected-mode application. Protected-mode software must use its operating environment’s services to map the physical range into an accessible linear address. Real-mode DOS has different addressing constraints; do not cast an arbitrary physical address to a 16:16 pointer and assume the entire framebuffer is reachable. The address map and memory manager are part of the design.

The scan-line pitch is equally important. Use the value reported for the chosen mode or a successfully queried logical line length, rather than calculating row offsets solely as horizontal pixels multiplied by bytes per pixel. A renderer should use a row formula based on returned pitch and pixel layout, and it should bounds-check every write against the mapped framebuffer or bank window. The available memory behind a mode and the displayed dimensions are related but not identical quantities.

Set, verify, and restore display state

Before changing mode, call Function 03h with AX=4F03h to record the current VBE mode. If the application requires a return to the user’s original display, preserve that value and any additional state the program modifies. Call Function 02h only after mode validation, then require a successful completion status and query the current mode again. VBE-aware software should use the VBE set/get functions rather than assuming legacy VGA mode calls fully describe an extended VBE mode.

A small control-flow sketch is:

INT 10h, AX=4F00h, ES:DI=controller buffer
Check AL=4Fh and AH=00h; verify returned VESA signature
Walk the returned mode list
For each candidate:
    INT 10h, AX=4F01h, CX=mode, ES:DI=256-byte mode buffer
    Check status, attributes, memory model, pitch, and required format
Record current mode with AX=4F03h
Set validated mode with AX=4F02h and BX=mode plus supported model bits
Check status, query mode, then initialize the renderer from returned fields

This is an algorithm outline, not assembler that can be pasted into a source file. A real-mode call wrapper must provide valid real-mode pointers, preserve registers according to the target compiler ABI, and ensure the information buffers do not cross an inaccessible segment boundary. A protected-mode client needs its DOS extender’s BIOS-call or real-mode callback mechanism. Test those transition services separately from the VBE function itself.

Restore the original display state on every normal exit path, including an input error or failed allocation after mode selection. If the program can be terminated through Ctrl-Break or an exception, a restoration attempt from an unsafe handler may be more dangerous than leaving the display mode changed. Design the lifecycle deliberately and provide an external reboot or shell recovery path for a test machine.

Do not assume optional VBE features

VBE 3.0 defines a protected-mode interface entry point, but that does not mean every VBE implementation exposes it or that every function is valid through it. The specification marks functions and mode capabilities with required or optional status and documents restrictions on protected-mode calls. Query the interface and follow its separate calling convention rather than issuing INT 10h from protected mode without a compatible thunk or host.

Likewise, no particular extended graphics mode is universally required. The VBE 3.0 specification states that there are no absolutely required modes or mode capabilities because they vary by hardware and application. Treat high resolutions, color formats, banked windows, linear framebuffer support, and refresh-rate controls as runtime capabilities. Keep a text-mode or known VGA fallback for diagnostics and for systems without a usable VBE implementation.

An emulator can provide a VBE implementation that differs from physical firmware. A boot path that does not initialize a legacy video BIOS may expose a different service set. Use a controlled compatibility matrix: the target emulator or machine, firmware settings, VBE version returned, exact mode list, requested mode, status codes, and framebuffer model. Do not call a VBE issue an application bug until the returned status and controller capabilities are recorded.

Validate rendering without trusting the first image

Begin with a small test pattern that touches the first and last pixel of each row, draws known colors, and exercises a row boundary. Verify it against the reported pitch and pixel encoding. Then test a mode with less memory, an unsupported mode number, a banked mode if available, and the linear model only where advertised. A screen that looks correct for the first few rows can still hide a pitch error that corrupts the display later.

Capture the returned VBE signature, version, mode-list entries, each selected ModeInfoBlock, completion codes, old and new mode, and any memory-mapping operation. Compare the report on at least one virtual machine and the actual target hardware if both are supported. Record whether the code was running in real mode, protected mode, or through a DOS extender; these are not interchangeable execution environments.

An acceptable graphics initialization has a bounded fallback when VBE is absent, validates all required attributes before setting a mode, uses the correct framebuffer model and pitch, checks every BIOS completion code, and restores the prior mode on normal completion. VBE makes extended display access more portable by standardizing calls, but it does not remove the need to respect each controller’s reported capabilities and memory model.

Related:

Sources:

Comments