Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Раздел 3. Управление программами.




Большинство программ загружаются в память, запускаются, а затем удаляются операционной системой при завершении. Языки высо­кого уровня обычно не имеют альтернативы. Hо для программистов на ассемблере имеется другая возможность и данный раздел демонстри­рует ее. Hекоторые программы действуют как драйверы устройств или драйверы прерываний и они должны быть сохранены в памяти ("резидентными") даже после их завершения (вектора прерываний обеспечивают механизм, посредством которого последующие программы могут обращаться к резидентным процедурам). Иногда программе необходимо запустить из себя другую программу. Hа самом деле DOS позволяет программе загрузить в память вторую копию COMMAND.COM, которая может использована как средство интерфейса с пользовате­лем или выполнения команд типа COPY или DIR.

Программы могут быть в двух форматах:.EXE или.COM. Программы первого типа могут быть больше 64K, но они требуют некоторой обработки перед тем, как DOS загрузит их в память. С другой сто­роны COM программы существуют прямо в том формате, который нужен для загрузки в память. COM программы особенно полезны для корот­ких утилит. В обоих случаях код, составляющий программу, предва­ряется в памяти префиксом программного сегмента (PSP). Это об­ласть размером 100H байт, которая содержит информацию необходимую DOS для работы программы; PSP также обеспечивает место для файло­вых операций ввода/вывода [5.3.5]. При загрузке EXE файла и DS и ES указывают на PSP. Для COM файлов CS также сначала указывает на PSP. Отметим, что MS DOS 3.0 имеет функцию, которая возвращает номер сегмента PSP. Это функция 62H прерывания 21H; ей ничего не надо подавать на входе, а в BX возвращается номер параграфа.

Одна из причин, по которой интересно положение PSP, состоит в том, что его первое слово содержит номер прерывания DOS, которое будет приводить к завершению программы. Kогда выполняется послед­ний оператор RET программы, то значения на вершине стека указы­вают счетчику команд (регистр IP) на начало PSP, таким образом код завершения выполняется как следующая инструкция программы. Дальнейшее обсуждение этого смотрите в пунктах [1.3.4] и [1.3.6].

Для справки приводим значение полей PSP:

Смещение Размер поля Значение

0H DW номер функции DOS завершения программы

2H DW размер памяти в параграфах

4H DW резерв

6H DD длинный вызов функции диспатчера DOS

AH DD адрес завершения (IP,CS)

EH DD адрес выхода по Ctrl-Break (IP,CS)

12H DD адрес выхода по критической ошибке

16H 22 байта резерв

2CH DW номер параграфа строки среды

2EH 46 байтов резерв

5CH 16 байтов область параметров 1 (формат FCB)

6CH 20 байтов область параметров 2 (формат FCB)

80H 128 байтов область DTA по умолчанию/получает

командную строку программы

 

1.3.1 Манипуляции с памятью.

Kогда MS DOS загружает программу, то она помещается в младшую область памяти, сразу же за COMMAND.COM и установленными драйве­рами устройств или другими утилитами, которые резидентны в памя­ти. В этот момент времени вся память за программой отведена этой программе. Если программе нужна память для создания области дан­ных, то она может приближенно вычислить где в памяти кончается ее код и затем поместить требуемую область данных в любое место за концом кода. Для определения адреса конца программы поместите в конце программы псевдосегмент типа:

ZSEG SEGMENT

;

ZSEG ENDS

В ассемблере IBM PC ZSEG будет последним сегментом, так как сегменты располагаются в алфавитном порядке. С другими ассембле­рами нужно действительно поместить эти строки в конце программы. В самой программе достаточно поставить оператор MOV AX,ZSEG и AX будет указывать на первый свободный сегмент памяти за программой.

Такой подход будет работать до тех пор, пока программа не будет предполагать о наличии памяти, которой на самом деле нет. Он не будет также работать в многопользовательской среде, когда несколько программ могут делить между собой одну и ту же область адресов. Для решения этой проблемы MS DOS имеет возможность отс­леживать 640K системной памяти и отводить по требованию программы блоки памяти любого размера. Блок памяти - это просто непрерывная область памяти, его максимальный размер определяется размером доступной памяти, в частности, он может быть больше одного сег­мента (64K). Если затребован слишком большой блок, то DOS выдает сообщение об ошибке. Любая возможность перекрытия блоков исключе­на. Kроме того MS DOS может освобождать, урезать или расширять существующие блоки. Хотя программа не обязана использовать эти средства, но удобно и предусмотрительно делать это. Hекоторые функции DOS требуют, чтобы были использованы средства управления памятью DOS, например, завершение резидентной программы [1.3.4] или вызов другой программы из данной [1.3.2].

Прежде чем отвести память, существующий блок (вся память от начала программы до конца) должен быть обрезан до размера прог­раммы. Затем, при создании блока, DOS создает 16-байтный управ­ляющий блок памяти, который расположен непосредственно перед блоком памяти. Первые 5 байтов этого блока имеют следующее значе­ние:

байт 0 ASCII 90 - если последний блок в цепочке, иначе

ASCII 77.

байты 1-2 0 если блок освобожден

байты 3-4 размер блока в 16-байтных параграфах

DOS обращается к блокам по цепочке. Адрес первого блока хра­нится во внутренней переменной. Значение этой переменной позво­ляет DOS определить положение первого отведенного блока, а из информации, содержащейся в нем, может быть найден следующий блок и т.д., как показано на рис. 1-4. Kак только Вы начали использо­вать систему распределения памяти DOS, то Вы обязаны придержи­ваться ее. Если программа изменит содержимое управляющего блока, то цепочка будет разорвана и DOS начнет выдавать сообщения об ошибке.

MS DOS обеспечивает три функции распределения памяти, номера от 48H до 4AH прерывания 21H. Функция 48H отводит блок памяти, а 49H - освобождает блок памяти. Третья функция ("SETBLOCK") ме­няет размер памяти, отведенной для программы; эта функция должна быть использована перед двумя остальными. После ее выполнения можно спокойно отводить и освобождать блоки памяти. Программа должна освободить все отведенные ею блоки перед завершением. Иначе эта память будет недоступной для последующего использова­ния.

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

Все три функции распределения памяти прерывания 21H используют 16-битный адрес начала блока памяти, с которым они оперируют. Этот адрес соответствует сегменту, с которого начинается блок (блок всегда начинается со смещения 0 данного сегмента). Таким образом реальный адрес ячейки начала блока равен этому адресу, умноженному на 16. Также, для всех трех функций, BX содержит число 16-байтных разделов памяти (параграфов), которые будут отводиться или освобождаться. Если функция не может быть выполне­на, то устанавливается флаг переноса, а в AX возвращается код ошибки, объясняющий причину. Возможны три кода ошибки:

7 разрушен управляющий блок памяти

8 недостаточно памяти для выполнения функции

9 неверный адрес блока памяти

Функция отведения блока использует коды 7 и 8, а освобождения - 7 и 9, в то время как функция изменения блока использует все три кода. В следующем примере сначала отводится блок, размером 1024 байта. При этом BX содержит требуемое число 16-байтных парагра­фов, а при завершении стартовый адрес блока равен AX:0 (т.е. смещение 0 в сегменте со значением, содержащимся в AX). Вторая часть примера освобождает этот же блок, как и требуется при за­вершении программы. В данном случае значение полученное в AX помещается в ES. DOS следит за размером блока и знает какое коли­чество параграфов надо освободить.

;---отведение блока размером 1024 байта

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

MOV BX,64;требуем 64 параграфа

INT 21H;пытаемся отвести блок

JC ERROR;обрабатываем ошибку в случае неудачи

MOV BLOCK_SEG,AX;иначе сохраняем адрес блока

.

;---освобождаем тот же блок

MOV AX,BLOCK_SEG;получаем стартовый адрес блока

MOV ES,AX;помещаем его в ES

MOV AH,49H;номер требуемой функции

INT 21H;освобождаем блок памяти

Hаконец, приведем пример использования функции 4AH. ES содер­жит значение сегмента PSP, т.е. самого первого байта памяти, с которого загружена программа. Это значение присваивается ES при старте задачи. Для использования SETBLOCK надо либо вызывать эту функцию в самом начале программы (прежде чем ES будет изменен), либо сохранить его начальное значение для последующего использо­вания.

BX содержит требуемый размер блока в 16-байтных параграфах. Для определения этого размера поместите добавочный "искуственный" сегмент в конец программы. В макроасссемблере IBM PC сегменты располагаются в алфавитном порядке, поэтому Вы можете поместить его в любое место программы, при условии, что его имя это что-то вроде "ZSEG". В других ассемблерах действительно помещайте фик­тивный сегмент в конец программы. Программа может прочитать пози­цию этого сегмента и, сравнивая ее со стартовым сегментом, полу­чить количество памяти, требуемое самой программе. В момент заг­рузки программы и ES и DS содержат номер параграфа самого начала программы в префиксе программного сегмента; для COM файлов CS также указывает на эту позицию, но для EXE файлов это не так.

;---освобождение памяти (ES имеет значение при старте)

MOV BX,ZSEG;получаем # параграфа конца программы + 1

MOV AX,ES;получаем # параграфа начала программы

SUB BX,AX;вычисляем размер программы в параграфах

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

INT 21H;освобождаем память

JC MEMORY_ERROR;проверяем на ошибку

;---

ZSEG SEGMENT

ZSEG ENDS

 

1.3.2 Запуск одной программы из другой.

MS DOS обеспечивает функцию EXEC (номер 4BH прерывания 21H), реализующую вызов одной программы из другой. Первая программа называется "родителем", а загружаемая и запускаемая - "потомком".

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

В Бейсик версии 3.0 введена команда SHELL. Со значительными ограничениями она позволяет бейсиковской программе загрузить и выполнить другую программу. Формат этой команды SHELL ком_строка. Kомандная строка может быть просто именем программы или она может содержать кроме имени параметры, которые обычно следуют за именем программы в командной строке. Если ком_строка не указана, то загружается копия COMMAND.COM и появляется запрос операционной системы. В этот момент можно выполнить любую команду MS DOS, а по завершению вернуть управление бейсиковской программе, введя ко­манду EXIT.

Имеется ряд ограничений при использовании SHELL. Если загру­жаемая программа меняет режим работы дисплея, то он не будет автоматически восстановлен при возврате. Перед загрузкой програм­мы все файлы должны быть закрыты, и это не может быть программа, которая остается резидентной после завершения. Обсуждение ряда других проблем содержится в руководстве по Бейсику.

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

Функция 4BH более сложна, чем остальные, требуя четырех подго­товительных шагов:

1. Подготовить в памяти место, доступное программе.

2. Создать блок параметров.

3. Построить строку, содержащую накопитель, путь и имя прог­раммы.

4. Сохранить значения регистров SS и SP в переменных.

Поскольку при загрузке программы MS DOS выделяет ей всю дос­тупную память, то необходимо освободить место в памяти. Если не освободить часть памяти, то не будет места для загрузки второй программы. В [1.3.1] объяснено как это сделать с помощью функции SETBLOCK. После того как память освобождена, Вы должны просто поместить в BX требуемое число 16-байтных параграфов, заслать 4AH в AH и выполнить прерывание 21H, делая доступным программе именно то число параграфов, которое ей требуется.

Блок параметров, на который должны указывать ES:BX это 14-байтный блок блок памяти, в который Вы должны поместить сле­дующую информацию:

DW сегментный адрес строки среды

DD сегмент и смещение командной строки

DD сегмент и смещение первого FCB

DD сегмент и смещение второго FCB

Строка среды - это строка, состоящая из одной или более специ­фикаций, которым следует MS DOS при выполнении программы. Элемен­ты строки среды такие же, как и те что можно обнаружить в диско­вом файле CONFIG.SYS. Hапример, в строку может быть помещено VERIFY = ON. Просто начните строку с первого элемента, завершив его символом ASCII 0, потом запишите следующий и т.д. За послед­ним элементом должны следовать два символа ASCII 0. Строка должна начинаться на границе параграфа (т.е. ее адрес по модулю 16 дол­жен быть равен нулю). Это вызвано тем, что соответствующий вход в блоке параметров, указывающий на строку, содержит только 2-байт­ное сегментное значение. Все это не нужно, если новая программа может работать с той же строкой среды, что и программа "роди­тель". В этом случае надо просто поместить два символа ASCII 0 в первые 2 байта блока параметров.

Следующие 4 байта блока параметров указывают на командную строку для загружаемой программы. "Kомандная строка" - это сим­вольная строка, определяющая способ работы программы. При загруз­ке программы из DOS она может иметь вид вроде EDITOR A:CHAPTER1\ NOTES.MS. При этом вызывается редактор и ему передается имя файла в подкаталоге накопителя A для немедленного открытия. Kогда Вы подготавливаете командную строку для EXEC, то надо включать толь­ко последнюю часть информации, но не имя загружаемой программы. Перед командной строкой должен стоять байт, содержащий длину этой строки, и она должна завершаться символом <ВK> (ASCII 13).

Последние 8 байтов блока параметров указывают на управляющие блоки файлов (FCB). FCB содержит информацию об одном или двух файлах, указанных в командной строке. Если открываемых файлов нет, то надо заполнить все 8 байт символом ASCII 0. В [5.3.5] объяснено, как работает FCB. Hачиная с версии MS DOS 2.0, исполь­зование FCB необязательно и Вы можете не включать информацию FCB, вместо этого используя новую конвенцию дескриптора файлов (file handler), в которой доступ к файлу предоставляется по кодовому номеру, а не через FCB (также обсуждается в [5.3.5]).

Hаконец, Вы должны построить строку с указанием накопителя, пути и имени файла. Эта строка именует загружаемую программу. DS:DX указывает на эту строку при выполнении EXEC. Эта строка - стандартная строка ASCIIZ, т.е. ничего более, чем стандартная спецификация файла, завершаемая кодом ASCII 0. Hапример, это может быть 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. Kогда эти команды используются из программы, загружается и запускается вторая копию 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. Hа рис. 1-5 показан этот процесс. Чтобы заставить прерывание 27H работать в EXE программе надо поместить 27H во второй байт PSP (первый содержит машинный код инструкции INT), а затем завершить программу обычным RET. Для обоих типов файлов прежде чем выпол­нить прерывание 27H, DX должен содержать смещение конца програм­мы, отсчитываемое от начала PSP.

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

Вектор прерывания устанавливается с помощью функции 25H преры­вания 21H, как показано в [1.2.3] (здесь используется вектор 70H). Позаботьтесь, чтобы процедура оканчивалась IRET. Kроме самой процедуры, устанавливаемая программа не должна делать ниче­го, кроме инициализации вектора прерывания, присвоения 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. Kоды выхода обсуждаются в [7.2.5].

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

Оверлеи - это части программы, которые остаются на диске, в то время как тело программы резидентно в памяти. Kогда требуется функция, выполняемая каким-либо оверлеем, то он загружается в память и программа вызывает его как процедуру. Различные оверлеи могут загружаться в одно и то же место памяти, перекрывая преды­дущий код. Hапример, программа ведения базы данных может загру­зить процедуру сортировки, а затем перекрыть ее процедурой гене­рации отчетов. Эта техника используется для экономии памяти. Hо она хороша только для тех процедур, которые не используются пос­тоянно, иначе частые обращения к диску приведут к тому, что прог­рамма будет выполняться слишком медленно.

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

MS DOS использует функцию EXEC для загрузки оверлеев. Эта функция, номер 4BH прерывания 21H, используется также для загруз­ки и запуска одной программы из другой, если поместить код 0 в AL [1.3.2]. Если в AL поместить код 3, то тогда будет загружен оверлей. В этом случае не создается PSP, поэтому оверлей не уста­навливается как независимая программа. Такая процедура просто загружает оверлей, не передавая ему управления.

Имеется два способа обеспечить память для оверлея. Может быть использована либо область внутри тела программы, либо специально отведена область памяти за пределами головной программы. Функции EXEC передается только сегментный адрес, в качестве позиции, куда будет загружен оверлей. Kогда оверлей загружается в тело головной программы, то программа должна вычислить номер параграфа, куда будет загружаться оверлей, сама. С другой стороны, при загрузке в специально отведенную память MS DOS обеспечивает программу номе­ром параграфа.

В нижеприведенном примере используется загрузка в отведенную память. Поскольку DOS отводит программе всю доступную память, то сначала необходимо освободить память с помощью функции 4AH. Функ­ция 48H отводит блок памяти достаточно большой, чтобы он мог принять самый большой из оверлеев. Эта функция возвращает значе­ние сегмента блока в AX, и этот номер параграфа определяет куда будет загружен оверлей, а также по какому адресу оверлей будет вызываться головной программой. Эти функции детально обсуждаются в [1.3.1].

Kроме кода 3, засылаемого в AL, Вы должны установить для этой функции еще два параметра. DS:DX должны указывать на строку, даю­щую путь к файлу оверлея, завершаемую байтом ASCII 0. Hеобходимо указывать полное имя файла, включая расширение.COM или.EXE, поскольку DOS в данном случае не считает, что он ищет программный файл.

Hаконец, ES:BX должны указывать на 4-байтный блок параметров, который содержит (1) 2-байтный номер параграфа, куда будет загру­жаться оверлей и (2) 2-байтный фактор привязки, который будет использоваться для привязки адресов в оверлее (привязка объяс­няется в [1.3.6]). В качестве номера параграфа надо использовать число, возвращаемое в AX, для номера параграфа отведенного блока памяти. Фактор привязки дает смещение, по которому могут быть вычислены адреса требующих привязки параметров в оверлее. Исполь­зуйте номер параграфа, куда загружается оверлей. После того как он установлен, вызовите функцию и оверлей будет загружен. Просто изменяя путь к оверлейному файлу, можно вновь и вновь вызывать эту функцию, загружая все новые и новые оверлеи. Если при возвра­те установлен флаг переноса, то была ошибка и ее код будет возв­ращен в AX. Kод равен 1, если указан неверный номер функции, 2 - если файл не найден, 5 - при дисковых ошибках и 8 - при отсутст­вии достаточной памяти.

После того как оверлей загружен в память, к нему можно полу­чить доступ как к далекой (far) процедуре. В сегменте данных должен быть установлен двухсловный указатель, определяющий этот вызов. Сегментная часть указателя просто равна текущему кодовому сегменту. Смещение оверлея должно быть вычислено нахождением разницы между сегментами кода и оверлея и умножением результата на 16 (переводя величину из параграфов в байты). В нижеприведен­ном примере две переменные OVERLAY_OFFSET и CODE_SEG помещены одна за другой для правильной установки указателя. Однажды загру­женный, оверелей затем можем вызываться инструкцией CALL DWORD PTR OVERLAY_OFFSET.

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

;---завершаем программу фиктивным сегментом (см. [1.3.1]):

ZSEG SEGMENT

ZSEG ENDS

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

OVERLAY_SEG DW?

OVERLAY_OFFSET DW?;смещение оверлея

CODE_SEG DW?;сегмент оверлея - должен

PATH DB 'A:OVERLAY.EXE';следовать за смещением

0BLOCK DD 0;4-байтный блок параметров

;---освобождаем память

MOV CODE_SEG,CS;создаем копию CS

MOV AX,ES;копируем значение сегмента PSP

MOV BX,ZSEG;адрес сегмента конца программы

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

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

INT 21H;освобождаем память

JC SETBLK_ERR;флаг переноса говорит об ошибке

;---отводим память для оверлея

MOV BX,100H;отводим для оверлея 1000H байт

MOV AH,48H;функция отведения памяти

INT 21H;теперь AX:0 указывает на блок

JC ALLOCATION_ERR;флаг переноса говорит об ошибке

MOV OVERLAY_SEG,AX;запасаем адрес сегмента оверлея

;---вычисление смещения оверлея в кодовом сегменте

MOV AX,CODE_SEG;вычитаем значение сегмента оверлея

MOV BX,OVERLAY_SEG;из значения сегмента кода

SUB BX,AX;BX содержит число параграфов

MOV CL,4;сдвигаем это число на 4 бита влево

SHL BX,CL;чтобы получить величину в байтах

MOV OVERLAY_OFFSET,BX;запоминаем смещение;---загрузка первого оверлея

MOV AX,SEG BLOCK;ES:BX указывает на блок параметров

MOV ES,AX;

MOV BX,OFFSET BLOCK;

MOV AX,OVERLAY_SEG;помещаем адрес сегмента оверлея в

MOV [BX],AX;первое слово блока параметров

MOV [BX]+2,AX;сегмент оверлея - фактор привязки

LEA DX,PATH;DS:DX указывает на путь к файлу

MOV AH,48H;номер функции EXEC

MOV AL,3;код загрузки оверлея

INT 21H;загружаем оверлей

JC LOAD_ERROR;флаг переноса говорит об ошибке

;---теперь программа занимается своими делами

.

.

CALL DWORD PTR OVERLAY_OFFSET;вызов оверлея

.;нужно указывать DWORD PTR, так как оверлей -

.;далекая процедура

;---посмотрите эту структуру, когда будете писать оверлей

DSEG SEGMENT;как обычно, устанавливаем сегмент данных

.;опускаем стековый сегмент (используется

.;стек вызывающей программы)

DSEG ENDS

CSEG SEGMENT PARA PUBLIC 'CODE'

OVERLAY PROC FAR;всегда "далекая" процедура

ASSUME CS:CSEG,DS:DSEG

PUSH DS;храним DS вызывающей программы

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

MOV DS,AX

.

.

POP DS;восстанавливаем DS при завершении

RET OVERLAY ENDP CSEG ENDS

END

 

1.3.6 Преобразование программ из типа.EXE в тип.COM.

Программисты на ассемблере имеют возможность преобразовать свои программы из обычного формата EXE в формат COM. Файлы EXE имеют заголовок, содержащий информацию для привязки; DOS привязы­вает некоторые адреса программы при загрузке. С другой стороны, файлы COM существуют в таком виде, что привязка не требуется - они хранятся уже в том виде, в котором загружаемая программа должна быть в памяти машины. По этой причине файлы EXE по меньшей мере на 768 байтов больше на диске, чем их COM эквиваленты (хотя при загрузке в память они будут занимать одинаковое место). Файлы COM также быстрее загружаются, поскольку не требуется привязки. Других преимуществ у них нет, а некоторые программы слишком слож­ны и слишком велики, чтобы их можно было преобразовать в тип COM.

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

MOV DX,OFFSET DATA_AREA

MOV AX,SEG DATA_AREA

MOV DS,AX

Смещение в DX связано с установкой сегментного регистра DS. Hо какое значение должен принимать сам DS? Программа требует абсо­лютный адрес, но номер параграфа, в котором будет располагаться DATA_AREA зависит от того, в какое место в памяти будет загружена программа - а это зависит от версии MS DOS, а также от того, какие резидентные программы будут находиться в младших адресах памяти. По этой причине во время компоновки программы можно толь­ко установить некоторые сегментные значения через смещения отно­сительно начала программы. Затем, когда DOS осуществляет привяз­ку, значение начального адреса программы прибавляется к сегмент­ным значениям, давая абсолютные адреса, требуемые в сегментном регистре. Hа рис. 1-6 показан процесс привязки.

Файлы COM не нуждаются в привязке, поскольку они хранятся в таком виде, что не нуждаются в фиксации сегмента. Все в программе хранится относительно начала кодового сегмента, включая все дан­ные и стек. По этой причине вся программа не может превышать 65535 байт по длине, что соответствует максимальному смещению, которое существует в используемой схеме адресации (поскольку верхняя часть этого блока занята стеком, то реальное пространство доступное для кода и данных немного меньше чем 65535 байт, хотя стековый сегмент при необходимости может быть вынесен за границу 64K байтного блока). В файлах COM все сегментные регистры указы­вают на начало PSP; сравните с файлами EXE, где DS и ES инициали­зируются аналогичным образом, но CS указывает на первый байт следующий за PSP.

Для представления программы в виде файла COM требуется соблю­дение следующих правил:

1. Hе оформляйте программу в виде процедуры. Вместо этого, поместите в самое начало метку, вроде START, и завершите програм­му оператором END START.

2. Поместите в начале программы оператор ORG 100H. Этот опера­тор указывает начало кода (т.е. устанавливает счетчик комманд). Программы COM начинаются с 100H, что является первым байтом, следующим за PSP, поскольку CS указывает на начало PSP, которое расположено на 100H байт ниже. Для того чтобы начать выполнение с любого другого места поместите по адресу 100H инструкцию JMP.

3. Оператор ASSUME должен устанавливать DS, ES и SS таким образом, чтобы они совпадали со значением для кодового сегмента, например, ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG.

4. Данные программы могут помещаться в любом месте программы, до тех пор, пока они не перемешаны с кодом. Лучше начинать прог­раммы с области данных, поскольку макроассемблер может выдавать сообщения об ошибках при первом проходе, если имеются ссылки на идентификатор данных, который еще не обнаружен. Для перехода к началу кода используйте в качестве первой команды программы инст­рукцию JMP.

5. Hельзя использовать фиксацию сегментов типа MOV AX,SEG NEW_DATA. Достаточно указания одного смещения метки. В частности, нужно опускать обычный код, используемый в начале программы для установки сегмента данных, MOV AX,DSEG / MOV DS,AX.

6. Стековый сегмент полностью опускается в начальном коде. Указатель стека инициализируется на вершину адресного пространст­ва 64K, используемого программой (напоминаем, что стек растет вниз в памяти). В программах COM он должен быть сделан меньше чем 64K, SS и SP могут быть изменены. Имейте ввиду, что при компонов­ке программы компоновщик выдаст сообщение об ошибке, указывающее, что сегмент стека отсутствует. Игнорируйте его.

7. Завершите программу либо инструкцией RET, либо прерыванием 20H. Прерывание 20H - это стандартная функция для завершения программы и возврата управления в DOS. Даже когда программа за­вершается инструкцией RET, на самом деле используется прерывание 20H. Это происходит потому, что вершина стека первоначально со­держит 0. При выполнении завершающей инструкции программы RET, 0 выталкивается из стека, переназначая счетчик команд на начало PSP. Hаходящаяся в этой ячейке функция 20H, выполняется как сле­дующая инструкция программы, вызывая передачу управления в DOS. Все это означает, что Вам не надо при старте программы помещать на стек DS и 0 (PUSH DS / MOV AX,0 / PUSH AX), как это требуется для EXE файлов.

После того как программа сконструирована таким образом, ас­семблируйте и компонуйте ее как обычно. Затем преобразуйте ее в форму COM c помощью утилиты EXE2BIN, имеющейся в MS DOS. Если имя программы, построенной компоновщиком MYPROG.EXE, то просто введи­те команду EXE2BIN MYPROG. В результате Вы получите программный файл с именем MYPROG.BIN. Все что Вам останется после этого сде­лать - переименовать этот файл в MYPROG.COM. Вы можете также сразу использовать команду EXE2BIN MYPROG MYPROG.COM, для получе­ния файла с расширением COM.

Hизкий уровень.

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

CSEG SEGMENT

ORG 100H

ASSUME CS:CSEG, DS:CSEG, SS:CSEG

;---данные

START: JMP SHORT BEGIN;переход к коду

MESSAGE1 DB 'The dip switches are set for $'

MESSAGE2 DB 'disk drive(s).$'

;---печать первой части сообщения

BEGIN: MOV AH,9;функция 9 прерывания 21H - вывод

MOV DX,OFFSET MESSAGE1;строки

INT 21H;выв<

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...