Create your own VBE driver in C

This is a guide for creating your own VBE driver for your custom operating system in C. This is a continuation of my previous blog post Create your own graphics library in C++, as I realised that not everyone may have a VBE or VGA driver. This post will also guide you through how to transfer the data from the uBuffer32 to the framebuffer.

Before we get started, you’ll need to find things like the address of your framebuffer (E.g. 0xFD000000). You’ll probably also want to define the width and height of the framebuffer (E.g. 1920x1080).

I suggest putting the vbe.c and vbe.h files in a drivers directory, so they don’t get mixed up. We’ll need to define quite a few things in the vbe.h before we do anything else.

#define VBE_DISPI_BANK_ADDRESS          0xA0000
#define VBE_DISPI_BANK_SIZE_KB          64

#define VBE_DISPI_MAX_XRES              1024
#define VBE_DISPI_MAX_YRES              768

#define VBE_DISPI_IOPORT_INDEX          0x01CE
#define VBE_DISPI_IOPORT_DATA           0x01CF

#define VBE_DISPI_INDEX_ID              0x0
#define VBE_DISPI_INDEX_XRES            0x1
#define VBE_DISPI_INDEX_YRES            0x2
#define VBE_DISPI_INDEX_BPP             0x3
#define VBE_DISPI_INDEX_ENABLE          0x4
#define VBE_DISPI_INDEX_BANK            0x5
#define VBE_DISPI_INDEX_VIRT_WIDTH      0x6
#define VBE_DISPI_INDEX_VIRT_HEIGHT     0x7
#define VBE_DISPI_INDEX_X_OFFSET        0x8
#define VBE_DISPI_INDEX_Y_OFFSET        0x9

#define VBE_DISPI_ID0                   0xB0C0
#define VBE_DISPI_ID1                   0xB0C1
#define VBE_DISPI_ID2                   0xB0C2
#define VBE_DISPI_ID3                   0xB0C3
#define VBE_DISPI_ID4                   0xB0C4

#define VBE_DISPI_DISABLED              0x00
#define VBE_DISPI_ENABLED               0x01
#define VBE_DISPI_VBE_ENABLED           0x40
#define VBE_DISPI_NOCLEARMEM            0x80

#define VBE_DISPI_LFB_PHYSICAL_ADDRESS  0xE0000000


#define VBE_DISPI_BPP_4   				      0x04
#define VBE_DISPI_BPP_8   				      0x08
#define VBE_DISPI_BPP_15  				      0x0F
#define VBE_DISPI_BPP_16  				      0x10
#define VBE_DISPI_BPP_24  				      0x18
#define VBE_DISPI_BPP_32			          0x20
#define VBE_DISPI_LFB_ENABLED 			    0x40

#define HD								              1280,720
#define FHD 							              1920,1080
#define UHD								              3840,2160

void BgaSetVideoMode(unsigned int Width, unsigned int Height, unsigned int BitDepth, int UseLinearFrameBuffer, int ClearVideoMemory);
void BgaSetBank(unsigned short BankNumber);

void vbe_putpixel(uint32_t x, uint32_t x y, uint32_t x color); // Draws a pixel on the framebuffer
uint32_t vbe_getpixel(uint32_t x x, uint32_t x y); // Gets a pixel from the framebuffer

void vbe_draw_buffer(uint32_t* buffer, uint32_t width, uint32_t height); // Draws the uBuffer32->buffer to the framebuffer

In the vbe.c file, we’ll also need to add a few things:

#include "vbe.h"

#define ADDRESS 0xFD000000 // Example, yours might be different
#define FONT_WIDTH 8    // Font width
#define FONT_HEIGHT 20  // Font height
int VBE_WIDTH  = 1920;
int VBE_HEIGHT = 1080;

static inline void outpw(uint16_t port, uint16_t value) {
    __asm__ __volatile__ ("outw %0, %1" : : "a"(value), "Nd"(port));
}

static inline uint16_t inpw(uint16_t port) {
    uint16_t value;
    __asm__ __volatile__ ("inw %1, %0" : "=a"(value) : "Nd"(port));
    return value;
}

void BgaWriteRegister(unsigned short IndexValue, unsigned short DataValue){
    outpw(VBE_DISPI_IOPORT_INDEX, IndexValue);
    outpw(VBE_DISPI_IOPORT_DATA, DataValue);
}

unsigned short BgaReadRegister(unsigned short IndexValue){
    outpw(VBE_DISPI_IOPORT_INDEX, IndexValue);
    return inpw(VBE_DISPI_IOPORT_DATA);
}

int BgaIsAvailable(void){
    return (BgaReadRegister(VBE_DISPI_INDEX_ID) == VBE_DISPI_ID4);
}

void BgaSetVideoMode(unsigned int Width, unsigned int Height, unsigned int BitDepth, int UseLinearFrameBuffer, int ClearVideoMemory){
    BgaWriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
    BgaWriteRegister(VBE_DISPI_INDEX_XRES, Width);
    BgaWriteRegister(VBE_DISPI_INDEX_YRES, Height);
    BgaWriteRegister(VBE_DISPI_INDEX_BPP, BitDepth);
    BgaWriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED |
        (UseLinearFrameBuffer ? VBE_DISPI_LFB_ENABLED : 0) |
        (ClearVideoMemory ? 0 : VBE_DISPI_NOCLEARMEM));
	VBE_WIDTH = Width;
	VBE_HEIGHT = Height;
}

void BgaSetBank(unsigned short BankNumber){
    BgaWriteRegister(VBE_DISPI_INDEX_BANK, BankNumber);
}

Now we can begin with the actual main part of it. If you want to initialize VBE and get it set up so you can use it, you’ll need to run BgaSetVideoMode(FHD,VBE_DISPI_BPP_32,1,1);. We’ll implement vbe_putpixel and vbe_getpixel.

void vbe_putpixel(uint32_t x, uint32_t y, uint32_t color) {
    uint32_t *framebuffer = (uint32_t*)ADDRESS;
    uint32_t *pixel = &framebuffer[y * VBE_WIDTH + x];

    uint32_t existing_color = *pixel;
    uint8_t alpha = (color >> 24) & 0xFF;

    if (alpha == 0xFF) {
        return;
    } else if (alpha == 0x00) {
        *pixel = color & 0xFFFFFF;
    } else {
        uint8_t src_r = (color >> 16) & 0xFF;
        uint8_t src_g = (color >> 8) & 0xFF;
        uint8_t src_b = color & 0xFF;

        uint8_t dst_r = (existing_color >> 16) & 0xFF;
        uint8_t dst_g = (existing_color >> 8) & 0xFF;
        uint8_t dst_b = existing_color & 0xFF;

        uint8_t blended_r = ((src_r * (255 - alpha)) + (dst_r * alpha)) / 255;
        uint8_t blended_g = ((src_g * (255 - alpha)) + (dst_g * alpha)) / 255;
        uint8_t blended_b = ((src_b * (255 - alpha)) + (dst_b * alpha)) / 255;

        *pixel = (blended_r << 16) | (blended_g << 8) | blended_b;
    }
}

uint32_t vbe_getpixel(uint32_t x, uint32_t y) {
	uint32_t *framebuffer = (uint32_t*)ADDRESS;
    return framebuffer[y * VBE_WIDTH + x];
}

Now we can implement the copying of the uBuffer32 to the framebuffer.

void vbe_draw_buffer(uint32_t* buffer, uint32_t width, uint32_t height) {
  for(uint32_t y = 0; y < height; y++) {
    for(uint32_t x = 0; x < width; x++) {
      vbe_putpixel(x, y, buffer[y * width + x]);
    }
  }
}

Loading


Discover more from WTDawson

Subscribe to get the latest posts sent to your email.

Leave a Reply