Префикс программного сегмента
Теперь займемся вплотную префиксом программного сегмента PSP. Формат PSP уже был описан ранее, для удобства приведем его еще раз вместе со структурой из файла sysp.h:
(0) 2 | int20h | двоичный код команды int 20h (программы могут использовать эту команду для завершения своей работы) |
(+2) 2 | mem_top | нижняя граница доступной памяти в системе в параграфах |
(+4) 1 | reserv1 | зарезервировано |
(+5) 5 | call_dsp | команда вызова FAR CALL диспетчера MS-DOS |
(+10) 4 | term_adr | адрес завершения (Terminate Address) |
(+14) 4 | cbrk_adr | адрес обработчика Ctrl-Break |
(+18) 4 | crit_err | адрес обработчика критической ошибки |
(+22) 2 | parn_psp | сегмент PSP программы, запустившей данную программу (программы-родителя) |
(+24) 20 | file_tab | таблица открытых файлов, если здесь находятся байты 0FFH, то таблица не используется |
(+44) 2 | env_seg | сегмент блока памяти, содержащего переменные среды |
(+46) 4 | ss_sp | адрес стека SS:SP программы |
(+50) 2 | max_open | максимальное число открытых файлов |
(+52) 4 | file_tba | адрес таблицы открытых файлов |
(+56) 24 | reserv2 | зарезервировано |
(+80) 3 | disp | диспетчер функций DOS |
(+83) 9 | reserv3 | зарезервировано |
(+92) 16 | fcb1 | форматируется как стандартный FCB, если первый аргумент командной строки содержит правильное имя файла |
(+108) 20 | fcb2 | заполняется для второго аргумента командной строки аналогично fcb1 |
(+128) 1 | p_size | число значащих символов в неформатированной области параметров, либо буфер обмена с диском DTA, назначенный по умолчанию |
(+129) 127 | parm | неформатированная область параметров, заполняется при запуске программы из командной строки |
#pragma pack(1)
typedef struct _PSP_ { unsigned char int20h[2]; unsigned mem_top; unsigned char reserv1; unsigned char call_dsp[5]; void far *term_adr; void far *cbrk_adr; void far *crit_err; unsigned parn_psp; unsigned char file_tab[20]; unsigned env_seg; void far *ss_sp; unsigned max_open; void far *file_tba; unsigned char reserv2[24]; unsigned char disp[3]; unsigned char reserv3[9]; unsigned char fcb1[16]; unsigned char fcb2[20]; unsigned char p_size; unsigned char parm[127]; } PSP;
#pragma pack()
Программы могут получить из PSP такую информацию, как параметры командной строки при запуске, размер доступной памяти, найти сегмент области переменных среды и т.д.
Как программе узнать адрес своего PSP? Очень просто сделать это для программ, написанных на языке ассемблера: при запуске программы этот адрес передается ей через регистры DS и ES. То есть этот адрес равен DS:0000 или ES:0000 (для COM-программ на PSP указывают также регистры CS и SS).
Для программ, составленных на языке Си, доступна глобальная переменная _psp типа unsigned. Эта переменная содержит сегментный адрес PSP.
В качестве примера приведем текст программы на языке ассемблера, которая выводит на экран передаваемые ей через PSP параметры запуска:
.MODEL tiny DOSSEG
.STACK 100h
.DATA
parm_msg DB "Укажите параметры", 13, 10, "$"
.CODE .STARTUP
mov cl,ds:80h ; количество символов ; в командной строке cmp cl,0 je ask_parm ; нет параметров - просим ; задать параметры
mov si,81h ; со смещением 81h ; начинается область ; параметров cld
get_parm:
lods BYTE PTR es:[si] ; загружаем в al ; очередной ; символ строки ; параметров
mov ah,2 ; выводим его на экран mov dl,al int 21h
loop get_parm jmp end_progr
ask_parm:
mov ah, 9h mov dx, OFFSET parm_msg int 21h
end_progr: .EXIT 0
END
Приведенная ниже программа, составленная на языке Си, определяет адрес своего PSP, затем показывает содержимое некоторых полей из PSP:
#include <stdio.h> #include <stdlib.h> #include <dos.h> #include "sysp.h"
void main(void);
void main(void) {
PSP far *psp_ptr;
psp_ptr = FP_MAKE(_psp,0); // Конструируем указатель // на PSP printf("PSP расположено по адресу: %Fp\n" "Доступно памяти, байт: %ld\n" "PSP родительской программы: %Fp\n" "\n", psp_ptr, (long)(psp_ptr->mem_top)*16L, FP_MAKE(psp_ptr->parn_psp,0)); exit(0); }
Используя поле parn_psp, можно определить адрес PSP родительской программы, то есть программы, запустившей Вашу программу.
Немного о назначении полей term_adr, cbrk_adr, crit_err.
Поле term_adr содержит значение, полученное из таблицы векторов прерываний для вектора 22h. Это адрес программы, которая получает управление, когда текущая программа завершает свою работу. Это может быть, например, COMMAND.COM. Программа может создать свою собственную подпрограмму, которая будет получать управление при завершении работы основной программы. Она может записать свой собственный адрес в вектор 22h, затем запустить другую программу. В таком случае в запущенной программе это поле в ее PSP будет содержать адрес родительской программы. Когда основная программа завершает свою работу, DOS восстанавливает адрес программы завершения в векторе 22h из поля term_adr PSP.
Поле cbrk_adr содержит адрес программы обработки прерывания по нажатию Ctrl-Break из вектора 23h таблицы векторов прерываний. Так как программа может устанавливать свою собственную программу обработки прерывания по Ctrl-Break, DOS при завершении работы программы восстанавливает оригинальное значение из поля cbrk_adr.
Аналогично поле crit_err предназначено для восстановления содержимого вектора 24h - адреса обработчика критических ошибок.
Способы переназначения векторов будут приведены в разделе, посвященном прерываниям.
Конечно, программы, составленные на языке Си, не обязательно должны использовать PSP для доступа к параметрам командной строки и переменным среды. Для этого есть параметры функции main и набор функций типа getenv, putenv и т.п., предназначенных для работы со средой. Но ведь PSP содержит и другую информацию!