DEV Community

William
William

Posted on

Guide to VBE graphics in x86

Preface πŸ“˜

This should act as your complete guide to implementing and understanding VBE graphics in x86 assembly, if you are unfamiliar with BIOS programming I recommend my article about making pong (https://dev.to/willy1948/making-pong-in-x86-assembly-11li). So, what is VBE graphics? VBE (VESA bios extension) graphics is a standard that aims to improve upon VGA graphics which was created by IBM. What are the benefits of using VBE graphics? Well, the biggest benefit is the improvement in screen resolution. VGA at best gives you 320x200 whereas using VBE one can have up to 1280x1024. It's not just the resolution, but in certain video you can have up to 255^3 colours, compare that to just 255 for VGA. If you are looking to make a OS or just a bootloader game you should definitely consider using VBE graphics.

  οΌοΌžγ€€ フ
  | γ€€_γ€€_| 
/` γƒŸοΌΏxγƒŽ 
/γ€€γ€€γ€€γ€€ |
/γ€€ ヽ   οΎ‰
β”‚γ€€γ€€|γ€€|γ€€|
/ ̄|γ€€γ€€ |γ€€|
( ̄ヽ__ヽ_)__)
Enter fullscreen mode Exit fullscreen mode

Setting the video mode πŸŽ›οΈ

We need to tell BIOS that we want to use VBE graphics. luckily there exists an interrupt that we can call with specific arguments to do this. Using int 0x10 with ax set to 0x4f02 and bx used to specify the video mode, (https://en.wikipedia.org/wiki/INT_10H).

mov ax, 0x4f02 ; code to set video mode
mov bx, 0x4118 ; specifies video + flags
int 0x10
cmp ax, 0x4f ;Check if video mode is supported by hardware
je  .successLabel
Enter fullscreen mode Exit fullscreen mode

To understand what the cmp line does we can look at the VBE specification document (https://pdos.csail.mit.edu/6.828/2011/readings/hardware/vbe3.pdf).

We want to check if the video mode in bx is supported and therefore we check to see if ax is 0x4f. I mentioned earlier that bx stores the mode, this is partly true but it also stores flags which specify certain behaviours.

It is a bit confusing but D means bit number. Breaking down 0x4118 into binary we get 0b|0|1|00|0|00|1|00011000| (I used | character to separate the different sections). Moving from left to right the sections are defined as follows:

  1. 0=clear display, 1=preserve display. This means weather or not what is in video memory will be cleared to black. Majority of the time it makes sense to clear the display when switching modes. Hence why the first bit is 0.
  2. 0=Use banked frame buffer, 1=linear frame buffer. The reason this exists is because with 16 bits one can only address so much memory, even with segment offsets. Video memory could need up to 1280x1024x3 = 3,932,160 bytes, which is greater than 64kb. To get around this issue video card manufactures split up memory in banks. Only one bank exists in memory at one time and one can switch to a particular bank through a BIOS call. Linear frame buffer is where the frame buffer is just stored as normal in memory. Because I am going to use 32 bit registers (this can be done in real mode), I am going to use a linear buffer. If you are developing for 16-bit retro hardware then you would need to set this bit to 0.
  3. Must be 0
  4. 0=Uses BIOS default refresh rate, 1=Use user specified CRTC (Cathode ray tube controller). You may think, well, I'm not using a CRT monitor. But you still specify values through the CRTC to control your up to date monitor. Using BIOS defaults will be fine. If you do want to specify the refresh rate, I believe you specify a pointer in ES:DI to a CRTC information struct.
  5. Should be 0
  6. 0=mode specifies VGA mode, 1=mode specifies super VGA mode. This should be set to 1 because we want access to higher resolutions that are available only in super VGA (super VGA is VBE).
  7. Specifies the video mode:  Entry one has mode number of 0 and so on, this is a little unclear from the table. 7 bit mode number means that the 8th bit(zero indexed) is set to 0.

Great, using this information you should be able to run your program and it should show a bigger window than before. There are still some problems we need to fix. Firstly we don't know important information like where video memory is, and secondly we do not have enough address lines to write into all of video memory. Lets take a look at how to solve these issues.

Finding video memory πŸ’Ύ

We can call a function that will give us all the information we need about the current mode. It takes a pointer to a struct and fills in all the information.

mov ax, 0x4F01       ; VBE function 01h which returns mode info
; mode number (remember to include the 8th bit)
; cx is mode 5 with 8th bit set because we are using super VGA
mov cx, 0x105        
mov di, video_info   ; pointer to structure
int 0x10
Enter fullscreen mode Exit fullscreen mode

Next we need to define the structure

That's a bit of a pain to write out by hand so here is a text version with labels for the important attributes.

video_info:
    dw 0h     ; ModeAttributes found on page 40 of VBE spec pdf
    db 0h             ; WinAAttributes
    db 0h             ; WinBAttributes
    dw 0h             ; WinGranularity
    dw 0h             ; WinSize
    dw 0h             ; WinASegment
    dw 0h             ; WinBSegment
    dd 0h             ; WinFuncPtr
video_bytesPerScanLine: dw 0h ; BytesPerScanLine
    dw 0h             ; XResolution
    dw 0h             ; YResolution
    db 0h             ; XCharSize
    db 0h             ; YCharSize
    db 0h            ; NumberOfPlanes
    db 0h              ; BitsPerPixel
    db 0h             ; NumberOfBanks
    db 0h             ; MemoryModel
    db 0h             ; BankSize
    db 0h             ; NumberOfImagePages
    db 0h             ; Reserved1
    db 0h             ; RedMaskSize
    db 0h             ; RedFieldPosition
    db 0h             ; GreenMaskSize
    db 0h             ; GreenFieldPosition
    db 0h             ; BlueMaskSize
    db 0h             ; BlueFieldPosition
    db 0h             ; RsvdMaskSize
    db 0h             ; RsvdFieldPosition
    db 0h             ; DirectColorModeInfo
video_address: dd 0h      ; PhysBasePtr
    dd 0h             ; OffScreenMemOffset
    dw 0h             ; OffScreenMemSize
    times 206 db 0h
Enter fullscreen mode Exit fullscreen mode

The video address label stores the pointer to the first pixel.

Accessing more memoryπŸš€

When the computer boots in real mode only 20 address lines are usable. This means we can only address 2^20=1MB of memory. This is not good enough, say if we want to use video mode 0x11B which is 1280x1024 with 3 colour channels, we would need about 4MB of memory just to access all of video memory. To fix this we can do something called enabling the A20 line. This doesn't just give us one more address line, which still wouldn't be enough, but it gives us access to all available address lines, circa 32 probably. This means we can now address up to 4GB of RAM.

Great, so how do I enable the A20 line? Most BIOS's provide a fast and simple way of doing this by setting some port signals. In fact some BIOS's come with the A20 lines enabled on boot, but this is not always the case so we better make sure.

in al, 0x92
or al, 2
out 0x92, al    
;A20 lines now enabled
Enter fullscreen mode Exit fullscreen mode

Drawing pixels 🎨

Jolly good, now we can start drawing pixels to the screen.

mov edi, dword[video_address]
add edi, 5; determins the x cord of the pixel / bytes per pixel
mov al, 0x4
mov byte[edi], al
Enter fullscreen mode Exit fullscreen mode

Depending on the resolution of the video mode you choose, you may find it difficult to see the pixel because it is so small. Here's some code that should set all the pixels on screen.

mov ecx, dword[video_address]
;set this to the number of pixels for your chosen video mode
mov edx, 1280*1024*3;resolution X * resolution Y * bytes per pix
add edx, ecx    
;edx is now the end value for the loop
;ecx being the start
.loopHead:
        ;set the colour the counter for more interesting display
        mov al, cl
    mov byte[ecx], al
        inc ecx ; move onto next pixel
    cmp ecx, edx
    jle .loopHead
jmp $ ; infinate loop, $ means address of current instruction
Enter fullscreen mode Exit fullscreen mode

Now you have all the tools you need to create something awesome.

Going further 🧭

I decided to draw the hello world of images, Lena. It took me a little while to get it working but in the end I managed it.


The pixelated effect is due to my code not a limitation of VBE.

This just leaves one question. What are you going to do with this knowledge? Let me know in the comments below. Perhaps coding a game, or maybe adding nicer graphics for your kernel.

Wrapping up 🎁

I have produced a GitHub repo to pair with this tutorial so if you want to look at sections in more detail head to (https://github.com/asdf-a11/VBE_Tutorial). If you have any questions make sure to leave them in the comments below. If you have enjoyed this article then make sure to check out my other articles, I think they'll be of interest to you. Have a great day!

Top comments (0)