User:Omarrx024/VESA Tutorial: Difference between revisions

Content deleted Content added
m I've taken the liberty of adding syntax highlighting, as I feel it makes the code snippets more readable. It's a nice article.
m Bot: Replace deprecated source tag with syntaxhighlight
 
(4 intermediate revisions by 3 users not shown)
Line 10:
Here are some modes the VESA defined with VBE 1.x:
<pre>MODE RESOLUTION BITS PER PIXEL MAXIMUM COLORS
0x0100 640x480640x400 48 16256
0x0101 640x480 8 256
0x0102 800x600 4 16
Line 51:
 
Anyway, the above function returns the following structure and stores it in ES:DI as they were on entry. On entry, ES:DI should contain a pointer to the following structure:
<sourcesyntaxhighlight lang="asm">vbe_info_structure:
.signature db "VBE2" ; indicate support for VBE 2.0+
.table_data: resb 512-4 ; reserve space for the table below</sourcesyntaxhighlight>
After the BIOS call, if it succeeded (AX is 0x004F), then the same structure above now contains the following:
<sourcesyntaxhighlight lang="c">struct vbe_info_structure {
char[4] signature = "VESA"; // must be "VESA" to indicate valid VBE support
uint16uint16_t version; // VBE version; high byte is major version, low byte is minor version
uint32uint32_t oem; // segment:offset pointer to OEM
uint32uint32_t capabilities; // bitfield that describes card capabilities
uint32uint32_t video_modes; // segment:offset pointer to list of supported video modes
uint16uint16_t video_memory; // amount of video memory in 64KB blocks
uint16uint16_t software_rev; // software revision
uint32uint32_t vendor; // segment:offset to card vendor string
uint32uint32_t product_name; // segment:offset to card model name
uint32uint32_t product_rev; // segment:offset pointer to product revision
char reserved[222]; // reserved for future expansion
char oem_data[256]; // OEM BIOSes store their strings in this area
} __attribute__ ((packed));</sourcesyntaxhighlight>
Notice that all segment:offset fields are in little-endian, which means the low word is the offset, and the high word is the segment.
Things that might be of interest to you from the above structure: "signature" will be changed from "VBE2" to "VESA". It must be "VBE2" on entry to indicate software support for VBE 2.0. If it contains "VBE2", the BIOS will return the 512 bytes of data for VBE 2.0+. If it contains "VESA", the BIOS will return 256 bytes of data for VBE 1.x. If it is not "VESA" after the call, you should assume that VESA BIOS Extensions are not available. "version" tells you the version of VBE; 0x100 is 1.0, 0x101 is 1.1, 0x102 is 1.2, 0x200 is 2.0, and 0x300 is 3.0 (the latest version). VBE 1.x returns 256 bytes of data in the above structure, VBE 2.0 and 3.0 return 512 bytes of data if the "signature" field contained "VBE2" on entry. "video_modes" is a segment:offset pointer to the list of supported video modes. Each entry in the array is a 16-bit word, and is terminated by a 0xFFFF. If while searching for your mode, you find a 0xFFFF, then the mode is not supported. "video_memory" contains how much VGA RAM the PC has in 64 KB chunks. So, to have it in KB, multiply the value in "video_memory" by 64.
Line 90:
 
Here's the structure returned by this function in ES:DI:
<sourcesyntaxhighlight lang="c">struct vbe_mode_info_structure {
uint16uint16_t attributes; // deprecated, only bit 7 should be of interest to you, and it indicates the mode supports a linear frame buffer.
uint8uint8_t window_a; // deprecated
uint8uint8_t window_b; // deprecated
uint16uint16_t granularity; // deprecated; used while calculating bank numbers
uint16uint16_t window_size;
uint16uint16_t segment_a;
uint16uint16_t segment_b;
uint32uint32_t win_func_ptr; // deprecated; used to switch banks from protected mode without returning to real mode
uint16uint16_t pitch; // number of bytes per horizontal line
uint16uint16_t width; // width in pixels
uint16uint16_t height; // height in pixels
uint8uint8_t w_char; // unused...
uint8uint8_t y_char; // ...
uint8uint8_t planes;
uint8uint8_t bpp; // bits per pixel in this mode
uint8uint8_t banks; // deprecated; total number of banks in this mode
uint8uint8_t memory_model;
uint8uint8_t bank_size; // deprecated; size of a bank, almost always 64 KB but may be 16 KB...
uint8uint8_t image_pages;
uint8uint8_t reserved0;
 
uint8uint8_t red_mask;
uint8uint8_t red_position;
uint8uint8_t green_mask;
uint8uint8_t green_position;
uint8uint8_t blue_mask;
uint8uint8_t blue_position;
uint8uint8_t reserved_mask;
uint8uint8_t reserved_position;
uint8uint8_t direct_color_attributes;
 
uint32uint32_t framebuffer; // physical address of the linear frame buffer; write here to draw to the screen
uint32uint32_t off_screen_mem_off;
uint16uint16_t off_screen_mem_size; // size of memory in the framebuffer but not being displayed on the screen
uint8uint8_t reserved1[206];
} __attribute__ ((packed));</sourcesyntaxhighlight>
 
Lots of useless sh*t, I know. The only things that interest us: "attributes" bit 7 (value 0x80) indicates the mode supports a linear frame buffer. "width", "height" are "bpp" are used while searching for the mode we want to use. "framebuffer" is the 32-bit physical pointer to the linear framebuffer. The linear framebuffer must be enabled while setting the VBE mode, which is discussed in the next function. If you are using paging, be sure to map the framebuffer somewhere known in the virtual address space!
Line 143:
 
So that means, if VBE mode 0x0118 is 1024x768x32bpp, and we wanted to set this mode and ask the BIOS to clear the screen for us, we can do this:
<sourcesyntaxhighlight lang="asm">mov ax, 0x4F02 ; set VBE mode
mov bx, 0x4118 ; VBE mode number; notice that bits 0-13 contain the mode number and bit 14 (LFB) is set and bit 15 (DM) is clear.
int 0x10 ; call VBE BIOS
Line 150:
 
after:
; ...</sourcesyntaxhighlight>
Anyway, like I mentioned at least a hundred times, you should first get the mode number from the video modes array. Those mode numbers only have the plain mode numbers (0x0118, 0x0103, etc...) and you should set bit 14 when you set the VBE mode.
 
Line 202:
Anyway, the table returned by ES:DI looks like this:
 
<sourcesyntaxhighlight lang="c">struct vbe2_pmi_table {
uint16uint16_t set_window; // offset in table for protected mode code for function 0x4F05
uint16uint16_t set_display_start; // offset in table for protected mode code for function 0x4F07
uint16uint16_t set_pallette; // offset in table for protected mode code for function 0x4F09
} __attribute__ ((packed));</sourcesyntaxhighlight>
That means, to set the bank from protected mode, one should first find the linear address of the set_window function, by doing ES * 0x10 + DI + [ES:DI] and then doing a near CALL, because all protected mode functions end with a near RET. Useless, I know, but perhaps some people want to use bank switching as a fallback when VBE 2.0 is not available, or when the specific mode doesn't support a linear frame buffer.
 
Line 213:
'''Need code?'''
Here's a function to set a VESA mode directly from a specified width/height/bpp, copied and pasted from my OS code. Notice that it runs in 16-bit real mode with DS = ES = FS = GS = 0.
<sourcesyntaxhighlight lang="asm">; vbe_set_mode:
; Sets a VESA mode
; In\ AX = Width
Line 334:
.segment dw 0
.offset dw 0
.mode dw 0</sourcesyntaxhighlight>
 
'''What's next?'''
Well, that's entirely up to you! Just kidding, here's some tips on implementing a graphics library.
Calculating pixel offset:
<sourcesyntaxhighlight lang="c">uint32 pixel_offset = y * pitch + (x * (bpp/8)) + framebuffer;</sourcesyntaxhighlight>
Where "pitch", "bpp" and "framebuffer" are gotten from the structure returned by function 0x4F01. Then, you can plot a pixel by writing a value there.
For 32-bit modes, each pixel value is 0x00RRGGBB in little endian, so to plot a red pixel, we would write 0x00FF0000. In 24-bit mode, each pixel is 0xRRGGBB in little endian. Due to the bad memory alignment, performance is relatively low with 24-bit modes. In 16-bit modes, we have a total of 64K colors, and the color has 5 bits of red, 6 bits of green, and 5 bits of blue. There is more green because according to Wikipedia, the human eye is more sensitive to green than red and blue...