|
|
|
|
Фоновая обработка с использованием прерывания int 28h
Финальной частью изучения TSR является фоновая обработка. Эта возможность является недокументированной, и поэтому недостаточно понятной. При
правильном использовании, пока выполняется другая программа,TSR может выполнять запросы к BIOS. Программа PRINT.COM использует эту возможность для
чтения блоков файла. Система подготовки текстов может использовать эту возможность для сохранения файла параллельно с редактированием переднего плана,
а средство ведения электронных таблиц при фоновой обработке может выполнять длинные вычисления.
DOS обеспечивает в помощь программисту некоторые "зацепки", но для их использования ему необходимо довольно много поработать. Такими "зацепками"
для фоновой обработки являются программы ISR критической секции, критической ошибки и 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 ; здесь программа инициализации за-
; писывает адрес старой ISR
BusyFlag DB ; защита нереентерабельных секций
; программы
Int28ISR PROC
Int28Exit0:
dec cs:BusyFlag ; освобождение Вашей блокировки и
iret ; возврат
NewInt28:
pushf ; имитация прерывания
call cs:OldInt28 ; диспетчирование оригинальной
; программы
inc cs:BusyFlag ; попытка снять блокировку
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), уменьшает значение
флажка критической секции, переключает стеки, восстанавливает индексные регистры и изменяет переменную 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
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 (проверка состояния
inc ah
int 16h ; выдача запроса
jz _bs1 ; если 0 -- нет доступного
; символа
cmp ax,BKCombo ; это символ заднего плана?
jnz _bs1 ; если не 0 -- нет
xor ah,ah ; ah <== 0 (запрос чтения)
int 16h ; удаление символа из буфера
pop ax ; восстановление ax
;
; Выполнение на заднем плане и запрос приостановки.
;
_bs0: call BKGRestoreDTA ; восстановление DTA
call BKGRestorePSP ; восстановление PSP
call BKGRestoreErrHndlr ; восстановление старых
; драйверов критической ошиб-
; ки и прерывания
call SCRBackground ; восстановление экрана
call BKGSaveAll ; сохранение регистров зад-
; него плана
les di,InDosFlag ; es:di <== адрес входа в DOS
dec BYTE PTR es:[di] ; уменьшение флажка входа в
; DOS
switch BKGStack,OldStack ; изменение стеков
call BKGRestoreAll ; восстановление регистров
mov cs:PgmState,BKG_C_BG ; программы на заднем
; плане
ret ; возврат
_bs1: pop ax ; восстановление начального
; значения ax
_bs2: ret ; возврат
SuspendResume ENDP
_text ENDS
----------------------------------------------------------------
|
|
|