к содержанию |
Во второй части второго тома "Библиотеки системного программиста" (глава 11) мы рассказывали вам о дополнительной памяти и об использовании для работы с ней спецификации EMS - Expanded Memory Specification.
Драйверы дополнительной памяти предоставляют программам интерфейс прерывания INT 67h, который мы тогда подробно описали. Мы также говорили о том, что для компьютеров на базе процессоров i80386 или i80486 существуют драйверы памяти, эмулирующие дополнительную память с использованием расширенной. Самые известные драйверы такого типа - EMM386.SYS и QEMM.SYS.
Эти драйверы используют защищённый (точнее, виртуальный) режим
работы процессора i80386 и страничную адресацию расширенной памяти.
Для прикладных программ предоставляется интерфейс, который называется
VCPI - Virtual Programm Control Interface. Этот интерфейс реализован
как подфункции функции DEh прерывания INT 67h:
Таблица 9. Функции интерфейса VCPI.
Подфункция | Выполняемые действия |
00 | Проверить наличие в системе интерфейса VCPI. |
01 | Получить адрес точки входа для работы с интерфейсом VCPI. |
02 | Определить максимальный физический адрес памяти. |
03 | Определить количество свободных страниц памяти размером 4 килобайта. |
04 | Получить страницу памяти. |
05 | Освободить страницу памяти. |
06 | Получить физический адрес страницы памяти, располагающейся в пределах первого мегабайта, т.е. в стандартной памяти. |
07 | Прочитать содержимое системного регистра CR0. |
08 | Прочитать содержимое отладочных регистров. |
09 | Установить отладочные регистры. |
0A | Получить отображение векторов прерываний, используемых контроллерами прерываний 8259. |
0B | Установить отображение векторов прерываний, используемых контроллерами прерываний 8259. |
0C | Переключить процессор из реального в защищённый режим, а также из защищённого в виртуальный режим. |
Перед вызовом прерывания 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 более подробно.
Регистры на входе: AX 0DE00h Регистры на выходе: AH равен 00h - если VCPI установлен, не равен 00h - если VCPI не установлен. BH Верхний (major) номер версии VCPI. BL Нижний (minor) номер версии 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 Физческий адрес страницы.
Регистры на входе: 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, игнорируются.
Регистры на входе: AX 0DE0Ah Регистры на выходе: AH равен 00h - успешное выполнение функции, не равен 00h - ошибка. BX Вектор прерывания, используемый для IRQ0. CX Вектор прерывания, используемый для IRQ8.
Регистры на входе: 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.
Смещение | Размер и назначение |
00h | DWORD, значение для регистра CR3. |
04h | DWORD, линейный адрес в пределах первого мегабайта для загрузки регистра GDTR. |
08h | DWORD, линейный адрес в пределах первого мегабайта для загрузки регистра IDTR. |
0Ch | WORD, значение для регистра LDTR. |
0Eh | WORD, значение для регистра TR. |
10h | PWORD, значение адреса CS:EIP точки входа в защищённый режим. |
Это переключение можно выполнить, если находясь в защищённом режиме вызвать точку интерфейса 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); }
к содержанию |