Предыдущий раздел | УПРАВЛЕНИЕ ПАМЯТЬЮ И ЗАПУСК ПРОГРАММ | Следующая глава |
Применение вызова EXEC для запуска различных программ редактора отличается лишь именами com-файлов, содержащих загрузочные модули этих программ. Для того чтобы осуществлять поиск этих имен, в программе диспетчера удобно использовать измененную таблицу переходов, содержащую вместо имен процедур имена дочерних файлов. При этом в соответствии с требованиями вызова EXEC каждое имя файла должно заканчиваться нулевым байтом.
Одновременно с запуском дочерней программы программа Dispatcher передает ей на вход данные, предназначенные для обработки в запускаемой программе. В качестве таких данных используется совокупность глобальных переменных, примером которых является Sector. Несмотря на то, что эти глобальные переменные использовались нами и ранее для информационной взаимосвязи между модулями программы, механизм их передачи теперь будет другой. Дело в том, что в отличие от процедур, входящих в состав одной программы, программы не имеют общих областей (разделов) памяти. Поэтому передаваемые данные включаются или в состав «наследуемых» данных – в состав PSP и блока окружения, или передающая программа передает с помощью PSP адрес области ОП, которая содержит входные данные.
Второй из этих подходов и будет использоваться нами в многопрограммном редакторе с тем отличием, что каждая из взаимодействующих программ имеет дело со своим набором глобальных переменных. Сразу же после своего инициирования дочерняя программа копирует в свою собственную память набор глобальных переменных, принадлежащий программе Dispatcher. И, наоборот, перед своим завершением дочерняя программа выполняет обратное копирование глобальных переменных из своей области памяти в область памяти диспетчера. Такое копирование глобальных переменных приводит к повышенным затратам памяти (каждая программа имеет свой экземпляр глобальных переменных), но повышает надежность всей программной системы, так как сбой в работе одной из программ не приведет к нарушению правильности главной совокупности глобальных переменных.
Для выполнения самой операции копирования удобно использовать рассмотренный нами ранее оператор movsb с префиксом rep. Напомним, что перед применением данного оператора следует сбросить флаг DF (оператором cld) и записать в регистр CX число переписываемых байтов. Что касается начальных адресов области-источника и области-приемника, то каждый из них задается не одним, а двумя регистрами:
(DS:SI) – адрес области-источника;
(ES:DI) – адрес области-приемника.
Вспомним, что ранее для передачи адресов нами использовались лишь регистры SI и DI. Такое отличие от прежнего применения оператора movsb обусловлено тем, что области памяти, участвующие в копировании, находятся теперь не в одном, а в различных сегментах памяти. Поэтому регистры DS и ES используются для передачи адресов этих сегментов.
Естественно, что при инициировании дочерней программы диспетчер должен передать ей начальный адрес своей области, содержащей глобальные переменные, в виде пары двухбайтовых чисел – (DS, SI). Для передачи этих двух чисел может быть использовано, например, поле PSP, предназначенное для устаревших блоков управления файлами и расположенное по относительному адресу 5ch. Допустим, что первым в это поле записывается требуемое содержимое DS, а затем SI.
Ниже приводится сокращенная запись модифицированной программы Dispatcher, предназначенной для многопрограммного редактора:
org 100h
;
; Координирует выполнение модулей редактора
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
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:
. . . . . . . .
; Вопрос о желании редактировать файл
. . . . . . . .
; Запрос имени файла
. . . . . . . .
; Открытие файла
. . . . . . . .
; Определение длины файла
. . . . . . . .
; Сообщение о длине файла
. . . . . . . .
; Прежний диспетчер
. . . . . . . .
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 ; Адрес-смещение данных
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. Напомним, что глобальные переменные образуют ту информацию, которой модули программной системы обмениваются между собой. Начальные адрес-сегмент и адрес-смещение этого блока записываются в поле 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 ; Буфер для редактируемого сектора
Address dw 0 ; Адрес-смещение сектора
N db 0 ; Номер сектора в сегменте памяти
Lognum db 0 ; Логический номер открытого файла
Lf dw 0 ; Длина файла в секторах
Lsec dw 0 ; Фактическая длина сектора
Prf db 0 ; Признак редактирования файла
; Другие данные программы
. . . . . . . .
Begin:
mov cx, 265
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
. . . . . . . .
Предыдущий раздел | В начало | Следующая глава |