
Init proc ;Секция инициализации
mov DX, (init-main+10Fh)/16 ;Размер в параграфах
mov AH, 3100h ;Функция "Завершить и оставить в
Int 21h ; памяти"
Init endp
text ends
end main
Программа пишется в формате.СОМ, поэтому в ней предусматривается только
один сегмент, с которым связываются сегментные регистры CS и DS; в начале
сегмента резервируется 100h байт для PSP.
При запуске программы с клавиатуры управление передается (в соответствии
с параметром директивы end) на начало процедуры main. Командой jmp сразу же
осуществляется переход на секцию инициализации, которая может быть оформлена
в виде отдельной процедуры или входить в состав процедуры main. В секции
инициализации, в частности, подготавливаются условия для работы программы уже
в резидентном состоянии. Последними строками секции инициализации вызывается
функция DOS 31h, которая выполняет завершение программы с оставлением в
памяти указанной ее части. Размер резидентной части программы (в параграфах)
передается DOS в регистре DX. Определить размер резидентной секции можно,
например, следующим образом. К разности смещений init-main, которая равна
длине резидентной части программы в байтах, прибавляется размер PSP (100h) и
еще число 15 (0Fh) для того, чтобы после целочисленного деления на 16
результат был округлен в большую сторону.
С целью экономии памяти секция инициализации располагается в конце
программы и отбрасывается при ее завершении.
Функция 31h, закрепив за резидентной программой необходимую для ее
функционирования память, передает управление командному процессору и
вычислительная система переходит в исходное состояние. Наличие программы,
резидентной в памяти, никак не отражается на ходе вычислительного процесса,
за исключением того, что уменьшается объем свободной памяти. Одновременно в
память может быть загружено любое число резидентных программ.
Взаимодействие элементов резидентной программы.
Любая резидентная программа имеет по крайней мере две точки входа. При
запуске с клавиатуры программы типа.СОМ управление всегда передается на
первый байт после PSP (IP=100h). Поэтому практически всегда первой командой
резидентной программы является команда jmp, передающая управление на начало
секции инициализации.
После отработки функции DOS 31h программа остается в памяти в пассивном
состоянии. Для того, чтобы активизировать резидентную программу, ей надо
как-то передать управление. Вызвать к жизни резидентную программу можно
разными способами, но наиболее употребительным является механизм аппаратных
или программных прерываний. В этом случае в процессе инициализации необходимо
заполнить соответствующий вектор адресом точки входа в программу. Адрес образует вторую точку входа в программу, через которую осуществляется ее активизация. Очевидно, что резидентная секция программы должна заканчиваться командой выхода из прерывания iret.
Рис.1 элементы резидентной программы и их взаимодействие
PSP |
main proc jmp init |
Резидентные данные |
entry . . Резидентные программы . main endp |
int proc . . Секция инициализации . mov AH, 31h int 21h init endp
|
end main |
После того, как программа загружена и осталась
резидентной, удалить ее из памяти уже нельзя никакими силами. Чтобы привести компьютер в обычное состояние, его придется перезагрузить. В DOS не предусмотрены средства удаления резидентных программ; резидентная программа должна иметь такие средства внутри себя. Трудность здесь заключается в том, что резидентные программы практически всегда заполняют какие-то векторы прерываний, и перед выгрузкой такой программы из памяти эти векторы следует восстановить, чтобы не нарушить работоспособность системы. Однако никто, кроме самой программы, не знает, какие векторы были разрушены, и что в них было до вмешательства резидентной программы. Поэтому почти невозможно корректно выгрузить резидентную программу без ее активного участия в этом процессе. Включение в резидентную программу средств ее выгрузки (обычно по команде пользователя) требует существенного ее усложнения.
Чтобы выгрузить резидентную программу, необходимо:
- восстановить прежние значения всех перехваченных векторов прерываний;
- освободить блок памяти занимаемый программой;
- освободить блок памяти занимаемый ее окружением.
Для выполнения функции освобождения блоков памяти служит прерывание:
Int 21,49 - освободить блок памяти
ES - сегментный адрес выгружаемого блока памяти
Размер выгружаемого блока памяти определяется чтением данных из
блока MCB внутри самого прерывания Int 21,49.
Восстановить прежние значения всех перехваченных векторов прерываний
можно только внутри собственной программы, так как никто не знает, где
спрятаны старые значения их адресов.
ЗАМЕЧАНИЕ: Корректно выгрузить можно только последнюю резидентную
программу, перехватившую заданное прерывание. В противном случае, все
позже загруженные резидентные программы, вызываемые тем же прерыванием,
останутся в памяти но вызываться не будут.
Текст программы:
SEG1 SEGMENT
ASSUME SS:Seg1, DS:Seg1, CS:Seg1,ES:Seg1
ORG 100h
BYT: JMP M1
;--------------------------------Рез.часть
;Поля данных резидентной секции
old_2F dd 0 ; Старый адрес Int 2Fh
old_1C dd 0 ; Старый адрес Int 1Сh
M4:
cmp AH,0C8h ;Наша функция прерывания 2Fh?
jne next_2f ;На переход, eсли не наша
cmp AL,00h ;Наша, пришла комманда 00h?
je in_mem ;Да, сообщим 'Я уже в памяти'
cmp AL,01h ;Пришла комманда 01h ?
je unset1 ;Да, на блок выгрузки
next_2f:
jmp CS:old_2f ;Переход к 'чужой' резидентной
;программе раньше нас перехватившей
;прерывание INT 2Fh