Страница 2 из 5 Главная функция WinMain() выполняет три характерные для нее процедуры: регистрацию класса главного окна, создание и показ главного окна, цикл обработки сообщений. В цикле обработки сообщений имеется два принципиальных отличия от примера предыдущей части статьи: в каждом шаге цикла проверяется состояние флага завершения измерений request, и если флаг оказывается установленным, то вызывается функция Windows PostMessage(), которая ставит в очередь сообщений приведенного выше приложения наше сообщение с кодом WM_USER. Для того чтобы в цикл обработки сообщений включить проверку флага, пришлось заменить в нем функцию GetMessage() на функцию PeekMessage(), которая, в отличие от GetMessage(), при отсутствии сообщений в очереди возвращает управление в программу, что и дает возможность включить в цикл дополнительные действия. Однако PeekMessage() не анализирует сообщение WM_QUIT о завершении программы, поэтому <вылавливание> этого сообщения (и завершение программы оператором return 0 в случае его прихода) приходится выполнять вручную. Конструкция: do{ ... }while(1) позволяет организовать бесконечный цикл, поскольку условие продолжения цикла, анализируемое оператором while, безусловно удовлетворяется (константа 1 никогда не может стать равной 0). В оконной функции WndProc() фиксируется приход трех сообщений: WM_COMMAND от пунктов меню, WM_DESTROY от команд завершения приложения и WM_USER, свидетельствующего об окончании измерений. Поскольку для сообщения WM_USER в файле windowsx.h отсутствует макрос HANDLE_WM_USER, его пришлось определить в начале программы с помощью оператора #define, построив макрорасширение по аналогии с каким-либо из макросов вида HANDLE_сообщение из файла windowsx.h, хотя бы с макросом HANDLE_WM_DESTROY. Фрагмент программы, выполняемый при выборе пользователем пункта меню <Пуск>, содержит лишь вызов функции InitCard(). В ней вызовом вложенной функции GetAPIEntry определяется адрес API-процедуры драйвера, а затем, после заполнения ряда регистров параметрами, передаваемыми в драйвер, вызывается эта процедура. В драйвер передаются следующие параметры: селектор сегмента данных приложения, три константы для инициализации платы, а также селектор и смещение обработчика прерываний приложения isr(). Передача в драйвер содержимого сегментного регистра DS (селектора сегмента данных) необходима потому, что при вызове драйвером (точнее, VMM) нашей функции isr() не восстанавливается операционная среда приложения, в частности регистр DS не указывает на поля данных приложения, которые в результате оказываются недоступными. Передав в драйвер содержимое DS, мы сможем вернуть его назад вместе с другими данными, передаваемыми из драйвера в приложение, восстановив тем самым адресуемость данных. При выборе пользователем пунктов меню <Чтение> или <Выход> выполняются те же действия, что и в предыдущем примере. По сравнению с предыдущим примером упростилась функция OnDestroy(). Поскольку восстановление маски в контроллере прерываний возложено теперь на драйвер, а исходный вектор мы в этом варианте программы не восстанавливаем, то в функции OnDestroy() лишь вызывается функция Windows PostQuitMessage(), приводящая к завершению программы. В обработчике прерываний приложения isr() после засылки в регистр DS нашего же селектора сегмента данных, переданного ранее в драйвер и полученного из него в качестве первого параметра функции isr(), выполняется инкремент флага request и пересылка в переменную data второго параметра функции isr() - результата измерений. Перейдем к рассмотрению программы виртуального драйвера, входящего в состав нашего программного комплекса. Текст виртуального драйвера, обрабатывающего аппаратные прерывания ;При вызове AX=DS приложения, BX=C0, CX=C1, DX=C2, DI=селектор isr ,SI=смещение isr .386p .XLIST include vmm.inc include vpicd.inc .LIST Declare_Virtual_Device VMyD,1,0,VMyD_Control,8000h, \ Undefined_Init_Order,,API_Handler ;======================= VxD_REAL_INIT_SEG BeginProc VMyD_Real_Init ;Текст процедуры инициализации реального режима (см. часть 2 этого цикла) EndProc VMyD_Real_Init VxD_REAL_INIT_ENDS ;====================== VxD_DATA_SEG Data dw 0 ;Ячейка для результата измерений DSseg dw 0 ;Ячейка для хранения селектора приложения Segment_Callback dw 0 ;Селектор функции isr приложения Offset_Callback dd 0 ;Смещение функции isr приложения IRQ_Handle dd 0 ;Дескриптор виртуального прерывания VMyD_Int13_Desc label dword;32-битовый адрес следующей далее структуры VPICD_IRQ_Descriptor <5,,OFFSET32 VMyD_Int_13>;Структура с информацией ;о виртуализованном прерывании VxD_DATA_ENDS ;====================== VxD_CODE_SEG BeginProc VMyD_Control ;Включим в состав драйвера процедуру обработки системного сообщения ;Device_Init об инициализации драйвера Control_Dispatch Device_Init, VMyD_Device_Init clc ret EndProc VMyD_Control ;---------------------- ;Процедура, вызываемая при инициализации драйвера системой BeginProc VMyD_Device_Init mov EDI,OFFSET32 VMyD_Int13_Desc;Адрес структуры VPICD_IRQ_Descriptor VxDCall VPICD_Virtualize_IRQ;Виртуализация устройства mov IRQ_Handle,EAX;Сохраним дескриптор виртуального IRQ clc ret EndProc VMyD_Device_Init ;------------------------- ;API-процедура, вызываемая из приложения BeginProc API_Handler ;Получим параметры из приложения push [EBP.Client_DI] pop Segment_Callback push [EBP.Client_AX];DS pop DSseg movzx ESI,[EBP.Client_SI] mov Offset_Callback,ESI ;Общий сброс mov DX,30Ch in AL,DX ;Размаскируем уровень 5 в физическом контроллере прерываний mov EAX,IRQ_Handle VxDCall VPICD_Physically_Unmask ;Засылаем управляющие слова по каналам mov DX,303h mov AL,36h ;Канал 0 out DX,AL mov AL,70h ;Канал 1 out DX,AL mov AL,0B6h ;Канал 2 out DX,AL ;Засылаем константы в каналы mov DX,300h ;Канал 0 mov AX,[EBP.Client_BX];Константа С0 out DX,AL ;Младший байт частоты xchg AL,AH out DX,AL ;Старший байт частоты mov DX,301h ;Канал 1 mov AX,[EBP.Client_CX];Константа С1 out DX,AL ;Младший байт интервала xchg AL,AH out DX,AL ;Старший байт интервала mov DX,302h ;Канал 2 mov AX,[EBP.Client_DX];Константа С2 out DX,AL ;Младший байт частоты xchg AH,AL out DX,AL ;Старший байт частоты ;Установим флаг S2 разрешения счета mov DX,30Bh in AL,DX ret EndProc API_Handler ;------------------------- ;Процедура обработки аппаратного прерывания IRQ5 (вектор 13) BeginProc VMyD_Int_13, High_Freq ;Получим результат измерений из выходного регистра счетчика mov DX,309h ;Порт старшего байта in AL,DX ;Получим старший байт mov AH,AL ;Отправим его в AH dec DX ;DX=308h in AL,DX ;Получим младший байт mov Data,AX ;Весь результат в Data ;Выполним завершающие действия в PIC и вызовем функцию приложения mov EAX,IRQ_Handle VxDCall VPICD_Phys_EOI;EOI в физический контроллер прерываний VxDCall VPICD_Physically_Mask;Маскируем наш уровень ;Перейдем на синхронный уровень mov EDX,0 ;Данные отсутствуют mov ESI,OFFSET32 Reflect_Int;Адрес синхронной процедуры VMMCall Call_VM_Event;Установим запрос на ее вызов из VMM clc ret EndProc VMyD_Int_13 ;------------------------- ;Процедура уровня отложенных прерываний BeginProc Reflect_Int Push_Client_State ;Выделим место на стеке для регистров клиента VMMCall Begin_Nest_Exec;Начнем вложенный блок выполнения mov AX,Data ;Отправим данное VMMCall Simulate_Push;в стек клиента mov AX,DSseg ;Отправим полученный ранее DS VMMCall Simulate_Push;в стек клиента mov CX,Segment_Callback;Зашлем полученный ранее адрес функции isr mov EDX,Offset_Callback;в CS:IP клиента, чтобы после возврата из VMM VMMCall Simulate_Far_Call;в виртуальную машину вызвалась эта функция VMMCall Resume_Exec;Возврат из VMM в текущую виртуальную машину VMMCall End_Nest_Exec;Завершим вложенный блок выполнения Pop_Client_State ;Освободим место на стеке для регистров клиента clc ret EndProc Reflect_Int VxD_CODE_ENDS end VMyD_Real_Init |