Способ создания TSR программ без PSP

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Оформил: DeeCo

В данной статье рассматривается способ построения резидентных программ — TSR (Terminate and Stay Resident), позволяющий использовать область PSP (Program Segment Prefix) для размещения резидентного кода программы.

Несколько слов о TSR.

При разработке TSR — программ стандартными средствами DOS в памяти после завершения программы остается PSP размером 256 байт или по крайней мере его часть, если программа использует область FCB (File Control Block) и параметров для собственных нужд. Мы расскажем Вам о способе, позволяющем полностью использовать область PSP в интересах резидентной программы и приведем пример такой программы. Данный способ был разработан авторами в начале 1989 года, когда потребовалось загрузить много крохотных TSR — программ, размер которых не превышал PSP.

Кое-что о недокументированных функциях DOS.

Для понимания механизма разработки TSR без PSP необходимо ознакомиться с некоторыми недокументированными функциями DOS и с форматами PSP и MCB — Memory Control Block.

PSP — program segment prefix.

PSP всегда строится DOS при запуске любой программы и непосредственно предшествует началу программы. Рассмотрим формат PSP. СмещениеРазмерОписание

00h2 БАЙТАсодержит команду INT 20h, которая используется для завершения программы

02hСЛОВОсегментный адрес свободной памяти, следующей, за памятью, выделенной программе. Это может быть либо адрес за памятью DOS (например, A000h), либо адрес следующего доступного MCB.

04hБАЙТРезерв

05h5 БАЙТдлинный вызов диспетчера функций DOS. Содержит команду длинного перехода к диспетчеру функций DOS. Используется в программах, ориентированных на CP/M. Смещение в команде длинного перехода

содержит количество байтов, доступных в сегменте кода программы.

0AhДВ.СЛОВОкопия вектора прерывания 22h, по которому управление передается для

завершения программы.

0EhДВ.СЛОВОкопия вектора прерывания 23h, по которому управление передается при нажатии CONTROL-BREAK или CONTROL-C.

12hДВ.СЛОВОкопия вектора прерывания 24h, по которому управление передается при обнаружении критической ошибки.

16hСЛОВОсегментный адрес PSP родительского процесса (адрес текущего PSP для процесса, у которого нет родителя).

18h20 БАЙТFILE HANDLE TABLE. Содержит 20 однобайтовых индексов для системной таблицы файлов. Первые пять входов предназначены для STDIN, STDOUT,STDERR, AUXIO и LSTOUT.

2ChСЛОВОсегментный адрес блока среды для процесса.

2EhДВ.СЛОВОобласть сохранения указателя стека процесса, когда процесс использует стек DOS (т.е. содержимое SS:SP перед последним подключением функции DOS с помощью INT 21h).

32hСЛОВОмаксимальное количество входов в FILE HANDLE TABLE (по умолчанию 20).

34hДВ.СЛОВОадрес FILE HANDLE TABLE (стандартно указывает на таблицу в текущем PSP).

38h24 БАЙТАРезерв

50h3 БАЙТАкоманда INT 21h, за которой следует команда far RET. Используется для вызова диспетчера функций DOS.

53h2 БАЙТАРезерв

55h7 БАЙТрасширение первого FCB.

5Ch16 БАЙТначальные байты первого неоткрытого FCB. Открытие данного FCB приведет к разрушению второго FCB и байта с длиной командной строки.

6Ch16 БАЙТ начальные байты второго неоткрытого FCB. Открытие данного FCB приведет к разрушению командной строки.

7СhДВ.СЛОВОРезерв

80h128 БАЙТобласть DTA (Data Transfer Area) по умолчанию. Перекрывает байт с длиной командной строки и буфер командной строки (127 байтов).

Размер блока — 256 байт.

MCB — memory control block.

MCB является блоком DOS описывающим каждый распределенный участок памяти. Как правило MCB всегда строится перед PSP исполняемой программы. Рассмотрим формат MCB. СмещениеРазмерОписание

00hБАЙТтип блока:

‘M’ (4Dh) — промежуточный блок;

‘Z’ (5Ah) — последний блок.

01hСЛОВОсегмент владельца блока, 0 — свободный блок.

03hСЛОВОколичество параграфов в блоке.

05h11 БАЙТРезерв

Размер блока — 16 байт.

Недокументированные функции DOS.

Рассмотрим теперь недокументированные функции DOS, которые используются для построения TSR без PSP.

установить текущий PSP.

Данная функция указывает DOS, что в качестве текущего следует использовать указанный PSP.

Входные параметры:

AH = 50h

BX = сегментный адрес нового PSP.

создать подчиненный PSP.

Данная функция требует от DOS создать подчиненный PSP. В отличии от функции 26h данные не копируются из текущего PSP, а строятся заново.

Входные параметры:

AH = 55h

BX = сегментный адрес для построения нового PSP.

SI = значение, которое требуется установить в поле со смещением 2 в новом PSP. Процесс завершения резидентной программы.

Сущность завершения резидентной программы без PSP состоит в создании нового PSP и указании DOS использовать этот PSP в качестве активного. При этом сам текст резидентной части перемещается на начало старого PSP.

Рассмотрим последовательность действий при завершении программы.

Освободить ENVIRONMENT, адрес которого находится в PSP по смещению 2Ch. Вообще говоря, это действие не связано с собственно процессом завершения резидентной программы без PSP и рекомендуется для любого способа завершения резидентных программ.

Изменить размер памяти, используемый программой, указав в качестве нового размера длину резидентной части программы в параграфах, а в качестве адреса модифицируемой памяти — сегментный адрес PSP. Для выполнения используется функция DOS 4Ah. Если резидентная часть начинается с начала программы, то ее следует сначала сохранить, т.к. при освобождении памяти в теле программы будет построен новый MCB. Также целесообразно зарезервировать после резидентной части участок длиной 16 байт для MCB, чтобы последующие действия не уничтожили дальнейший код программы.

Вычисляется сегментный адрес для построения нового PSP по формуле 1 (размер MCB в параграфах).

Строится новый PSP по вычисленному сегментному адресу с использованием функции 55h. В качестве значения SI берется поле со смещением 2 в старом PSP.

Корректируется адрес родительского PSP в новом PSP по смещению 16h. Значение выбирается из поля с таким же смещением из старого PSP.

Новый PSP указывается DOS в качестве текущего с использованием функции 50h.

Корректируется указатель на собственника памяти в новом MCB по смещению 01h. MCB расположен по сегментному адресу равному сегменту нового PSP минус 1. Значение указателя устанавливается равным сегментному адресу нового PSP.

Резидентная часть программы пересылается по адресу сегмента старого PSP со смещением 0.

Выполняются необходимые действия для настройки резидентной части. Их можно также выполнить заранее.

Программа завершается обычным образом по функции DOS 04Ch. Совместимость.

Данный метод опробован в различных совместимых операционных средах:

— MS/PC DOS 3.30;

— MS/PC DOS 4;

— MS DOS 5 beta release;

— DR DOS 3.41;

— DR DOS 5;

— с использованием загрузчика LOADHI от системы QEMM 5.0;

— с использованием загрузки в старшую память HILOAD DR DOS 5.

Пример резидентной программы без PSP.

Для лучшего понимания порядка завершения резидентной программы без PSP приведем пример.

page 60, 132

title NONPSP — Резидентная программа без PSP

;

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

= = = = = = = = = = = = = = = = =

;

;

Пример построения резидентной программы без PSP

;

;

Авторские права ГРУППЫ ПРОГРАММИСТОВ 2 B

;

;

Язык программирования: Ассемблер

;

Транслятор: MASM

;

;

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

= = = = = = = = = = = = = = = = = =

;

;

сообщить транслятору адресацию

;

assume cs: code, ds: code, es: nothing

;

;

определить сегмент кода

;

code segment para

;

subttl Резидентная часть программы

;

ResStart label

byte;

определим начало программы

;

Текст резидентной части.

ResEnd label

byte;

конец резидентной части

ResLen equ ResEnd — ResStart;

размер резидентной части в

;

байтах

ResSize equ(ResEnd — ResStart 15) / 16;

размер резидентной

;

части в параграфах

org ResStart ResSize * 16;

для выравнивания на

;

границу параграфа

;

page

subttl Инициализация резидентной программы без PSP

;

MCBLen equ 10 h;

размер MCB

;

PSPMCB db MCBlen dup(0);

резервная область для MCB

SavRes db ResLen dup(0);

область сохранения

;

резидентной части

;

PspOld dw 0;

для адреса старого PSP

PspNew dw 0;

для адреса нового PSP

;

;

сообщения

;

MsgInst label

byte

db 0 ah, 0 dh

db ‘Программа установлена’

db 0 ah, 0 dh

db ‘$’

MsgNoInst label

byte

db 0 ah, 0 dh

db ‘Ошибка. Программа не установлена’

db 0 ah, 0 dh

db ‘$’

;

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

= = = = = = = = = = = = = = = = =

;

;

процедура инициализации резидентной программы без PSP

;

;

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

= = = = = = = = = = = = = = = = =

ini proc far

;

cld;

для операций

пересылки

mov cs: PspOld, ds;

сохраним старый PSP

;

;

Освобождение environment

;

mov ax, ds: [2 ch];

адрес сегмента

mov es, ax;

environment

cmp ax, 0;

есть environment ?

je Go1;

нет

mov ah, 49 h;

код функции

int 21 h;

освободить

;

память

jnc Go1;

успешно

jmp ErrorRet;

неуспешно

Go1:

mov word ptr ds: [2 ch], 0;

укажем, что нет

;

environment

push cs;

установим

pop es;

ES

push cs;

установим

pop ds;

DS

;

;

запомним резидентную часть

;

т.к.она будет разрушена при построении новых PSP и MCB

;

mov si, Offset ResStart;

откуда

mov di, Offset SavRes;

куда

mov cx, ResLen;

сколько

rep movsb;

перешлем

;

;

модифицируем размер памяти текущей программы от сегмента

;

PSP на длину резидентной части

;

это приведет к построению нового MCB в теле программы

;

mov ah, 4 ah;

модифицируем память

mov bx, ResSize;

размер резидентной

;

части

mov es, PspOld;

сегмент старого PSP

int 21 h;

выполним

jnc Go2;

успешно

jmp ErrorRet;

неуспешно

Go2:

;

;

подсчитаем сегмент памяти для нового PSP

;

mov ax, ResSize;

длина резидентной

;

части в параграфах

add ax, PspOld;

плюс сегмент

;

старого PSP

add ax, MCBLen / 16;

плюс длина MCB

mov PspNew, ax;

запомним сегмент

;

для нового PSP

;

;

создадим новый PSP

;

mov es, PspOld;

адрес старого PSP

mov si, word ptr es: [02 h];

конец памяти

mov ah, 55 h;

построим

;

подчиненный PSP

mov dx, PspNew;

сегмент нового PSP

int 21 h;

выполним

;

;

скорректируем указатель на родительский PSP в новом PSP

;

mov es, PspOld;

сегмент старого PSP

mov ax, word ptr es: [16 h];

адрес родительского

;

PSP

mov es, PspNew;

сегмент нового PSP

mov word ptr es: [16 h], ax;

скорректируем

;

;

сообщим DOS о новом активном PSP

;

mov bx, PspNew;

сегмент нового PSP

mov ah, 50 h;

функция указания нового PSP

int 21 h;

выполним

;

;

укажем принадлежность полученной памяти для нового PSP

;

mov ax, bx;

сегмент нового PSP

sub ax, 1;

сегмент

;

построенного MCB

mov es, ax

;

= = = = = = = = = = = = = = = = = =

mov word ptr es: [01 h], bx;

скорректируем адрес

;

владельца памяти

;

;

переместим резидентную часть на старый PSP

;

mov es, PspOld;

адрес старого PSP

mov cx, ResLen;

размер резидентной

;

части

mov di, 0;

куда

mov si, Offset SavRes;

откуда

rep movsb;

переместим

push cs;

восстановим

pop es;

ES

;

;

Успешный выход из программы

;

PrgEnd:

mov ah, 09 h;

выведем сообщение

mov dx, offset MsgInst;

об успешной

;

установке

int 21 h;

mov ax, 4 c00h;

завершим программу

int 21 h;

обычным

;

образом с кодом 0

;

;

Выход из программы по ошибке

;

ErrorRet:

push cs;

установим адресацию

pop ds;

данных

mov ah, 09 h;

выведем сообщение

mov dx, offset MsgNoInst;

о неуспешной

;

установке

int 21 h;

mov ax, 4 c04h;

завершим программу

int 21 h;

обычным

;

образом с кодом 4

ini endp

;

code ends

;

;

СТЕК

;

stack segment stack

dw 512 dup(0)

stack ends

end ini

{/codecitation}

Добавить комментарий