Защищенный режим процессоров Intel 80286/80386/80486© Александр Фролов, Григорий ФроловТом 4, М.: Диалог-МИФИ, 1993, 234 стр. 6.3. Интерфейс EMS/VCPIВо второй части второго тома "Библиотеки системного программиста" (глава 11) мы рассказывали вам о дополнительной памяти и об использовании для работы с ней спецификации EMS - Expanded Memory Specification. Драйверы дополнительной памяти предоставляют программам интерфейс прерывания INT 67h, который мы тогда подробно описали. Мы также говорили о том, что для компьютеров на базе процессоров i80386 или i80486 существуют драйверы памяти, эмулирующие дополнительную память с использованием расширенной. Самые известные драйверы такого типа - EMM386.SYS и QEMM.SYS. Эти драйверы используют защищённый (точнее,
виртуальный) режим работы процессора i80386 и
страничную адресацию расширенной памяти. Для
прикладных программ предоставляется интерфейс,
который называется VCPI - Virtual Programm Control Interface. Этот
интерфейс реализован как подфункции функции DEh
прерывания INT 67h: Таблица 9. Функции интерфейса VCPI.
Перед вызовом прерывания INT 67h регистр AH должен содержать DEh, а номер требуемой подфункции должен быть загружен в регистр AL. Кроме того, прежде чем вызывать прерывание INT 67h, в самом начале работы программы необходимо убедиться в том, что в системе установлен драйвер EMS. О том, как это сделать, мы рассказывали в главе 11 второго тома "Библиотеки системного программиста". Там же приведён соответствующий пример программы. Функции VCPI позволяют перевести процессор в защищённый или виртуальный режим работы и предоставляют программам полноценный доступ к расширенной памяти. Поэтому использование интерфейса VCPI более предпочтительно, чем интерфейса драйвера HIMEM.SYS, особенно в тех случаях, когда требуется интенсивная работа с расширенной памятью. Другое принципиальное новшество интерфейса VCPI - поддержка схемы преобразования адресов процессоров i80386/i80486, а именно страничной памяти. С помощью VCPI программа может легко получать и освобождать страницы памяти, не работая непосредственно с системными регистрами процессора. Драйверы EMM386 и QEMM обеспечивают для программ DOS интерфейс VCPI и сами пользуются этим интерфейсом. Вы знаете, что в пределах первого мегабайта адресного пространства имеется 640 килобайт памяти. Остальная память используется видеоадаптерами, ПЗУ BIOS и другой аппаратурой. Вся эта память называется зарезервированной памятью. Зарезервированная память задействована не полностью, в ней есть окна. Страницы памяти, соответствующие свободным окнам, с использованием механизма трансляции страниц отображаются в адресное пространство за пределами первого мегабайта памяти, т.е. на расширенную память. При этом для программ DOS появляется возможность воспользоваться окнами зарезервированной памяти для размещения там драйверов и резидентных программ. Процессор при этом работает, разумеется, не в реальном режиме, а в виртуальном, т.к. в реальном режиме трансляция страниц не используется. Рассмотрим функции интерфейса VCPI более подробно. Проверка наличия в системе интерфейса VCPIРегистры на входе: AX 0DE00h Регистры на выходе: AH равен 00h - если VCPI установлен, не равен 00h - если VCPI не установлен. BH Верхний (major) номер версии VCPI. BL Нижний (minor) номер версии VCPI. Получить адрес интерфейса VCPIРегистры на входе: AX 0DE01h ES:DI Адрес буфера размером в 4 килобайта для таблицы страниц. DS:SI Адрес GDT, состоящей из трёх элементов, в первый будет записан дескриптор сегмента кода, остальные два будут использованы драйвером VCPI. Регистры на выходе: AH равен 00h - успешное выполнение функции, не равен 00h - ошибка. DI Номер первого свободного элемента в таблице страниц, которая размещена в заказанном ранее буфере. EBX Смещение в сегменте кода точки входа в защищённый режим. Определить максимальный физический адрес памятиРегистры на входе: AX 0DE02h Регистры на выходе: AH равен 00h - успешное выполнение функции, не равен 00h - ошибка. EDX Максимальный физический адрес страницы памяти размером 4 килобайта. Определить количество свободных страниц памятиРегистры на входе: AX 0DE03h Регистры на выходе: AH равен 00h - успешное выполнение функции, не равен 00h - ошибка. EDX Количество свободных страниц памяти, доступных для всех задач в системе. Эта функция доступна в защищённом режиме через вызов драйвера в его интерфейсной точке, адрес которой можно получить с помощью функции 01h. Получить страницу памятиРегистры на входе: AX 0DE04h Регистры на выходе: AH равен 00h - успешное выполнение функции, не равен 00h - ошибка. EDX Физический адрес полученной страницы памяти. Программа, использующая эту функцию, перед завершением своей работы должна освободить все полученные страницы памяти. Эта функция доступна в защищённом режиме через вызов драйвера в его интерфейсной точке. Освободить страницу памятиРегистры на входе: AX 0DE05h EDX Физический адрес освобождаемой страницы памяти. Регистры на выходе: AH равен 00h - успешное выполнение функции, не равен 00h - ошибка. Эта функция доступна в защищённом режиме через вызов драйвера в его интерфейсной точке. Получить физический адрес страницы памяти, располагающейся в пределах первого мегабайтаРегистры на входе: AX 0DE06h CX Номер страницы, равен линейному адресу страницы, сдвинутому вправо на 12 бит. Регистры на выходе: AH равен 00h - успешное выполнение функции, не равен 00h - неправильный номер страницы. EDX Физческий адрес страницы. Прочитать содержимое системного регистра CR0Регистры на входе: AX 0DE07h Регистры на выходе: AH 00h EBX Значение системного регистра CR0. Прочитать содержимое отладочных регистровРегистры на входе: AX 0DE08h ES:DI Адрес буфера размером 8 двойных слов. Регистры на выходе: AH 00h EBX Значение системного регистра CR0. В буфере располагается содержимое отледочных регистров в следующем порядке: DR0, DR1, DR3, DR4, DR5, DR6, DR7. Регистры DR4 и DR5 зарезервированы и в процессоре i80386 не используются. Установить отладочные регистрыРегистры на входе: AX 0DE09h ES:DI Адрес буфера размером 8 двойных слов, содержащего новые значения для отладочных регистров. Регистры на выходе: AH 00h EBX Значение системного регистра CR0. Значения, подготовленные для зарезервированных регистров DR4 и DR5, игнорируются. Получить отображение векторов прерываний для контроллеров прерываний 8259Регистры на входе: AX 0DE0Ah Регистры на выходе: AH равен 00h - успешное выполнение функции, не равен 00h - ошибка. BX Вектор прерывания, используемый для IRQ0. CX Вектор прерывания, используемый для IRQ8. Установить отображение векторов прерываний для контроллеров прерываний 8259Регистры на входе: AX 0DE0Bh BX Вектор прерывания, используемый для IRQ0. CX Вектор прерывания, используемый для IRQ8. Регистры на выходе: AH равен 00h - успешное выполнение функции, не равен 00h - ошибка. После выполнения этой функции прерывания запрещены. Перед завершением своей работы программа должна установить прежнее отображение векторов для контроллеров прерываний. Переключить процессор в защищённый режимРегистры на входе: AX 0DE0Ch ESI Линейный адрес массива значений для системных регистров, массив должен располагаться в первом мегабайте памяти. Регистры на выходе: Загружаются регистры GDTR, IDTR, LDTR, TR. В стеке, на который указывают регистры SS:ESP, необходимо отвести по крайней мере 16 байт для возможности обработки прерываний. Содержимое регистров EAX, ESI, DS, ES, FS, GS после выполнения функции будет потеряно. Перед вызовом функции прерывания должны быть запрещены. После выполнения переключения в защищённый режим прерывания также запрещены. Приведём формат области для загрузки системных
регистров перед переходом в защищённый режим: Таблица 10. Формат буфера для загрузки регистров и перехода в защищённый режим средствами VCPI.
Переключить процессор в виртуальный режимЭто переключение можно выполнить, если находясь в защищённом режиме вызвать точку интерфейса VCPI с регистрами, загруженными следующим образом: AX DE0Ch DS Селектор, полученный от функции DE01h. SS:ESP Стек должен быть расположен в пределах первого мегабайта памяти Пример программыПриведём пример программы, определяющей присуствие в системе драйвера дополнительной памяти XMM. Если этот драйвер присутствует, программа проверяет поддержку этим драйвером интерфейса VCPI. Затем, если интерфейс VCPI поддерживается, программа выводит его версию на экран. Листинг 19. Определение версии VCPI Файл vcpi.c ----------------------------------------------------------- #include <stdio.h> #include <dos.h> void main(void) { unsigned err; char ver, ver_hi, ver_lo; clrscr(); printf("Virtual Control Program Interface Demo, © Frolov A.V., 1992\n\r" "-------------------------------------------------------------\n\r\n\r"); // Проверяем наличие драйвера EMS/VCPI if(ems_init()) { printf("Драйвер EMS/VCPI не загружен."); exit(-1); } printf("Драйвер EMS/VCPI загружен"); // Выводим номер версии драйвера if((err = ems_ver(&ver)) != 0) { printf("\nОшибка %02.2X при определении версии EMM", err); exit(-1); } printf("\nВерсия EMM: %02.2X", ver); // Определяем присутствие VCPI и его версию if(vcpi_ver(&ver_hi, &ver_lo) != 0) { printf("\nДрайвер EMM не поддерживает VCPI\n"); exit(-1); } printf("\nВерсия VCPI: %02.2X.%02.2X", ver_hi, ver_lo); } /** *.Name ems_init *.Title Функция проверяет установку драйвера EMS * *.Descr Эта функция проверяет наличие драйвера EMS * *.Proto int ems_init(void); * *.Params Не используются * *.Return 0 - драйвер EMS установлен; * 1 - драйвер EMS не установлен. * *.Sample ems_test.c **/ int ems_init(void) { void (_interrupt _far *EMS_driver_adr)(void); char _far *EMS_driver_name; char test_name[8]; int i; EMS_driver_adr = _dos_getvect(0x67); FP_SEG(EMS_driver_name) = FP_SEG (EMS_driver_adr); FP_OFF(EMS_driver_name) = 10; for(i=0; i<8; i++) test_name[i] = EMS_driver_name[i]; if(strncmp(test_name, "EMMXXXX0", 8) == 0) return(0); else return(1); } /** *.Name ems_ver *.Title Определение версии драйвера EMS * *.Descr Эта функция возвращает номер версии * драйвера EMS в двоично-десятичном формате. * *.Proto int ems_ver(char *ver); * *.Params char *ver - указатель на байт, в который * будет записан номер версии. * *.Return Номер версии драйвера EMS в формате BCD * *.Sample ems_test.c **/ int ems_ver(char *ver) { union REGS reg; reg.x.ax = 0x4600; int86(0x67, ®, ®); *ver = reg.h.al; return(reg.h.ah); } int vcpi_ver(char *ver_hi, char *ver_lo) { union REGS reg; reg.x.ax = 0xDE00; int86(0x67, ®, ®); *ver_hi = reg.h.bh; *ver_lo = reg.h.bl; return(reg.h.ah); } |