Предыдущий раздел УПРАВЛЕНИЕ ПАМЯТЬЮ И ЗАПУСК ПРОГРАММ Следующая глава

18.2. Запуск прикладных программ

Работа интерпретатора команд DOS основана на использовании системного вызова EXEC – “int 21h” (функция 4bh, подфункция 00h). Именно этот вызов обеспечивает загрузку заданного com- или exe-файла, его настройку (для exe-файла), выполнение полученной машинной программы, а затем возврат в ту точку программы ИК, откуда был сделан этот системный вызов.

Применив вызов EXEC в своей прикладной программе, мы сможем запустить (загрузить и выполнить) любую другую прикладную или системную обрабатывающую программу. В результате наша прикладная программа начнет выполнять некоторые функции ИК, а при некоторой доработке сможет даже заменить его. В этом нет ничего удивительного, так как основная часть ОС (ядро ОС) рассматривает ИК как обычную обрабатывающую программу (лингвистический процессор). Коль скоро вызов EXEC так важен, то внимательно рассмотрим его.

Во-первых, обратим внимание на то, что отношение между «родительской» и «дочерней» программами логически эквивалентно отношению между программой и подпрограммой, вызываемой с помощью команды call или команды int. Подобно подпрограмме, дочерняя программа является по отношению к родительской программе логической процедурой, так как на время выполнения дочерней программы выполнение родительской программы приостанавливается, а затем продолжается с той команды, которая расположена в программе сразу же после вызова EXEC.

Во-вторых, подобно обычным программным процедурам, дочерние программы могут быть вложенными. То есть дочерняя программа сама может содержать вызов EXEC, порождающий другую программу, и так далее.

В-третьих, прежде, чем выдавать вызов EXEC, родительская программа должна позаботиться о том, чтобы имелась в наличии свободная память, достаточная для размещения дочерней программы. С этой целью родительская программа должна освободиться от излишков памяти, ранее выделенной ей, используя системные вызовы, рассмотренные выше.

В-четвертых, запуск программы с помощью вызова EXEC предполагает наличие между родительской и дочерней программами не только управляющего, но и информационного взаимодействия. При этом информация может быть передана как в прямом – от родительской программы к дочерней, так и в обратном направлении.

Передача информации к дочерней программе выполняется с помощью ее PSP, который создается при выполнении вызова EXEC. Если информация не большая, то она может быть размещена в «хвосте». Большой объем информации (до 32 К) может быть размещен в блоке окружения программы. Кроме того, для передачи данных на вход дочерней программы может использоваться поле PSP, предназначенное для хранения устаревших типов блоков управления файлами.

Для того чтобы выполнение вызова EXEC привело к созданию требуемого PSP, в момент вызова регистры должны содержать следующие данные:

(DS:DX) – (адрес-сегмент: адрес-смещение) символьной строки, содержащей имя com- или exe-файла, подлежащего загрузке. Сразу же после имени файла должен находиться нулевой байт (00h);

(ES:BX) – (адрес-сегмент: адрес-смещение) блока параметров.

Блок параметров содержит:

1) 2 байта – адрес-сегмент блока окружения. Если эти байты содержат 0, то дочерняя программа получает копию блока окружения родительской программы;

2) 4 байта – адрес-смещение и адрес-сегмент символьной строки, содержащей «хвост» команды. Эта строка заканчивается символом «возврат каретки» (0Dh), который не учитывается при подсчете длины «хвоста»;

3) 8 байтов – адрес-смещение и адрес-сегмент двух блоков управления файлами (по 4 байта).

В простейшем случае, когда ни «хвост», ни блоки управления файлами не используются для передачи информации на вход дочерней программы, а блок окружения такой же, как и у родительской программы, блок параметров может быть оформлен в программе как последовательность 14-и байтов, заполненных нулями.

В случае ошибочного завершения вызова EXEC в родительскую программу возвращается флаг CF=1, а в регистре AX возвращается код ошибки.

 В случае успешного запуска и выполнения дочерней программы, она может передать в родительскую программу любой объем информации, используя для этого ту область ОП, адрес которой был получен ей ранее в составе другой информации, переданной от родительской программы.

Пример. Следующая программа выполняет роль простейшего интерпретатора команд DOS. Она запрашивает у пользователя имя файла, содержащего загрузочный модуль требуемой программы, а затем эту программу запускает. При этом могут запускаться только такие программы, которые не требуют от родительской программы каких-то входных данных. После запуска дочерней программы (успешного или неуспешного) на экран опять выводится приглашение ввести имя файла. Для прекращения работы программы достаточно вместо набора имени файла нажать клавишу <Enter>.              

 

   ;             Программа запускает другие программы 

   ;             -----------------------------------------------------

   ;

org    100h           ; Следующий байт имеет адрес-смещение 100h

Start:                     jmp    Begin

;        Данные программы

Buff            db     81                                  ; Максимальная длина имени файла

Lname        db     0                                    ; Фактическая длина имени файла

Namefile     times  81     db     0                 ; Здесь размещается имя файла

Msg1          db     ‘Ошибка распределения памяти’, 0Dh, 0Ah, ‘$’

Msg2          db     ‘Введите имя com или exe-файла’, 0Dh, 0Ah, ‘$’

Msg3          db     ‘Ошибка запуска программы’,  0Dh, 0Ah, ‘$’

Blocpar      times  14     db     0                 ; Блок параметров

;        Освобождение  лишней памяти

Begin:        

mov   sp, Lprog                       ; SP ß новая база стека

mov   ah, 4ah                          ; Функция изменения области памяти

mov   bx, (Lprog + 0Fh)/16      ; Новый размер области памяти

int     21h                                ; Изменение размера области

jnc     .M1                               ; Переход при отсутствии ошибки

mov   ah, 09h                          ; Функция вывода строки

mov   dx, Msg1                        ; DX ß адрес сообщения об ошибке

int     21h                                ; Вывод строки

jmp    .Exit                              ; Переход на завершение программы

;        Ввод имени файла

.M1:       

mov   ah, 09h                          ; Функция вывода строки

mov   dx, Msg2                       ; DX ß  адрес строки-приглашения

int     21h                               ; Вывод строки

mov   ah, 0ah                          ; Функция ввода строки

mov   dx, Buff                          ; DX ß адрес-смещение буфера ввода

int     21h                                ; Ввод строки

mov   ah, 2                              ; Функция вывода символа

mov   dl, 0ah                           ; DL ß символ перевода строки

int     21h                                ; Вывод символа

cmp   [Lname], byte 0             ; Имя файла отсутствует ?

jz       .Exit                             ; Если да

;        Запуск новой программы

xor    bx, bx                            ; BX ß 0

mov   bl, [Lname]                   ; BL ß длина имени файла

mov   [Namefile+bx], byte 0    ; Запись нуля после имени файла                     

mov   dx, Namefile                  ; DX ß смещение имени файла

mov   bx, Blocpar                   ; BX ß смещение блока параметров

mov   ax, 4b00h                      ; Функция и подфункция запуска

int     21h                                ; Запуск дочерней программы

jnc     .M2                               ; Переход при отсутствии ошибки

mov   ah, 9                              ; Функция вывода строки

mov   dx, Msg3                        ; Адрес строки-сообщения об ошибке

int     21h                                ; Вывод строки

.M2:

jmp    .M1                               ; Повторение для нового файла

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

.Exit:

mov   ax, 4c00h                       ;  Возврат в DOS с

int     21h                                ;    кодом завершения 0

;        Определение области стека

Stek                       times  64     dw     0    ; Новая область стека

Lprog                    equ    $ - Start +100h  ; Длина программы (включая PSP)

 

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

int 21h” (функция 9) – вывод строки. Данный вызов уже использовался ранее.

int 21h” (функция 2) – вывод символа. Перед применением вызова программа помещает в регистр DL код выводимого символа. В программе данный системный вызов используется для перевода строки, что исключает наложение на экране строк, принадлежащих родительской и дочерней программам.

int 21h” (функция 0Ah) – ввод с клавиатуры символьной строки. В качестве последнего символа строки вводится символ 0Dh (возврат каретки). (Этот символ помещается в водимую строку драйвером клавиатуры при нажатии клавиши <Enter>.) Перед применением вызова программа помещает в регистры DS и DX соответственно адрес-сегмент и адрес-смещение для младшего байта буфера, в который должна быть введена строка. Данный буфер имеет три поля:

1) младший байт содержит максимальное число вводимых символов. Запись этого числа выполняет сама программа;

2) второй байт содержит число фактически введенных символов. Запись в этот байт выполняет DOS;

3) остальные байты буфера содержат введенную строку, заканчивающуюся кодом 0Dh.

Первый исполнительный оператор jmp «перепрыгивает» через данные программы на ее фрагмент, выполняющий возврат в систему «лишней» памяти. Вспомним, что для com-программы DOS выделяет всю ОП, оставшуюся после загрузки самой ОС. Так как наша программа использует лишь небольшую часть этой памяти, то она желает сообщить об этом системе. Простейший вариант этого – оставить за программой те 64 К, в которые  первоначально загружается com-программа. Но наша программа делает больше: она создает свой новый стек, разместив его сразу за своим кодом, а от всей остальной памяти отказывается.

Само освобождение памяти выполняет ранее рассмотренный системный вызов “int 21h” (функция 4Ah). При этом количество запрашиваемых параграфов памяти определяется в результате целочисленного деления длины программы (константа Lprog), увеличенной на 15 (0Fh), на длину одного параграфа – 16.    

После того, как память освобождена, программа выводит на экран просьбу ввести имя загружаемого файла. Если искомый файл находится в текущем каталоге, то достаточно ввести имя файла и расширение имени (com или exe). Иначе – следует ввести имя-путь файла. Если файл находится на другом логическом диске, то имя-путь должно предваряться именем этого диска, например:  c:\distant\pr1.com .

Введенное имя файла передается на вход системного вызова “int 21h” (функция 4Bh, подфункция 0). Этому предшествует небольшая корректировка имени: в последний байт (с кодом 0Dh) записывается число 0.

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

Н а б е р и т е текст программы и проверьте практически это и другие свойства данного простейшего интерпретатора команд.

 


Предыдущий раздел В начало Следующая глава