6.3.3. Передача между режимами в DPMI

Вызов любого программного прерывания, кроме INT ЗШ и FNT 21h/AH = 4Ch (функция DPMI: завершение программы), приводит к тому, что DPMI-сервер переключается в режим V86 и передает управление обработчику этого же преры­вания, скопировав все регистры, кроме сегментных регистров и стека (состояние сегментных регистров не определено, а стек предоставляет сам DPMI-сервер). После того как обработчик прерывания возобновит управление, DPMI-сервер возвратиться в защищенный режим и вернет управление программе. Таким спо­собом можно вызывать все прерывания, передающие параметры только в регист­рах, например проверку нажатия клавиши или вывод символа на экран. Чтобы вызвать прерывание, использующее сегментные регистры, например вывод стро­ки на экран, а также в других ситуациях, когда требуется обращение к процедуре, работающей в другом режиме, применяются следующие функции.

АХ = Вызов прерывания в реальном режиме

Вход:   АХ = 0300h

ВН = О, BL = номер прерывания

СХ = число слов из стека защищенного режима, которое будет скопи­ровано в стек реального режима и обратно

ES:EDI = селекторхмещение структуры регистров v86_regs (см. ниже) Выход: если CF = Q, структура в ES:EDI модифицируется Значения регистров CS и IP в структуре v86_regs игнорируются. Вызываемый обработчик прерывания должен восстанавливать стек в исходное состояние (на­пример, INT 25h и INT 26h этого не делают).

INT31h, АХ = 0301h: Вызов дальней процедуры в реальном режиме Вход: АХ = 03011т ВН = 0

СХ = число слов из стека защищенного режима, которое будет скопи­ровано в стек реального режима и обратно

ES:EDI = селекторхмещение структуры регистров v86_regs (см. ниже) Выход: если CF = Q, структура в ES:EDI модифицируется Вызываемая процедура должна заканчиваться командой RETF.

АХ 0302h: Вызов обработчика прерывания в реальном режиме

Вход: АХ = 0302h

ВН = Q

СХ = число слов из стека защищенного режима, которое будет скопи­ровано в стек реального режима и обратно

ES:EDI ~ селекторхмещение структуры регистров v86_regs (см. ниже) Выход: если CF = Q, структура в ES:EDI модифицируется Вызываемая процедура должна заканчиваться командой IRET.

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

+QQh: 4 байта - EDI +Q4h: 4 байта - ESI

+08h: 4 байта - ЕВР

+0СЫ 4 байта - игнорируются

+ 10h: 4 байта - ЕВХв

+14h: 4 байта - EDX +18h: 4 байта - ЕСХ +1Сп:4байта-ЕАХ +20h: 2 байта - FLAGS +22h: 2 байта - ES +24h: 2 байта - DS +26h: 2 байта - FS .   +28h: 2 байта - GS +2Ah: 2 байта - IP

+2Ch: 2 байта - CS +2Eh: 2 байта - SP +3Qh: 2 байта - SS

Значения SS и SP могут быть нулевыми, тогда DPMI-сервер сам предоставит стек для работы прерывания.

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

INT31h, АХ - 0303h: Выделить точку входа для вызова из реального режима

Вход: АХ = 03031т

DS:ESI = селекторхмещение процедуры в защищенном режиме (закан­чивающейся IRET), которую будут вызывать из реального ES:EDI = селекторхмещение структуры v86_regs, которая будет ис­пользоваться для передачи регистров

Выход: если CF = О, CX:DX = сегментхмещение точки входа

При передаче управления в такую процедуру DS:ESI указывает на стек реаль­ного режима, ES:EDI - на структуру v86_regs, SS:ESP - на стек, предоставлен­ный DPMI-сервером, и остальные регистры не определены.

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

INT31h, AX = 0304к. Освободить точку входа для вызова из реального режима Вход:   АХ - 0304h

CX:DX - сегментхмещение точки входа Выход: CF = Q, если точка входа удалена

Обработчики прерываний

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

обработчик - стандартный DPMI - переходит в режим V86, а затем управление проходит по цепочке обработчиков прерывания в реальном режиме (обработчи­ки прерываний и исключений в реальном режиме совладают).

INT31h, АХ = 0200к Определить адрес реального обработчика прерывания

Вход: AX = Q2QQh

BL = номер прерывания Выход: CF = Q всегда, CX:DX - сегментхмещение обработчика прерывания в реальном режиме

INT31h, АХ = 0201к Установить реальный ' обработчик прерывания

Вход: AX = Q2Q1h

BL = номер прерывания

CX:DX = сегментхмещение обработчика прерывания в реальном режиме Выход: CF = 0 всегда ' *

INT31H, АХ= 0204к Определить адрес защищенного обработчика прерывания

Вход: АХ = Q2Q4h

BL = номер прерывания Выход: CF = Q всегда, CX:EDX = селекторхмещение обработчика

INT31h, АХ^ 0205h: Установить защищенный обработчик прерывания

Вход: AX = Q2Q5h

BL = номер прерывания CX:EDX = селекторхмещение обработчика Выход: CF = О

INT31K АХ = 0202h: Определить адрес обработчика исключения Вход: AX= 0202h

BL= номер исключения (00h - lFh) Выход: если CF = О, CX:EDX = селекторхмещение обработчика исключения

INT 31k, АХ = 0203k: Установить обработчик исключения Вход: AX= 0203h

. BL = номер исключения (OOh — lFh) CX:EDX = селекторхмещение обработчика исключения Выход: CF = 0, если не было ошибок

Если обработчик исключения передает управление дальше по цепочке на стан­дартный обработчик DPMI-сервера, следует помнить, что только исключения О, 1, 2, 3, 4, 5 и У передаются обработчикам из реального режима, а остальные ис­ключения приводят к прекращению работы программы.

Пример программы

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

в Windows 95.

; dpmiex.asm

;   Выполняет переключение в защищенный режим средствами DPMI.

; При компиляции с гюмощью WASM необходима опция /D_WASM_.

.386 ;   32-битный защищенный режим появился в 80386.

; 16-битный сегмент - в нем выполняется подготовка и переход в защищенный режим. RM_seg   segment  byte      public use16

assume   cs:RM_seg,  ds:PM_seg, ss:RM_stacb

; Точка входа программы. RM_entry:

;  Проверить наличие DPMI.

mov

ax,1687h

Номер 1678h.

int

2Fh

Прерывание мультиплексора.

test

ax, ax

Если АХ не ноль,

jnz

DPMI^error

произошла ошибка (DPMI отсутствует).

test

Ы, 1 •

Если не поддерживается 32-битный режим,

jz

DPMI_error

нам тоже нечего делать.

; Подготовить базовые адреса для будущих

mov eax,PM_seg

mov ds,ax ;  DS - сегментный адрес PM_seg.

shl eax,4 ;   EAX - линейный адрес начала сегмента PM_seg.

mov ptr

or dword ptr GDT_flatCS+2,eax ;  Дескриптор для CS.

or dword ptr GDT_flatDS+2,eax ;   Дескриптор для DS.

;   Сохранить адрес процедуры переключения

mov       word ptr DPMI_ModeSwitch,di mov       word ptr DPMIJ1odeSwitch+2,es

;   ES должен указывать на область данных для переключения режимов. ; У нас она будет совпадать с началом будущего 32-битного стека.

add        еах,offset DPMI_data ;  Добавитв к ЕАХ смещение

;   и превратить в сегментный адрес.

inc ax

mov

; Перейти в защищенный режим.

mov        ах, 1 ;  АХ = 1 - мы будем 32-м приложением,

ifdef JKASM_

db 67h        ;   Поправка для wasm.

endif

call      dword ptr DPMI_ModeSwitch

jc        DPMI_error      •    ;  Если переключения не произошло - выйти.

Теперь мы находимся в защищенном режиме,   но лимиты всех сегментов ; установлены на 64 Кб,   а разрядности сегментов - на 16 бит. Нам надо подготовить два 32-битных селектора с лимитом 4 Гб -один для  кода и один для данных.

push ds

pop        es ;   ES вообще был сегментом PSP с лимитом 100h.

mov

Цикл по всем дескрипторам в нашей GDT,

sel_loop

; EDI

адрес таблицы

mov

edx, 1

;   которых всего два  (0,   1),

xor

ax, ax

;   Функция DPMI 00.

mov

cx, 1

 

int

31h

; Создать локальный дескриптор.

mov

word ptr

selectors[edx»2],ax         ;   Сохранить селектор

mov

bx, ax

; в таблице selectors.

mov

ax.OOOCh

; Функция DPMI OCh.

int

31h

; Установить селектор.

add

di,8

;   EDI - следующий дескриптор.

dec

dx

 

jns

sel_loop

 

Загрузить селектор сегмента кода в СЭ при помощи команды ГЖТТ\'

ifdef endif

push WASM

push

retf ptr Sel_flatCS

db 066h

offset

db 066h

Селектор для CS.

EIP

Префикс размера операнда.

Выполнить переход в 32-битный сегмент.

;  Сюда передается управление,  если произошла ошибка при инициализации DPMI

;   (обычно,  если DPMI просто нет).

DPMI_error:

push

cs

 

 

pop

ds

 

 

mov

dx, offset nodpmijnsg

 

 

mov.

ah,9h

; Вывод

строки на экран.

int

2111

 

 

mov

ah,4Ch

; Конец

ЕХЕ-программы.

int

21h

 

 

nodpmijnsg RM_seg ends

db

"Ошибка

;  Сегмент PM_seg содержит код, данные и стек для защищенного режима. PM_seg    segment  byte      public use32 assume

;  Таблица дескрипторов. GDT        label byte ; Дескриптор для CS.

GDT_flatCS db        OFFh,OFFh,Oh,Oh, Oh, OFAh, OCFh, Oh

;   Дескриптор для DS.

GDT_flatDS db OFFh,OFFh,Oh,Oh,Oh,0F2h,OCFh,Oh

; Точка входа в 32-битный режим

PM_entry:

mov ptr Sel_flatDS

mov

загружен только CS.

; Селектор для данных :  б DS,тшт

Программирование в PM

mov       es.ax ; в ES

mov       ss.ax ; и в SS.

mov • И установить стек.

Отсюда начинается текст собственно программы.

Программа работает в модели памяти flat с ненулевой базой,

база" CS, OS, ES и SS совпадает и равна линейному адресу начала PM_ssg, все лимиты - 4 Гб.

mov

mov xor mov int

mov

int

ax,0300h bx,0021h ecx,ecx

v86_regs

31h

hello_msg

v86_regs: vj36j3dx v86_eax

v86 ds

ah,4Ch 21n

db

dd dd

dd dd

dw dw dw

Функция DPMI

Прерывание DOS 21h. Стек не копировать. ES:EDI - адрес v86_regs. Вызвать прерывание.

Это единственный способ правильно завершить

"Hello world из 32-битного защищенного

Значения регистров для функции DPMI

;

0,0,0,0,0 offset hello_msg

О

0900h

0,0

PM_seg 0,0,0,0,0,0

EDI, ESI, EBP, О, EBX

EDX

ECX

EAX (AH = 09h, вывод строки на экран) FLAGS, ES

DS

FS, GS,  0,  0, SP, SS

; Различные временные переменные, нужные для переключения режимов.

DPMI_ModeSwitch   dd        ? ; Точка входа DPMI.

PM_seg_addr        dd        ? ; Линейный адрес сегмента PMjseg.

; Значения селекторов, selectors:

Sel_flatDS dw ?

Sel_flatCS dw ?

; Стек для.нашей 32-битной программы

DPMI_data: ; и временная область данных DPMI одновременно.

db        16384 dup (?)

PM_stack_bottom: PM_seg ends

Стек 16-битной программы, который использует DPMI-сервер при переключении режимов.

Windows 95 требует 16 байт,

CWS0PMI требует 32 байта,

QDPMI требует 96 байт. Мы выберем по максимуму. RM_stack segment byte stack "stack" use16

db 96 dup (?)

RM_stack ends

end      RM_entry ; точка входа для DOS - RM_entryттш

Несмотря на то что DPMI разрешает пользоваться многими прерываниями напрямую и всеми через функцию 0300h, он все равно требует некоторой подго­товки для переключения режимов. Кроме того, программа, работающая с DPMI для переключения режимов, должна сочетать в себе 16- и 32-битный сегменты, что неудобно с точки зрения практического программирования. На самом деле для написания приложений, идущих в защищенном режиме под DOS, никто не применяет переключение режимов вообще - это делают специальные программы, называющиеся расширителями DOS.