; zr=0 ==> не установлена
NotLoaded: or al,1 ; установка zr=0
ret ; возврат
TSRResdnt ENDP
----------------------------------------------------------------
Заметим, что TSR не может просто так перехватывать прерывание
int 2Fh. Если некоторая другая TSR, загружаемая позже других,
"захватит" этот вектор прерывания, то вход IVT будет указывать не
на первую TSR, а на загруженную позже других.
- 4-34 -
Запись адреса сегмента программного префикса (PSP)
Сегмент программного префикса (PSP) является важной структу-
рой данных в DOS. Операционная система DOS использует адрес PSP
для управления программами и поддержки многих служебных функций
ввода/вывода. DOS не знает как управлять несколькими PSP, она мо-
жет управлять только текущим PSP. Если Ваша подпрограмма TSR пе-
решла к выполнению какого-либо действия, то за судьбу текущего
PSP отвечаете Вы. Позднее мы узнаем, как сообщать DOS о том, ка-
кой PSP использовать. Если какой-либо подпрограмме TSR после ее
инициализации в последующем понадобится адрес ее PSP, то она
должна сохранить этот адрес на этапе выполнения инициализации.
Только на этапе инициализации можно быть полностью уверенным, что
текущий PSP относится непосредственно к Вам. Следующая программа
иллюстрирует, как определить адрес Вашего PSP (Листинг 4-21).
Листинг 4-21. Получение адреса PSP
----------------------------------------------------------------
MyPSP DW 0 ; здесь записывается адрес PSP
mov ah,62h ; обращение к DOS для получения текущего-
; го PSP
int 21h ; получение адреса PSP, относящегося к
; нам
mov MyPSP,bx ; сохранение PSP
----------------------------------------------------------------
Запись адреса критической секции (INDOS)
и адреса критической ошибки
После того, как TSR завершает выполнение запроса "оставить
резидентной" (функция 31h), то для последующей своей активации
она ожидает прерывание захвата. Когда TSR пробуждается, необходи-
мо иметь способ определения, что в данный момент делает программа
переднего плана, или кто активен в данный момент: DOS или BIOS.
Так как операционная система DOS не является повторно вводимой,
то в целях оказания помощи резидентной подпрограмме в решении
вопроса о безопасности приема запросов BIOS она поддерживает
флажки критической секции и критической ошибки.
Для гарантии безопасности продолжения своего выполнения при
повторной активации TSR должна проверять состояние обоих флажков:
критической ошибки и критической секции. Адрес флажка критической
секции DOS выбирает благодаря недокументированному запросу на вы-
зов функции AH=34h прерывания int 21h. В версии DOS 3.10 отсутс-
твует функция BIOS для возврата адреса флажка критической ошибки;
этот флажок размещается непосредственно перед флажком критической
секции. В версии DOS 3.20 адрес флажка критической ошибки возвра-
щает функция int 21h AX=5D06h в паре ES:BX, а в версиях DOS 3.3 и
4.0 это значение возвращается в паре DS:SI.
Учитывая способ обработки операционной системой DOS запросов
int 21h, внутри ISR нельзя гарантировать надежное получение этих
адресов. Самым надежным способом доступа к этим флажкам является
сохранение этих адресов только во время секции инициализации TSR.
Следующий фрагмент программы, приведенный в листинге 4-22, ил-
люстрирует захват адресов флажков критической секции и критичес-
кой ошибки.
- 4-35 -
Листинг 4-22. Размещение флажков критической секции
и критической ошибки
----------------------------------------------------------------
CsectFlg DW 0,0 ; адрес флажка критической секции
CErrflg DW 0,0 ; адрес флажка критической ошибки
GetCritFlags PROC NEAR
mov ah,30h ; ah <== проверка версии DOS
int 21h
cmp al,03h ; версия 3.00?
jnz WrongVersion ; если не 0 -- нет
push ax ; сохранение номера версии
mov ah,34h ; получить адрес флажка крити-
int 21h ; ческой секции
mov CSectFlg,bx ; адрес в ES:BX
mov CSectFlg+2,es ; запомнить адрес
dec bx ; предполагается, что адрес флажка
; критической ошибки предшествует
; адресу флажка критической секции
pop ax ; восстановить номер версии
cmp ah,1eh ; версия 3.30?
jnz v3xx ; если не 0 -- нет
mov ax,5d06h ; получение адреса критической
; ошибки
int 21h ; (только DOS 3.3)
v3xx: mov CErrflg,bx ; запоминание адреса критич.ошибки
mov CErrflg + 2,es ; адрес в ES:BX
; DS:SI в версиях 3.3 и 4.0
clc ; индикация успешности и
ret ; возврат
WrongVersion: ; плохая версия
stc ; индикация отказа и
ret ; возврат
GetCritFlags ENDP
----------------------------------------------------------------
Захват векторов прерываний
В какой-либо точке секции своей инициализации TSR может объ-
явить свою собственную ISR int 2Fh, чтобы впоследствии при акти-
вациях программы можно было бы локализовать ее резидентную часть.
Для этого TSR может также потребоваться модификация других входов
IVT. Прерывания int 25h (чтение с диска по абсолютным адресам) и
int 26h (запись на диск по абсолютным адресам) затрудняют измене-
ние стека. Благодаря своей природе, прерывание int 13h (нижний
уровень в/в диска) не может быть прервано. Представьте себе, что
произойдет, если прерывание с кодом int 13h было бы прервано меж-
ду поиском и передачей. Если при отработке этого прерывания слу-
чится еще одна операция в/в, то первая передача, по всей вероят-
ности, нанесет серьезный ущерб структуре диска.
В связи с этим DOS не подразумевает никаких прерываний во
время обслуживания одного из этих запросов. За защиту операцион-
ной системы DOS в подобные моменты ответственность несет програм-
ма TSR. Захват этих векторов позволяет TSR управлять активностью
диска. Эти ISR должны писаться с использованием определенных трю-
ков из-за способа использования флажков процессора. Исходная ISR
- 4-36 -
int 13h возвращает результат в регистре флажков; новая ISR должна
возвращать эти результаты быстрее, чем инструкция int 13h занесет
их в стек. Исходные ISR int 25h и int 26h добавляют, кроме того,
другое искажение, занося флажки в стек инструкцией INT. Заметим,
что новые программы ISR NewInt25 и NewInt26 перед вызовом исход-
ной подпрограммы не выполняют инструкцию push, и что все эти
ISR используют возврат far далекий. Что необходимо делать при
захвате этих прерываний, показано в листинге 4-23.
При захвате прерывания будьте осторожны. Так как вход IVT мо-
дифицирован, процессор будет диспетчировать новую ISR, даже если
адрес ISR больше не указывает на правильную программу. Можно ожи-
дать возникновения прерываний и критических ошибок. Если после
захвата прерывания возникнет любое из этих условий, то они могут
вызвать завершение программы. Операционная система DOS будет по-
вторно использовать память, занятую Вашей программой и ее ISR.
Так как это происходит, то входы IVT недолго указывают на пра-
вильные программы ISR.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ П Р Е Д О С Т Е Р Е Ж Е Н И Е ¦
¦ ¦
¦ Перед модификацией любого вектора прерывания необходимо ¦
¦ установить свои собственные программы ISR для break и кри- ¦
¦ ческой ошибки.Не пытайтесь восстановить любой из этих век- ¦
¦ торов.При завершении Вашей программы DOS будет фиксировать ¦
¦ входы IVT для этих функций. Если Вы пытаетесь восстановить ¦
¦ адрес критической ошибки, либо адрес прерывания и захвати- ¦
¦ ли другие векторы, то Ваша программа станет уязвимой к ¦
¦ преждевременному завершению. ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Листинг 4-23. Типовое замещение программ ISR дискового в/в
----------------------------------------------------------------
DiskIO PROC FAR
OldInt13 DD 0 ; здесь программа инициализации
OldInt25 DD 0 ; записывает адреса оригинальных
OldInt26 DD 0 ; int 13h, int 25h и int 26h
BusyFlag DB -1 ; флажок защиты от прерывания
; нереентерабельной программы
DiskIOExit0:
pushf ; сохраняет флажки дискового в/в
dec cs:BusyFlag ; снимает блокировку
popf ; восстанавливает флажки диск.в/в
ret 2 ; возврат с удалением флажков, по-
; мещенных при прерывании
DiskIOExit1:
pushf ; сохраняет флажки дискового в/в
dec cs:BusyFlag ; снимает блокировку
popf ; восстанавливает флажки диск.в/в
ret
NewInt13:
inc cs:BusyFlag ; снимает блокировку
pushf ; имитирует прерывание
call cs:OldInt13 ; диспетчирование реальной прог-
; раммой
jmp SHORT DiskIOExit0
- 4-37 -
; переход к общему выходу
NewInt25: inc cs:BusyFlag ; снимает блокировку
call cs:OldInt25 ; диспетчирование реальной прог-
; раммой
jmp SHORT DiskIOExit1
; переход к общему выходу
NewInt26: inc cs:BusyFlag ; снимает блокировку
call cs:OldInt26 ; диспетчирование реальной прог-