┌──┌─┐┌──
──┘├─┘──┘ Presents
┐ ┌┐┐┌─┤ VMag, Issue 2, 1 June 1998
└─┘┘ ┘└─┘ ──────────────────────────
Terminate & Stay Resident - II.
Создание TSR-программ без PSP.
"Старые песни о главном" ;). Эта статья является логическим
продолжением TSR.Txt из LMD #1'97. За два номера журнала это уже
третья статья, посвященная написАнию резидентных программ (вторая -
MCB.Txt by Denis Vechersky aka BeHeMoT из этого же номера). В моей
первой статье упомяналось о TSR без PSP, но это было не совсем то,
что подразумевается под этим выражением. В ней описывалось, как
самому создать блок MCB. Более подробно об этом написАл Денис.
Преимуществом этого способа является то, что при распределении памяти
мы можем попасть в "дырку", находящуюся на более младших ;) адресах
памяти, чем то место, в которое нас поместил ДОС, или если мы хотим
выделить себе память, например, в UMB (для этого достаточно изменить
стратегию распределения памяти, выделить память, а потом восстановить
стратегию - функции 5800h - получить текущую, 5801h - установить
стратегию Int 21h). Если говорить о первом случае, то обычно такие
"дырки" либо очень маленькие, либо их нет совсем. А вот и недостаток
этого способа - тогда мы можем выделить себе память только _за_ тем
местом, в котором мы сейчас находимся, и при завершении программы
получим фрагментацию памяти. Этого можно избежать, освободив
занимаемую нами память, но тогда мы не сможем завершиться обычным
способом. А вот теперь пришло время кратко описать PSP, если кто-то
это не очень хорошо себе представляет.
При разработке TSR-программ стандартными средствами DOS в
памяти после завершения программы остается PSP (Program Segment
Prefix) размером 256 байт, который строится DOS при запуске любой
программы и находится перед началом программы.
Формат PSP.
Смещение Размер ОписАние
00h DW Инструкция Int 20h.
02h DW Размер памяти в параграфах.
04h DW Резерв.
06h DD Длинный вызов диспетчера функций DOS.
0Ah DD Копия вектора Int 22h, по которому управление
передается для завершения программы.
0Eh DD Копия вектора Int 23h, по которому управление
передается при нажатии Ctrl-Break или Ctrl-C.
12h DD Копия вектора Int 24h, по которому управление
передается при обнаружении критической ошибки.
16h DW Сегментный адрес PSP родительского процесса
(адрес текущего PSP для процесса, у которого нет
родителя).
18h 20 байт File Handle Table. Содержит 20 однобайтовых
индексов для системной таблицы файлов. Первые
пять входов предназначены для Stdin, Stdout,
Stderr, Auxio и Lstout.
2Ch DW Сегментный адрес блока среды для процесса.
2Eh DD Область сохранения указателя стека процесса,
когда процесс использует стек DOS (т.е.
содержимое SS:SP перед последним вызовом Int
21h).
32h DW Максимальное количество входов в File Handle
Table (default 20).
34h DD Адрес File Handle Table (default указывает на
таблицу в текущем PSP).
38h 24 байта Резерв.
50h 3 байта Инструкции Int 21h, RetF. Используется для
вызова диспетчера функций DOS.
53h DW Резерв.
55h 7 байт Расширение первого FCB.
5Ch 16 байт Начальные байты первого неоткрытого FCB.
Открытие приведет к разрушению второго FCB и
байта с длиной командной строки.
6Ch 16 байт Начальные байты второго неоткрытого FCB.
Открытие приведет к разрушению командной строки.
7Ch DD Резерв.
80h 128 байт Область DTA (Data Transfer Area) default. Байт
с длиной командной строки и буфер командной
строки (127 байтов).
Рассмотрим теперь некоторые недокументированные функции DOS,
которые используются для построения TSR без PSP.
Установить текущий PSP.
Данная функция указывает DOS, что в качестве текущего следует
использовать указанный PSP.
In: AH = 50h
BX = Сегментный адрес нового PSP.
Создать подчиненный PSP.
Данная функция требует от DOS создать подчиненный PSP. В
отличии от функции 26h данные не копируются из текущего PSP, а
строятся заново.
In: AH = 55h
DX = Сегментный адрес для построения нового PSP.
SI = Значение, которое требуется установить в поле со
смещением 2 в новом PSP.
Теперь процесс завершения резидентной программы представляет
собой следующие действия - создание нового PSP, указание DOS
использовать этот PSP в качестве активного и либо перенос резидентной
порции программы на место старого PSP и обрезание текущего блока
памяти под необходимый размер, либо освобождение памяти, занимаемой
нами и попытка выделения нового блока (на тот случай, если вдруг
получится "сесть" ближе к DOS).
Ниже приведен пример такого алгоритма:
---8<-----
model tiny
.code
jumps
org 100h
start:
mov es,word ptr ds:[2ch]
mov ah,49h
int 21h
mov dx,cs
add dx,(end_ptr+15) shr 4
push dx
xor si,si
mov ah,55h
int 21h
mov es,dx
mov si,16h
mov di,si
movsw
push cs
pop es
mov ah,49h
int 21h
; Тут можно, например, установить другую стратегию распределения памяти
; [ ... ]
mov bx,(tsr_size+15) shr 4
mov ah,48h
int 21h
dec ax
mov es,ax
inc ax
mov word ptr es:[01h],ax
mov di,08h
mov si,offset mcb_part
mov cx,tsr_size+mcb_size
rep movsb
; А тут восстановить старую
; [ ... ]
pop bx
mov ah,50h
int 21h
mov ax,4c00h
int 21h
mcb_part:
db 'Test_TSR'
mcb_size = $-mcb_part
tsr_part:
db 100h dup (90h)
tsr_size = $-tsr_part
end_ptr = $-start+100h
end start
---8<-----
//Scout