cassemblyx86osdevvesa

How do I draw a pixel in VESA VBE?


I'm trying to convert my kernel.cpp to use VESA, though all I've gotten is an array of weird errors. Following a tutorial, I got the bootloader side of the VESA Graphics working rather smoothly using this bit of code:

mov ax, 0x4F02   ; set VBE mode
mov bx, 0x4118   ; VBE mode number
int 0x10         ; call VBE BIOS
cmp ax, 0x004F   ; test for error
jne error

;Get video mode info
mov ax, 4f01h
mov cx, 105h
mov di, modeInfo 
int 10h

The issues arose, however, when I tried drawing pixels from kernel.cpp. No matter what I changed in my code, I got an error (with a surprising range of variety too, might I add). I tried drawing pixels from the bootloader (code from here), and it worked just fine, so the issue in in kernel.cpp. One common error I got was a strip of random colors across the top of the screen as seen here. Here I'll leave that file and my GitHub (for the rest of the code):

kernel.cpp:

//VIDEO MODE IN 1024x768x32bpp
typedef unsigned char uint8_t;
typedef unsigned char u8;
typedef unsigned short uint16_t;
typedef unsigned int u32;
typedef u32 size_t;
#define SCREEN_WIDTH 1024
#define SCREEN_HEIGHT 768
#define BPP 32
#define SCREEN_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT)
#define FPS 30
#define PIT_HERTZ 1193131.666
#define CLOCK_HIT (int)(PIT_HERTZ/FPS)
#define KEY_LEFT 0x4B
#define KEY_UP 0x48
#define KEY_RIGHT 0x4D
#define KEY_DOWN 0x50

static u32 *BUFFER = (u32 *) 0xA0000;

// double buffers
u32 _sbuffers[2][SCREEN_SIZE];
u32 _sback = 0;

#define CURRENT (_sbuffers[_sback])
#define SWAP() (_sback = 1 - _sback)

#define screen_buffer() (_sbuffers[_sback])

// #define screen_set(_p, _x, _y)\
//     (_sbuffers[_sback][((_y)/* * pitch */ + ((_x) * (BPP/8)))]=(_p))

//u32 pixel_offset = y * pitch + (x * (bpp/8)) + framebuffer;

static inline void outb(uint16_t port, uint8_t val)
{
    asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) );
}

//+++++++++++This is the troublesome function++++++++++++++++++
void screen_set(u32 color,int x,int y) {
    _sbuffers[_sback][y * SCREEN_WIDTH + (x * (BPP/8))]=color;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static inline uint8_t inb(uint16_t port)
{
    uint8_t ret;
    asm volatile ( "inb %1, %0"
                   : "=a"(ret)
                   : "Nd"(port) );
    return ret;
}

const unsigned char font[128-32][8] = {
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0020 (space)
           /*hidden to help length if code...*/
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}    // U+007F
};

static inline void *memcpy(void *dst, const void *src, size_t n)
{
    u8 *d = (u8*)dst;
    const u8 *s = (const u8*)src;

    while (n-- > 0) {
        *d++ = *s++;
    }

    return d;
}

void screen_swap() {
    memcpy(BUFFER, CURRENT, SCREEN_SIZE);
    SWAP();
}

unsigned read_pit(void) {
    unsigned count = 0;
 
    // al = channel in bits 6 and 7, remaining bits clear
    outb(0x43,0b0000000);
 
    count = inb(0x40);          // Low byte
    count |= inb(0x40)<<8;      // High byte
 
    return count;
}
 
void draw_char(char c, int x, int y, u32 color)
{
    const unsigned char *glyph = font[(int)c-32];
 
    for(int cy=0;cy<8;cy++){
        for(int cx=0;cx<8;cx++){
            if(((int)glyph[cy]&(1<<cx))==(1<<cx)){
                screen_set(color,x+cx,y+cy);
            }
        } 
    }
}

void draw_string(const char * s, int x, int y, u32 color) {
    int i = 0;
    while(s[i] != false) {
        draw_char(s[i],x+(i*8),y,color);
        i++;
    }
}

void draw_rect(int pos_x, int pos_y, int w, int h, u32 color) {
    for(int y = 0; y<h; y++) {
        for(int x = 0; x<w; x++) {
            screen_set(color,x+pos_x,y+pos_y);
        }
    }
}

static void render(int c0, int c1) {
    //draw_rect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,0x09);
    //draw_string("Hello, reader. This is written text.", 100, 180, 16777215);
    //draw_string("If this is displayed, my code works.", 100+c0, 100+c1, 16777215);
}

extern "C" void main(){
    
    int clock = 0;
    int incC1 = 0;
    int incC0 = 0;
    while(true) {
        uint16_t scancode = (uint16_t) inb(0x60);
        clock++;
        if(read_pit()!= 0 && clock == CLOCK_HIT) {
            if(scancode == KEY_LEFT) {
                incC0--;
            }else if(scancode == KEY_RIGHT) {
                incC0++;
            }
            if(scancode == KEY_DOWN) {
                incC1++;
            }else if(scancode == KEY_UP) {
                incC1--;
            }
            clock = 0;
            render(incC0,incC1);
            screen_swap();
        }
    }

    return;
}

Solution

  • Using the mode info structure you already got (at modeInfo in real mode) you'll need 3 pieces of information:

    Once you have these 3 values the formula for calculating the physical address of a pixel is:

    physical_address = physical_address_of_frame_buffer + y * bytes_per_scan_line + x * bytes_per_pixel
    

    Of course more often you map the frame buffer into the virtual address space wherever you feel like, but the formula is mostly the same (just with virtual addresses and a virtual_address_of_frame_buffer that you chose).

    Other notes: