критической секции, критической ошибки и int 28h. Довольно часто
программы много времени расходуют на ожидание ввода информации.
Путем захвата прерывания int 28h TSR может использовать для своих
целей циклы центрального процессора, которые иначе бы тратились
на ожидание ввода информации. В связи с тем, что эту возможность
также могут использовать и другие программы TSR, то ISR прерыва-
ния int 28h, когда она выполняется, должно образовывать цепочку с
предыдущей ISR.
ISR прерывания int 28 запускает TSR только в том случае, если
прикладная программа переднего плана использует функцию DOS в/в
символов. TSR, которая нуждается в фоновой работе, обычно "захва-
тывает", кроме того, одно или два прерывания таймера. ISR таймера
обеспечивает TSR доступ к процессору даже в том случае, если при-
оритетная прикладная программа является программой с интенсивными
вычислениями, или не использует функции в/в символов.
Написание ISR int 28h является достаточно простым делом. Но-
вая ISR сначала вызывает старую ISR и затем увеличивает значение
того же самого флажка BusyFlag (флажок "занято"), используемого
программами ISR прерываний int 8, int 9 и дискового в/в. Если ре-
зультат не нулевой, то выполняются некоторые непрерываемые функ-
ции. Так как эта ISR должна получать управление только тогда,
когда доступ к диску безопасен, увеличение должно всегда выраба-
тывать нулевой результат. Тем не менее, необходимо быть готовым к
блужданиям по прерываниям int 28h. После повторной активации TSR
программа ISR уменьшает значение флажка BusyFlag и возвращает уп-
равление в DOS. Заметим, что не нужно выполнять проверку флажка
критической ошибки: поскольку выполняется запрос int 21h, то Вы
знаете, что он установлен; однако он всегда сохраняется для вы-
полнения запросов int 21h, номер функции которых больше значения
0ch. Для предохранения TSR от повторной активации при нажатии
горячего ключа или при прерывании таймера необходимо увеличить
значение флажка BusyFlag.
Листинг 4-30. Подпрограмма обслуживания прерывания int 28h
----------------------------------------------------------------
OldInt28 DD 0 ; здесь программа инициализации за-
; писывает адрес старой ISR
BusyFlag DB -1 ; защита нереентерабельных секций
; программы
Int28ISR PROC FAR
Int28Exit0:
dec cs:BusyFlag ; освобождение Вашей блокировки и
iret ; возврат
NewInt28:
pushf ; имитация прерывания
call cs:OldInt28 ; диспетчирование оригинальной
; программы
inc cs:BusyFlag ; попытка снять блокировку
- 4-45 -
jg Int28Exit0 ; если больше -- есть непрерываемые
call BKGResume ; диспетчирование фоновой задачи
dec cs:BusyFlag ; снятие блокировки
iret ; и возврат
Int28ISR ENDP
----------------------------------------------------------------
Процесс повторной активации TSR довольно прост: сохраните все
регистры в текущем стеке и переключитесь на личный стек TSR.
Большинство фоновых TSR выполняется короткое время и затем пере-
водят сами себя в состояние ожидания. Обычно они сохраняют ре-
гистры в своих собственных стеках и возвращают управление в пре-
рванную программу. Как часть последовательности повторной
активации восстанавливайте регистры, сохраненные стеками TSR,
когда они переводятся в состояние ожидания.
Программа PRINT.COM во время выполнения последовательности
действий при своей повторной активации, увеличивает значение
флажка критической секции. Эта утилита выполняет довольно необыч-
ные действия. Она обходит DOS и непосредственно вызывает драйвер
устройства печати. Вероятно, увеличение значения флажка критичес-
кой секции устраняет проблемы возможного повторного входа в драй-
вер устройства. Если Ваша TSR осуществляет непосредственный до-
ступ к драйверу, то, по всей видимости, очень полеэно подражать
действиям программы PRINT.COM.
Далее, установите свои собственные драйверы прерывания и кри-
тической ошибки, сделайте текущим PSP для Вашей TSR, и переключи-
тесь на личную DTA. TSR, из которой была выбрана следующая под-
программа, поддерживает как активацию горячего ключа, так и акти-
вацию фоновой обработки. Если повторная активация выполняется в
ответ на нажатие горячего ключа, то необходимо сохранить содержи-
мое текущего изображения на экране дисплея и выключить из работы
буфер опережающего ввода информации с клавиатуры. Здесь сделано
предположение о том, что любые клавиши в буфере опережающего вво-
да информации с клавиатуры были нажаты для предыдущей текущей
программы и внесут только путаницу в повторную активацию TSR. Ин-
струкция возврата управления передает управление TSR. По заверше-
нии работы TSR будет вызывать подпрограмму BKGSuspend (перевод в
состояние ожидания для фоновой работы).
Коды, обеспечивающие ожидание, немного странны. TSR, исполь-
зующая эти коды, периодически вызывает BKGSuspend. При определен-
ных условиях BKGSuspend отправляет TSR на задний план, а в других
случаях она ничего не делает. Пользователь может пожелать активи-
зировать TSR с заднего плана путем нажатия горячего ключа. Если
TSR, выполняющаяся на переднем плане, вызывает подпрограмму
BKGSuspend, то эта подпрограмма проверяет буфер опережающего вво-
да информации с клавиатуры на наличие особой клавиши (BGCombo),
нажатие которой отправляет ее на задний план. Если в буфере этой
клавиши нет, то подпрограмма BKGSuspend игнорирует запрос на при-
остановку. Если клавиша BGCombo в буфере обнаружена, или если в
настоящий момент TSR выполняется на заднем плане, то она переста-
ет быть активной.
Приостановка выполняется по шагам, аналогично активации, но в
обратном порядке. Подпрограмма BKGSuspend восстанавливает сохра-
ненные DTA и PSP, восстанавливает драйверы прерывания и критичес-
кой ошибки, сохраняет текущие регистры, восстанавливает экран
(SCRBackground), уменьшает значение флажка критической секции,
переключает стеки, восстанавливает индексные регистры и изменяет
- 4-46 -
переменную PGMState. Инструкция RET в конце подпрограммы
BKGSuspend возвращает управление в ISR, которая активизирует TSR.
Если TSR выполнялась на заднем плане, то подпрограмма
SCRBackground не выполняет переключение экрана.
Листинг 4-31. Перевод в состояние ожидания
и возобновление работы TSR
----------------------------------------------------------------
SuspendResume PROC NEAR
AltF10 EQU 113 ; расширенный код ASCII для
; клавиши ALT F10
BGCombo EQU AltF10 SHL 8 ; LSB расширенного ASCII=0
BKG_C_FG EQU 1
BKG_C_BG EQU 2
SaveStack STRUC
rSP DW 0
rSS DW 0
SaveStack ENDS
switch MACRO sstack,dstack ;; переключение стеков
cli ;; запрещение прерывания во
;; время переключения стеков
mov sstack.rSS,SS ;; запись текущего стека
mov sstack.rSP,SP
mov SS,dstack.rSS ;; установка нового стека
mov SP,dstack.rSP
sti ;; разрешение прерываний
ENDM
_text SEGMENT BYTE PUBLIC 'code'
PgmState DB 0 ; сохранение дорожки состояния
; программы
InDosFlag DD 0 ; здесь программа инициализации
; сохраняет адрес флажка крити-
; ческой секции
OldStack SaveStack <> ; стек прерванной программы
BKGStack SaveStack <> ; стек TSR. Устанавливается
; программой инициализации
BKGResume:
call BKGSaveAll ; сохранение всех регистров в
; текущем стеке
cld ; флаг начального направления
mov ax,cs
mov ds,ax ; ds <== программный сегмент
switch OldStack,BKGStack ; переключение на стек
; заднего плана
call BKGRestoreAll ; восстановление регистров
; заднего плана
pushr
les di,InDosFlag ; es:di <== флаг входа в DOS
inc BYTE PTR es:[di] ; установка флага входа в
; DOS
popr
call BKGNewErrHndlr ; установка своих собственных
; драйверов критической ошиб-
; ки и прерывания
call BKGSetPSP ; изменение PSP
call BKGSetDTA ; изменение DTA
- 4-47 -
cli
cmp PopupPending,0 ;;; ожидание popup?
jz _br0 ;;; если 0 -- нет
dec PopupPending ;;; уменьшение на 1
mov PgmState,BKG_C_FG ;;; перевод программы на
;;; передний план
call SCRForeground ;;; перевод экрана
call BKGBufFlush ;;; выключение буфера клавиа-
;;; туры
_br0: sti
ret
BKGSuspend:
cmp PgmState,BKG_C_FG ; выполнение на переднем
; плане?
jl _bs0 ; если меньше -- задний
; план
jg _bs2 ; если больше -- инициали-
; зация (игнорирование
; приостановки)
;
; Текущее выполнение на переднем плане. Проверка нажатия
; клавиши
;
push ax ; сохранение текущего значе-
; ния ax
xor ah,ah ; ah <== 1 (проверка состояния