иерархия средств для работы в защищенном режиме

6. ИЕРАРХИЯ СРЕДСТВ ДЛЯ РАБОТЫ В ЗАЩИЩЁННОМ РЕЖИМЕ


6.1. Интерфейс BIOS
6.2. Интерфейс HIMEM.SYS
6.3. Интерфейс EMS/VCPI
6.4. Интерфейс DPMI
6.5. DOS-экстендеры

Вы уже, наверное, обратили внимание на то, что программирование для защищённого режима значительно сложнее, чем для реального, и требует учёта большого количества важных деталей. Даже для того, чтобы просто перевести процессор i80286 в защищённый режим, требуется провести основательную подготовительную работу. Процессоры i80386 и i80486 значительно сложнее, чем i80286. Использование всех возможностей этих процессоров доступно только высококвалифицированным системным программистам и требует подробного ознакомления с руководствами по процессорам, поставляемыми фирмой Intel.

К счастью, существует программное обеспечение, облегчающее процесс создания программ для защищённого режима. Это программное обеспечение выступает в качестве интерфейса между программой пользователя и некоторой управляющей программой (например, мультизадачной операционной системой защищённого режима).

Мы рассмотрим средства, доступные программистам в среде MS-DOS версии 5.0 и MS WINDOWS версий 3.0 и 3.1.

Существует несколько уровней программной поддержки защищённого режима и поддержки работы с расширенной памятью:
  • интерфейс BIOS;
  • интерфейс драйвера HIMEM.SYS;
  • интерфейс EMS/VCPI;
  • интерфейс DPMI;
  • расширители DOS (DOS-экстендеры).


Интерфейсом самого низкого уровня является интерфейс BIOS, предоставляемый программам в виде нескольких функций прерывания BIOS INT 15h. Интерфейс BIOS позволяет программе перевести процессор из реального режима в защищённый, переслать блок памяти из стандартной памяти в расширенную или из расширенной в стандартную. Этим все его возможности и ограничиваются. Интерфейс BIOS используется для старта мультизадачных операционных систем защищённого режима (таких, как OS/2) или в старых программах, работающих с расширенной памятью в защищённом режиме (например, СУБД ORACLE версии 5.1).

Назначение драйвера HIMEM.SYS и его возможности были подробно описаны во второй части второго тома "Библиотеки системного программиста" (глава 10). С помощью функций, предоставляемых этим драйвером, программа может выполнять различные действия с блоками расширенной памяти, а также управлять адресной линией A20. Основное различие между способом работы с расширенной памятью драйвера HIMEM.SYS и интерфейсом прерывания BIOS INT 15h заключается в том, что первый выполняет выделение программе и внутренний учёт блоков расширенной памяти, а второй рассматривает всю расширенную память как один непрерывный участок.

Если в системе установлен драйвер HIMEM.SYS, ваша программа не должна пользоваться прерыванием INT 15h во избежание конфликта со схемой распределения расширенной памяти, используемой драйвером. Однако драйвер HIMEM.SYS не открывает для программ доступ к защищённому режиму. Он полностью работает в реальном режиме, а для обращения к расширенной памяти использует либо недокументированную машинную команду LOADALL (если используется процессор 80286), либо возможности процессора 80386, который позволяет адресовать расширенную память в реальном режиме (при соответствующей инициализации системных регистров и таблиц).

В приложении мы описали действия, выполняемы командой LOADALL. Вы убедитесь, что команда полностью оправдывает своё название! (Load All - загрузить всё).

Следующий уровень - интерфейс EMS/VCPI. Во второй части второго тома "Библиотеки системного программиста", в главе 11, мы подробно рассматривали интерфейс EMS, который используется для работы с дополнительной памятью. Там же разъясняются отличия между расширенной и дополнительной памятью. Используя трансляцию страниц, некоторые драйверы памяти (например, EMM386 или QEMM) могут эмулировать присуствие дополнительной памяти, используя расширенную память. При этом стандартный набор функций управления дополнительной памятью, реализованный в рамках прерывания INT 67h, дополнен ешё несколькими функциями для работы в защищённом режиме процессора.

Эти новые функции реализуют интерфейс виртуальной управляющей программы VCPI (Virtual Control Programm Interface). Они позволяют устанавливать защищённый и виртуальный режимы работы процессора, работать с расширенной памятью на уровне страниц и устанавливать специальные отладочные регистры процессора i80386. Интерфейс VCPI облегчает использование механизма трансляции страниц, освобождая программиста от необходимости работать с системными регистрами процессора.

Интерфейс DPMI (DOS Protected Mode Interface - интерфейс защищённого режима для DOS) реализуется модулем, называющимся сервером DPMI. Этот интерфейс доступен для тех программ, которые работают на виртуальной машине WINDOWS или OS/2 версии 2.0 (позже мы обсудим некоторые детали, связанные с использованием интерфейса DPMI в WINDOWS).

Интерфейс DPMI предоставляет полный набор функций для создания однозадачных программ, работающих в защищённом режиме. В этом интерфейсе имеются функции для переключения из реального режима в защищённый и обратно (!), для работы с локальной таблицей дескрипторов LDT, для работы с расширенной и стандартной памятью на уровне страниц, для работы с прерываниями (в том числе для вызова прерываний реального режима из защищённого режима), для работы с отладочными регистрами процессора i80386. Это наиболее развитый интерфейс из всех рассмотренных ранее.

Последний, самый высокий уровень программной поддержки защищённого режима - расширители DOS или DOS-экстендеры (DOS-extender). Они поставляются, как правило, вместе со средствами разработки программ (трансляторами) в виде библиотек и компонуются вместе с создаваемой программой в единый загрузочный модуль.

DOS-экстендеры значительно облегчают использование защищённого режима и расширенной памяти в программах, предназначенных для запуска из среды MS-DOS. Программы, составленные с использованием DOS-экстендеров, внешне очень похожи на обычные программы MS-DOS, однако они получают управление, когда процессор уже находится в защищённом режиме.

К формируемому с помощью DOS-экстендера загрузочному модулю добавляются процедуры, необходимые для инициализации защищённого режима. Эти процедуры первыми получают управление и выполняют начальную инициализацию таблиц GDT, LDT, IDT, содержат обработчики прерываний и исключений, систему управления виртуальной памятью и т.д.

Пример DOS-экстендера, поставляемого вместе с транслятором - 386-DOS/Extender фирмы Phar Lap.

6.1. Интерфейс BIOS



Этот интерфейс реализуется в рамках прерывания BIOS INT 15h в компьютерах моделей IBM AT на основе процессоров i80286, i80386 или i80486.

Определить размер расширенной памяти

Регистры на входе: AH 88h Регистры на выходе: AX Размер доступной расширенной памяти в килобайтах.

Эта функция предназначена для определения размера расширенной памяти, доступной для использования функциями прерывания INT 15h.

Учтите, что если в системе установлен драйвер HIMEM.SYS, функция 88h может вернуть нулевой размер доступной расширенной памяти. Некоторые программы (например, СУБД Oracle версии 5.1) могут оказаться несовместимыми с драйвером HIMEM.SYS, так как они работают с расширенной памятью средствами прерывания INT 15h. Аналогичные проблемы могут возникнуть и при использовании других драйверов расширенной памяти, например, QEMM.

Как правило, драйверы расширенной памяти позволяют зарезервировать часть расширенной памяти для программ, использующих интерфейс INT 15h. Для этого необходимо задать соответствующие параметры. Например, для драйвера HIMEM.SYS размер зарезервированной расширенной памяти можно указать следующим образом: device=c:\dos\himem.sys /int15=xxxx

В этой строке "xxxx" - размер зарезервированной памяти в килобайтах.

Переслать блок расширенной памяти

Регистры на входе: AH 87h CX Размер пересылаемого блока в словах. ES:SI Адрес таблицы GDT, подготовленной специальным образом. Регистры на выходе: CARRY = 0 Функция выполнилась без ошибки. AX 00h В случае ошибки: CARRY = 1 Произошла ошибка при пересылке блока. AH Код ошибки: 01h - ошибка чётности; 02h - произошло исключение; 03h - сбой адресной линии A20.

Перед вызовом этой функции необходимо подготовить GDT, состоящую из 6 дескрипторов. Два первых и два последних дескриптора должны содержать нули. Третий дескриптор должен указывать на начало области памяти, из которой будет выполняться копирование. Четвёртый дескриптор должен указывать на область памяти, в которую будет выполняться копирование блока данных.

В третьем и четвёртом дескрипторе необходимо заполнить поля предела для копируемого блока памяти (в них должно быть записано значение CX*2 - 1), и поле доступа (значение 93):


Таблица 6. GDT для пересылки блока памяти средствами BIOS.

Смещение байтаСодержимое
00h - 0FhЭто поле должно содержать нули.
10h - 11hПредел сегмента (CX*2 -1).
12h - 14h24-разрядный физический адрес исходного блока памяти.
15hБайт доступа, должен быть равен 93h.
16h - 17hЭто поле должно содержать нули.
18h - 19hПредел сегмента (CX*2 -1).
1Ah - 1Ch24-разрядный физический адрес результирующего блока памяти.
1DhБайт доступа, должен быть равен 93h.
1Eh - 2FhЭто поле должно содержать нули.


Для пересылки блока функция 87h переводит процессор в защищённый режим, используя подготовленную таблицу GDT. Так как указываются 24-разрядные физические адреса исходного и результирующего блоков, возможна пересылка блоков из любого места памяти в любое место памяти. Размер блока, очевидно, ограничен 64 килобайтами.

Пересылка блока выполняется в защищённом режиме обычной командой MOVS, причём во время пересылки прерывания запрещены. Перед возвратом функция выполняет сброс процессора и установку реального режима.

Вся процедура пересылки оказывается достаточно длительной, так как необходимо выполнить сброс процессора. Из-за того, что во время пересылки прерывания запрещены, возможен конфликт с устройствами ввода/вывода (потеря прерываний).

Установить защищённый режим работы процессора



Функция переключает процессор из реального режима в защищённый. Кроме этого, она производит перепрограммирование контроллеров прерываний, необходимое из-за конфликта используемых в реальном режиме векторов аппаратных прерываний с зарезервированными прерываниями защищённого режима. Регистры на входе: AH 89h BH Номер прерывания для IRQ0, используется для перепрограммирования первого контроллера прерывания. Этот номер должен быть кратен 8. BL Номер прерывания для IRQ8, используется для перепрограммирования второго контроллера прерывания. Этот номер также должен быть кратен 8. ES:SI Адрес таблицы GDT, подготовленной специальным образом. Регистры на выходе: CARRY = 0 Функция выполнилась без ошибки. AH 00h CS, DS, ES, SS В эти регистры заносятся значения в соответствии с подготовленной перед вызовом функции таблицей GDT, адрес которой задаётся в регистрах ES:SI. В случае ошибки: CARRY = 1 Произошла ошибка при входе в защищённый режим. AH FF

Подготовленная перед вызовом функции 89h таблица GDT должна состоять из восьми дескрипторов:


Таблица 7. GDT для перехода в защищённый режим средствами BIOS.

0Пустой дескриптор, содержит нули во всех полях.
1Дескриптор, описывающий таблицу GDT.
2Дескриптор, описывающий таблицу IDT.
3Дескриптор для сегмента данных, сответствует селектору, который будет загружен в регистр DS.
4Дескриптор дополнительного сегмента данных (регистр ES).
5Дескриптор сегмента стека (регистр SS).
6Дескриптор сегмента кода (регистр CS).
7Этот дескриптор инициализировать не надо, он будет использоваться функцией 89h для адресации сегмента данных BIOS.


В рамках прерывания INT 15h нет функции для возврата из защищённого режима в реальный. Почему?

Потому, что во-первых, в защищённом режиме прерывание 15h зарезервировано фирмой Intel, во-вторых, для работы в защищённом режиме вами подготавливается таблица IDT и определяются заново все обработчики прерываний. Обработчики прерываний BIOS рассчитаны на работу в реальном режиме и после перехода в защищённый режим становятся недоступны.

Пример использования интерфейса BIOS



Наш пример демонстрирует использование функции 89h прерывания INT 15h для установки защищённого режима работы процессора. Программа устанавливает защищённый режим, выдаёт первое сообщение и через некоторое время, выдав второе сообещние, возвращается в реальный режим. После того, как будет нажата любая клавиша, работа программы будет завершена.

Обратите внимание на то, как в файле tos.c подготавливается таблица GDT. Адрес подготовленной таблицы передаётся функции protected_mode(), которая передаёт его функции 89h прерывания INT 15h. Вызов этой функции выполняется в файле tossyst.asm.

6.2. Интерфейс HIMEM.SYS



Как мы уже говорили, назначение драйвера HIMEM.SYS и его возможности были подробно описаны во второй части второго тома "Библиотеки системного программиста" (глава 10). Поэтому здесь мы не будем подробно рассматривать эти функции и говорить об их использовании, а приведём только краткий перечень:


Таблица 8. Функции XMS.

00hПолучить версию XMS (XMS - eXtended Memory Specification - спецификация расширенной памяти).
01hЗапросить управление областью старшей памятью HMA.
02hОсвободить область HMA.
03hГлобальное разрешение линии A20.
04hГлобальное запрещение линии A20.
05hЛокальное разрешение линии A20.
06hЛокальное запрещение линии A20.
07hОпределение текущего состояния линии A20.
08hОпределение размера свободной расширенной памяти.
09hПолучить блок расширенной памяти EMB.
0AhОсвободить блок EMB.
0BhКопирование блоков расширенной памяти EMB.
0ChБлокирование блока EMB. Для заблокированного EMB можно определить его физический адрес.
0DhРазблокирование EMB.
0EhПолучить информацию об индексе EMB.
0FhИзменить размер блока EMB.
10hЗапросить управление областью UMB.
11hОсвободить область UMB.


Для проверки наличия в системе драйвера, поддерживающего спецификацию XMS, необходимо загрузить в регистр AX значение 4300h и вызвать прерывание INT 2Fh. Если в регистре AL окажется значение 80h, драйвер установлен. В этом случае можно получить адрес управляющей программы, которую надо вызывать для выполнения функций. Если загрузить в регистр AX значение 4310h и вызвать прерывание INT 2Fh, в регистрах ES:BX будет записан искомый адрес.

Как можно заметить, функции XMS позволяют управлять линией A20 и копировать блоки расширенной памяти. Но вы не найдёте среди них функции для перехода в защищённый режим!

И это не случайно. Всё дело в том, что для работы с расширенной памятью драйвер HIMEM.SYS, реализующий спецификацию XMS, не использует защищённый режим работы процесора!

Как это может быть? Ведь процессор, находясь в реальном режиме не может адресовать память за границей первого мегабайта.

Секрет заключается в том, что для процессора i80286 драйвер HIMEM.SYS использует недокументированную машинную команду LOADALL. Эта команда предназначена для тестирования процессора и не приведена в документации на процессор i80286 (см. описание команды LOADALL в приложении).

С помощью этой команды можно выполнить загрузку всех регистров процессора, в том числе и некоторых недоступных программам. Используя нестандартную загрузку регистров, драйвер HIMEM.SYS может адресовать расширенную память, находясь в реальном режиме.

Такой способ адресации расширенной памяти значительно ускоряет процесс копирования по сравнению с использованием функции 87h прерывания INT 15h. Кроме того, во время копирования функцией 0Bh драйвера HIMEM.SYS прерывания остаются разрешёнными.

Процессор i80386 тоже имеет недокументированную команду LOADALL, которая отличается от имеющейся в процессоре i80286 и кодом, и выполняемыми действиями. Однако процессор i80386 способен адресовать расширенную память, находясь в реальном режиме (при соответствующей загрузке системных регистров, которая может быть выполнена с использованием только документированных команд).

В том, что на уровне интерфейса HIMEM.SYS не используется защищённый режим, заключена ещё одна причина, по которой мы не стали подробно рассматривать этот интерфейс в книге, посвящённой защищённому режиму.

Использование интерфейса HIMEM.SYS оправдано в тех случаях, когда для работы вашей программы требуется буфер памяти большого размера, который может быть размещён только в расширенной памяти. Однако программа не сможет непосредственно адресовать этот буфер и вынуждена выполнять операцию копирования данных из стандартной памяти в расширенную и обратно, что ведёт к непроизводительной потере времени.

В приложении описана утилита MEMOSCOP. Эта утилита выводит на экран список установленных в системе драйверов дополнительной памяти и доступных интерфейсов с защищённым режимом. В файле xmmc.asm находятся функции для работы с интерфейсом XMS. Этот файл и сами функции XMS были подробно описаны во втором томе "Библиотеки системного программиста".

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

6.4. Интерфейс DPMI



Для создания программ, работающих в защищённом режиме, фирмы Microsoft, Intel, IBM, Lotus, Phar Lap, Rational Systems, Borland, Quarterdeck разработали интерфейс с защищённым режимом - DPMI (DOS Protected Mode Interface). Пользуясь этим интерфейсом, программы, стартующие как обычные DOS-программы реального режима, могут переключиться в защищённый режим и работать с расширенной или даже виртуальной памятью. Интерфейс DPMI обеспечивается специальной программой, называемой сервером DPMI. Сервером может быть отдельная резидентная программа, либо средства для поддержки DPMI могут входить в состав операционной системы (например, WINDOWS и OS/2 версии 2.0 позволяют работающим под их управлением DOS-программам использовать интерфейс DPMI).

В этом разделе мы кратко опишем функции DPMI, а в разделе, посвященном виртуальной машине WINDOWS, приведём пример программы, работающей в защищённом режиме с использованием функций DPMI спецификации 0.9. Спецификация DPMI версии 0.9 поддерживается операционной системой WINDOWS версий 3.0 и 3.1, работающей в расширенном режиме на процессорах i80386 или i80486.

Все функции DPMI реализованы в рамках прерывания INT 31h, обработчиком этого прерывания является сервер DPMI. Прежде чем использовать функции DPMI, программа с помощью описанных ниже функций прерываний INT 2Fh должна убедиться в присутствии поддержки DPMI, получить адрес процедуры переключения в защищённый режим и выполнить это переключение.

Программа должна вызывать прерывание INT 31h после переключения в защищённый режим, в реальном режиме это прерывание вызывать нельзя.

Получить адрес перехода в защищённый режим



С помощью этой функции программа может определить наличие в системе интерфейса DPMI и получить адрес процедуры перехода в защищённый режим работы.

Для вызова этой функции необходимо использовать прерывание INT 2Fh, загрузив регистр AX следующим образом: Регистры на входе: AX 1687h

Если функция была успешно выполнена, в регистры будут записаны следующие значения: Регистры на выходе: AX 00h BX Если установлен бит 0, данная реализация DPMI поддерживает работу с 32-разрядными программами. CL Тип процессора: 02h = 80286 03h = 80386 04h = 80486 DH Верхний (major) номер версии используемой спецификации DPMI. DL Нижний (minor) номер версии используемой спецификации DPMI. SI Количество параграфов памяти, требуемых для личной области данных сервера DPMI. Программа должна заказать эту память у операционной системы перед переходом в защищённый режим. ES:DI Адрес процедуры, которую необходимо вызвать для входа в защищённый режим.

Если выполнение функции закончилось с ошибкой, содержимое регистра AX не равно нулю.

Вход в защищённый режим



После того, как программа получила адрес процедуры для входа в защищённый режим, она может вызвать эту процедуру при помощи команды CALL. Перед вызовом необходимо загрузить регистры: Регистры на входе: AX Разрядность программы. Если программа является 32-разрядной, в регистре AX необходимо установить бит 0 в единицу. ES В этот регистр необходимо загрузить сегментный адрес буфера, который будет использован сервером DPMI. Размер буфера должен быть определён при помощи предыдущей функции (регистр SI).

После загрузки регистров необходимо выполнить вызов процедуры с адресом, который был получен в регистрах ES:DI после вызова предыдущей функции.

Если функция выполнилась успешно, флаг CARRY сброшен и программа выполняется в защищённом режиме. Регистры на выходе CS Селектор, которому соответствует базовый адрес сегмента кода программы и предел 64 килобайта. SS Селектор для сегмента стека, базовый адрес соответствует стеку реального режима, предел - 64 килобайта. DS Селектор, соответствующий сегменту данных реального режима, предел - 64 килобайта. ES Селектор, указывающий на PSP программы с пределом 100h байт. FS, GS 0 (если программа работает на процессоре i80386 или i80486). ESP Если программа работает в 32-разрядном режиме, старшее слово регистра ESP будет равно 0.

Остальные регистры не изменяются.

Если функция выполнилась с ошибкой, флаг CARRY устанавливается в единицу и программа продолжает выполнение в реальном режиме.

После входа в защищённый режим вам становится доступен интерфейс DPMI через функции прерывания INT 31h.

Для завершения своей работы программа, использующая DPMI, должна выдать прерывание INT 21h (функция 4Ch) - это обычный способ завершения программ, работающих в среде MS-DOS.

Определение текущего режима работы



Если ваша программа может работать и в реальном режиме и в защищённом, с помощью этой функции вы можете определить текущий режим работы.

Для выполнения функции необходимо вызвать прерывание INT 2Fh, предварительно загрузив регистр AX: Регистры на входе: AX 1686h Регистры на выходе: AX 0, если программа работает в защищённом режиме под управлением DPMI, не равно 0, если программа работает в реальном или виртуальном режиме.

Создать дескриптор в таблице LDT



С помощью этой функции программа может создать один или несколько дескрипторов в LDT, принадлежащей задаче, в рамках которой работает программа, т.е. для своей задачи.

Вся работа по инициализации полученных дескрипторов должна выполняться самой программой, для чего в интерфейсе DPMI имеются соответствующие функции. Регистры на входе: AX 0000h CX Количество создаваемых дескрипторов. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. AX Базовый селектор.

Если программа заказывала несколько селекторов, AX содержит первый селектор из созданного массива. Для получения остальных селекторов необходимо воспользоваться функцией 00003h.

Освободить дескриптор в таблице LDT



Функция используется для освобождения дескрипторов, созданных в LDT предыдущей функцией. Регистры на входе AX 0001h BX Селектор освобождаемого дескриптора. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Для освобождения массива дескрипторов функцию необходимо вызывать отдельно для каждого дескриптора.

Преобразование сегмента в дескриптор



Функция используется для преобразования сегментной компоненты адреса реального режима в дескриптор. Регистры на входе AX 0002h BX Сегментный адрес реального режима. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. AX Селектор, соответствующий дескриптору, созданному для адресации сегмента реального режима.

Дескриптор, созданный данной функцией, не может быть освобождён, поэтому эту функцию имеет смысл использовать для адресации таких областей памяти, которые будут нужны на всём протяжении работы программы (область данных BIOS, память видеоконтроллера и т.д.).

Получить значение инкремента для вычисления следующего селектора в массиве



Если вы создали несколько дескрипторов в LDT при помощи функции 0000h, то этой функцией будет возвращён только первый селектор, соответствующий первому дескриптору в созданном массиве. Для вычисления остальных селекторов ваша программа должна использовать функцию 0003h. Эта функция возвращает значение, которое вы будете прибавлять к базовому селектору для вычисления остальных селекторов в массиве. Регистры на входе AX 0003h Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. AX Значение для инкремента базового селектора.

Зарезервированные функции



Функции 0004h и 0005h в спецификации DPMI версии 0.9 зарезервированы и не должны вызываться из вашей программы.

Получить базовый адрес сегмента



С помощью этой функции ваша программа может определить 32-разрядный базовый адрес сегмента по его селектору. Регистры на входе AX 0006h BX Селектор сегмента, для которого требуется получить базовый адрес. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. CX:DX 32-разрядный базовый адрес сегмента.

Установить базовый адрес сегмента



Эта функция устанавливает базовый адрес сегмента в дескрипторе, соответствующем заданному селектору. Вы можете модифицировать только те дескрипторы, которые создала ваша программа. Регистры на входе AX 0007h BX Селектор сегмента, для которого требуется установить базовый адрес. CX:DX Устанавливаемый 32-разрядный линейный базовый адрес сегмента. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Установить предел сегмента



Эта функция устанавливает предел сегмента в дескрипторе, соответствующем заданному селектору. Регистры на входе AX 0008h BX Селектор сегмента, для которого требуется установить новое значение предела. CX:DX Устанавливаемое 32-разрядное значение предела сегмента. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Установить поле прав доступа в дескрипторе



При помощи этой функции вы можете модифицироать поле прав доступа в дескрипторе, созданного вашей программой. Регистры на входе AX 0009h BX Селектор сегмента, для которого требуется установить новые права доступа. CL Байт прав доступа. CH Расширение байта прав доступа для процессора i80386/i80486, используется только 32-разрядными реализациями DPMI. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

В расширении байта прав доступа старший бит (бит 7) - бит гранулярности, который определяет интерпретацию поля предела (0 - предел в байтах, 1 - предел в страницах). Бит 6 указывает режим работы процессора (0 - 16-разрядный, 1 - 32-разрядный). Бит 5 и 2-3 должны быть равны 0. Бит 4 может содержать любое значение.

Создание алиасного дескриптора для сегмента кода



Так как в защищённом режиме модификация сегмента кода с использованием селектора CS невозможна, для решения такой задачи необходимо создать дескриптор сегмента данных, который бы указывал на сегмент кода (т.е. имел бы такой же базовый адрес и предел). Такой дескриптор является алиасом дескриптору сегмента кода и может быть создан при помощи функции 000Ah.

Если алиас больше не нужен, он может быть уничтожен при помощи функции 0001h. Регистры на входе AX 000Ah BX Селектор сегмента кода, для которого требуется создать алиас. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Получить дескриптор



С помощью этой функции программа может скопировать в буфер размером 8 байт дескриптор, соответствующий заданному селектору. Регистры на входе AX 000Bh BX Селектор сегмента, для которого требуется получить дескриптор. ES:(E)DI Указатель на буфер размером 8 байт, в который будет скопирован дескриптор. Для 32-разрядных программ необходимо использовать регистр EDI. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Установить дескриптор



Функция выполняет действия, обратные предыдущей - копирует содержимое 8-байтового буфера в дескриптор, заданный селектором. Регистры на входе AX 000Ch BX Селектор сегмента, для которого требуется установить дескриптор. ES:(E)DI Указатель на буфер размером 8 байт, из которого будет взята информация для установки дескриптора. Для 32-разрядных программ необходимо использовать регистр EDI. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Программа может модифицировать только дескрипторы из своей локальной таблицы LDT, созданные там с помощью функции 0000h.

Получить конкретный дескриптор в таблице LDT



Функция позволяет получить дескриптор по его селектору. Регистры на входе AX 000Dh BX Селектор сегмента, для которого требуется получить дескриптор. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Для этой функции сервер DPMI резервирует первые 16 дескрипторов в таблице LDT. Программа может освободить полученные с помощью этой функции дескрипторв при помощи функции 0001h.

Получить блок памяти из пула свободной памяти DOS



С помощью этой функции программа может получить от DOS блок памяти, лежащий в пределах первого мегабайта адресного пространства. Для полученного блока памяти функция создаёт один или несколько дескрипторов (в зависимости от размера блока) и возвращает как сегмент блока для реального режима, так и базовый селектор для доступа к блоку в защищённом режиме.

Если размер запрошенного блока превышает 64 килобайта, функция создаст массив дескрипторов и возвратит базовый селектор. Для получения остальных селекторов вы сможете использовать значение инкремента, полученное после вызова функции 0003h.

Дескрипторы, созданные этой функцией, можно освобождать только с помощью функции 0101h. Регистры на входе AX 0100h BX Размер блока в параграфах (напомним, что размер параграфа составляет 16 байт). Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. В случае ошибки регистр AX содержит код ошибки, полученный от DOS: 07h - разрушен блок MCB; 08h - слишком большой размер заказанного блока. В последнем случае регистр BX содержит максимально возможный размер блока в параграфах. AX Сегмент реального режима полученного блока. DX Селектор для доступа к полученному блоку в защищённом режиме.

Освободить блок памяти, взятый из пула DOS



Функция предназначена для освобождения памяти, полученной при помощи предыдущей функции. Регистры на входе AX 0101h DX Селектор освобождаемого блока. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. В случае ошибки регистр AX содержит код ошибки, полученный от DOS: 07h - разрушен блок MCB; 09h - неправильное задание сегментного адреса освобождаемого блока памяти.

Изменить размер блока памяти, полученного из пула DOS



С помощью этой функции программа может увеличить или уменьшить размер блока памяти, полученного функцией 0100h. Регистры на входе AX 0102h BX Новый размер блока памяти в параграфах. DX Селектор модифицируемого блока. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. В случае ошибки регистр AX содержит код ошибки, полученный от DOS: 07h - разрушен блок MCB; 08h - слишком большой размер заказанного блока. В этом случае регистр BX содержит максимально возможный размер блока в параграфах. 09h - неправильное задание сегментного адреса модифицируемого блока памяти.

Получить вектор прерывания реального режима



Функция возвращает для заданного вектора прерывания адрес Функция возвращает для заданного вектора прерывания адрес его обработчика в формате реального ржима. Регистры на входе AX 0200h BL Номер прерывания. Регистры на выходе: CARRY 0 CX:DX Адрес обработчика прерывания в формате <сегмент:смещение>.

Установить вектор прерывания реального режима



Функция позволяет задать для любого вектора прерывания адрес обработчика в формате реального режима. Регистры на входе AX 0201h BL Номер прерывания. CX:DX Адрес обработчика прерывания в формате <сегмент:смещение>. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Получить адрес обработчика исключения с заданным номером



Функция возвращает адрес текущего обработчика исключения с заданным номером в формате защищённого режима. Регистры на входе AX 0202h BL Номер исключения (00h - 1Fh). Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. CX:(E)DX Адрес обработчика исключения в формате <селектор:смещение>.

Установить адрес обработчика исключения с заданным номером



Функция может быть использована для установки собственного обработчика исключений. Регистры на входе AX 0203h BL Номер исключения (00h - 1Fh). CX:(E)DX Адрес обработчика исключения в формате <селектор:смещение>. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Получить вектор прерывания защищённого режима



Функция возвращает адрес обработчика заданного прерывания в формате защищённого режима. Регистры на входе AX 0204h BL Номер прерывания. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. CX:(E)DX Адрес обработчика прерывания в формате <селектор:смещение>.

Установить вектор прерывания защищённого режима



С помощью этой функции программа может установить собственный обработчик защищённого режима для любого прерывания. Регистры на входе AX 0205h BL Номер прерывания. CX:(E)DX Адрес обработчика прерывания в формате <селектор:смещение>. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Эмуляция прерывания реального режима



Эта функция предназначена для эмуляции выполнения прерывания в реальном режиме. С её помощью вы сможете находясь в защищённом режиме обращаться к функциям BIOS, DOS и к другим прерываниям реального режима. Регистры на входе AX 0300h BL Номер прерывания. BH Байт флагов. Если установлен в единицу бит 0, выполняется сброс контроллера и линии A20. Остальные биты зарезервированы и должны быть сброшены в нуль. CX Количество слов, которые должны быть скопированы из стека защищённого режима в стек реального режима. ES:(E)DI Адрес управляющей структуры для вызова прерывания. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. ES:(E)DI Адрес модифицированной (в результате выполнения обработчика прерывания) управляющей структуры в формате защищённого режима.

Управляющая структура содержит значения для инициализации регистров перед вызовом эмулируемого прерывания реального режима. Она имеет следующий формат:


Таблица 11. Формат управляющей структуры для эмуляции прерывания реального режима средствами DPMI.

СмещениеРегистр
00hEDI
04hESI
08hEBP
0ChЗарезервировано
10hEBX
14hEDX
18hECX
1ChEAX
20hFLAGS
22hES
24hDS
26hFS
28hGS
2AhIP
2ChCS
2EhSP
30hSS


Ваша программа, находясь в защищённом режиме, может не только вызвать прерывание реального режима, но и передать параметры через стек, указав в управляющей структуре их количество. Наример: push Parametr1 push Parametr2 push Parametr3 mov cx, 3 ; Копируем три параметра mov ax, 0301h int 31h add sp, 6 ; Восстанавливаем стек

Обработчик прерывания реального режима получит стек, подготовленный следующим образом: Parametr1 Parametr2 Parametr3 CS для возврата IP для возврата

Указатель стека реального режима SS:SP установлен на слово, содержащее IP для возврата.

Вызов процедуры реального режима, заканчивающейся командой RET FAR



Эта процедура позволяет выполнить вызов процедуры, предназначенной для работы в реальном режиме и возвращающей управление при помощи команды RET FAR. Регистры на входе AX 0301h BH Байт флагов. Если установлен в единицу бит 0, выполняется сброс контроллера и линии A20. Остальные биты зарезервированы и должны быть сброшены в нуль. CX Количество слов, которые должны быть скопированы из стека защищённого режима в стек реального режима. ES:(E)DI Адрес управляющей структуры для вызова процедуры реального режима. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. ES:(E)DI Адрес модифицированной (в результате выполнения процедуры реального режима) управляющей структуры в формате защищённого режима.

Вызов процедуры реального режима, заканчивающейся командой IRET



Эта процедура позволяет выполнить вызов процедуры, предназначенной для работы в реальном режиме и возвращающей управление при помощи команды IRET. Регистры на входе AX 0302h BH Байт флагов. Если установлен в единицу бит 0, выполняется сброс контроллера и линии A20. Остальные биты зарезервированы и должны быть сброшены в нуль. CX Количество слов, которые должны быть скопированы из стека защищённого режима в стек реального режима. ES:(E)DI Адрес управляющей структуры для вызова процедуры реального режима. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. ES:(E)DI Адрес модифицированной (в результате выполнения процедуры реального режима) управляющей структуры в формате защищённого режима.

Перехват передачи управления программе реального режима



Эта функция может быть использована для перехвата передачи управления процедуре реального режима.

Например, драйвер мыши выполняет вызов процедуры реального режима при перемещении мыши. Программа может сама задавать адрес реальной процедуры, которая будет получать управление при перемещении мыши или при нажатии на её кнопки. С помощью данной функции программы защищённого режима могут перехватить передачу управления этой процедуры и заменить её на другую процедуру, работающую в защищённом режиме. Регистры на входе AX 0303h DS:(E)SI Адрес процедуры в формате защищённого режима, которая будет вызвана вместо перехваченной. ES:(E)DI Адрес управляющей структуры для вызова процедуры реального режима (в формате защищённого режима). Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. ES:(E)DI Адрес модифицированной (в результате выполнения процедуры реального режима) управляющей структуры в формате защищённого режима. CX:DX Адрес вызова перехватываемой программы в формате реального режима.

Процедура защищённого режима, перехватывающая управление, вызывается с запрещёнными прерываниями и получает следующие параметры:


DS:(E)SIАдрес стека реального режима в формате <селектор:смещение>.
ES:(E)DIАдрес управляющей структуры в формате <селектор:смещение>.
SS:(E)SPСтек защищённого режима.


Остальные регистры остаются в неопределённом состоянии.

Для выполнения возврата из процедуры перехвата необходимо выполнить команду IRET, предварительно загрузив регистры следующим образом: ES:(E)DI Адрес управляющей структуры в формате <селектор:смещение>.

Отмена перехвата передачи управления процедуре реального режима.



Эта функция отменяет действие предыдущей. Регистры на входе AX 0304h CX:DX Адрес вызова перехватываемой программы в формате реального режима, для которой отменяется перехват. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Получить адреса процедур сохранения/восстановления состояния.



С помощью этой функции программа может сохранять или восстанавливать состояние задачи (в том числе содержимое всех регистров процессора). это бывает необходимо перед изменением реального режима работы на защищённый и обратно. Регистры на входе AX 0305h Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. AX Размер буфера для сохранения состояния. BX:CX Адрес программы сохранения/восстановления для реального режима в формате <сегмент:смещение>. ES:(E)DI Адрес программы сохранения/восстановления для защищённого режима в формате <селектор:смещение>.

Процедура сохранения/восстановления состояния вызывается командой CALL FAR и получает следующие параметры: ES:(E)DI Указатель на буфер сохранения состояния. AL 0 - для сохранения состояния, 1 - для восстановления состояния.

Получить адрес процедуры переключения режима



С помощью этой функции программа может получить адреса процедур для переключения из реального режима в защищённый и из защищённого в реальный. Регистры на входе AX 0306h Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. BX:CX Адрес программы переключения из реального режима в защищённый в формате <сегмент:смещение>. SI:(E)DI Адрес программы для переключения из защищённого режима в реальный в формате <селектор:смещение>.

Перед переключением режима, которое выполняется командой JMP FAR, необходимо подготовить регистры:


AXНовое содержимое регистра DS.
CXНовое содержимое регистра ES
DXНовое содержимое регистра SS
(E)BXНовое содержимое регистра (E)SP
SIНовое содержимое регистра CS
(E)DIНовое содержимое регистра (E)IP


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

Для процессоров i80386 и i80486 после переключения режима в регситры FS и GS бует записано нулевое значение.

Получить версию спецификации DPMI



Функция позволяет получить текущую версию DPMI. Регистры на входе AX 0400h Регистры на выходе: CARRY 0 AH Верхний (major) номер версии. AL Нижний (minor) номер версии. BX Байт флагов: бит 0 = 1 если программа работает под управлением DPMI для процессора i80386; бит 1 = 1 если процессор вернулся в реальный режим для обработки прерывания; бит 2 = 1 если в системе поддерживается виртуальная память; бит 3 и все остальные зарезервированы для использования в будущем. CL Тип процессора: 02 = i80286 03 = i80386 04 = i80486 DH Текущее значение номера прерывания для IRQ0. DL Текущее значение номера прерывания для IRQ8.

Получить информацию о свободной памяти

Регистры на входе AX 0500h ES:(E)DI Адрес бфера размером 30h байт в формате <селектор:смещение>. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. ES:(E)DI Адрес бфера размером 30h байт в формате <селектор:смещение>, заполненного информацией о свободной памяти.

Формат буфера:


Таблица 12. Формат буфера для информации о свободной памяти, получаемой средствами DPMI.

СмещениеОписание
00hРазмер наибольшего доступного свободного блока в байтах
04hМаксимальное количество доступных незаблокированных страниц памяти.
08hМаксимальное количество доступных заблокированных страниц памяти.
0ChРазмер линейного адресного пространства в страницах.
10hОбщее количество незаблокированных страниц.
14hКоличество свободных страниц.
18hОбщее количество физических страниц.
1ChРазмер свободного линейного адресного пространства в страницах.
20hРазмер страничного файла или раздела в страницах.
24h-2FhЗарезервировано.


Если текущая реализация DPMI не поддерживает виртуальную память, данная функция заполняет только первое поле структуры. Остальные поля устанавливаются в -1 (0FFFFFFFFh).

Получить блок памяти



Эта функция предназначена для получения программой участка линейной памяти. Для полученного блока памяти не выполняется автоматического создания селектора, программа должна создать селектор самостоятельно. Регистры на входе AX 0501h BX:CX Размер блока памяти в байтах. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. BX:CX Линейный адрес полученного блока. SI:DI Индекс полученного блока памяти, нужен для выполнения операций с блоком памяти (изменение его размера или освобождение).

Освободить блок памяти



Функция освобождает блок памяти, полученный при помощи предыдущей функции. Регистры на входе AX 0502h SI:DI Индекс освобождаемого блока памяти. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Изменить размер блока памяти



Функция изменяет размер блока памяти, полученного при помощи функции 0501h. Регистры на входе AX 0503h BX:CX Новый размер блока памяти в байтах. SI:DI Индекс изменяемого блока памяти. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. BX:CX Новый линейный адрес полученного блока. SI:DI Новый индекс блока памяти.

Зафиксировать линейную область памяти



Эта функция фиксирует в памяти область, задаваемую линейным адресом. Для зафиксированной области памяти не выполняется свопинг страниц. Регистры на входе AX 0600h BX:CX Начальный линейный адрес фиксируемого участка памяти. SI:DI Размер фиксируемого блока памяти в байтах. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Расфиксирование блока памяти



Эта функция должна быть использована для расфиксирования блока памяти, зафиксированного предыдущей функцией. Регистры на входе AX 0601h BX:CX Начальный линейный адрес расфиксируемого участка памяти. SI:DI Размер расфиксируемого блока памяти в байтах. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Отмена фиксации страниц для области памяти виртуального режима



Область памяти, используемая виртуальной машиной, обычно зафиксирована и не подвергается свопингу. С помощью этой функции вы можете разрешить свопинг для заданного участка памяти, принадлежащей виртуальной машине. Регистры на входе AX 0602h BX:CX Начальный линейный адрес расфиксируемого участка памяти. SI:DI Размер расфиксируемого блока памяти в байтах. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Фиксация страниц для области памяти виртуального режима



Функция предназначена для отмены действия предыдущей функции. Она фиксирует участок памяти виртуальной машины, отменяя для него свопинг страниц. Регистры на входе AX 0603h BX:CX Начальный линейный адрес фиксируемого участка памяти. SI:DI Размер фиксируемого блока памяти в байтах. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Получить размер страницы памяти



Функция возвращает размер страницы памяти в байтах. Регистры на входе AX 0604h Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. BX:CX Размер страницы памяти в байтах.

Зарезервированные функции



Функции 0700h и 0701h зарезервированы и не должны вызываться вашей программой.

Отметка страницы для свопинга



Функция используется для того, чтобы сообщить операционной системе о возможности выгрузки (свопинга) на диск указанной страницы или диапазона страниц. Регистры на входе AX 0702h BX:CX Начальный линейный адрес отмечаемых страниц. SI:DI Размер отмечаемого блока памяти. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Отвергнуть содержимое страниц



Функция отмечает страницы как не содержащие полезной информации. Операционная система может использовать данные страницы для удовлетворения запросов на память. Регистры на входе AX 0703h BX:CX Начальный линейный адрес страниц, отмечаемых как не содержащие полезной информации. SI:DI Размер отмечаемого блока памяти в байтах. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Преобразование физического адреса в линейный



Функция может быть использована для работы с периферийными устройствами, адресное пространство ввода/вывода которых отображается в диапазон физических адресов. Регистры на входе AX 0800h BX:CX Физический адрес памяти. SI:DI Размер блока памяти в байтах. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. BX:CX Линейный адрес памяти.

Запретить виртуальные прерывания



Функция сбрасывает флаг виртуального прерывания и возвращает предыдущее состояние этого флага. Регистры на входе AX 0900h Регистры на выходе: CARRY 0 AL 0, если виртуальные прерывания были запрещены, 1, если виртуальные прерывания были разрешены.

Разрешить виртуальные прерывания.



Функция устанавливает флаг виртуальных прерываний, разрешая виртуальные прерывания, и возвращает предыдущее сосотояние этого флага. Регистры на входе AX 0901h Регистры на выходе: CARRY 0 AL 0, если виртуальные прерывания были запрещены, 1, если виртуальные прерывания были разрешены.

Получить состояние флага виртуальных прерываний



Функция позволяет узнать текущее состояние флага виртуальных прерываний. Регистры на входе AX 0902h Регистры на выходе: CARRY 0 AL 0, если виртуальные прерывания запрещены, 1, если виртуальные прерывания разрешены.

Получить адрес для использования расширений DPMI



DOS-экстендеры могут расширять сервис DPMI своими функциями. Для получения доступа к этим функциям можно использовать функцию 0A00h. Вызывающая программа должна задать в регистрах DS:(E)SI адрес строки, закрытой нулём. Строка должна содержать название производителя DOS-экстендера или другой уникальный идентификатор расширения. Регистры на входе AX 0A00h DS:(E)SI Указатель на строку, закрытую нулём. Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. ES:(E)DI Точка входа для вызова расширений

Содержимое регистров DS, FS, GS, EAX, EBX, ECX, EDX, ESI, и EBP не сохраняется.

Установить точку останова для отладки



Функция позволяет установить отладочную точку останова по заданному линейному адресу. Регистры на входе AX 0B00h BX:CX Линейный адрес точки останова. DL Размер используемого для точки останова операнда (1, 2, или 4 байта). DH Тип точки останова 0 = Выполнение 1 = Запись 2 = Чтение/запись Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. BX Индекс для доступа к отладочной точке останова

Отмена отладочной точки останова



Функция отменяет точку останова, установленную при помощи предыдущей функции. Регистры на входе AX 0B01h BX Индекс отладочной точки останова Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

Получить состояние отладочной точки останова



Функция возвращает состояние отладочной точки останова, определённой при помощи функции 0B00h. Регистры на входе AX 0B02h BX Индекс отладочной точки останова Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка. AX Флаги: Бит 0 = 1 если произошло выполнение точки останова.

Сброс точки останова



Функция сбрасывает состояние для заданной отладочной точки останова. Регистры на входе AX 0B03h BX Индекс отладочной точки останова Регистры на выходе: CARRY 0, если функция выполнилась без ошибки, 1, если произошла ошибка.

6.5. DOS-экстендеры



Вы уже наверное заметили, что все программы, приведённые в этой книге, не поддаются отладке с помощью обычных отладчиков, таких как Borland Turbo Debugger или Microsoft Code View. Эти отладчики зависают уже на этапе загрузки регистра дескрипторной таблицы прерываний IDTR, до перехода в защищённый режим дело так и не доходит. И это понятно - такие отладчики рассчитаны на обычные программы реального режима.

Другая трудность связана с тем, что программе, работающей в защищённом режиме, недоступны прерывания MS-DOS и BIOS. Программа должна работать с аппаратурой на уровне регистров и аппаратных прерываний.

Для облегчения разработки программ, стартующих из MS-DOS в реальном режиме и переключающихся в защищённый режим разработаны специальные средства, называемые DOS-экстендерами или расширителями DOS.

Эти средства прикомпоновывают к разрабатываемой программе специальный модуль, реализующий интерфейс с защищённым режимом. Программа, созданная с использованием DOS-экстендера, загружается специальным загрузчиком в память и получает управление, когда процессор уже находится в защищённом режиме. Всю работу по переводу процессора в защищённый режим и по обработке прерываний в защищённом режиме берёт на себя экстендер.

Кроме того, DOS-экстендер предоставляет программе возможность обращения к прерываниям MS-DOS и BIOS! В своих программах, работающих в защищённом режиме под управлением DOS-экстендера вы можете пользоваться привычными вам функциями MS-DOS и BIOS (правда, не всеми, а только документированными). Возможности этих функций возрастут. Например, с помощью функции DOS можно будет заказать для программы буфер размером в несколько мегабайт.

Модуль DOS-экстендера, прикомпонованный к программе, может использовать интерфейсы DPMI, VCPI, XMS (драйвер HIMEM.SYS), INT 15h, или может использовать собственную схему управления памятью в защищённом режиме и собственные средства переключения режима процессора или состояния адресной линии A20. В спецификации DPMI приведены рекомендации для разработчиков DOS-экстендеров по использованию перечисленных выше интерфейсов. DOS-экстендер должен проверять наличие интерфейсов и по возможности использовать более высокоуровневый интерфейс. Проверка должна выполняться в следующем порядке:
  • DPMI
  • EMS/VCPI
  • XMS
  • Функции прерывания INT 15h


К сожалению, не все поставляющиеся DOS-экстендеры следуют этим рекомендациям. В результате могут возникнуть проблемы при попытке запустить разработанную с помощью DOS-экстендера программу на виртуальной машине WINDOWS в режиме "Enhanced 386 Mode" или в MS-DOS при установленных драйверах EMM386 или QEMM.

Необходимость обеспечения совместимости с интерфейсами DPMI, VCPI и XMS требует тщательного выбора DOS-экстендера. Так как в настоящее время операционная система WINDOWS находится в состоянии взрывообразного распространения среди пользователей персональных компьютеров, неудачный выбор DOS-экстендера может привести к тому, что огромное количество потенциальных покупателей не смогут использовать вашу программу в среде WINDOWS. То же относится и к значительному количеству пользователей дрйверов расширенной памяти EMM386 и QEMM. В документации на DOS-экстендер должно содержаться подтверждение совместимости с интерфейсами DPMI, VCPI и XMS. Такой DOS-экстендер будет совместим с современными операционными системами и драйверами расширенной памяти.

Примерами программ, созданных с использованием несовместимых с DPMI интерфейсом служат СУБД ORACLE версии 5.1 и FOXPRO версии 2.0. Эти программные продукты не будут работать на виртуальной машине WINDOWS в режиме "Enhanced 386 Mode".

Исходные тексты программ, составленных для DOS-экстендеров, внешне очень похожи на тексты программ реального режима. В них отсутствуют строки, специфические для программ, рассмотренных нами ранее и выполняющие переключение в защищённый режим, либо подготовку системных таблиц, либо перепрограммирование контроллера прерывания.

DOS-экстендеры поставляются в комплекте с трансляторами, редакторами связей, отладчиками и библиотеками стандартных функций (например, библиотеками для транслятора языка Си). Поэтому создание и отладка программ защищённого режима, созданных с использованием DOS-экстендеров, обычно не вызывает затруднений.

Мы кратко рассмотрим возможности двух DOS-экстендеров: 386-DOS/Extender фирмы Phar Lap и виртуальную машину операционной системы WINDOWS в режиме "Enhanced 386 Mode".

Phar Lap DOS-экстендер



В состав Phar Lap DOS-экстендера входят транслятор для языка Си hc386.exe, ассемблер 386asm.exe, редактор связей 386link.exe, отладчик minibug.exe и программа загрузки run386.exe.

С помощью транслятора языка Си или ассемблера получаются объектные модули, которые компонуются редактором связей 386link.exe в загрузочный модуль. Этот загрузочный модуль имеет расширение "exp" и запускается при помощи программы загрузки run386.exe. Полученный загрузочный модуль может работать только на процессорах i80386 или i80486. Версия 2.2 Phar Lap DOS-экстендера не поддерживает интерфейс DPMI, поэтому разработанные с использованием экстендера этой версии программы не будут работать на виртуальной машине WINDOWS в режиме "Enhanced 386 Mode".

Phar Lap DOS-экстендер предоставляет программе, которая получает управление сразу в защищённом режиме, возможность использовать документированные прерывания MS-DOS и BIOS. Кроме того, в рамках прерывания INT 21h DOS-экстендером реализуются дополнительные функции, связанPhar Lap DOS-экстендер предоставляет программе, которая получает управление сразу в защищённом режиме, возможность использовать документированные прерывания MS-DOS и BIOS. Кроме того, в рамках прерывания INT 21h DOS-экстендером реализуются дополнительные функции, связанные с работой в защищённом режиме.

Для того, чтобы у вас было представление о возможностях Phar Lap DOS-экстендера, приведём таблицу дополнительных функций, реализованных в рамках прерывания INT 21h:


Таблица 13. Функции Phar Lap DOS-экстендера.

Регистр AXВыполняемая функция
2501hУстановка в исходное состояние структур данных DOS-экстендера.
2502hПолучить вектор прерывания защищённого режима.
2503hПолучить вектор прерывания реального режима.
2504hУстановить вектор прерывания защищённого режима.
2505hУстановить вектор прерывания реального режима.
2506hУстановить режим, при котором прерывание будет всегда обрабатываться в защищённом режиме.
2507hУстановить вектора прерываний реального и защищённого режима.
2508hУстановить линейный базовый адрес сегмента.
2509hПреобразовать линейный адрес в физический
250AhОтобразить физическую память в конце сегмента.
250ChПолучить вектора аппаратных прерываний.
250DhПолучить информацию связи с реальным режимом.
250EhВызвать процедуру реального режима.
250FhПреобразовать адрес защищённого режима в адрес реального режима.
2510hВызвать процедуру реального режима с заданным содержимым регистров.
2511hВызвать прерывание реального режима с заданным содержимым регистров.
2512hЗагрузить программу для отладки.
2513hСоздать алиасный дескриптор сегмента (т.е. для заданного дескриптора создаётся ещё один, указывающий на тот же сегмент).
2514hИзменить атрибуты сегмента.
2515hПолучить атрибуты сегмента.
2516hОсвободить всю память, распределённую при помощи LDT.
2517hПолучить информацию о буферах данных DOS.
2518hОпределить драйвер для обработки перемещения сегмента.
2519hПолучить дополнительную информацию об ошибке памяти.
251AhЗафиксировать страницы в памяти.
251BhРасфиксировать страницы.
251ChОсвободить страницы физической памяти.
251DhПрочитать элемент таблицы страниц.
251EhЗаписать элемент таблицы страниц.
251FhОбменять элементы таблицы страниц.
2520hПолучить статистическую информацию о памяти.
2521hМаксимальный размер доступной программам расширенной памяти.
2522hОпределить альтернативный драйвер, обрабатывающей ситуацию отсутствия страницы в памяти.
2525hМаксимальный размер доступной программам стандартной памяти.
25C0hПолучить блок стандартной памяти MS-DOS.
25C1hОсвободить блок стандартной памяти MS-DOS.
25C2hИзменить размер блока стандартной памяти MS-DOS.
25C3hВыполнить программу.


Сравните это с функциями интерфейса DPMI, вы увидите что между этими интерфейсами есть много общего. Есть специальная функция, предназначенная для отладчиков - "Загрузить программу для отладки".

Программе доступны селекторы, облегчающие работу с наиболее часто используемыми областями данных. Например, таблица LDT содержит следующие селекторы, готовые для использования:


Таблица 14. Таблица LDT Phar Lap DOS-экстендера.

0008hСегмент кода программы.
0010hСегмент данных программы.
0018hСегмент видеопамяти для работы в текстовом режиме.
0020hPSP программы.
0028hСегмент строк среды DOS (DOS environment).
0030hСегмент данных для доступа к первому мегабайту памяти, доступен для записи.
0038hСегмент для работы с сопроцессором Weitek 1167. В отличие от сопроцессора i80287/i80387 для обращения к сопроцессору Weitek 1167 используется определённая область адресов памяти.
0040hСегмент видеопамяти для работы в графическом режиме.


В документации на Phar Lap DOS-экстендер подробно описаны форматы таблиц LDT и GDT. Программы могут пользоваться определёнными в этих таблицах селекторами для адресации системных областей памяти, таких как память видеоадаптера.

Обратите внимание на то, что в приведённой выше программе не выполняется загрузка сегментного регистра DS. Так как программа стартует сразу в защищённом режиме, загрузчик run386 загружает сам все сегментные регистры. В частности, он загружает в регистр DS селектор сегмента данных.

Виртуальная машина WINDOWS



Операционная система WINDOWS позволяет разделять ресурсы персонального компьютера между несколькими параллельно работающими программами. При этом в среде WINDOWS могут выполняться три типа программ:
  • Программы, созданные специально для работы под управлением WINDOWS - приложения WINDOWS. Они не могут работать вне WINDOWS, так как для своей работы используют модули, находящиеся в ядре WINDOWS. Приложения WINDOWS всегда начинают своё выполнение и выполняются в защищённом режиме, им доступен интерфейс DPMI.
  • Программы, созданные для работы в среде MS-DOS и ничего не знающие о WINDOWS. Это хорошо знакомые вам программы реального режима. Если WINDOWS работает в стандартном режиме (Standart Mode) на компьютере с процессором i80286, выполнение этих программ происходит в реальном режиме. Если WINDOWS работает в расширенном режиме (Enhanced Mode) на процессорах i80386 или i80486, такие программы выполняются в режиме виртуального процессора 8086.
  • Программы, начинающие свою работу в режиме виртуального процессора 8086 (как обычные программы реального режима) и переключающиеся затем в защищённый режим. Эти программы используют интерфейс DPMI, который доступен им только в том случае, если WINDOWS работает в расширенном режиме на процессорах i80386 или i80486.


Программы первого типа - тема для отдельной книги объёмом в несколько сотен страниц (см. список литературы). Программы второго типа - это обычные программы MS-DOS, о которых мы говорили в предыдущих томах "Библиотеки системного программиста".

Мы займёмся программами последнего типа. Для составления этих программ вы можете использовать обычную технику, применяемую для программ реального режима MS-DOS (за исключением отладки - как и все программы, приведённые в этой книге, программы третьего типа не поддаются отладке стандартными для реального режима средствами).

Наша следующая глава - о WINDOWS и о тех возможностях, которые операционная система WINDOWS предоставляет программам, составленным в старом "стиле" MS-DOS.

© 2003 virt.lab., all rights reserved.
Hosted by uCoz