簡體   English   中英

從 Int 10h/AX=4F00h 檢索 VESA 視頻模式列表

[英]Retrieve list of VESA video modes from Int 10h/AX=4F00h

我正在嘗試開發一個概念驗證操作系統。 但是,在此過程中,我遇到的問題之一是 vesa 視頻模式。 在 vesa 告訴我們從 vbe bios 信息中獲取它們並找到適合我們需要的視頻模式編號之后,似乎缺少硬編碼的視頻模式編號。 但是我無法接收視頻模式,因為我不知道如何使用 32 位vbeFarPtr kernel 中的 vbeFarPtr

這是我的 kernel 代碼:

在接收到 int 0x10 ax=0x4f00 的信息后,我將VbeInfoBlock作為參數從我的第二階段引導加載程序傳遞給 kernel


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);
}

如果您不知道,則 VbeInfoBlock 定義如下

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));

我無法理解這個問題。 還有其他方法嗎? 還是我的方式正確但我的代碼不正確?

我認為問題在於 VbeInfoBlock 中的VbeInfoBlock部分被定義為段:偏移量對。 我不知道如何在 32 位 C 代碼中使用它。

(您可以請求我的第二階段引導加載程序或我原來的引導加載程序,但對於這個問題,我認為沒有必要)

編輯:

我在 Brendan 回答后嘗試的代碼

    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++;
    }

還有我的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   


編輯2:

圖片

myy output截圖

編輯3:

我使用的代碼:


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);
}

和沒有 itoa 的我的 output 的屏幕截圖

編輯4:

gcc -v

C:\Users\Asus>gcc -v 使用內置規格。 COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=D:/MinGW/mingw32/bin/../libexec/gcc/i686-w64-mingw32/8.1.0/lto-wrapper.exe 目標:i686-w64-mingw32 配置:../。 ./../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++,Z56121CC37D7123E665C0F8317F17AA5- -enable-libstdcxx-time=yes --enable-threads=win32 --enable-libgomp --enable-libatomic --enable-lto --enable-graphite --enable-checking=release --enable-fully-dynamic-字符串 --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-z lib --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,由MinGW-構建W64 項目'--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/先決條件/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' 線程 model:win32 gcc 版本 8.1.dwarf-win6322 , 由 MinGW-W64 項目構建)

編輯5:

output的屏幕截圖,不帶*

首先,稍微改變一下結構,將video_modes分成 2 個字段,如下所示:

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

    uint16_t video_modes_offset;
    uint16_t video_modes_segment;

    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));

接下來,計算視頻模式列表的物理地址,如下所示:

uint32_t physical_address = (vbeinfo->video_modes_segment << 4) + vbeinfo->video_modes_offset;

接下來,盡您所能將物理地址轉換為可用作指針的虛擬地址。 如果您不使用分頁並且段寄存器基地址為零,那么這將是微不足道的,例如uint16_t *videoListPointer = (uint16_t *)physical_address; . 如果段寄存器基數不為零,那么您需要從物理地址中減去它們(並確保使用“32 位無符號”減法,以便如果結果為負,它會環繞為有效的正結果) . 如果使用分頁,那么它將取決於如何使用分頁(例如,您可能在 map 包含您喜歡的任何虛擬地址的視頻模式列表的物理頁面)。

在任何情況下,一旦你有了一個可用的指針,你就可以執行以下操作:

    while(*videoListPointer != 0xFFFF) {
        printf("0x%04X\n", *videoListPointer);
        videoListPointer++;
    }

然而; 如果可行,您將獲得一個無意義數字列表(舊的“固定模式編號”已被棄用,現在任何模式編號都可以表示任何含義)。 您必須使用“int 0x10, ax = 0x4F01, Get VBE mode information”來找出實際的模式是什么(分辨率,顏色深度,...); 而且您不能在保護模式下執行此操作,並且必須為此切換回實模式。

鑒於您必須切換回實模式才能理解模式編號,切換回實模式然后迭代模式編號列表可能會更容易(使用 VBE 的實模式“分段和偏移”)給你沒有任何轉換)。

這是 Brendan 答案的附錄。 在您的第一次編輯中,您合並了 Brendan 建議的更改並執行了以下操作:

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++;
}

首先char chr = '\0'僅保證分配一個初始化為 0 的字節。您確實需要一個足夠大的字符緩沖區,以容納itoa可能返回的最長字符串。 對於包含 8 個十六進制數字和 NUL(\0) 終止符的 9 個字符的十六進制。 對於基數 2(二進制)的最壞情況,它是 33 個字符,包括 NUL(\0) 終止符。 你可以這樣聲明一個緩沖區:

char buf[9];

您可以將該緩沖區傳遞給itoa 如果在每個之間放置一個空格字符,則更容易閱讀視頻模式編號。 修改后的代碼可能如下所示:

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++;
}

最重要的是:直到我最終查看了您在 GitHub 上的所有代碼,我才發現這個錯誤。 Brendan 建議進行正確的更改,通過替換來分解VBEInfoBlock結構的video_modes成員:

    uint32_t video_modes; // segment:offset pointer to list of supported video mode

和:

    uint16_t video_modes_offset;
    uint16_t video_modes_segment;

實模式段:偏移對存儲在 memory 中,偏移后跟段。 問題出在 GitHub 中,您通過執行以下操作反轉了偏移和分段:

    uint16_t video_modes_segment; // segment:offset pointer to list of supported video modes
    uint16_t video_modes_offset;

什么時候應該:

    uint16_t video_modes_offset;  // segment:offset pointer to list of supported video modes
    uint16_t video_modes_segment;

由於這個錯誤,您為視頻模式列表計算的地址是錯誤的,這會導致生成不正確的列表。


如果進行了這些更改,output 應該類似於:

在此處輸入圖像描述

這看起來像是一個正確的列表,特別是因為列表的末尾包括 EGA/VGA 視頻模式:

 0 1 2 3 4 5 6 7 DEF 10 11 12 13 6A

視頻模式8 9 AB C通常是保留的,或者不是 QEMU 支持的標准 EGA/VGA 視頻模式的一部分。 模式6A脫穎而出,因為它恰好是標准的 VESA 800x600 16 位顏色模式。 基於此,我假設我正在查看適合 QEMU 的列表。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM