Предыдущий раздел | РАЗДЕЛЬНАЯ ТРАНСЛЯЦИЯ ПРОГРАММЫ | Следующий раздел |
Такая программа получается в результате загрузки com-файла, которая сводится к простому переписыванию содержимого этого файла с диска в ОП, а также к записи в начале сегмента памяти, занимаемого программой, ее PSP.
На рис.61 приведено содержимое сегментных регистров и регистров-указателей IP и SP после загрузки com-программы в момент передачи ей управления. (Собственно передача управления заключается в записи в CS и IP тех значений, которые соответствуют первой исполняемой команде программы.) В этот момент все сегментные регистры содержат одно и то же – начальный адрес (а точнее – номер начального параграфа) области ОП, в которую загружена программа. Так как программа начинается с PSP, то, следовательно, номер начального параграфа PSP и содержится в сегментных регистрах CS, DS, ES и SS.
Первоначальное содержимое указателя команд IP задает адрес-смещение первой исполняемой команды программы. Для com-программ это всегда число 100h, так как первая исполняемая команда начинается в памяти сразу же после завершения PSP. Что касается указателя стека SP, то он содержит адрес-смещение вершины стека, в который при загрузке программы уже было помещено одно нулевое слово. По мере того, как в стек будут записываться новые слова данных, его вершина будет приближаться к области памяти, занимаемой командами и данными программы. В случае наложения стека на другие данные программы, дальнейшее ее продолжение будет или ошибочным, или вообще невозможным.
Рис.61. Результат загрузки com-программы
Для получения загрузочного модуля типа com можно применить два способа. Первый из этих способов, который мы использовали до сих пор во всех своих программах, заключается в том, что вся работа по получению com-файла поручается программе NASM. При этом исходная программа на ассемблере (asm-файл) имеет следующие особенности:
1) первому исполнительному оператору программы предшествует псевдооператор “org 100h”, который обеспечивает требуемое смещение соответствующей машинной команды относительно начала программы;
2) исходная программа может не иметь виртуальных сегментов. Отсутствие виртуального, то есть явно определенного в исходной программе сегмента не означает, что в машинной программе не будет логических сегментов, а лишь говорит о том, что эти сегменты будут созданы автоматически, то есть без участия программиста;
3) исходная программа не должна содержать исполнительных операторов, выполняющих действия с сегментными адресами. (Такие адреса должны настраиваться загрузчиком в зависимости от размещения машинной программы в ОП, а настройка сом-файла не производится.) Например, недопустимы следующие операторы:
mov ax, _Data ; _Data – сегмент данных
mov si, seg Buff ; si ← адрес-сегмент переменной Buff
Пример исходной программы, ориентированной на получение загрузочного модуля типа com:
; Программа выводит сообщение “Здравствуйте” на экран
; -------------------------------------------------------------------------
;
cr equ 0dh ; Код ASCII возврата каретки
lf equ 0ah ; Код ASCII перевода строки
org 100h
jmp Begin ; Переход через данные
Msg db ‘Здравствуйте’, cr, lf, ‘$’ ; Выводимое сообщение
Begin: mov dx, Msg ; DX ß адрес сообщения
mov ah, 9 ; Вывод строки
int 21h ; на экран
mov ax, 4c00h ; Возврат в DOS с
int 21h ; кодом завершения 0
Данные, обрабатываемые программой, размещены в сегменте памяти, занимаемой программой, так, чтобы они не могли попасть на ЦП в качестве исполняемых команд, что неизбежно привело бы к сбою в работе процессора. Считается хорошим тоном при написании com-программы на ассемблере располагать ее данные в начале программы, так как это упрощает трансляцию. Первый исполнительный оператор jmp «перепрыгивает» через эти данные на исполнительную часть программы.
Для вывода строки на экран программа использует системный вызов “int 21h” (функция 9). Перед применением вызова программа помещает в регистры DS и DX соответственно адрес-сегмент и адрес-смещение для младшего байта выводимой строки. В нашей программе DS уже содержит требуемый адрес (это сделал загрузчик) и поэтому не загружается. Выводимая строка обязательно должна заканчиваться символом “$”. Сама строка может содержать любые другие символы, в том числе и управляющие. В программе используются управляющие символы 0dh (возврат каретки) и 0ah (перевод строки).
Второй метод получения com-файла предполагает использование для этого не только транслятора (NASM) но и редактора связей (ALINK). При этом транслятор обеспечивает преобразование исходного фрагмента в объектный модуль (obj-файл), а редактор связей получает из объектного модуля загрузочный модуль (com-файл). Реализация данного метода предполагает внесение существенных изменений в исходную программу. Особенности данных изменений опять рассмотрим на примере «вежливой» программы, выводящей «Здравствуйте» на экран. Ее текст:
; Программа выводит сообщение “Здравствуйте” на экран
; -------------------------------------------------------------------------
;
cr equ 0dh ; Код ASCII возврата каретки
lf equ 0ah ; Код ASCII перевода строки
segment code
resb 100h
..start:
jmp Begin ; Переход через данные
Msg db ‘Здравствуйте’, cr, lf, ‘$’ ; Выводимое сообщение
Begin: mov dx, Msg ; DX ß адрес сообщения
mov ah, 9 ; Вывод строки
int 21h ; на экран
mov ax, 4c00h ; Возврат в DOS с
int 21h ; кодом завершения 0
Во-первых, обратим внимание на наличие виртуального сегмента кода. В п.2.3 шла речь о логических сегментах программы. Напомним, что каждый виртуальный сегмент начинается с рассмотренного ранее псевдооператора absolute или с псевдооператора segment. Параметром этого оператора является тип сегмента – code. Это слово “code” может использоваться в программе в качестве имени (метки) этого сегмента.
Кроме сегмента code в программе возможно наличие сегментов других типов – data (данные) и stack (стек). Отсутствие других виртуальных сегментов в рассматриваемой исходной программе обусловлено тем, что она ориентирована на получение машинной com-программы. Поэтому наличие других виртуальных сегментов является излишним, так как после загрузки com-программы загрузчик копирует содержимое регистра CS в качестве содержимого других сегментных регистров – DS, ES и SS.
Во-вторых, обратим внимание на отсутствие псевдооператора “org”. Данный оператор нельзя использовать в исходных программах, предназначенных для получения объектных модулей. Вместо него в начале виртуального сегмента может использоваться знакомый нам псевдооператор resb. Данный оператор задает адрес-смещение для самой первой команды сегмента кода внутри сегмента ОП, выделенного машинной программе. Обратите внимание на порядок записи операторов segment и resb. Если их поменять местами, то сегмент кода будет начинаться не от начала сегмента памяти, а со смещением 100h, что не соответствует размещению логических сегментов для com-программы (см. рис. 61).
В-третьих, первый исполнительный оператор виртуального сегмента кода имеет стандартную метку входа в программу “..start:”.
Для получения объектного модуля программы с помощью транслятора NASM необходимо использовать при вызове этой программы дополнительный параметр “-fobj”, уточняющий тип целевого файла. Допустим, что приведенная выше программа записана в исходный файл test.asm. Тогда для получения объектного файла можно воспользоваться командой DOS:
nasm test.asm -f obj -o test.obj
В данном примере первый параметр команды задает имя исходного файла, второй параметр – тип целевого файла, а третий – имя целевого файла.
Преобразование одного или нескольких объектных модулей в загрузочный модуль (com- или exe-файл) выполняет редактор связей ALINK. В качестве одного или нескольких параметров команды запуска ALINK задается имя одного или нескольких объектных модулей. В качестве следующего параметра – тип целевого файла (загрузочного модуля). А в качестве последнего параметра – имя загрузочного модуля. Пример команды запуска ALINK:
alink test.obj -oCOM -o test.com
Обратите внимание, что второй параметр записан слитно, а тип файла (COM) записан большими буквами. П о л у ч и т е com-файл для приведенной выше «вежливой» программы, а затем выполните ее.
Предыдущий раздел | В начало | Следующий раздел |