|
|
|
|
Размещение резидентных копий TSR
Управление некоторыми действиями DOS и работой аппаратных средств предписывает использование входа IVT. TSR также использует прерывания и входы
IVT TSR для размещения резидентных копий своих программ. При этом может возникнуть необходимость размещения в памяти нескольких копий резидентных
программ TSR или необходимость размещения данных, записываемых с помощью резидентной программы. Если при выполнении TSR выбирает некоторый вход IVT,
то последовательность выполняемых программ активации размещает резидентную программу путем выполнения инструкции INT или проверки программного кода,
указываемого входом IVT.
Какой вход IVT следует выбрать? Это, прежде всего, определяется тем, что выбор прерывания для размещения резидентной программы зависит от автора
TSR. Абсолютно простой механизм отсутствует.
DOS и аппаратные средства персонального компьютера используют только некоторые из имеющихся в распоряжении входов IVT. Теоретически можно выбрать
любой неиспользуемый вход. Если TSR действительно выполняет инструкцию INT, то вход IVT должен указывать на допустимую программу обработки прерывания
(ISR). Однако, гарантия того, что вход IVT ее содержит, если TSR не инициализировала ее, отсутствует. Один из путей выхода из этой дилеммы "Catch
22" ("Ловушка 22") состоит в проверке входа IVT.
DOS загружает все программы на границу сегмента. Если вектор прерывания "захватила" предыдущая копия программы, то значение смещения (младшее
слово) во входе IVT должно соответствовать смещению ISR в текущей программе. Так как надежда на то, что программы обработки прерываний (ISR) для двух
различных программ TSR используют один и тот же вход IVT и имеют одинаковое смещение, довольно слабая, то необходимо выполнить некоторую
дополнительную проверку. Пример этого приведен в листинге 4-19.
В этом примере ищется строка ASCII UniqueID; мы могли бы выполнить в программе ISR сравнение строк. Недостатком этого способа является то, что он не
разрешает проблему "конфликтующих" прерываний. Если две программы TSR решили использовать один и тот же вход IVT, то практически не существует способа
определения того, какую TSR загружать первой.
Начиная с версии 3.0 DOS, фирма "Майкрософт" документирует многократные прерывания, что является ее первой попыткой решения проблемы
"конфликтующих" прерываний. Многократные прерывания обеспечивают гарантируемые правильные входы IVT для int 2Fh и протокол размещения программ
TSR. Начальные входы IVT этого прерывания int 2Fh указывают на инструкцию IRET. Каждая TSR, ожидающая использования мультиплексируемого прерывания,
сначала ищет предыдущие загруженные копии своей программы, а затем устанавливает свою собственную программу ISR прерывания int 2Fh.
Листинг 4-19. Размещение TSR путем использования
произвольно выбранного вектора прерывания
----------------------------------------------------------------
NewISRVector EQU ?? ; заполнение номера вектора
OldISRxx DD 0 ; здесь программа сохраняет старый вектор
UniqueID DB 'уникальная строка' ; для идентификации ISR
IDLength EQU $-UniqueID ; длина строки
NewISRxx PROC FAR ; устанавливается программой инициализации
;
; ... ; все, что выполняет ISR
;
iret
NewISRxx ENDP
Locate PROC NEAR
mov al,NewISRVector ; al <== номер вектора
mov ah,35h ; ah <== получение функции вектора
; прерывания
int 21h ; запрос к DOS для вектора прерывания
ret ; es:bx имеет адрес ISR
LocateISR ENDP
CheckISR PROC NEAR
cmp bx,OFFSET NewISRxx ; существующее смещение
; хорошее
jnz done ; если не 0 -- нет
mov si,OFFSET UniqueID ; si <== смещение UniqueID
mov di,si ; di <== смещение UniqueID
mov cx,IDLength ; cx <== длина идентификатора
cld
repnz cmpsb ; сравнение идентификаторов
done: ret ; возврат :
; zr=1 ==> результат установлен
CheckISR ENDP ; zr=0 ==> результат не установлен
TSRResdnt PROC NEAR ; определяет резидентная ли TSR
call LocateISR ; получение адреса ISR
call CheckISR ; проверяет идентификатор ID
ret ; и возвращает:
; zr=1 ==> результат установлен
; zr=0 ==> результат не установлен
TSRResdnt ENDP
----------------------------------------------------------------
|
TSR сама осуществляет поиск резидентных копий путем загрузки уникального идентификатора в регистр AH, нуля в регистр AL и выполнения инструкции int
2Fh. Программа ISR 2Fh проверяет значение в регистре AH. Если ISR распознает идентификатор, то она устанавливает AL=00fh и возвращает управление по
инструкции IRET; в противном случае она переходит к ранее сохраненной ISR int 2Fh. В конце концов, либо будет достигнут конец этой цепочки, либо
ISR распознает значение в регистре AH.
Снова возможны конфликты. Для их разрешения TSR должна выбрать дополнительные проверки. Для облегчения этой проверки можно расширить протокол
прерывания int 2Fh, но при этом следует иметь в виду, что стандарты на дополнительные проверки отсутствуют. Необходимо защищать программу. Один из
возможных подходов иллюстрирует листинг 4-20.
Фактически, получение положительного ответа на запрос int 2Fh AL=0 означает, что ответила некоторая TSR. ISR int 2Fh, показанная в листинге,
отвечает на функцию AL=1 путем возврата ее сегмента кодов в регистре ES. TSR, сделавшая начальный запрос, может использовать это значение для
нахождения некоторой уникальной строки. Если строки совпадают между собой, то можно быть уверенным, что найдена правильная ISR.
Такое расширение протокола многократного прерывания не является стандартным. Отсутствует гарантия того, что некоторая другая TSR будет выполняться в
ответ на запрос int 2Fh AL=1. Обнуление регистра ES перед выполнением этого второго запроса позволяет, по крайней мере, знать, что ответившая TSR
возвращает некоторое значение в регистре ES. (Вам конечно известно, что TSR не должна загружаться в сегмент 0).
Листинг 4-20. Размещение TSR путем использования
мультиплексируемых прерываний
----------------------------------------------------------------
OurID EQU 81h ; TSR выбирает значение AH
OldISR2f LABEL FAR ;здесь сохраняется старый вектор int 2Fh
UniqueID DB 'уникальная строка' ;для идентификации TSR
IDLength EQU $-UniqueID ; длина строки
OldInt2f DD 0 ; здесь программа инициализации запи-
; сывает начальный адрес ISR
NewISR2f PROC FAR ; новая ISR int 2Fh
cmp ah,OurID ; запрос для нас?
jz ItsMe ; если 0 -- для нас
jmp cs:Oldint2f ; передача запроса
ItsMe: or al,al ; загрузка проверена?
jnz GetAddress ; если не 0 -- нет
mov al,0ffh ; загружена
iret ; возврат
GetAddress:
cmp al,1 ; проверен ли адрес?
jnz BadFunction ; если не 0 -- нет
push cs ; возврат сегмента в ES pop es iret
BadFunction:
stc ; индуцирует ошибку
iret
NewISR2f ENDP
LocateISR PROC NEAR
mov ax,OurID SHL 8 ; ожидание чего-либо?
int 2Fh
cmp al,0ffh ; проверка выдаваемого ответа
jnz NotFound ; не 0 ==> нет ответа
xor ax,ax ; затирание сегмента
mov es,ax ; проверка выдаваемого ответа
mov ax,(OurID SHL 8) OR 1
; запрос сегмента
int 2Fh
jc NotFound ;если cy=1, то это не нам
xor ax,ax ; изменения ES проводились?
mov bx,es ; если изменения ES не проводились
cmp bx,ax
jz NotFound ; изменения ES не проводились
lea bx,NewISR2f ; ES:BX имеет адрес ISR
clc ; индикация успешна
ret ; возврат
NotFound: stc
ret
LocateISR ENDP
TSRResdnt PROC NEAR ; определяет резидентная ли TSR
call LocateISR ; получает адрес TSR
jc NotLoaded
call CheckISR ; проверяет идентификатор ID
ret ; возврат
; zr=1 ==> установлена
; zr=0 ==> не установлена
NotLoaded: or al,1 ; установка zr=0
ret ; возврат
TSRResdnt ENDP
----------------------------------------------------------------
|
Заметим, что TSR не может просто так перехватывать прерывание int 2Fh. Если некоторая другая TSR, загружаемая позже других, "захватит" этот
вектор прерывания, то вход IVT будет указывать не на первую TSR, а на загруженную позже других.
|
|