this is my first post here, even if I spent a lot of time reading articles in https://wiki.osdev.org/. Therefore, I just wanted first to thank you all for maintaining that wiki and this forum.
As many people here, I'm writing my own OS. Recently, I implemented in it a graphics-mode console in order to fully support a pure UEFI boot (it uses UEFI's framebuffer) and, up to here so far, so good. Yesterday, I decided that it would be cool to make also my legacy bootloader to support booting my kernel using a VBE graphical mode.
The problem I hit is that, instead of drawing pixels, my temporary debug function draw_pixel() draws some kind-of dashes on-screen. This happens even if I write a SINGLE byte in the framebuffer. I tried my best to be on the safe side by:
- querying the bios for the available modes (not using a hard-coded one)
- checking that in ModeAttributes the supported bit is set (bit 0)
- checking that in ModeAttributes the graphics mode bit is set (bit 4)
- checking that in ModeAttributes the linear framebuffer support bit is set (bit 7)
- checking that the MemoryModel is 0x6 (direct color)
- OR-ing the mode with bit 14 (linear framebuffer) when calling VBE set mode (0x4f02)
- using BytesPerScanLine (checking also LinBytesPerScanLine, which was the same) when drawing
- using (in the debug code) directly framebuffer's physical address to avoid kind of the issues
- checking that {Red,Green,Blue}MaskSize and {Red,Green,Blue}FieldPosition are what I expect to be
- checking with vbe get current mode (0x4f03) that the bit 14 in mode is set (=> I'm actually using a linear framebuffer, nothing "fancy").
- checking that all the other mode info properties are what they should be
- checking that the pixel (0, 0) is actually (0,0) [=> it is a dash at 0,0 to 8,0]
- checking that the pixel (0, height-1) is actually there (of course, as a dash)
- [clearly, for the width the check fails. The width appears to be 1/8-th]
But, at the end, when I try to draw on-screen:
- pixels look like dashes actually (apparently 8-pixels long)
- the x-res therefore is not what is should be. It looks 1/8 of what it should be.
- the y-res is good
- the BytesPerPixel seems to be 4, even if bpp is 24. If I try to use BytesPerPixel = 3, I actually draw 2 "pixels" (dashes) and the color is wrong.
Also: QEMU accepts only to switch to 640x480x24bpp, even if while querying the modes, many others are supported as well.
Here is my debug draw_pixel function, placed directly in the bootloader:
Code: Select all
/* temporary debug code */
static inline void draw_pixel(u32 x, u32 y, u32 color)
{
if (fb_bpp == 32) {
/*
* This code works perfectly when a UEFI framebuffer is used. In case of
* a VBE framebuffer instead, it still causes a dash to appear instead
* of a single point.
*/
*(volatile u32 *)
(fb_paddr + (fb_pitch * y) + (x << 2)) = color;
} else if (fb_bpp == 24) {
/*
* NOTE: bytes_per_pixel=3 produces 2 dashes (instead of a single point!)
* having also different colors, while using bytes_per_pixel=4
* produces a single dash having the right color. It looks like that bpp
* is 32, even if when it should be 24. Anyway, the biggest problem is
* that even by writing a single byte (e.g. p[0] = ...) a whole dash
* appears. On real hardware, the effect is identical.
*/
u32 bytes_per_pixel = fb_bpp / 8;
volatile u8 *p =
(volatile u8 *)(fb_paddr + (fb_pitch * y) + (x * bytes_per_pixel));
p[0] = ((u8*)&color)[0];
p[1] = ((u8*)&color)[1];
p[2] = ((u8*)&color)[2];
}
}
/* END of temporary debug code */
Code: Select all
/* draw one white pixel */
draw_pixel(0, 10, 0x00ffffff);
/* draw one red pixel */
draw_pixel(0, 11, 0x00ff0000);
/* draw one green pixel */
draw_pixel(0, 12, 0x0000ff00);
/* draw one blue pixel */
draw_pixel(0, 13, 0x000000ff);
Also note that, the dashes are not strictly one below the other (y+1) because the pitch value (bytes per scanline) is inconsistent with the actual width.
I tried the code on QEMU and on 3 physical machines and all of them showed the same output.
I believe that the problem is definitively in my code but, after reading the relative osdev wiki, ralph brown interrupt list and the VBE 3.0 spec, I have no more ideas about what I'm doing wrong.
My intuition is that it looks like I'm not using a regular linear framebuffer. I believe that, because when I boot with UEFI, the linear framebuffer works exactly as I expect, so here the problem should not be the draw function itself. It looks like there's something wrong with the video mode I use, but I have no idea in which direction to further investigate. I can post more code snippets if necessary, of course.
Does anybody have any suggestions? What can possibly be?
For anybody curious to see the ugly dashes "live", just checkout the "bootloader" branch: https://github.com/vvaltchev/experiment ... bootloader and follow the instructions in README.md.
Thank you very much!
[and sorry for the long post]
Vlad