Получить список видеорежимов VESA из Int 10h / AX = 4F00h

Я пытаюсь разработать концептуальную ОС. Однако в процессе одна из проблем, с которыми я сталкиваюсь, - это режимы видео vesa. Похоже, что после того, как vesa сказал нам получить их из информации биографии vbe и найти тот, который соответствует нашим потребностям, не хватает жестко запрограммированных номеров видеорежимов. Однако я не могу получить видеорежимы, так как не знаю, как использовать vbeFarPtr из ядра C в 32-битной версии.

Вот мой код ядра:

Я передал VbeInfoBlock в качестве параметра ядра из моего загрузчика второго этапа после получения информации с 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);
}

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

Я не мог понять проблему. Есть ли другой способ сделать это? Или мой способ правильный, но мой код неверен?

Я думаю, проблема в том, что часть video_modes в VbeInfoBlock определяется как пара сегмент: смещение. Я не знаю, как использовать его в 32-битном коде C.

(Вы можете запросить мой загрузчик второго уровня или мой оригинальный загрузчик, но для этой проблемы я думаю, что это не нужно)

РЕДАКТИРОВАТЬ:

Код, который я пробовал после ответа Брендана

    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:

Изображение

Скриншот моего вывода

Изменить 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

Edit4:

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 ++, fortran, lto - -enable-libstdcxx-time = yes --enable-threads = win32 --enable-libgomp --enable-libatomic --enable-lto --enable-graphite --enable-check = release --enable-full-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 - с-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, Создано проектом MinGW-W64' --with-bugurl = https://sourceforge.net/projects/mingw-w64 CFLAGS = '- O2 -pipe -fno-identify -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-identify -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 / включить -I / c / mingw810 / prerequisites / i686-zlib-static / include -I / c / mingw810 / prerequisites / i686-w64- mingw32-static / include 'LDFLAGS =' - pipe -fno-identify -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 'Модель потока: win32 gcc версии 8.1.0 (i686-win32-dwarf- rev0, построенный проектом MinGW-W64)

Edit5:

Снимок экрана без *


person Özgür Güzeldereli    schedule 10.10.2019    source источник
comment
Я думаю, это потому, что вы не выделяете память для str. Прямо сейчас он просто указывает на место в разделе данных. Попробуйте использовать char str[9];. Вам, вероятно, также следует использовать printf("%4s", vbeinfo->signature); для печати подписи.   -  person S.S. Anne    schedule 10.10.2019
comment
@ JL2210 Я могу точно распечатать VESA (подпись), проблема связана с режимами видео. Я запутался. Если я перейду на char str [9], ничего не изменится, я все равно получу 1053   -  person Özgür Güzeldereli    schedule 10.10.2019
comment
Я действительно не понимаю, что ты хочешь. Не могли бы вы подробнее рассказать о результате, которого вы хотите для меня?   -  person S.S. Anne    schedule 10.10.2019
comment
@ JL2210 ОК. У меня есть информационный блок vbe. Которая содержит список видеорежимов. Список режимов 16-битные записи для каждого режима. И список заканчивается на FF FF. У меня есть указатель на сегмент: смещение. Как мне распечатать этот список. Я не могу его распечатать, получаю нежелательный текст 1053   -  person Özgür Güzeldereli    schedule 10.10.2019
comment
Я почти уверен, что вам нужно будет прочитать указатель сегмента: смещение в сборке и сохранить результат ниже 64 КБ. Тогда вы сможете прочитать его в защищенном режиме.   -  person S.S. Anne    schedule 10.10.2019
comment
@ JL2210 Я могу получить указатель смещения сегмента. Он уже хранится в информационном блоке vbe.   -  person Özgür Güzeldereli    schedule 10.10.2019
comment
Да, но вам нужно разыменовать указатель сегмента: смещение и поместить that ниже 64K.   -  person S.S. Anne    schedule 10.10.2019
comment
@ JL2210 Могу ли я сделать это с помощью C. Если я не могу, как я сделаю это в сборке   -  person Özgür Güzeldereli    schedule 10.10.2019
comment
См. Ответ Брендана ниже. Это объясняет, насколько хорошо.   -  person S.S. Anne    schedule 10.10.2019
comment
@MichaelPetch github.com/Danyy427/OSDEV.git. Это видеорежим. Остальные старые. И мой код довольно запутанный.   -  person Özgür Güzeldereli    schedule 11.10.2019
comment
Да, это было личное. я изменил это   -  person Özgür Güzeldereli    schedule 11.10.2019
comment
В этом примере я @ MichaelPetch получаю полную страницу нулей и через 5 секунд она становится полностью черной. Это означает, что что-то не так с виртуальным или физическим адресом (возможно), но я не могу найти какой   -  person Özgür Güzeldereli    schedule 11.10.2019
comment
@MichaelPetch не думаю, что это полный список, он не заканчивается на FFFF. Полагаю, это случайная часть воспоминаний. По моему пусто. В твоем он забит хламом. Я могу ошибаться   -  person Özgür Güzeldereli    schedule 11.10.2019
comment
@MichaelPatch Можете ли вы как-нибудь опубликовать свой код, чтобы я мог попробовать его на своей машине и дать вам лучший отзыв?   -  person Özgür Güzeldereli    schedule 11.10.2019
comment
@MichaelPetch См. Скриншот с моим результатом в edit2   -  person Özgür Güzeldereli    schedule 11.10.2019
comment
@MichaelPetch Я думал, но никогда не думал о возможности поврежденного VbeInfloBlock, возможно, я не могу правильно передать его в качестве параметра. Но как вы это сделали, используя мой код?   -  person Özgür Güzeldereli    schedule 11.10.2019
comment
В дополнение к комментарию Майкла Петча; Я бы предположил, что что-то не так, если вы не получили ошибку (или предупреждение?) От компилятора, например expected char * but argument is char. Например. возможно, функция itoa() имеет неправильные типы входных параметров.   -  person Brendan    schedule 12.10.2019
comment
@MichaelPetch Я удалил itoa, чтобы посмотреть, что произойдет, и получил повторный мусор, см. Edit3, пожалуйста.   -  person Özgür Güzeldereli    schedule 12.10.2019
comment
@MichaelPetch Я думаю, что нажал последнюю версию (я немного новичок в github). Gcc -v находится в редактировании 4   -  person Özgür Güzeldereli    schedule 12.10.2019
comment
@MichaelPetch Мне удалось получить список (но я не знаю, как проверить его точность, поскольку он, кажется, содержит много мусора, например, блок нулей и т. Д.) С заменой VbeInfoBlock * vbeinfo на VbeInfoBlock vbenifo (без *) см. Править 5 для вывода   -  person Özgür Güzeldereli    schedule 15.10.2019


Ответы (2)


Это дополнение к ответу Брендана. В свое первое редактирование вы включили изменения, предложенные Бренданом, и сделали следующее:

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. Для шестнадцатеричного числа это 9 символов, включая 8 шестнадцатеричных цифр и терминатор NUL (\ 0). В худшем случае с основанием 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. Брендан предложил правильное изменение, чтобы разбить video_modes член структуры VBEInfoBlock, заменив:

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

С участием:

    uint16_t video_modes_offset;
    uint16_t video_modes_segment;

Сегмент реального режима: пары смещений сохраняются в памяти со смещением, за которым следует сегмент. Проблема в том, что в 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;

Из-за этой ошибки адрес, который вы вычисляете для списка видеорежимов, неверен, что приводит к созданию неверного списка.


Если эти изменения внесены, результат должен выглядеть примерно так:

введите здесь описание изображения

Это выглядит как правильный список, тем более что в конце списка находятся видеорежимы EGA / VGA:

0 1 2 3 4 5 6 7 D E F 10 11 12 13 6A

Видеорежимы 8 9 A B C обычно зарезервированы или не являются частью стандартных видеорежимов EGA / VGA, поддерживаемых QEMU. Режим 6A выделяется тем, что это стандартный 16-битный цветовой режим VESA 800x600. Исходя из этого, я предполагаю, что ищу список, который подходит для QEMU.

person Michael Petch    schedule 23.10.2019
comment
Да, это сработало. Спасибо за ваши усилия и труд. Я очень благодарен. - person Özgür Güzeldereli; 24.10.2019

Во-первых, немного измените структуру, чтобы 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-битное беззнаковое», чтобы, если результат будет отрицательным, оно вернется к действительному положительному результату) . Если используется пейджинг, это будет зависеть от того, как используется пейджинг (например, возможно, вы сопоставляете физические страницы, содержащие список видеорежимов, на любой виртуальный адрес, который вам нравится).

В любом случае, когда у вас есть полезный указатель, вы можете сделать что-то вроде:

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

Тем не мение; если это сработает, у вас будет список бессмысленных чисел (старые «фиксированные номера режима» устарели, и теперь любой номер режима может означать что угодно). Вы должны использовать «int 0x10, ax = 0x4F01, Получить информацию о режиме VBE», чтобы узнать, что это за режим на самом деле (разрешение, глубина цвета, ...); и вы не можете сделать это в защищенном режиме, и для этого придется вернуться в реальный режим.

Учитывая, что вам придется переключиться обратно в реальный режим, чтобы понять номера режимов, вероятно, будет проще переключиться обратно в реальный режим, а затем выполнить итерацию списка номеров режимов (используя «сегмент и смещение» реального режима, которое VBE дал вам без всяких преобразований).

person Brendan    schedule 10.10.2019
comment
Я еще не пробовал ваш ответ, но как можно скорее отмечу ваш ответ как принятый, если он сработает. - person Özgür Güzeldereli; 11.10.2019
comment
Я пробовал, но не смог получить список. Сначала я посмотрел на свой gdt и увидел, что мой код и базы сегментов данных равны нулю, поэтому я был счастлив. Я написал код, который вы отправили, с небольшими изменениями (моя функция printf не имеет всех причудливых преобразований с% и т. Д., Мне нужно было использовать itoa), но все, что я напечатал, было страницей, заполненной нулями. Что я сделал не так, я неправильно понял часть gdt? Вы можете увидеть код, который я пробовал, в части редактирования - person Özgür Güzeldereli; 11.10.2019
comment
См. Комментарий Майкла Петча (о том, что char chr = '\0'; не является строкой - вы не можете хранить несколько символов в одном char). - person Brendan; 12.10.2019