{
WORD wCountRet ;

_asm {
mov ax,SEG wCount
mov es,ax
xor ax,ax
xchg ax,es:wCount ;получить счетчик, установить в нуль
mov wCountRet,ax
}

return wCountRet ;
}

void EXPORT FAR PASCAL BogusStop(void)
{
hWndEvent - 0x0000 ; /*команда для ISR "завершить работу"*/
if (!lpfnPrevISR)
return ; /* возвратиться, если программа не стартовала */

_asm {
mov dx,FAKE_PORT
l1:
in al,dx
rcr al,1
jnc l1 ; цикл, если занято

cli
in al,PIC01
or al,INT_MASK
out PIC01,al ; маскировать уровень прерывания
sti
}

DPMI_SetRMVector(INT_DEV, lpfnPrevRMISR) ;
/* Восстановить вектор реального режима */
FreeIntReflector(hReflector) ;
/* Освободить отражатель */
SetPMVector(INT_DEV, lpfnPrevISR) ;
/* Восстановить вектор защищенного режима */

lpfnPrevISR = NULL ;
}
/* конец файла*/
_____________________________________________________________________

    Листинг 4. Программа bogus.c




    Интерфейс системы MS-DOS для защищенного режима




Чтобы установить связь с вектором реального режима из кода
системы Windows защищенного режима, необходимо работать с интерфейсом
системы MS-DOS для защищенного режима (MS-DOS Protected Mode
Interface - DPMI). (Текущая версия DPMI представляет собой уровень
1.0, но система Windows наиболее полно реализует только уровень 0.9.
Некоторые функции уровня 1.0 реализованы в системе Windows 3.1.)
Функция DPMI_SetRMVector вызывает интерфейс DPMI, чтобы
установить вектор реального режима. Можно видеть, что интерфейс DPMI
взаимодействует через регистры (регистр AX содержит функциональный
код) и INT31h. Автор включил высокоуровневый интерфейс в данную
и другие функции DPMI (доступен только на диске кодов или в
интерактивном режиме), чтобы можно было иметь доступ к интерфейсу
DPMI из языка Си и выделил код, написанный на языке ассемблер, на
случай, если возникнет необходимость использовать что-то отличное от
компилятора Си фирмы Microsoft.
Функция DPMI_AllocateRMCallback вызывает интерфейс DPMI, чтобы
распределить обратный вызов (callback), представляющий собой адрес,
вызываемый из реального режима, который передает управление коду
защищенного режима. Например, программа TSR системы MS-DOS может
вызвать код в библиотеке DLL системы Windows через обратный вызов.
Функция DPMI_AllocateRMCallback принимает два параметра: адрес
кода защищенного режима, который будет вызываться обратно, и
регистровую структуру, которая обновляется при выполнении реального
обратного вызова, таким образом код защищенного режима может
исследовать содержимое регистров реального режима во время обратного
вызова.
Функция DPMI_FreeRMCallback освобождает все структуры, которые
были распределены в результате обращения к функции
DPMI_AllocateRMCallback. Функция DPMI_FreeRMCallback должна вызываться
только тогда, когда больше нет необходимости в обратном вызове.


    Программа ISR в реальном режиме




Несмотря на то, что автор рекомендовал обеспечивать раздельную
программу ISR в реальном режиме, в данном примере эта рекомендация не
была выполнена. Вместо этого, автор предоставил программы,
необходимые при реализации программы ISR на языке Си. Фактически,
данный пример устанавливает связь с прерываниями реального режима
только для того, чтобы переключить ЦПУ в защищенный режим для
обработки прерывания. Таково по умолчанию поведение системы Windows,
когда с прерываниями реального режима не устанавливается связь
вообще, таким образом автор рассматривает несколько циклов, которые
не имеют никакого другого назначения, кроме как показать, каким
образом все работает.
Рассмотрим код для точки входа BogusStart. По существу он
работает так же, как работал бы в системе MS-DOS. Код сохраняет
старое значение прерывания, обеспечивает связь с текущим значением и
подает устройству знак начать работу. Однако вместо обеспечения связи
только с вектором защищенного режима, он устанавливает связь как с
вектором реального режима, так и с вектором защищенного режима.
Устанавливая связь с вектором реального режима, код вызывает
AllocIntReflector, чтобы обеспечить ссылку вектора прерываний
реального режима на обратный вызов, который просто обращается к
программе ISR защищенного режима. Точка входа BogusStart подает знак
устройству начинать работу одинаковым образом при обоих режимах
работы: защищенном и реальном. Она размаскирует бит IRQ для
контроллера PIC и подает знак устройству начинать работу, записывая 1
в бит START порта управления устройством. Как только приложение
обращается к данной программе, начинается обработка прерываний и
регистрация сообщений в соответствии с программой ISR.
Программа BogusStop тривиальна и просто отключает устройство и
разрывает связи, установленные программой BogusStart. Итак, осталось
привести пример прикладной программы, чтобы показать работу операций
ввода-вывода.


    Приложение WINTEST



Приложение wintest.c, показывающее работу ввода-вывода (см.
листинг 5), состоит главным образом из немодульного диалогового
блока, в котором непрерывно высвечивается количество прерываний,
обработанных с начала работы программы.
Программа MainDlgProc вызывает программу BogusStart во время
выполнения WM_INITDIALOG, передавая в качестве параметра обработчик
окна диалогового блока. Программа ISR регистрирует сообщения к
данному обработчику в тех случаях, когда счетчик прерываний
изменяется от нуля к единице.
Программа MainDlgProc сохраняет текущее суммарное значение
счетчика в переменной wCountTotal. Всякий раз, когда диалог получает
сообщение WM_COMMAND с параметром wParam, равным IDM_BOGUSEVENT,
программа обновляет суммарный счетчик, отображаемый в диалоговом
блоке. Следует отметить, что хотя программа ISR регистрирует
сообщение только тогда, когда счетчик изменяется от нуля к единице,
возможна (и весьма вероятно) обработка количества прерываний до того,
как сообщение WM_COMMAND фактически будет передано диалоговой
процедуре. Методика, показанная в данной программе, при которой
программа ISR регистрирует сообщение только при первом переходе, а
программа BogusCheck чистит счетчик, обеспечивает точный подсчет
количества прерываний, даже если на уровне приложения нельзя учесть
каждое прерывание в момент его возникновения.
При выполнении данной программы можно наблюдать, что счетчик
прерывания в диалоговом блоке непрерывно увеличивается, указывая
количество выполненных операций ввод-вывода.

_____________________________________________________________________
#include
#include "bogus.h"
#include "wintest.h"

HANDLE hPgmInstance ;
#define IDM_BOGUSEVENT 0x3000

void CenterWindow(HWND hWnd)
{
int xSize, ySixe, xPos, yPos ;
RECT rc ;

xSize = GetSystemMetrics(SM_CXSCREEN) ;
ySize = GetSystemMetrics(SM_CYSCREEN) ;
GetWindowRect(hWnd, &rc) ;
xPos = (xSize - (rc.right - rc.left)) / 2 ;
yPos = (ySize - (rc.bottom - rc.top)) / 2 ;
SetWindowRect(hWnd, NULL, xPos, yPos, 0, 0,
SWP_DRAWFRAME | SWP_NOSIZE | SWP_NOZORDER) ;
}

LRESULT _loadds FAR PASCAL MainDlgProc(HWND hwndDlg,
UINT msg, WPARAM wParam, LPARAM lParam)
{
static WORD wCountTotal = 0;
WORD wCount ;

lParam = lParam ;
switch (msg)
{
case WM_INITDIALOG:
RemoveMenu(GetSystemMenu(hwndDlg,0),
SC_CLOSE,MF_BYCOMMAND) ;
BogusStart(hwndDlg, IDM_BOGUSEVENT) ;
break ;

case WM_SHOWWINDOW:
if (wParam)
CenterWindow(hwndDlg) ;
break ;

case WM_COMMAND:
switch (wParam)
{
case IDM_BOGUSEVENT:
wCount = BogusGetEvent() ;
while 9wCount)
{
wCountTotal += wCount ;
wCount = BogusGetEvent() ;
}
SetDlgItemInt(hwndDlg, IDM_COUNT, wCountTotal, FALSE);
break ;

case IDCANCEL:
EndDialog(hwndDlg, 0) ;
break ;
}
break ;

default:
return FALSE ;
}
return TRUE ;
}

int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpCmdLine, intnCmdShow)
{
hPgmInstance = hInstance ;
hPrevInstance = hPrevInstance ;
lpCmdLine = lpCmdLine ;
nCmdShow = nCmdShow ;

if (!hPrevInstance)
{
if (BogusCheck())
{
if (MessageBox(0, "Press OK to begin bogus I/O",
"WinTest", MB_OKCANCEL|MB_APPLMODAL) == IDOK)
{
DialogBox(hPgmInstance, "MainDlg", 0,
(FARPROC) MainDlgProc) ;
BogusStop() ;
}
}
else
MessageBox(0, "Bogus device not found", "WinTest",
MB_ICOMMAND|MB_OK|MB_APPLMODAL) ;
}
else
MessageBox(0, "Another instance already running",
"WinTest", MB_ICONEXCLAMATION|MB_OK|MB_APPLMODAL) ;

return 0 ;
}
/* конец файла */
______________________________________________________________________

    Листинг 5. Программа wintest.c.



    Драйвер виртуального устройства




Файл vxd2.asm (листинги 6 и 7) представляет собой исходный код
драйвера фиктивного устройства. Следует отметить, что для того, чтобы
построить этот драйвер, необходимо иметь комплект драйверов устройств
(Device Driver Kit - DDK) системы Windows фирмы Microsoft, т.к. код
написан для 32-битового ассемблера, предусмотренного в комплекте DDK
(MASM5). Результирующий модуль может быть скомпонован только
DDK-компоновщиком LINK386 и утилитой послекомпоновочной обработки
ADDHDR. Кроме того, данный исходный код ссылается на определенное
количество включаемых файлов (include files), которые входят в состав
только комплекта DDK.
Как было указано, типичный драйвер VxD содержит обязательные
включаемые файлы, а кроме того он начинается с вызова макроса Declare
_Virtual_Device, который создает блок данных, описывающий виртуальный
драйвер для ядра системы Windows. Этот блок данных, фактически, -
единственное обозначение, экспортируемое из драйвера VxD. Все
остальные точки входа являются производными от данных, содержащихся
внутри. Кроме всего прочего, данный макрос описывает имя устройства,
порядок его инициализации и его точки входа. Виртуальный драйвер VxD
может обслуживать запросы приложений как в реальном, так и в защищен-
ном режимах. Точки входа для такого обслуживания также описываются
данным макросом.

_____________________________________________________________________
PAGE ,132
title VxD2B.ASM - Пример драйвера устройства #2b
;EM VxD2B - Пример драйвера устройства #2b
;
; Copyright 1992, Cherry Hill Software
; All rights reserved
;
; SUMMARY (Резюме)
; Данный драйвер имитирует прерываемое устройство. Порт
; управления (выход) имеет следующее назначение битов:
;
; Бит 0 - Начать (Start) ввод-вывод. Запись нуля в данный бит
; начинает пересылку ввода-вывода. Пересылка длится
; около 1/10 секунды. Запись единицы в этот бит не
; дает результата.
;
; Бит 1 - Устройству посылается EOI. Запись нуля в данный бит
; приводит к посылке признака "Конец прерывания" (End-
; of-interrupt - EOI) устройству и удаляет любой запрос
; на отложенное прерывание. Запись единицы в этот бит не
; дает результата.
; Все остальные биты: Всегда записываются единицы для дальнейшей
; совместимости.
;
; При чтении порта следующие значения возвращаются:
;
; Бит 0 - Первоначально присваивается значение 1, бит
; сбрасывается, когда бит 1 выходного порта сбрасывается,
; и устанавливается, когда добавляется запрос на
; прерывание. Данный бит равен нулю, когда устройство
; передает данные и устанавливается в 1, чтобы указать,
; когда передача завершена.
;
; Бит 1 - Первоначально присваивается значение 1, бит
; сбрасывается, когда добавляется запрос на прерывание и
; устанавливается, когда устройство удаляет запрос на
; прерывание. Значение данного бита, равное нулю,
; указывает на отложенное прерывание, бит устанавливается
; в 1, если нет отложенного прерывания.
;
; Все остальные биты: возвращаемое значение игнорируется для
; дальнейшей совместимости.
;
; WARNINGS (Предупреждения)
;
;
.386p

.xlist
include vmm.inc
include debug.inc
include v86mmgr.inc
include vpicd.inc
include ..\include\bogus.inc
.list

VM_Not_Executable equ VM_Not_Executeable ; acckk;

subttl VxD Declaration/Definition
page

VxD2B_Init_Order equ VNETBIOS_Init_Order+100 ; Данная операция
выполняется после запуска виртуальной сети
VxD2B_Device_ID equ Bogus_Device_ID

Declare_Virtual_Device VXD2, 1, 0, Vxd2B_Control, VxD2B_Device_ID, \
VxD2B_Init_Order

VxD_DATA_SEG

;
; Структура дескриптора виртуального прерывания
;
; Данная структура передается VPIDC_Virtualize_IRQ. В данной
; структуре описывается уровень прерывания, процедура прерывания
; аппаратных средств, и процедура, которую вызывает VPICD, когда
; прерывание диспетчируется в виртуальной машине VM и когда VM
; возвращается из прерывания.
;
IRQD VPICD_IRQ_Descriptor ,\
OFFSET32 VxD2_Mask_Change_Proc,\
OFFSET32 VxD2_IRET_Proc>

hIRQ dd -1 ; обработчик IRQ
hOwner dd -1 ; обработчик, владеющий VM
hTimeout dd 0 ; обработчик к обратному вызову по тайм-ауту
bFakeData dd 01111111b ; имитировать данные порта ввода-вывода

VxD_DATA_ENDS

subttl Dispatch VxD Control
page
VxD_LOCKED_CODE_SEG

BeginProc CheckOwner, NO_LOG
cmp ebx,hOwner
jne short col
ret ; выйти, если вызывается владелец
col:
cmp hOwner,-1
jne short co2 ; пропустить, если вызов не владельца
mov hOwner,ebx ;установить владельца
ret
co2:
mov al,-1
ret
EndProc CheckOwner

BeginProc TimeoutProc
mov hTimeout,0 ;почистить обработчик
cmp edx,hOwner ; все еще тот же владелец?
jne short tol ; пропустить, если нет
test bFakeData,FAKE_STAT_BUSY ;отложенный ввод-вывод?
jnz short tol ; пропустить, если нет
cmp hOwner,-1 ; имеется ли владелец?
je short tol ; пропустить, если нет
mov eax,hIRQ
mov ebx,hOwner
VxDcall VPICD_Set_Int_Request ;добавить прерывание
mov al,bFakeData
and al,NOT (FAKE_STAT_IRQ) ; указывает также в порте состояния
or al,FAKE_STAT_BUSY ; указывает, что больше не занято
mov bFakeData,al
tol:
ret
End Proc TimeoutProc

;IP Port_IO_Callback - выполняет доступ к FAKE_PORT
;
; ENTRY (вход)
; EAX - выходное значение (для выходных операторов)
; EBX - обработчик к текущему VM
; ECX - тип операции ввода-вывода
; DS,ES - FLAT
;
; EXIT (выход)
; EAX - входное значение (для входных операторов)
;
; WARNINGS (предупреждения)
;
; NOTES (примечания)
; Следует отметить, что мы даже не смотрим регистровый фрейм
; клиента.
;
; Мы просто читаем и увеличиваем.
;
; CALLS (вызовы)

BeginProc Port_IO_Callback, NO_LOG
Dispatch_Byte_IO Fall_through,Port_Output_Callback

Port_Input_Callback:
call CheckOwner
jc short ioexit
mov al,bFakeData
or bFakeData,FAKE_STAT_ERROR ; почистить отложенную ошибку
ioexit:
ret

Port_Output_Callback:
call CheckOwner
jc short ioexit ;игнорировать ввод-вывод, если не владелец
test al,FAKE_CTL_START
jnz short,poc1 ;пропустить, если не начинается ввод-вывод
test bFakeData,FAKE_START_BUSY
jz short,poc1 ;пропустить, если уже занято
test bFakeData,FAKE_START_IRQ
jz short,poc1 ;пропустить, если отложенное IRQ
push eax
push edx
and bFakeData,NOT (FAKE_STAT_ERROR) ; предположить ошибку
mov eax,100 ; обратный вызов в 1/10 секунды
mov edx,hOwner ; передать владельца обратному вызову
mov esi,OFFSET32 TimeoutProc
VMMcall Set_VM_Time_Out
pop edx
pop eax
or esi,esi
jz short,poc1 ;пропустить, если ошибка
and bFakeData,NOT (FAKE_STAT_BUSY) ; указать на занятость
or bFakeData,FAKE_STAT_ERROR ; в противном случае почистить
индикацию ошибки
mov hTimeout,esi ;сохранить обработчик тайм-аута
poc1:
test al,FAKE_CTL_EOI
jnz short poc2 ; пропустить, если не посылается EOI
test bFakeData,FAKE_STAT_IRQ ;прерывание отложено?
jnz short poc2 ; пропустить, если нет
or bFakeData,FAKE_STAT_IRQ ;показать, что прерывание уже не-
отложеное
push eax
mov eax,hIRQ
VxDcall VPICD_Clear_Int_Request
pop eax
poc2:
ret
EndProc Port_IO_Callback

; ECX == 0 if unmasking (enabling), ECX != 0 if masking (disabling).
BeginProc VxD2_Mask_Change_Proc
call CheckOwner
jc short mcp9 ; игнорировать, если нет владельца
jcxz mcp9 ; пропустить, если не маскировано (включено)
;
; Владелец освобождает управление. Разрешается другой VM войти в
; систему.
;
mov hOwner,-1 ; почистить владельца

mcp9:
ret
EndProc VxD2_Mask_Change_Proc

; Вызывается, когда выполняется программа ISR
BeginProc VxD2_VInt_Proc
mov eax,High_Pri_Device_Boost
VMMCall Adjust_Exec_Priority ;повышенный приоритет для начальной
обработки
ret
EndProc VxD2_VInt_Proc

; вызывается при возврате из программы ISR (IRETs)
BeginProc VxD2_IRET_Proc
mov eax,-(High_Pri_Device_Boost)
VMMCall Adjust_Exec_Priority ;восстановить приоритет
ret
EndProc VxD2_IRET_Proc

ifdef DEBUG
BeginProc VxD2B_Debug_Query
Trace_Out "VxD2 has no debug command support."
clc
ret
End Proc VxD2B_Debug_Query
endif

;
; VxD2B_Control
;

CtlDisp macro x
Control_Dispatch x, VxD2B_&x
endm

Begin_Control_Dispatch VxD2B
CtlDisp Device_Init
ifdef DEBUG
CtlDisp Debug_Query
endif
End_Control_Dispatch VxD2B

VxD_LOCKED_CODE_ENDS
VxD_CODE_SEG
VxD_CODE_ENDS

subttl VxD Initialization
page
VxD_ICODE_SEG

page
; EP VxD2B_Device_Init - Некритическая инициализация устройства
;
; ENTRY (вход)
; EBP - фрейм клиента
; EBX - системный обработчик VM
; DS,ES - FLAT
;
; EXIT (выход)
; SUCCESS (успешный)
; Carry clear ("нет переноса")
; FAILURE (аварийный)
; Carry set ("есть перенос")
;
;
; WARNINGS (предупреждения)
;
; NOTES (примечания)
;
; CALLS (вызовы)
;
BeginProc VxD2B_Device_Init
Debug_Out "VxD2B_Device_Init"

mov edi,OFFSET32 IRQD
VxDcall VPICD_Virtualize_IRQ ; виртуализировать прерывание
jc short vdi1 ; выход, если ошибка
mov hIRQ,eax ; сохранить обработчик

mov edx,FAKE_PORT
mov esi,OFFSET32 Port_IO_Callback
VMMCall Install_IO_Handler
VMMCall Enable_Global_Trapping ;

clc ; нет ошибки
vdi1:
ret
EndProc VxD2B_Device_Init

VxD_ICODE_ENDS

VxD_REAL_INIT_SEG

VxD2B_Real_Init LABEL FAR ;вызывается перед тем, как система Windows
входит в защищенный режим
mov ax,Device_Load_OK ;позволяет VxD загрузиться
xor bx,bx ; нет исключенных (Exclude) страниц EMM
xor si,si ; нет элементов экземпляров данных
; передать edx немодифицированным
ret

VxD_REAL_INIT_ENDS

END VxD2B_Real_Init
; Конец файла

_____________________________________________________________________

    Листинг 6. Программа vxd2.asm



_____________________________________________________________________

LIBRARY VXD2

DESCRIPTION 'Enhanced Windows VXD2(B) Device (Version 1.0)'

EXETYPE DEV386

SEGMENTS
_LTEXT PRELOAD NONDISCARDABLE
_LDATA PRELOAD NONDISCARDABLE
_ITEXT CLASS 'ICODE' DISCARDABLE
_IDATA CLASS 'ICODE' DISCARDABLE
_TEXT CLASS 'PCODE' NONDISCARDABLE
_DATA CLASS 'PCODE' NONDISCARDABLE

EXPORTS
VXD2_DDB @1
_____________________________________________________________________

    Листинг 7. Программа vxd2.def




    События, управляющие устройством




По мере того, как система Windows в своей работе проходит
различные стадии, начиная со стадии инициализации самой системы,
через инициализацию виртуальной машины VM и так далее, каждый
установленный драйвер VxD вызывается неоднократно, а именно один раз
на каждую стадию. В таблице, приведенной ниже, перечисляются фазы
системы Windows и главные события, для которых вызывается каждый
драйвер VxD.

Таблица
Управляющие сообщения драйвера VxD
-------------------+--------------------------------------------------
Sys_Critical_Init | Первое управляющее событие; прерывания
| отключаются. Драйвер VxD определяет готовность
| устройства.
-------------------+--------------------------------------------------
Device_Init | Прерывания разрешаются; драйвер VxD инициализиру-
| ет устройство; могут быть вызваны программы и
| драйверы системы DOS.
-------------------+--------------------------------------------------
Init_Complete | Указывает, что все драйверы VxD прошли стадию
| Device_Init.
-------------------+--------------------------------------------------
System_Exit | Указывает, что система Windows готовится к
| закрытию и возврату в систему DOS. Память для
| системы DOS восстановлена в состояние, которое
| было до работы системы Windows.
-------------------+--------------------------------------------------
Sys_Critical_Exit | Последнее управляющее событие; прерывания
| отключаются.
-------------------+--------------------------------------------------
Create_VM | Вызывается перед моментом создания виртуальной
| машины VM; драйвер VxD указывает, доступны ли
| ресурсы для создания виртуальной машины VM.
-------------------+--------------------------------------------------
VM_Critical_Init | Вторая фаза создания виртуальной машины VM.
-------------------+--------------------------------------------------
VM_Init | Третья фаза создания виртуальной машины VM.
Sys_VM_Init | Драйвер VxD может аварийно завершить работу
| виртуальной машины VM.
-------------------+--------------------------------------------------
Query_Destroy | Позволяет драйверу VxD предупредить пользователя
| о затруднениях при разрушении виртуальной машины
| VM.
-------------------+--------------------------------------------------
VM_Terminate | Первая стадия успешного завершения виртуальной
Sys_VM_Terminate | машины VM. Если это системная виртуальная
| машина VM, то сообщение указывает, что
| производится нормальное, вызванное пользователем,
| завершение системы Windows.
-------------------+--------------------------------------------------
VM_Not_Executeable | Виртуальная машина VM закрывается. Первая стадия
| аварийного завершения виртуальной машины VM.
-------------------+--------------------------------------------------

Драйвер VxD примера выполняет управление только фазой
Device_Init. На этой стадии устанавливается связь с портом ввода-
вывода и уровнем прерывания 11, а также производится их
виртуализация. Обычно драйвер VxD виртуализирует порты ввода-вывода и
прерывание в соответствии с физическим аппаратным оборудованием. Но в
данном случае драйвер VxD может виртуализировать и делает это с
портом и прерыванием, которые не имеют соответствующего подключенного
аппаратного оборудования.
Код Install_IO_Handler вызывается, чтобы виртуализировать
единственный порт ввода-вывода. Затем всякий раз, когда
осуществляется доступ к описанному порту ввода-вывода из виртуальной
машины VM, программа управления виртуальной машиной системы Windows
(Virtual Machine Manager - VMM) вызывает обратно драйвер VxD для
того, чтобы разрешить ему имитировать операции ввода-вывода.
Код VPICD_Virtualize_IRQ вызывается, чтобы виртуализировать
уровень прерывания. Выполняя его, можно имитировать прерывание
аппаратного оборудования (в частности IRQ 11) в виртуальной машине.


    "Фиктивное" устройство




Когда к порту ввода-вывода (141) устройства осуществляется
доступ виртуальной машиной VM (либо в реальном, либо в защищенном
режиме), то машина вызывает программу драйвера VxD Port_IO_Callback
(см. Листинг 6). В этой программе подпрограмма Dispatch_Byte_IO
сводит большое количество возможных типов доступа ввода-вывода (а
именно: byte, word, dword, string и т.д.) к двум: байтовому вводу и
байтовому выводу.
Для устройства из примера байтовый ввод представляет собой чтение
из регистра состояния устройства. Он возвращает просто переменную,
которая сохраняется в памяти.
Байтовый вывод - немного более сложная операция, так как
представляет фактическую работу устройства. При запуске устройства
также запускается таймер, который выполняет обратный вызов (к коду
TimeoutProc) в течении 1/10 секунды и устанавливает состояние BUSY.
Если вывод подтверждает прием прерывания, то производится очистка
виртуального запроса на прерывание путем вызова кода VPICD_Clear_Int_
Request и очистка состояния в регистре состояния.
Обратный вызов кода TimeoutProc представляет завершение операции
ввода-вывода на устройстве и именно в данный момент он моделирует
прерывание аппаратного оборудования к виртуальной машине VM путем
вызова кода VPICD_Clear_Int_Request и очистки состояния занятости
устройства. Драйвер устройства в приложениях dostest и wintest будет
обычно обрабатывать прерывание путем подтвержения приема его (посылая
EOI) и повторного запуска процесса на всем протяжении снова.
Следует отметить процедуры VxD2_VInt_Proc и VxD2_IRET _Proc. На
данные две процедуры существует ссылка в структуре, которая
передается коду VPICD_Virtualize_IRQ. Они вызываются в начале и конце
процесса виртуализации прерывания в виртуальную машину VM. Все их
функции сводятся к увеличению и сохранению приоритета виртуальной
машины VM, которая временно обрабатывает данное прерывание. Таким
способом драйвер VxD может управлять приоритетом виртуальной машины
VM, которая считается соответствующей. (Всегда желательно, чтобы
программа обслуживания прерывания в любой виртуальной машине VM имела
приоритет выше, чем приоритет обычной обработки в других виртуальных
машинах VM.)


    Установка драйвера VxD




После построения драйвера VxD, до первого обращения к нему
программы Windows необходимо добавить его как строку device= в
секцию [386Enh] кода system.ini. Система Windows должна быть запущена
заново, чтобы включить драйвер VxD и виртуальное устройство. После
этого, можно выполнять и тестировать приложения dostest и wintest.


    Заключение




Хотя драйверы устройств системы Windows кажутся в настоящее
время очень сложными, обычные и виртуальные драйверы устройств
предоставляют огромное количество возможностей. Однако следует
учитывать, насколько более сложными они должны быть на машине MIPS,
эксплуатирующей систему Windows NT и код эмулятора 80x86, чтобы
обеспечить работу виртуальной машины системы MS-DOS.