I am trying to develop a proof-of-concept os. Howsoever in the process one of the problems I encounter is the vesa video modes. It seems there is a lack of hardcoded video modes numbers after vesa told us to get them from vbe bios information and find the one that fits our needs. However I am unable to receive the video modes as I don't know how to use vbeFarPtr
from C kernel in 32 bit
Here is my kernel code:
I passed a VbeInfoBlock
as a parameter to the kernel from my second stage bootloader after receiving the information with int 0x10 ax=0x4f00
int kmain(struct VbeInfoBlock *vbeinfo)
{
init_idt();
SetPITSpeed(100);
init_DTCursor();
printf(vbeinfo->signature); // I can print VESA here means I have the vbeinfoblock
char* str = "";
itoa(vbeinfo->video_modes,str,16); // I want a hex dump so I convert it to hex
printf(str); // I get "VESA" for the signature followed by a string "1053" and nothing else while the list should be like this
// If for example video mode 0x0103, 0x0118 and 0x0115 are supported
// The list should be as 03 01 15 01 18 01 FF FF
// So I should atleast get some FF FF
// My output is "VESA 1053"
while(1);
}
The VbeInfoBlock is defined a follows if you dont know
struct VbeInfoBlock
{
char signature[4]; // must be "VESA" to indicate valid VBE support
uint16_t version; // VBE version; high byte is major version, low byte is minor version
uint32_t oem; // segment:offset pointer to OEM
uint32_t capabilities; // bitfield that describes card capabilities
uint32_t video_modes; // segment:offset pointer to list of supported video modes
uint16_t video_memory; // amount of video memory in 64KB blocks
uint16_t software_rev; // software revision
uint32_t vendor; // segment:offset to card vendor string
uint32_t product_name; // segment:offset to card model name
uint32_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));
I couldn't understand the problem. Are there any other way of doing it? Or is my way correct but my code is incorrect?
I think the problem is that video_modes part in VbeInfoBlock
is defined as a segment:offset pair. I don't know how to use it in 32 bit C code.
(You can request my second stage bootloader or my original bootloader but for this problem I think it is unnecessary)
EDIT:
The code I tried after Brendan's answer
uint32_t physical_address = (vbeinfo->video_modes_segment << 4) + vbeinfo->video_modes_offset;
uint16_t *videoListPointer = (uint16_t *)physical_address;
char chr = '\0';
while(*videoListPointer != 0xffff) {
itoa(*videoListPointer,chr,16);
printf(chr);
videoListPointer++;
}
and my gdt
gdt_start :
gdt_null : ; the mandatory null descriptor
dd 0x0 ; 'dd ' means define double word ( i.e. 4 bytes )
dd 0x0
gdt_code :
dw 0xffff ; Limit ( bits 0 -15)
dw 0x0 ; Base ( bits 0 -15)
db 0x0 ; Base ( bits 16 -23)
db 10011010b ; 1st flags , type flags
db 11001111b ; 2nd flags , Limit ( bits 16 -19)
db 0x0 ; Base ( bits 24 -31)
gdt_data :
dw 0xffff ; Limit ( bits 0 -15)
dw 0x0 ; Base ( bits 0 -15)
db 0x0 ; Base ( bits 16 -23)
db 10010010b ; 1st flags , type flags
db 11001111b ; 2nd flags , Limit ( bits 16 -19)
db 0x0 ; Base ( bits 24 -31)
gdt_end :
gdt_descriptor :
dw gdt_end - gdt_start - 1
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
Edit 2:
Image
Edit 3:
Code I used:
int kmain(struct VbeInfoBlock *vbeinfo)
{
init_idt();
SetPITSpeed(100);
init_DTCursor();
uint32_t physical_address = (vbeinfo->video_modes_segment << 4) + vbeinfo->video_modes_offset;
uint16_t *videoListPointer = (uint16_t *)physical_address;
char chr[9];
while(*videoListPointer != 0xffff) {
//itoa(*videoListPointer, chr,16);
printf(*videoListPointer);
videoListPointer++;
}
while(1);
}
and screenshot of my output without itoa
Edit4:
gcc -v
C:\Users\Asus>gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=D:/MinGW/mingw32/bin/../libexec/gcc/i686-w64-mingw32/8.1.0/lto-wrapper.exe Target: i686-w64-mingw32 Configured with: ../../../src/gcc-8.1.0/configure --host=i686-w64-mingw32 --build=i686-w64-mingw32 --target=i686-w64-mingw32 --prefix=/mingw32 --with-sysroot=/c/mingw810/i686-810-win32-dwarf-rt_v6-rev0/mingw32 --enable-shared --enable-static --disable-multilib --enable-languages=c,c++,fortran,lto --enable-libstdcxx-time=yes --enable-threads=win32 --enable-libgomp --enable-libatomic --enable-lto --enable-graphite --enable-checking=release --enable-fully-dynamic-string --enable-version-specific-runtime-libs --disable-sjlj-exceptions --with-dwarf2 --disable-libstdcxx-pch --disable-libstdcxx-debug --enable-bootstrap --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-gnu-as --with-gnu-ld --with-arch=i686 --with-tune=generic --with-libiconv --with-system-zlib --with-gmp=/c/mingw810/prerequisites/i686-w64-mingw32-static --with-mpfr=/c/mingw810/prerequisites/i686-w64-mingw32-static --with-mpc=/c/mingw810/prerequisites/i686-w64-mingw32-static --with-isl=/c/mingw810/prerequisites/i686-w64-mingw32-static --with-pkgversion='i686-win32-dwarf-rev0, Built by MinGW-W64 project' --with-bugurl=https://sourceforge.net/projects/mingw-w64 CFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/i686-810-win32-dwarf-rt_v6-rev0/mingw32/opt/include -I/c/mingw810/prerequisites/i686-zlib-static/include -I/c/mingw810/prerequisites/i686-w64-mingw32-static/include' CXXFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/i686-810-win32-dwarf-rt_v6-rev0/mingw32/opt/include -I/c/mingw810/prerequisites/i686-zlib-static/include -I/c/mingw810/prerequisites/i686-w64-mingw32-static/include' CPPFLAGS=' -I/c/mingw810/i686-810-win32-dwarf-rt_v6-rev0/mingw32/opt/include -I/c/mingw810/prerequisites/i686-zlib-static/include -I/c/mingw810/prerequisites/i686-w64-mingw32-static/include' LDFLAGS='-pipe -fno-ident -L/c/mingw810/i686-810-win32-dwarf-rt_v6-rev0/mingw32/opt/lib -L/c/mingw810/prerequisites/i686-zlib-static/lib -L/c/mingw810/prerequisites/i686-w64-mingw32-static/lib -Wl,--large-address-aware' Thread model: win32 gcc version 8.1.0 (i686-win32-dwarf-rev0, Built by MinGW-W64 project)
Edit5:
This is an addendum to Brendan's answer. In your first edit you incorporated changes suggested by Brendan and did this:
uint32_t physical_address = (vbeinfo->video_modes_segment << 4) + \
vbeinfo->video_modes_offset;
uint16_t *videoListPointer = (uint16_t *)physical_address;
char chr = '\0';
while(*videoListPointer != 0xffff) {
itoa(*videoListPointer,chr,16);
printf(chr);
videoListPointer++;
}
First of all char chr = '\0'
only guarantees allocation of a single byte initialized to 0. You really need a buffer of characters big enough for the longest string that might be returned by itoa
. For hexadecimal that is 9 characters which includes 8 hexadecimal digits and the NUL(\0) terminator. For the worst case of base 2 (binary) it is 33 characters including the NUL(\0) terminator. You can declare a buffer this way:
char buf[9];
You can pass that buffer to itoa
. It is easier to read the video mode numbers if you place a space character between each. The modified code could look like this:
uint32_t physical_address = (vbeinfo->video_modes_segment << 4) + \
vbeinfo->video_modes_offset;
uint16_t *videoListPointer = (uint16_t *)physical_address;
char buf[9];
while(*videoListPointer != 0xffff) {
itoa(*videoListPointer, buf, 16);
printf(buf);
printf(" ");
videoListPointer++;
}
Most Important: I didn't catch this mistake until I finally reviewed all your code on GitHub. Brendan suggested a correct change to break apart the video_modes
member of the VBEInfoBlock
structure by replacing:
uint32_t video_modes; // segment:offset pointer to list of supported video mode
With:
uint16_t video_modes_offset;
uint16_t video_modes_segment;
Real mode segment:offset pairs are stored in memory with offset followed by segment. The problem is in GitHub you reversed offset and segment by doing this:
uint16_t video_modes_segment; // segment:offset pointer to list of supported video modes
uint16_t video_modes_offset;
When it should be:
uint16_t video_modes_offset; // segment:offset pointer to list of supported video modes
uint16_t video_modes_segment;
Because of this bug the address you compute for the Video Mode List is wrong which results in producing an incorrect list.
If these changes are made the output should look similar to:
This looks like a proper list especially since the end of the list includes the EGA/VGA video modes:
0 1 2 3 4 5 6 7 D E F 10 11 12 13 6A
Video modes 8 9 A B C
are usually reserved or are not part of the standard EGA/VGA video modes supported by QEMU. Mode 6A
stands out because that happens to be the standard VESA 800x600 16-bit color mode. Based on this I assume I am looking at a list that would be appropriate for QEMU.