Информатика
.pdf231
“int 21h” (функция 2) – вывод символа. Перед применением вызова программа помещает в регистр DL код выводимого символа. В программе данный системный вызов используется для перевода строки, что исключает наложение на экране строк, принадлежащих родительской и дочерней программам.
“int 21h” (функция 0Ah) – ввод с клавиатуры символьной строки. В качестве последнего символа строки вводится символ 0Dh (возврат каретки). (Этот символ помещается в водимую строку драйвером клавиатуры при нажатии клавиши <Enter>.) Перед применением вызова программа помещает в регистры DS и DX соответственно адрес-сегмент и адрес-смещение для младшего байта буфера, в который должна быть введена строка. Данный буфер имеет три поля:
1)младший байт содержит максимальное число вводимых символов. Запись этого числа выполняет сама программа;
2)второй байт содержит число фактически введенных символов. Запись в этот байт выполняет DOS;
3)остальные байты буфера содержат введенную строку, заканчивающуюся кодом 0Dh.
Первый исполнительный оператор jmp «перепрыгивает» через данные программы на ее фрагмент, выполняющий возврат в систему «лишней» памяти. Вспомним, что для com-программы DOS выделяет всю ОП, оставшуюся после загрузки самой ОС. Так как наша программа использует лишь небольшую часть этой памяти, то она желает сообщить об этом системе. Простейший вариант этого – оставить за программой те 64 К, в которые первоначально загружается com-программа. Но наша программа делает больше: она создает свой новый стек, разместив его сразу за своим кодом, а от всей остальной памяти отказывается.
Само освобождение памяти выполняет ранее рассмотренный системный вызов “int 21h” (функция 4Ah). При этом количество запрашиваемых параграфов памяти определяется в результате целочисленного деления длины
232
программы (константа Lprog), увеличенной на 15 (0Fh), на длину одного параграфа – 16.
После того, как память освобождена, программа выводит на экран просьбу ввести имя загружаемого файла. Если искомый файл находится в текущем каталоге, то достаточно ввести имя файла и расширение имени (com или exe). Иначе – следует ввести имя-путь файла. Если файл находится на другом логическом диске, то имя-путь должно предваряться именем этого диска,
например: c:\distant\pr1.com .
Введенное имя файла передается на вход системного вызова “int 21h” (функция 4Bh, подфункция 0). Этому предшествует небольшая корректировка имени: в последний байт (с кодом 0Dh) записывается число 0.
Интересно отметить, что данная программа может запускать сама себя. В результате создается новый экземпляр программы, обладающий всеми ее свойствами. Н а б е р и т е текст программы и проверьте практически это и другие свойства данного простейшего интерпретатора команд.
18.3.Многопрограммный редактор
18.3.1.Структура многопрограммного редактора
Переделаем разработанный в разделе 16 редактор так, чтобы он состоял не из одной, а из нескольких программ. В результате должна получиться программная система, структура которой приведена на рис.64. На этом рисунке программы изображены в виде параллелограммов. Подобное изображение программ используется для того, чтобы подчеркнуть возможную независимость (параллельность) выполнения программ. При этом выполнение каждой программы представляет собой отдельный программный процесс, изображаемый в виде параллелограмма. Несмотря на то, что подобная параллельность реализуется лишь в мультипрограммных ОС, к которым DOS
233
не относится, мы будем использовать изображение выполнения программ в виде процессов.
Dispatcher
|
|
|
|
|
|
|
|
|
|
Init_sector |
Write_sect |
|
N_sector |
||||||
. . . |
Рис. 64. Структура многопрограммного редактора
Небольшие прямоугольники внутри процессов на рис.64 обозначают точки входа в процесс. Точка входа – место в алгоритме процесса (программы), находясь в котором процесс “простаивает”, ожидая инициирующего (запускающего) воздействия со стороны других процессов.
Как видно из рис.64, главной программой многопрограммного редактора является Dispatcher. Но в отличие от однопрограммного варианта, она инициирует не свои внутренние процедуры, а отдельные программы, каждая из которых инициируется при нажатии пользователем своей функциональной клавиши. Прежде чем инициировать соответствующую программу, диспетчер должен загрузить в ОП загрузочный модуль программы. И загрузка и инициирование программы выполняются диспетчером с помощью рассмотренного ранее системного вызова EXEC.
Стрелки на рис.64 обозначают управляющие (инициирующие) воздействия. Такое воздействие поступает на точку входа инициируемой программы. Одновременно иницирующая программа сама оказывается в одной из точек своих входов. Например, диспетчер после запуска любой своей дочерней программы сразу же оказывается в той своей точке входа, которой
234
соответствует оператор исходной программы, расположенный сразу же после вызова EXEC.
18.3.2. Программы
Применение вызова EXEC для запуска различных программ редактора отличается лишь именами com-файлов, содержащих загрузочные модули этих программ. Для того чтобы осуществлять поиск этих имен, в программе диспетчера удобно использовать измененную таблицу переходов, содержащую вместо имен процедур имена дочерних файлов. При этом в соответствии с требованиями вызова EXEC каждое имя файла должно заканчиваться нулевым байтом.
Одновременно с запуском дочерней программы программа Dispatcher передает ей на вход данные, предназначенные для обработки в запускаемой программе. В качестве таких данных используется совокупность глобальных переменных, примером которых является Sector. Несмотря на то, что эти глобальные переменные использовались нами и ранее для информационной взаимосвязи между модулями программы, механизм их передачи теперь будет другой. Дело в том, что в отличие от процедур, входящих в состав одной программы, программы не имеют общих областей (разделов) памяти. Поэтому передаваемые данные включаются или в состав «наследуемых» данных – в состав PSP и блока окружения, или передающая программа передает с помощью PSP адрес области ОП, которая содержит входные данные.
Второй из этих подходов и будет использоваться нами в многопрограммном редакторе с тем отличием, что каждая из взаимодействующих программ имеет дело со своим набором глобальных переменных. Сразу же после своего инициирования дочерняя программа копирует в свою собственную память набор глобальных переменных, принадлежащий программе Dispatcher. И, наоборот, перед своим завершением дочерняя программа выполняет обратное копирование глобальных переменных из своей области памяти в область
235
памяти диспетчера. Такое копирование глобальных переменных приводит к повышенным затратам памяти (каждая программа имеет свой экземпляр глобальных переменных), но повышает надежность всей программной системы, так как сбой в работе одной из программ не приведет к нарушению правильности главной совокупности глобальных переменных.
Для выполнения самой операции копирования удобно использовать рассмотренный нами ранее оператор movsb с префиксом rep. Напомним, что перед применением данного оператора следует сбросить флаг DF (оператором cld) и записать в регистр CX число переписываемых байтов. Что касается начальных адресов области-источника и области-приемника, то каждый из них задается не одним, а двумя регистрами:
(DS:SI) – адрес области-источника;
(ES:DI) – адрес области-приемника.
Вспомним, что ранее для передачи адресов нами использовались лишь регистры SI и DI. Такое отличие от прежнего применения оператора movsb обусловлено тем, что области памяти, участвующие в копировании, находятся теперь не в одном, а в различных сегментах памяти. Поэтому регистры DS и ES используются для передачи адресов этих сегментов.
Естественно, что при инициировании дочерней программы диспетчер должен передать ей начальный адрес своей области, содержащей глобальные переменные, в виде пары двухбайтовых чисел – (DS, SI). Для передачи этих двух чисел может быть использовано, например, поле PSP, предназначенное для устаревших блоков управления файлами и расположенное по относительному адресу 5ch. Допустим, что первым в это поле записывается требуемое содержимое DS, а затем SI.
Ниже приводится сокращенная запись модифицированной программы Dispatcher, предназначенной для многопрограммного редактора:
org 100h
236
;
;Координирует выполнение модулей редактора
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Dispatcher:
cr |
equ |
0dh |
|
lf |
equ |
0ah |
|
|
jmp |
Begin |
; Переход через данные |
;Глобальные переменные
Sector |
times |
256 db 0 |
; Буфер для редактируемого сектора |
Address |
dw |
0 |
; Адрес-смещение сектора |
N |
db |
0 |
; Номер сектора в сегменте памяти |
Lognum |
db |
0 |
; Логический номер открытого файла |
Lf |
dw |
0 |
; Длина файла в секторах |
Lsec |
dw |
0 |
; Фактическая длина сектора |
Prf |
db |
0 |
; Признак редактирования файла |
;Другие данные программы
. . . . . . . . |
|
||||
Blocpar |
times |
6 |
db |
0 |
; Первые 6 байт блока параметров |
Param |
times |
8 |
db |
0 |
; Последние 8 байт блока параметров |
; Освобождение лишней памяти
Begin:
. . . . . . . .
;Вопрос о желании редактировать файл
. . . . . . . .
;Запрос имени файла
. . . . . . . .
;Открытие файла
. . . . . . . .
;Определение длины файла
237
. . . . . . . .
;Сообщение о длине файла
. . . . . . . .
;Прежний диспетчер
. . . . . . . .
int 20h
;
;Интерпретатор команд
;- - - - - - - - - - - - - - - - -
;Входы: DL – скан-код, соответствующий команде
;Чтение: Table – таблица переходов
; |
|
|
|
|
Command: |
|
|
|
|
|
push |
ax |
|
|
|
push |
bx |
|
|
|
mov |
bx,Table |
; ВХÅадрес таблицы |
|
.M1: |
cmp |
byte[bx], 0ffh |
; Конец таблицы? |
|
|
je |
.Exit |
; Да, кода нет в таблице |
|
|
cmp |
dl,[bx] |
; Это вход в таблицу? |
|
|
je |
.Dispatch |
; Да, выполнить команду |
|
|
add |
bx,18 |
; Нет, переход к |
|
|
clc |
|
; |
следующему |
|
jmp |
.M2 |
; |
элементу таблицы |
;Обработчик команды выполняет запуск соответствующей
; |
дочерней программы |
|
|
|
.Dispatch: |
|
|
|
|
|
inc |
bx |
|
; В BX адрес имени файла |
|
mov |
dx, bx |
|
; DX Å BX |
|
mov |
[Param], ds |
; Адрес-сегмент передаваемых данных |
|
|
mov |
[Param+2], Sector |
; Адрес-смещение данных |
238 |
|
|
|
|
mov |
bx, Blocpar |
|
|
mov |
ax, 4b00h |
|
|
int |
21h |
; Запуск программы |
; |
|
|
|
.Exit: |
stc |
|
; CF Å 1 |
.M2: |
jnc |
.M1 |
; Повторение для нового элемента таблицы |
|
pop |
bx |
|
|
pop |
ax |
|
|
ret |
|
|
;Таблица переходов содержит скан-коды управляющих клавиш и
;имена программ, выполняющих соответствующие команды
Table |
db |
3bh |
; <F1> |
|
db |
‘Init_sector.com’, 0, 0 |
|
|
db |
3ch |
; <F2> |
|
db |
‘Write_sector.com’, 0 |
|
|
db |
3dh |
; <F3> |
|
db |
‘Next_sector.com’, 0, 0 |
|
|
db |
3eh |
; <F4> |
|
db |
‘Edit_sector.com’, 0, 0 |
|
|
db |
3fh |
; <F5> |
|
db |
‘Prev_sector.com’, 0, 0 |
|
|
db |
40h |
; <F6> |
|
db |
‘N_sector.com’, 0, 0, 0, 0, 0 |
|
|
db |
0ffh |
; Конец таблицы |
|
%include |
‘Video_io.asm’ |
|
|
%include |
‘Cursor_io.asm’ |
|
|
%include |
‘Kbd_io.asm’ |
В приведенной выше программе глобальные переменные программы собраны вместе в единый блок, начинающийся с метки Sector. Напомним, что
239
глобальные переменные образуют ту информацию, которой модули программной системы обмениваются между собой. Начальные адрес-сегмент и адрес-смещение этого блока записываются в поле Param, содержимое которого копируется в PSP дочерней программы во время выполнения системного вызова EXEC.
Обратим внимание, что все поля таблицы переходов, содержащие имена файлов, имеют одинаковую длину 17 байт. Это обеспечивается путем записи справа дополнительных нулей. Кроме того, следует заметить, что в конце программы не подсоединен файл Disp_sec.asm, но зато подсоединены файлы
Video_io.asm, Cursor.asm и Kbd_io.asm. Эти файлы подсоединялись к программе и раньше, но через файл Disp_sec.asm, который теперь в главной программе не нужен.
Дочерние программы удобнее реализовать в виде com-программ. Это удобство заключается в том, что все сегментные регистры содержат одно и то же, указывая на начало сегмента памяти, выделенного программе. Что касается исходных текстов дочерних программ, то они мало отличаются от текстов соответствующих процедур. Эти отличия находятся лишь в начале и в конце программ. Причем во всех дочерних программах отличия одинаковы:
;Программа выполняет одну из команд пользователя
;-------------------------------------------------------------------
|
absolute |
5ch |
; Следующий байт имеет адрес-смещение 5ch |
|
Param |
resb |
4 |
|
; Поле входных параметров |
; |
|
|
|
|
|
segment |
code |
; Виртуальный сегмент кода |
|
|
org |
100h |
|
|
|
jmp |
Begin |
|
; Переход через данные |
;Глобальные переменные (экземпляр программы)
Sector |
times 256 db 0 |
; Буфер для редактируемого сектора |
240
Address |
dw |
0 |
N |
db |
0 |
Lognum |
db |
0 |
Lf |
dw |
0 |
Lsec |
dw |
0 |
Prf |
db |
0 |
;Другие данные программы
;Адрес-смещение сектора
;Номер сектора в сегменте памяти
;Логический номер открытого файла
;Длина файла в секторах
;Фактическая длина сектора
;Признак редактирования файла
. . . . . . . .
Begin:
mov cx, 265 cld
mov ds, [Param] mov si, [Param+2] mov di, Sector
rep movsb
. . . . . . . .
mov |
cx, 265 |
cld |
|
mov |
si, Sector |
mov |
es, [Param] |
mov |
di, [Param+2] |
rep movsb |
|
;Подсоединение файлов операторами %include
. . . . . . . .
18.4. Лабораторная работа 16
Требуется реализовать описанный выше многопрограммный редактор информации.