VESA Video Modes: Difference between revisions

m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
m (→‎Don't Assume The Monitor Supports A Video Mode: The windows approach to changing video mode)
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(30 intermediate revisions by 19 users not shown)
Line 1:
Under legacy BIOS (and CSM-capable UEFI systems in legacy BIOS mode) you can access the video card functions using the standardized VESA/VBE functions.
 
In UEFI mode, instead, one uses the [[GOP]] (Graphics Output Protocol) to access the video card in a standardized way.
 
== VESA Functions ==
You'll want to look in the VESA VBE docs for these functions:
Line 5 ⟶ 9:
;INT 0x10, AX=0x4F00
: Get Controller Info. This is the one that returns the array of all supported video modes.
<sourcesyntaxhighlight lang="c">
struct vbeControllerInfoVbeInfoBlock {
char signature[4]; VbeSignature[4]; // == "VESA"
shortuint16_t versionVbeVersion; // == 0x0300 for VBE 3.0
shortuint16_t oemStringOemStringPtr[2]; // isa vbeFarPtr
unsigneduint8_t char capabilitiesCapabilities[4];
shortuint16_t videomodesVideoModePtr[2]; // isa vbeFarPtr
shortuint16_t totalMemoryTotalMemory; // as # of 64KB blocks
uint8_t Reserved[492];
};
} __attribute__((packed));
 
vbeInfoBlockVbeInfoBlock *vib = dos_alloc(512);
v86_bios(0x10, {ax:0x4f00, es:SEG(vib), di:OFF(vib)}, &out);
if (out.ax!=0x004f) die("Something wrong with VBE get info");
</syntaxhighlight>
</source>
 
In assembly(nasm):
;INT 0x10, AX=0x4F01, CX=mode
<syntaxhighlight lang="asm">
: Get Mode Info. Call this for each member of the mode array to find out the details of that mode.
struc VesaInfoBlock ; VesaInfoBlock_size = 512 bytes
.Signature resb 4 ; must be 'VESA'
.Version resw 1
.OEMNamePtr resd 1
.Capabilities resd 1
 
.VideoModesOffset resw 1
<source lang="c">
.VideoModesSegment resw 1
struct vbeModeInfo {
word attributes;
byte winA,winB;
word granularity;
word winsize;
word segmentA, segmentB;
VBE_FAR(realFctPtr);
word pitch; // bytes per scanline
 
.CountOf64KBlocks resw 1
word Xres, Yres;
.OEMSoftwareRevision resw 1
byte Wchar, Ychar, planes, bpp, banks;
.OEMVendorNamePtr resd 1
byte memory_model, bank_size, image_pages;
.OEMProductNamePtr resd 1
byte reserved0;
.OEMProductRevisionPtr resd 1
.Reserved resb 222
.OEMData resb 256
endstruc
 
Main:
byte red_mask, red_position;
push ds
byte green_mask, green_position;
pop es
byte blue_mask, blue_position;
mov di, VesaInfoBlockBuffer
byte rsv_mask, rsv_position;
call get_vesa_info
byte directcolor_attributes;
jmp $
 
; in:
dword physbase; // your LFB address ;)
; es:di - 512-byte buffer
dword reserved1;
; out:
short reserved2;
; cf - set on error
};
get_vesa_info:
</source>
clc
mov ax, 0x4f00
int 0x10
cmp ax, 0x004f
jne .failed
ret
.failed:
stc
ret
 
ALIGN(4)
;INT 0x10, AX=0x4F02, BX=mode: Set Video Mode. Call this with the mode number you decide to use.
 
VesaInfoBlockBuffer: istruc VesaInfoBlock
at VesaInfoBlock.Signature, db "VESA"
times 508 db 0
iend
</syntaxhighlight>
 
'''INT 0x10, AX=0x4F01, CX=mode, ES:DI=256 byte buffer'''
: Get Mode Info. Call this for each member of the mode array to find out the details of that mode. The 256 byte buffer will be filled by the mode info block.
 
<syntaxhighlight lang="c">
struct vbe_mode_info_structure {
uint16_t attributes; // deprecated, only bit 7 should be of interest to you, and it indicates the mode supports a linear frame buffer.
uint8_t window_a; // deprecated
uint8_t window_b; // deprecated
uint16_t granularity; // deprecated; used while calculating bank numbers
uint16_t window_size;
uint16_t segment_a;
uint16_t segment_b;
uint32_t win_func_ptr; // deprecated; used to switch banks from protected mode without returning to real mode
uint16_t pitch; // number of bytes per horizontal line
uint16_t width; // width in pixels
uint16_t height; // height in pixels
uint8_t w_char; // unused...
uint8_t y_char; // ...
uint8_t planes;
uint8_t bpp; // bits per pixel in this mode
uint8_t banks; // deprecated; total number of banks in this mode
uint8_t memory_model;
uint8_t bank_size; // deprecated; size of a bank, almost always 64 KB but may be 16 KB...
uint8_t image_pages;
uint8_t reserved0;
 
uint8_t red_mask;
uint8_t red_position;
uint8_t green_mask;
uint8_t green_position;
uint8_t blue_mask;
uint8_t blue_position;
uint8_t reserved_mask;
uint8_t reserved_position;
uint8_t direct_color_attributes;
 
uint32_t framebuffer; // physical address of the linear frame buffer; write here to draw to the screen
uint32_t off_screen_mem_off;
uint16_t off_screen_mem_size; // size of memory in the framebuffer but not being displayed on the screen
uint8_t reserved1[206];
} __attribute__ ((packed));
</syntaxhighlight>
 
In assembly(nasm):
<syntaxhighlight lang="asm">
struc VesaModeInfoBlock ; VesaModeInfoBlock_size = 256 bytes
.ModeAttributes resw 1
.FirstWindowAttributes resb 1
.SecondWindowAttributes resb 1
.WindowGranularity resw 1 ; in KB
.WindowSize resw 1 ; in KB
.FirstWindowSegment resw 1 ; 0 if not supported
.SecondWindowSegment resw 1 ; 0 if not supported
.WindowFunctionPtr resd 1
.BytesPerScanLine resw 1
 
; Added in Revision 1.2
.Width resw 1 ; in pixels(graphics)/columns(text)
.Height resw 1 ; in pixels(graphics)/columns(text)
.CharWidth resb 1 ; in pixels
.CharHeight resb 1 ; in pixels
.PlanesCount resb 1
.BitsPerPixel resb 1
.BanksCount resb 1
.MemoryModel resb 1 ; http://www.ctyme.com/intr/rb-0274.htm#Table82
.BankSize resb 1 ; in KB
.ImagePagesCount resb 1 ; count - 1
.Reserved1 resb 1 ; equals 0 in Revision 1.0-2.0, 1 in 3.0
 
.RedMaskSize resb 1
.RedFieldPosition resb 1
.GreenMaskSize resb 1
.GreenFieldPosition resb 1
.BlueMaskSize resb 1
.BlueFieldPosition resb 1
.ReservedMaskSize resb 1
.ReservedMaskPosition resb 1
.DirectColorModeInfo resb 1
 
; Added in Revision 2.0
.LFBAddress resd 1
.OffscreenMemoryOffset resd 1
.OffscreenMemorySize resw 1 ; in KB
.Reserved2 resb 206 ; available in Revision 3.0, but useless for now
endstruc
 
Main:
; after getting VesaInfoBlock:
push word [VesaInfoBlockBuffer + VesaInfoBlock.VideoModesSegment]
pop es
mov di, VesaModeInfoBlockBuffer
mov bx, [VesaInfoBlockBuffer + VesaInfoBlock.VideoModesOffset] ; get video modes list address
mov cx, [bx] ; get first video mode number
cmp cx, 0xffff ; vesa modes list empty
je .NoModes
call get_vesa_mode_info
.NoModes:
jmp $
 
 
; in:
; cx - VESA mode number
; es:di - 256-byte buffer
; out:
; cf - set on error
get_vesa_mode_info:
clc
mov ax, 0x4f01
int 0x10
cmp ax, 0x004f
jne .failed
ret
.failed:
stc
ret
 
ALIGN(4)
 
VesaModeInfoBlockBuffer: istruc VesaModeInfoBlock
times VesaModeInfoBlock_size db 0
iend
</syntaxhighlight>
 
'''INT 0x10, AX=0x4F02, BX=mode, ES:DI=CRTCInfoBlock'''
:Set Video Mode. Call this with the mode number you decide to use. If you choose a mode that makes use of a linear framebuffer, you should OR the mode number with 0x4000. This sets the "Use LFB" bit in the mode number. Set the bit 11 of BX to instruct the BIOS to use the passed CRTCInfoBlock structure, see the specification for more information.
 
:BIOSs can switch to protected mode to implement this, and might reset the GDT. This is observable on QEMU 2.2.x.
 
== Will it work with Bochs? ==
Line 56 ⟶ 207:
For VBE to work in Bochs you need the "VGABIOS-lgpl" BIOS and have a version of Bochs that was compiled with the <tt>--enable-vbe</tt> option... See [[Topic:9655|Vesa Information in Bochs]] thread for more info. Also read about [[Bochs Graphics Adaptor]].
 
==How to pick the mode I wish ?==
 
VESA stopped assigning codes for video modes long ago -- instead they standardized a much better solution: you can query the video card for what modes it supports, and query it about the attributes of each mode. In your OS, you can have a function that you call with a desired width, height, and depth, and it returns the video mode number for it (or the closest match). Then, just set that mode
 
Here's a sample code, assuming you have a VirtualMonitor already ... Basically, you will scan the 'modes list' referenced by the vbeInfoBlockVbeInfoBlock.videomodes[] and then call 'get mode info' for each mode. You can then compare width, height and colordepth of each mode with the desired one.
 
<sourcesyntaxhighlight lang="c">
UInt16uint16_t findMode(int x, int y, int d)
{
struct vbeControllerInfoVbeInfoBlock *ctrl = (ControllerInfoVbeInfoBlock *)0x2000;
struct vbeModeInfoModeInfoBlock *inf = (ModeInfoModeInfoBlock *)0x3000;
UInt16uint16_t *modes;
int i;
UInt16uint16_t best = 0x13;
int pixdiff, bestpixdiff = DIFF(320 * 200, x * y);
int depthdiff, bestdepthdiff = 8 >= d ? 8 - d : (d - 8) * 2;
 
strncpy(ctrl->signatureVbeSignature, "VBE2", 4);
intV86(0x10, "ax,es:di", 0x4F00, 0, ctrl); // Get Controller Info
if ( (UInt16uint16_t)v86.tss.eax != 0x004F ) return best;
 
modes = (UInt16uint16_t*)REALPTR(ctrl->VideoModePtr);
for ( i = 0 ; modes[i] != 0xFFFF ; ++i ) {
intV86(0x10, "ax,cx,es:di", 0x4F01, modes[i], 0, inf); // Get Mode Info
 
if ( (UInt16uint16_t)v86.tss.eax != 0x004F ) continue;
 
// Check if this is a graphics mode with linear frame buffer support
Line 106 ⟶ 257:
return best;
}
</syntaxhighlight>
</source>
 
== Common Mistakes ==
Line 138 ⟶ 289:
===Don't Assume The Monitor Supports A Video Mode===
 
We all like pretty graphics and high resolution video modes. Unfortunately, if VBE says that a video mode is supported by the video card it does not mean that the video mode is also supported by the monitor. VESA has defined 2 video mode timings that are meant to be supported by all monitors (640 * 480 standard VGA timing and 720 * 480 standard VGA timing). For all other video modes you should either use [[EDID]] to find out if the monitor supports the video mode's timing (or not), or provide a way for the user to test the video mode and change it if it doesn't work. This is the approach used by Windows, with a dialog box appearing with the option to accept or revert. No action within 15 seconds reverts. Just an idea ;).
 
Using EDID for this purpose is complicated. Unless you provide a "CRTC information block" structure when you set the video mode you can't be entirely sure what timing the video card will use; and only some video cards that support VBE 3.0 support the "CRTC information" correctly.
Line 152 ⟶ 303:
* 320 * 240 (actually uses "640 * 480" timing)
Colour depth doesn't/shouldn't effect the video timing signals. This means that the best possible safe video mode would be 720 * 480 * 24/32-bpp.
 
===Don't Read From Video Memory===
 
Reading from the video memory is slooow! Use [[Double Buffering|double buffering]] instead.
 
==See Also==
===Articles===
* [[User:Omarrx024/VESA_Tutorial|VESA Tutorial]]
* [[VGA Hardware]] - VESA predecessor
* [[GOP|Graphic Output Protocol]] - VESA successor on [[UEFI]] machines
 
===Threads===
* [[Topic:9700|VESA, higher modes]] - Initial Thread, reply by Dreamsmith (aka DaidalosGuy)
 
=== External Links ===
Line 161 ⟶ 321:
* [http://www.delorie.com/djgpp/doc/ug/graphics/vesa.html Guide: VESA graphics modes]
* [http://www.petesqbsite.com/sections/tutorials/tuts/vbe3.pdf The VESA VBE 3.0 specification]
* [http://www.monstersoft.com/tutorial1/VESA_intro.html Introduction to VESA programming]
[[Category:Video]]
[[Category:FAQ]]
[[de:VESA_BIOS_Extensions]]