Предыдущий раздел | УПРАВЛЕНИЕ ПАМЯТЬЮ И ЗАПУСК ПРОГРАММ | Следующая глава |
Работа интерпретатора команд 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.
Интересно отметить, что данная программа может запускать сама себя. В результате создается новый экземпляр программы, обладающий всеми ее свойствами.
Н а б е р и т е текст программы и проверьте практически это и другие свойства данного простейшего интерпретатора команд.
Предыдущий раздел | В начало | Следующая глава |