Если версия некорректна, то TSR должна осуществить выход с выда-
чей соответствующего сообщения об ошибке.
DOS записывает номер версии своей системы в глобальной пере-
менной и делает доступным это значение через запрос к BIOS с функ-
цией 30h. При выполнении этого запроса диспетчер int 21h не выпол-
няет переключение стеков или изменение любых глобальных перемен-
ных. Хотя этот запрос всегда безопасен, правила хорошего тона при
программировании требуют, чтобы этот запрос выдавался бы среди ко-
дов, выполняющих в программе инициализацию. Пример определения но-
мера версии используемой операционной системы показан ниже в
листинге 4-18.
Листинг 4-18. Проверка версии DOS
----------------------------------------------------------------
VersionID EQU 0a03h ; DOS 3.10 (заметим, что млад-
; шая часть номера в MCB)
mov ah,30h ; ah <== функция для проверки
; версии DOS
int 21h ; выдача запроса
cmp ax,VersionID ; версия возвращается в ax
jnz WrongVersion ; версия ошибочна
----------------------------------------------------------------
Размещение резидентных копий TSR
Управление некоторыми действиями DOS и работой аппаратных
средств предписывает использование входа IVT. TSR также использу-
ет прерывания и входы IVT TSR для размещения резидентных копий
своих программ. При этом может возникнуть необходимость размеще-
ния в памяти нескольких копий резидентных программ TSR или необ-
ходимость размещения данных, записываемых с помощью резидентной
программы. Если при выполнении TSR выбирает некоторый вход IVT,
то последовательность выполняемых программ активации размещает
резидентную программу путем выполнения инструкции INT или провер-
ки программного кода, указываемого входом IVT.
Какой вход IVT следует выбрать? Это, прежде всего, определя-
- 4-31 -
ется тем, что выбор прерывания для размещения резидентной прог-
раммы зависит от автора 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
LocateISR 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 ; существующее смещение
- 4-32 -
; хорошее
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
- 4-33 -
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 ==> установлена