Предыдущий раздел | РАЗДЕЛЬНАЯ ТРАНСЛЯЦИЯ ПРОГРАММЫ | Следующая глава |
В отличие от com-программы, загружаемой по умолчанию в один стандартный сегмент ОП объемом 64K, программа типа exe может занимать несколько таких сегментов. Следствием этого является наличие в программе команд, выполняющих запись в регистры адресов-сегментов, требующих своей настройки во время загрузки.
В результате, получение exe-программы требует не простого копирования соответствующего файла в ОП, а предполагает преобразование этого файла загрузчиком. В результате, загрузчик не только записывает PSP программы (это он делает и для com-файла), но и настраивает некоторые адреса в полях машинных команд. В начале exe-файла находится заголовок, размер которого кратен 512 байтам, содержащий управляющую информацию, используемую загрузчиком для получения exe-программы.
Свою работу по созданию exe-файла редактор связей начинает с того, что последовательно записывает в свою память код (команды), данные и стек будущей программы. Порядок расположения этих частей программы, в отличие от com-файла, может быть любым. Их содержимое редактор связей считывает из доступных ему объектных модулей программы. Выполняя размещение программы в памяти, редактор связей обращается с ее адресами так, как будто программа загружена в ОП, начиная с условного адреса 00000h. Поэтому считается, что часть программы (код, данные или стек), размещаемая первой, имеет начальный логический адрес 0000h:0000h. Если, например, вторая часть программы начинается с условного реального адреса 000E7h, то ему соответствует начальный логический адрес 000Eh:0007h. Применение таких условных адресов позволяет редактору связей выполнить запись численных значений для всех внешних адресов, оставшихся не проставленными после трансляции.
Исключение составляют команды, выполняющие запись в регистры значений адресов-сегментов. К таким командам относятся, во-первых, команды дальнего перехода jmp и call (они выполняют запись адреса-сегмента в регистр CS), а во-вторых, команды mov, выполняющие запись адресов-сегментов в регистры данных (из регистров данных другие команды mov переписывают значения в регистры сегментов DS, ES, SS). Например, следующие два оператора исходной программы выполняют запись в регистр DS того адреса-сегмента, который соответствует точке исходной программы с меткой Msg:
mov dx, seg Msg
mov ds, dx
Первый оператор mov транслируется в трех-, а второй – в двухбайтовую машинную команду. При этом второй и третий байты первой команды используются для размещения адреса-сегмента, загружаемого в регистр DX.
Так как адреса-сегменты зависят от фактического размещения будущей программы в ОП, которое редактор связей «не знает», то он не может проставить численные значения адресов-сегментов в поля машинных команд. Тем не менее, он выполняет значительную подготовку для будущей простановки этих значений загрузчиком. Для этого, во-первых, редактор связей помещает в эти поля условные адреса-сегменты, полученные в предположении, что программа загружается в память, начиная с нулевого адреса.
Во-вторых, редактор связей создает таблицу настройки, занимающую почти весь объем заголовка exe-файла. Эта таблица состоит из четырехбайтовых полей, каждое из которых соответствует одному адресу-сегменту, используемому какой-то командой программы, и который требует настройки в зависимости от фактического размещения программы в памяти. В качестве содержимого этого поля редактор связей записывает условный логический адрес (сегмент и смещение) той ячейки (двух байтов) программы, которая содержит адрес-сегмент, требующий настройки.
Сама настройка адресов-сегментов программы производится загрузчиком сразу же после размещения программы (в том числе и PSP) в ОП. Для этого загрузчик последовательно просматривает таблицу настройки, считывая из нее указатель на очередную последовательность из двух байтов программы, требующую настройки. А затем он прибавляет к их содержимому (то есть к условному адресу-сегменту) номер того параграфа ОП, начиная с которого программа загружена фактически.
После того, как программа загружена, загрузчик передает ей управление. На рис.62 приведено содержимое сегментных регистров и указателя стека SP в момент передачи управления exe-программе. В этот момент сегментные регистры данных содержат номер начального параграфа PSP. Благодаря этому программа может использовать полезную информацию, содержащуюся в PSP. (Запомнив где-то первоначальное значение этих регистров, программа обычно записывает в них новое значение, указывающее на начало области данных программы.) Что касается CS, то в него загрузчик помещает начальный номер параграфа не PSP, а области кода программы. Аналогично содержимое SS указывает на границу области стека программы.
Исходная программа на ассемблере, ориентированная на получение exe-программы, обязательно имеет кроме виртуальных сегментов кода и данных виртуальный сегмент стека. Порядок записи этих сегментов в программе значения не имеет. В качестве точки входа в программу может быть задан любой исполнительный оператор программы. Для этого он должен иметь в исходной программе стандартную метку “..start:”.
Пример. Приведем исходный текст “вежливой” программы, ориентированной на получение загрузочного модуля типа exe:
; Программа выводит сообщение “Здравствуйте” на экран
; -------------------------------------------------------------------------
;
cr equ 0dh ; Код ASCII возврата каретки
lf equ 0ah ; Код ASCII перевода строки
segment stack ; Сегмент
times 64 db 0 ; стека
segment code ; Сегмент кода
..start:
mov ax, data ; Сделаем сегмент данных
mov ds, ax ; адресуемым
mov dx, Msg ; DX ß адрес сообщения
mov ah, 9 ; Вывод строки
int 21h ; на экран
mov ax, 4c00h ; Возврат в DOS с
int 21h ; кодом завершения 0
segment data ; Сегмент данных
Msg db ‘Здравствуйте’, cr, lf, ‘$’ ; Выводимое сообщение
Рис. 62. Результат загрузки exe-программы
В данной исходной программе три виртуальных сегмента – сегмент кода code, сегмент данных data и сегмент стека stack. Параметр code, data или stack оператора segment означает тип виртуального сегмента – код, данные или стек. Одновременно единственный параметр оператора segment задает и имя своего виртуального сегмента. Если виртуальному сегменту требуется присвоить какое-то другое имя, то это имя записывается в качестве дополнительного параметра оператора segment. Причем этот параметр записывается первым. Например, следующий оператор присваивает сегменту кода имя “Program”:
segment Program code
Все одноименные виртуальные сегменты, независимо от того, в каких исходных файлах и фрагментах они находятся, представлены в готовой машинной программе в виде единого логического сегмента.
Обратите внимание, что в начале сегмента кода в регистр DS загружается адрес-сегмент, соответствующий началу данных программы. Это делается для того, чтобы фактическое размещение данных в памяти никак не зависило от размещения в ней PSP, а также от размещения в памяти других сегментов программы. (Напомним, что для com-программы адрес-смещение данных вычисляется относительно начала PSP.) Обратите внимание, что запись значения в сегментный регистр DS производится не непосредственно, а через рабочий регистр AX.
Еще одним отличием исходных exe- и com-программ является то, что в exe-программе никак не задается расположение сегмента кода в памяти. Это полностью остается на усмотрение редактора связей и загрузчика.
Допустим, что записанная выше исходная exe-программа имеет имя test1.asm. Тогда для получения соответствующего объектного модуля можно использовать следующую команду DOS:
nasm test1.asm -f obj -o test1.obj
Команда DOS для получения exe-файла:
alink test1.obj -oEXE -o test1.exe
П о л у ч и т е exe-файл для приведенной выше программы, а затем выполните его.
Предыдущий раздел | В начало | Следующая глава |