Файл: jourdain_spravochnik_programmista.docx

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 04.07.2020

Просмотров: 3128

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

handler), в которой доступ к файлу предоставляется по кодовому

номеру, а не через FCB (также обсуждается в [5.3.5]).

Наконец, Вы должны построить строку с указанием накопителя,

пути и имени файла. Эта строка именует загружаемую программу.

DS:DX указывает на эту строку при выполнении EXEC. Эта строка -

стандартная строка ASCIIZ, т.е. ничего более, чем стандартная

спецификация файла, завершаемая кодом ASCII 0. Например, это

может быть B:\NEWDATA\FILER.EXE<NUL>, где символом <NUL> обозна-

чен код ASCII 0.

После того как вся указанная информация подготовлена, остается

последняя задача. Поскольку все регистры будут изменены вызывае-

мой задачей, то надо сохранить сегмент стека и указатель стека, с

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

возвращено вызвавшей задаче. Для их сохранения создайте перемен-

ные. Поскольку значение регистра DS также будет изменено, то эти

переменные не могут быть найдены, до тех пор пока не будут повто-

рены операторы MOV AX,DSEG и MOV DS,AX. После того как SS и SP

сохранены, поместите 0 в AL, для выбора операции "загрузка и

запуск" (EXEC используется также для оверлеев [1.3.5]). Затем

поместите 4AH в AH и вызовите прерывание 21H. В этот момент запу-

щены две программы, причем программа "родитель" находится в оста-

новленном состоянии. MS DOS предоставляет возможность программе

потомку передать родителю код возврата, таким образом могут быть

переданы ошибки и статус. В [7.2.5] объяснено как это сделать.

Что касается самой функции запуска, то при возникновении ошибки

устанавливается флаг переноса, а регистр AX в этом случае будет

возвращать 1 - для неправильного номера функции, 2 - если файл не

найден, 5 - при дисковой ошибке, 8 - при нехватке памяти, 10 -

если неправильна строка среды и 11 - если неверен формат.



Приводимый пример - простейший из возможных, но часто больше

ничего и не надо. Здесь оставлен нулевым блок параметров и не

создана строка среды. Это означает, что загружаемой программе не

будет передаваться командная строка и что среда будет такой же,

как и для вызывающей программы. Вы должны только изменить распре-

деление памяти, создать имя и (пустой) блок параметров и сохра-

нить значения SS и SP.


;---в сегменте данных

FILENAME DB 'A:TRIAL.EXE',0 ;загружаем TRIAL.EXE

PARAMETERS DW 7DUP(0) ;нулевой блок параметров

KEEP_SS DW 0 ;переменная для SS

KEEP_SP DW 0 ;переменная для SP


;---перераспределение памяти

MOV BX,ZSEG ;получить # параграфа конца

MOV AX,ES ;получить # параграфа начала

SUB BX,AX ;вычислить размер программы

MOV AH,4AH ;номер функции

INT 21H ;перераспределение

;---указываем на блок параметров

MOV AX,SEG PARAMETERS ;в ES - сегмент

MOV ES,AX ;

MOV BX,OFFSET PARAMETERS ;в BX - смещение

;---сохранить копии SS и SP

MOV KEEP_SS,SS ;сохраняем SS

MOV KEEP_SP,SP ;сохраняем SP

;---указываем на строку имени файла

MOV DX,OFFSET FILENAME ;смещение - в DX


MOV AX,SEG FILENAME ;сегмент - в DS

MOV DS,AX ;

;---загрузка программы

MOV AH,4BH ;функция EXEC

MOV AL,0 ;выбираем "загрузку и запуск"

INT 21H ;запускаем задачу

;---впоследствии, восстанавливаем регистры

MOV AX,DSEG ;восстанавливаем DS

MOV DS,AX ;

MOV SS,KEEP_SS ;восстанавливаем SS

MOV SP,KEEP_SP ;восстанавливаем SP


;---в конце программы создаем фиктивный сегмент

ZSEG SEGMENT ;см. [1.3.1]

ZSEG ENDS




1.3.3 Использование команд интерфейса с пользователем из

программы.




Программа может иметь в своем распоряжении полный набор команд

интерфейса с пользователем DOS, таких как DIR или CHKDSK. Когда

эти команды используются из программы, загружается и запускается

вторая копию COMMAND.COM. Хотя такой подход может сэкономить

много усилий при программировании, для его успешной реализации

требуется достаточное количество памяти для этой второй копии и

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


Высокий уровень.


Бейсик 3.0 может загрузить вторую копию COMMAND.COM с помощью

оператора SHELL. SHELL обсуждается в [1.3.2]. COMMAND.COM загру-

жается когда не указано имя файла, поэтому вводя просто SHELL, Вы

получаете запрос MS DOS. В этот момент можно использовать любую

из утилит DOS, включая командные файлы. Для возврата в вызвавшую

программу надо ввести EXIT.


Средний уровень.


В этом случае к примеру, приведенному в [1.3.2] нужно добавить

командную строку. Обычно она начинается с байта длины строки,

затем следует сама командная строка и, наконец, код ASCII 13. При

передаче команды COMMAND.COM Вы должны указать /C перед строкой

(см. пункт "Вызов вторичного командного процессора" руководства

по MS DOS). Вы должны также указать накопитель, на котором нахо-

дится COMMAND.COM, поместив имя накопителя в начале командной

строки. Чтобы вывести каталог накопителя A:, а COMMAND.COM при

этом находится на накопителе B:, нужна строка:


COMMAND_LINE DB 12,'B: /C DIR A:',13


Следующий кусочек кода устанавливает адрес командной строки в

блок параметров, используемый в примере [1.3.2]:


LEA BX,PARAMETERS ;получение адреса блока пар-ров

MOV AX,OFFSET COMMAND_LINE ;получение смещения ком. строки

MOV [BX]+2,AX ;пересылка в 1-е 2 байта блока

MOV AX,SEG COMMAND_LINE ;получение сегмента ком. строки

MOV [BX]+4,AX ;пересылка во 2-е 2 байта блока




1.3.4 Сохранение программы в памяти после завершения.




Программы, оставленные резидентными в памяти, могут служить в

качестве утилит для других программ. Обычно такие программы вызы-

ваются через неиспользуемый вектор прерывания. MS DOS рассматри-

вает такие программы как часть операционной системы, защищая их

от наложения других программ, которые будут загружены впоследст-

вии. Резидентные программы обычно пишутся в форме COM, что обсуж-

дается в пункте [1.3.6]. Программы, написанные в форме EXE оста-

вить резидентными в памяти немного труднее.

Завершение программы прерыванием 27H оставляет ее резидентной


в памяти. CS должен указывать на начало PSP для того, чтобы эта

функция работала правильно. В программах COM, CS сразу устанавли-

вается соответствующим образом, поэтому надо просто завершить

программу прерыванием 27H. В программах EXE , CS первоначально

указывает на первый байт, следующий за PSP (т.е. 100H). При нор-

мальном завершении EXE программы последняя инструкция RET вытал-

кивает из стека первые положенные туда значения: PUSH DX / MOV

AX,0 / PUSH AX. Поскольку DS первоначально указывает на начало

PSP, то при получении этих значений из стека счетчик команд ука-

зывает на смещение 0 в PSP, где при инициализации записывается

инструкция INT 20H. Поэтому INT 20H выполняется, а это стандарт-

ная функция для завершения программы и передачи управления в DOS.

На рис. 1-5 показан этот процесс. Чтобы заставить прерывание 27H

работать в EXE программе надо поместить 27H во второй байт PSP

(первый содержит машинный код инструкции INT), а затем завершить

программу обычным RET. Для обоих типов файлов прежде чем выпол-

нить прерывание 27H, DX должен содержать смещение конца програм-

мы, отсчитываемое от начала PSP.


Средний уровень.


Вектор прерывания устанавливается с помощью функции 25H преры-

вания 21H, как показано в [1.2.3] (здесь используется вектор

70H). Позаботьтесь, чтобы процедура оканчивалась IRET. Кроме

самой процедуры, устанавливаемая программа не должна делать ниче-

го, кроме инициализации вектора прерывания, присвоения DX значе-

ния смещения конца процедуры и завершения. Для COM файлов просто

поместите оператор INT 27H в конец программы. Для EXE файлов

поместите этот оператор в первое слово PSP и завершите программу

обычным оператором RET. Для того чтобы выполнить процедуру, впос-

ледствии загруженная программа должна вызвать INT 70H.

Приведены примеры для обоих типов файлов (COM и EXE). В обоих

установлена метка FINISH для отметки конца процедуры прерывания

(напоминаем, что знак $ дает значение счетчика команд в этой

точке). Для COM файлов FINISH дает смещение от начала PSP, как и

требуется для прерывания 27H. Для EXE файлов смещение отсчиты-

вается от первого байта, следующего за PSP, поэтому к нему необ-

ходимо прибавить 100H, чтобы пересчитать на начало PSP. Заметим,

что поместив процедуру в начало программы, мы можем исключить

установочную часть кода из резидентной порции. Другой возможный

фокус состоит в использовании инструкции MOVSB для пересылки кода

процедуры вниз в неиспользуемую часть PSP, начиная со смещения

60H, что освобождает 160 байт памяти.



Случай файла COM:

;---здесь процедура прерывания

BEGIN: JMP SHORT SET_UP ;переход на установку

ROUTINE PROC FAR

PUSH DS ;сохранение регистров

.

(процедура)

.

POP DS ;восстановление регистров

IRET ;возврат из прерывания

FINISH EQU $ ;отметка конца процедуры

ROUTINE ENDP


;---установка вектора прерывания


SET_UP: MOV DX,OFFSET ROUTINE ;смещение процедуры в DX

MOV AL,70H ;номер вектора прерывания

MOV AH,25H ;функция установки вектора

INT 21H ;устанавливаем вектор

;---завершение программы, оставляя резидентной

LEA DX,FINISH ;определяем треб. смещение

INT 27H ;завершение


Случай файла EXE:


;---здесь резидентная процедура

JMP SHORT SET_UP ;переход на установку

ROUTINE PROC FAR

PUSH DS ;сохранение регистров

.

(процедура)

.

POP DS ;восстановление регистров

IRET ;возврат из прерывания

FINISH EQU $ ;отметка конца процедуры

ROUTINE ENDP


;---установка вектора прерывания

SET_UP: MOV DX,OFFSET ROUTINE ;смещение процедуры в DX

MOV AX,SEG ROUTINE ;сегмент процедуры в DS

MOV DS,AX ;

MOV AL,70H ;номер вектора прерывания

MOV AH,25H ;функция установки вектора

INT 21H ;установка вектора

;---завершение программы

MOV DX,FINISH+100H ;вычисляем смещение конца

MOV BYTE PTR ES:1,27H ;посылаем 27H в PSP

RET ;завершаем процедуру


Функция 31H прерывания 21H работает аналогично, за исключением

того, что в DX должно содержаться число 16-байтных параграфов,

требуемых процедуре (вычисление размера процедуры, начиная от

начала PSP - см. в примере [1.3.1]). Преимуществом этой функции

является то, что она передает родительской программе код выхода,

дающий информацию о статусе процедуры. Родительская программа

получает этот код с помощью функции 4DH прерывания 21H. Коды

выхода обсуждаются в [7.2.5].




1.3.5 Загрузка и запуск программных оверлеев.




Оверлеи - это части программы, которые остаются на диске, в то

время как тело программы резидентно в памяти. Когда требуется

функция, выполняемая каким-либо оверлеем, то он загружается в

память и программа вызывает его как процедуру. Различные оверлеи

могут загружаться в одно и то же место памяти, перекрывая преды-

дущий код. Например, программа ведения базы данных может загру-

зить процедуру сортировки, а затем перекрыть ее процедурой гене-

рации отчетов. Эта техника используется для экономии памяти. Но

она хороша только для тех процедур, которые не используются пос-

тоянно, иначе частые обращения к диску приведут к тому, что прог-

рамма будет выполняться слишком медленно.


Средний уровень.


MS DOS использует функцию EXEC для загрузки оверлеев. Эта

функция, номер 4BH прерывания 21H, используется также для загруз-

ки и запуска одной программы из другой, если поместить код 0 в

AL [1.3.2]. Если в AL поместить код 3, то тогда будет загружен

оверлей. В этом случае не создается PSP, поэтому оверлей не уста-

навливается как независимая программа. Такая процедура просто

загружает оверлей, не передавая ему управления.

Имеется два способа обеспечить память для оверлея. Может быть

использована либо область внутри тела программы, либо специально


отведена область памяти за пределами головной программы. Функции

EXEC передается только сегментный адрес, в качестве позиции, куда

будет загружен оверлей. Когда оверлей загружается в тело головной

программы, то программа должна вычислить номер параграфа, куда

будет загружаться оверлей, сама. С другой стороны, при загрузке в

специально отведенную память MS DOS обеспечивает программу номе-

ром параграфа.

В нижеприведенном примере используется загрузка в отведенную

память. Поскольку DOS отводит программе всю доступную память, то

сначала необходимо освободить память с помощью функции 4AH. Функ-

ция 48H отводит блок памяти достаточно большой, чтобы он мог

принять самый большой из оверлеев. Эта функция возвращает значе-

ние сегмента блока в AX, и этот номер параграфа определяет куда

будет загружен оверлей, а также по какому адресу оверлей будет

вызываться головной программой. Эти функции детально обсуждаются

в [1.3.1].

Кроме кода 3, засылаемого в AL, Вы должны установить для этой

функции еще два параметра. DS:DX должны указывать на строку, даю-

щую путь к файлу оверлея, завершаемую байтом ASCII 0. Необходимо

указывать полное имя файла, включая расширение .COM или .EXE,

поскольку DOS в данном случае не считает, что он ищет программный

файл.

Наконец, ES:BX должны указывать на 4-байтный блок параметров,

который содержит (1) 2-байтный номер параграфа, куда будет загру-

жаться оверлей и (2) 2-байтный фактор привязки, который будет

использоваться для привязки адресов в оверлее (привязка объяс-

няется в [1.3.6]). В качестве номера параграфа надо использовать

число, возвращаемое в AX, для номера параграфа отведенного блока

памяти. Фактор привязки дает смещение, по которому могут быть

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



зуйте номер параграфа, куда загружается оверлей. После того как

он установлен, вызовите функцию и оверлей будет загружен. Просто

изменяя путь к оверлейному файлу, можно вновь и вновь вызывать

эту функцию, загружая все новые и новые оверлеи. Если при возвра-

те установлен флаг переноса, то была ошибка и ее код будет возв-

ращен в AX. Код равен 1, если указан неверный номер функции, 2 -

если файл не найден, 5 - при дисковых ошибках и 8 - при отсутст-

вии достаточной памяти.

После того как оверлей загружен в память, к нему можно полу-

чить доступ как к далекой (far) процедуре. В сегменте данных

должен быть установлен двухсловный указатель, определяющий этот

вызов. Сегментная часть указателя просто равна текущему кодовому

сегменту. Смещение оверлея должно быть вычислено нахождением

разницы между сегментами кода и оверлея и умножением результата

на 16 (переводя величину из параграфов в байты). В нижеприведен-

ном примере две переменные OVERLAY_OFFSET и CODE_SEG помещены

одна за другой для правильной установки указателя. Однажды загру-