SFT_M_Written EQU 0040h ; (FILE) пользовательский файл
SFT_M_DriveMask EQU 003fh ; (FILE) маска для битов драйвера (0-5)
----------------------------------------------------------------
Сегмент программного префикса (PSP)
Когда DOS загружает программу, он создает сегмент префикса
программы. В предыдущей главе описаны многие из полей PSP. DOS
всегда помещает PSP на шестнадцатибайтовую границу параграфа, так
что он может быть описан, как значение длиной в слово (сегмент, а
смещение ноль). Команда DOS 62h возвращает адрес текущего PSP в
регистре BX (недокументированная функция AH=51h также возвращает
PSP в BX).
- 4-21 -
Листинг 4-15 показывает структуру PSP. Поля PSP
PSP_D_JFTAddr и PSP_W_JFTSize содержат адрес и размер рабочей
таблицы файлов (JFT). PSP содержит также копию (по умолчанию)
JFT, начинающуюся с JFT_T_JFT. DOS использует некоторые другие
поля PSP для обработки критических ошибок и запроса завершения;
подробнее об этих полях позднее.
Листинг 4-15. Структура PSP
----------------------------------------------------------------
PSP STRUC
PSP_W_int20 DW 0cd20h ; [00] команда int 20
PSP_W_MemSiz DW 0 ; [02] начало памяти (para)
PSP_B_Unused0 DB 0 ; [04] неизвестно
PSP_T_Call DB 09aH,0f0h ; [05] дальний вызов DOS
DB 0feH,01dh,0f0h ; диспетчер (CPM relic)
PSP_D_Term DD 0 ; [0a] конечный адрес
PSP_D_Break DD 0 ; [0e] адрес прерывания
PSP_D_CritErr DD 0 ; [12] критическая ошибка
PSP_W_Parent DW 0 ; [16] родительский PSP
PSP_T_JFT DB 14h DUP (0ffn) ; [18] таблица JFT
PSP_W_Envron DW 0 ; [2c] окружение
PSP_D_SSSP DD 0 ; [2e] SS:SP пользователя на
; время int 21
PSP_W_JFTSize DW 14h ; [32] размер JFT
PSP_D_JFTAddr DD 0 ; [34] адрес JFT
PSP_D_NextPSP DW 0ffffH,0ffffh ; [38] не применяется
PSP_T_Unused2 DB 14h DUP (0) ; [3c] не применяется
PSP_W_Int21 DW 0cd21h ; [50]
PSP_B_Retf DB 0 ; [52]
PSP_T_Unused3 DB 9 DUP (0) ; [53]
PSP_T_Parm1 DB 10h DUP (0) ; [5c] форматировано param 1
PSP_T_Parm2 DB 14h DUP (0) ; [6c] форматировано param 2
PSP_T_DTA DB 80h DUP (0) ; [80] по умолчанию DTA
PSP ENDS
----------------------------------------------------------------
Рабочая таблица файлов (JFT)
В большинстве случаев PSP будет содержать саму рабочую таб-
лицу файлов. По умолчанию JFT позволяет открыть одновременно 20
файлов, но имеется возможность создания альтернативной JFT для
увеличения максимального количества открытых файлов. В DOS 3.3
для этого имеется специальная функция (int 21h AH=67h). В DOS
версии ниже 3.3 можно изменять адрес JFT в PSP вручную. DOS для
ввода/вывода будет использовать заново определенную JFT, но будет
иметь трудности клонирования этой JFT при обработке запроса за-
грузки (int 21h AH=4bh).
Рабочая таблица файлов заносит описатели в качестве входов
таблицы файловой системы. Каждый вход JFT занимает один байт. Ес-
ли вход не использован, он содержит 0FFh; в противном случае он
содержит системный номер файла(SFN), который используется как ин-
декс в таблице файловой системы. DOS использует описатель файла в
JFT как индекс.
Листинги 4-16 и 4-17 иллюстрируют связи между PSP, JFT, SFN
и SFT. Первая подпрограмма принимает описатель в BX и возвращает
- 4-22 -
соответствующий системный номер файла (SFN) в AX. Подпрограмма
использует функцию BIOS AH=62h для размещения текущего PSP, когда
получает адрес JFT из PSP, и в конечном счете использует описа-
тель, как индекс в JFT. Макроопределения pushr и popr сохраняют и
перезапоминают регистры, описанные как аргументы. Если встрети-
лась ошибка, то эта подпрограмма возвращает флаг переноса уста-
новленным (CY=1).
Вторая подпрограмма принимает SFN в AX и возвращает адрес
соответствующего входа SFT в ES:DI. Она получает адрес "списка
списков" с функцией AH=52h и затем получает описатель списка за-
головков SFT в ES:DI. Каждый блок имеет "следующее" поле и часть
оглавления, которая показывает, сколько входов в этом блоке. Эта
подпрограмма просматривает цепочку блоков SFT до тех пор, пока не
найдет блок, содержащий вход SFT. Если описатель неверен или ес-
ли SFT испорчена, подпрограмма возвращает флаг переноса установ-
ленным.
Листинг 4-16. Использование описателя для получения
номера системного файла
----------------------------------------------------------------
GetSFN PROC NEAR
pushr ; макрокоманда сохранения
; регистров
mov ah,62h... ; получить текущий PSP
int 21h
mov ds,bx ; ds <== текущий PSP
pop bx ; описатель
cmp bx,0ffh ; проверка описателя
Jz BadHandle ; описатель не может быть
; отрицательным
cmp bx,ds:PSP_W_JFTSize
; описатель слишком велик?
jge BadHandle ; если ge - да
les di,ds:PSP_D_JFTAddr
; es:di <== JFT
mov al,es:[di][bx] ; al <== SFN (описатель)
cbw ; ax <== SFN (описатель)
clc ; успешная индикация
Done: popr ; восстановление регистров
ret ; возврат
BadHandle stc ; ошибка индикации
jmp SHORT Done ; общий выход
GetSFN ENDP
----------------------------------------------------------------
Листинг 4-17. Поиск системной таблицы файлов
----------------------------------------------------------------
LocateSFT PROC NEAR
push ax ; сохранение SFN
mov ah,52h ; запрос адреса
int 21h ; списка списков
;
; es:di <== 1-ый блок описателя оглавления списка SFT
;
- 4-23 -
les di,es:[bx].DOS_D_HDLSFT
pop ax ; восстановление SFN
xor bx,bx ; bx <== 0
_l0 cmp di,0ffffh ; конец последовательности
jz _l2 ; если z - да
;
; bx <== первый SFN в следующем блоке
;
add bx,es:[di].SFTBLK_W_Count
cmp ax,bx ; SFN в этом блоке?
jl _l1 ; если l - да
;
; es;ds <== следующий блок SFT
;
les di,es:[di].SFTBLK_D_Next
jmp SHORT _l0 ; продолжение поиска
;
; bx <== первый SFN этого блока
;
_l1 sub bx,es:[di].SFTBLK_W_Count
sub ax,bx ; ax <== смещение блока
mov bl,SFT_K_SIZE; bl <== размер входа
mul bl ; перевод смещения в байты
add di,ax ; di <== смещение в блоке
; (почти)
add di,SFTBLK_K_Size ; добавить сверху
clc ; успешная индикация
ret ; возврат
_l2 stc ; ошибка индикации
ret ; возврат
LocateSFT ENDP
----------------------------------------------------------------
Диспетчер BIOS, Int 21h
Когда загружается DOS, IBMDOS инициализирует для int 21h
вход IVT, чтобы указать на код внутри загрузочного модуля IBMDOS.
ISR обрабатывает все запросы int 21h. Так как эта программа пе-
реключает стеки и использует статические переменные, она нереен-
терабельна. Если TSR запрашивает обслуживание BIOS в неподходящее
время, она испортит сохраненную DOS информацию о программе перед-
него плана. Результаты этого разрушения обычно катастрофические.
Если Вам повезет, Ваша система гробанется, не испортив Ваш диск.
Обработка Int 21h начинается с прерываний, запрещенных в ре-
зультате команды INT. Диспетчер содержит таблицу действующих
подпрограмм, которые завершают обработку различных запросов BIOS.
Она содержит точки входа для каждой действующей функции int 21h.
Каждой строке этой таблицы непосредственно предшествует байт, со-
держащий номер входа таблицы. В конечном счете DOS использует код
функции в AH, как индекс в этой таблице и сначала проверяет зна-
чение, переданное в AH. Если запрос не выполнен, то диспетчер
возвращает ошибку.
Диспетчер Int 21h немедленно обслуживает запросы функций:
AH=51h (недокументированная - получить текущий PSP),
AH=62h (документированная - получить текущий PSP),
AH=50h (недокументированная - получить текущий PSP),
AH=33h (получить/установить прерывание).
- 4-24 -
Так как диспетчер не переключает стеки и не сохраняет контекстную
информацию в статических переменных, эти запросы всегда безопас-
ны.
Все по-другому, если запрос не является одним из этих четы-
рех немедленно обслуживаемых запросов (почти все остальные функ-
ции Int 21h). DOS cохраняет все регистры в текущем стеке, сохра-
няет текущее содержание DS:BX в статической переменной и
увеличивает флаг критического интервала (известный также, как
InDOS). Для продолжения обработки запроса BIOS диспетчеру нужны
регистры DS и BX; DOS будет перезагружать эти регистры перед пе-
редачей управления подпрограмме, которые будут завершать обработ-
ку запроса.
В это время регистры SS:SP все еще содержат адрес стека
программы переднего плана. DOS записывает в статических перемен-
ных значения SS:SP для текущего и предшествующего входа (то есть
соответствующие последние значения SS:SP диспетчера). Диспетчер