- 4-10 -
без передачи сигнала нажатия клавиши программе переднего плана.
При обычном подходе каждое нажатие клавиши перед считыванием его
программой переднего плана анализируется. Для просмотра ввода в
буфер клавиатуры Вы можете перехватить прерывание 16h или Вы мо-
жете опрашивать буфер клавиатуры, используя прерывания от таймера
(int 1ch), или, наконец, Вы можете управлять содержимым буфера
клавиатуры при перехвате прерывания 9. Довольно часто Вы можете
посчитать полезным назначить горячие ключи, влияющие на состояние
клавиатуры, но не добавляющие символов в буфер клавиатуры. Каждый
из этих подходов имеет определенные преимущества и проблемы. Вы
имеете возможность решить, какая техника лучше для Вашего прило-
жения.
Подмена Int 16h
Простейшим путем создания горячего ключа является подмена
прерывания int 16h. Большинство хорошо работающих приложений ис-
пользуют это прерывание для ввода с клавиатуры. Установка собс-
твенного прерывания int 16h ISR позволит Вам анализировать каждый
символ и отклонять любые горячие ключи. Листинг 4-5 демонстрирует
типичную замену для int 16h ISR.
Листинг 4-5. Замена прерывания int 16h для просмотра
используемых клавиш
----------------------------------------------------------------
OldInt16 DD 0 ; сохранение кода инициализации
; адрес первоначальной isr
Hotkey DW (?) ; определение нажатой клавиши
NewInt16 PROC FAR
cmp ah,1 ; проверка функции
jg DoShift ; если g -- сдвиг
jl DoRead ; ah=0 ==> чтение
DoStatus: ; ah=1 ==> проверка состояния
pushf ; моделирование int 16
call cs:OldInt16 ; передача запроса BIOS
pushf ; сохранение флагов
cmp ax,HotKey ; найдена нажатая клавиша?
jnz Done1 ; нет
xor ax,ax ; ah <== 0 (запрос чтения)
call cs:OldInt16 ; удаление нажатой клавиши
call ActivateTSR ; нажатая клавиша вызывает TSR
mov ah=1 ; ah <== 1 (запрос состояния)
jmp SHORT DoStatus ; повторение запроса
DoRead:
pushf ; моделирование int 16h
call cs:OldInt16
cmp ax,HotKey ; найдена нажатая клавиша?
jnz Done0 ; если nz - нет
call ActivateTSR ; нажатая клавиша вызывает TSR
xor ah,ah ; ah <== 0 (запрос чтения)
jmp SHORT DoRead ; повторение запроса
DoShift: ; передача запроса
jmp cs:OldInt16 ; старая ISR. Сброс
Done0: ; ax имеет не используемые флаги
- 4-11 -
iret ; возврат для вызова
Done1: ; ax имеет символ
popf ; восстановление флагов int 16h
ret 2 ; отбрасывание флагов, смоделированных
; командой int и возврат
NewInt16 ENDP
----------------------------------------------------------------
Новая int 16h ISR проверяет результаты каждого считывания
(AH=0) и запроса о состоянии буфера (AH=1), но не осуществляет
проверку состояния shift запросов (AH=2). Если код ROM-BIOS воз-
вращает горячий ключ, новая ISR удаляет код клавиши из буфера
клавиатуры, инициирует TSR, и повторяет запрос. Если только пер-
вый символ буфера клавиатуры оказывается горячим ключом , ISR не
повторяет запрос. Этот пример сделан при упрощающем предположе-
нии, что реактивация резидентной программы будет безопасной.
(Подробное обсуждение этой темы смотри в разделе "Реактивация,
архитектура DOS и сервис". Следовательно, коды в листинге 4-5 яв-
ляются только моделью, и не совсем корректны).
Ограничение для этого метода заключается в том, что горячий
ключ можно выявить только один раз, когда программа переднего
плана задает считывание. Если эта программа производит большой
объем вычислений, то между временем нажатия клавиши и ответом TSR
задержка может быть большая.
Опрос буфера клавиатуры прерыванием от таймера Int 1Ch
Вы можете обеспечить постоянный контроль клавиатуры подменой
прерывания от таймера и проверкой буфера клавиатуры с помощью Ва-
шей программы обработки прерываний от таймера. Листинг 4-6 прове-
ряет используемую клавишу при каждом прерывании от таймера. Если
первый код клавиши в буфере клавиатуры соответствует используемой
клавише, новая TSR удаляет код клавиши и активизирует TSR. В про-
тивном случае новая ISR обращается к первоначальной программе об-
работки прерываний от таймера.
Листинг 4-6. Использование прерывания Int 1ch
для опроса клавиатуры
----------------------------------------------------------------
HotKey DW (?) ; определение нажатой клавиши
; заметим, что ascii
; не может быть расширена
OldInt1c DD 0 ; запоминание старого адреса ISR
NewInt1c PROC FAR ; новый таймер isr
puch ax ; необходимо для int 16h
xor al,al ; xor быстрее очищает al,чем
inc al ; mov al,1
int 16h ; проверка буфера клавиатуры
jz NoHotKey ; если z - буфер пустой
cmp ax,HotKey ; не пустой -- нажатая клавиша?
jnz NoHotKey ; если nz -- клавиша не нажата
xor al,al ; al <== запрос чтения
int 16h ; удаление нажатой клавиши
call ActivateTSR; обращение к TSR
- 4-12 -
NoHotKey pop ax ; восстановление ax
jmp cs:OldInt1ch; передача отметки времени
NewInt1c ENDP
----------------------------------------------------------------
При использовании этого метода доступен только первый символ
буфера клавиатуры. Распознавание присутствия обычного символа
спрячет от этой подпрограммы опроса горячий ключ. Предполагая,
что пользователь никогда заранее не в состоянии предвидеть запро-
сы программ на ввод, горячий ключ будет обеспечивать ожидаемую
реакцию при как угодно частом обращении. Но так как действия
пользователя непредсказуемы, этот метод не является надежным пу-
тем для распознавания горячего ключа. Еще раз заметим, что этот
пример не обеспечивает окончательно безопасность завершения TSR.
Ловушка для Int 9
Другим обращением к управлению клавиатурой является Int 9.
При нажатии или освобождении клавиши аппаратные средства генери-
руют прерывание Int 9. Новая ISR Int 9 вызывает ISR ROM клавиату-
ры и использует Int 16h для просмотра первого символа буфера кла-
виатуры. Недостатком этого обращения является то, что непустой
буфер клавиатуры скрывает горячий ключ. Если Вы можете обеспе-
чить, что ни одна TSR не будет впоследствии загружена в буфер, Вы
можете использовать этот метод, сканируя буфер целиком при каждом
нажатии клавиши.
TSR, которые расширяют буфер клавиатуры, используются до-
вольно широко. Они замещают ISR int 9 и int 16h. Их код int 9 вы-
зывает старую ISR int 9 для обслуживания прерывания от клавиатуры
и затем вызывает старую ISR int 16h для просмотра буфера клавиа-
туры. Новая ISR int 9 запоминает эти символы в своем собственном
буфере. Замещенная ISR int 16h удаляет символы из этого нового
буфера.
TSR, которые переопределяют или привязывают к клавишам мак-
роопределения, также используют этот метод. Если Ваша TSR загру-
жает перед собой другую TSR, которая пересылает буфер клавиатуры,
Ваша TSR всегда будет находить буфер пустым. Это не лучший способ
написания TSR, корректность работы которого зависит от порядка
загрузки.
Управление состоянием клавиатуры
Альтернативой для проверки буфера клавиатуры является наблю-
дение за байтом состояния клавиатуры. Этот метод исключает необ-
ходимость знать местонахождение ROM-BIOS буфера клавиатуры, но
требует, чтобы пользователь выбрал комбинацию клавиш, которая при
ее нажатии изменяет состояние клавиатуры (т. е. Alt-Shift, напри-
мер). Этот метод будет работать до тех пор, пока любая загружен-
ная после нее TSR не изменит байт состояния клавиатуры. Так как
состояние клавиатуры влияет на обработку сканируемого кода, этот
способ будет работать, пока TSR не будет изменена.
Листинг 4-7 представляет замену для ISR ROM-BIOS клавиатуры.
Некоторые вещи, которые делает эта программа, могут прямо сейчас
показаться слегка неверными, потому что процесс распознавания го-
рячего ключа возлагается на сервисную подпрограмму обработки пре-
рываний. Ниже Вы увидите,что Вы не можете обеспечить безопасность
прерывания некоторых команд DOS'а. Одной из претензий написанной
- 4-13 -
TSR является снятие этих ограничений.
В этом примере новая ISR выполняется каждый раз, когда горя-
чий ключ нажат или опущен. Сначала она вызывает старую ISR клави-
атуры для считывания и обработки сканируемого кода клавиатуры.
Новая TSR проверяет переменную PgmState, поддерживаемую TSR, для
определения, является ли TSR программой переднего плана. Если TSR
выполняется не в переднем плане и ISR распознает горячий ключ,
она попытается вызвать TSR в передний план. Если TSR в данный мо-
мент работает в переднем плане, то прерывание дальнейшей обработ-
ки не потребует.
Если биты состояния клавиатуры,соответствующие горячему клю-
чу, установлены, ISR добавляет флаг ожидания (Popup Pending) и
проверяет возможность безопасного вызова TSR в передний план.
Механизм этого процесса описывает раздел "Реактивация, архи-
тектура DOS и сервис". Если безопасность обеспечена, то чтобы ре-
активировать TSR, ISR вызывает BKGResume. DOSSafe добавляет
BusyFlag, предупреждающий повторный запуск TSR; перед возвращением
к прерванной программе ISR должна эту переменную восстановить.
Листинг 4-7. Пример замены ISR клавиатуры