к содержанию

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.

ПодфункцияВыполняемые действия
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 более подробно.

Проверка наличия в системе интерфейса 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.

СмещениеРазмер и назначение
00hDWORD, значение для регистра CR3.
04hDWORD, линейный адрес в пределах первого мегабайта для загрузки регистра GDTR.
08hDWORD, линейный адрес в пределах первого мегабайта для загрузки регистра IDTR.
0ChWORD, значение для регистра LDTR.
0EhWORD, значение для регистра TR.
10hPWORD, значение адреса 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, &reg, &reg);

        *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, &reg, &reg);

        *ver_hi = reg.h.bh;
        *ver_lo = reg.h.bl;
        return(reg.h.ah);
}




к содержанию
Hosted by uCoz