|
Структура программы на языке Ассемблера
Как было указано выше, исходный программный модуль – это последовательность предложений. Различают два типа предложений: инструкции процессора и директивы Ассемблера. Инструкции управляют работой процессора, а директивы указывают Ассемблеру и редактору связей каким образом следует объединять инструкции для создания модуля, который и станет работающей программой. Инструкция процессора на языке Ассемблера состоит не более чем из четырех поле и имеет следующий формат:
[[метка:]] мнемоника [[операнды]] [[;комментарии]]
Единственное обязательное поле – поле кода операции (мнемоника), определяющее инструкцию, которую должен выполнить микропроцессор. Поле операндов определяется кодом операции и содержит дополнительную информацию о команде. Каждому коду операции соответствует определенное число операндов. Метка служит для обозначения какого-то определенного места в памяти, то есть содержит в символическом виде адрес, по которому храниться инструкция. Преобразование символических имен в действительные адреса осуществляется программой Ассемблера. Часть строки исходного текста после символа «;» (если он не является элементом знаковой константы или строки знаков) считается комментарием и Ассемблером игнорируется. Комментарии вносятся в программу как поясняющий текст и могут содержать любые знаки до ближайшего символа конца строки. Для создания комментариев, занимающих несколько строк, может быть использована директива COMMENT. Например,
Метка Код операции Операнды ;Комментарий MET: MOVE AX, BX ;Пересылка
Структура директивы аналогична структуре инструкции. Второе поле – код псевдооперации определяет смысловое содержание директивы. Как и у инструкции, у директивы есть операнды, причем их может быть один или несколько и они отделяются друг от друга запятыми. Допустимое число операндов в директиве определяется кодом псевдооперации. Например,
ARRAY DB 0, 0, 0, 0, 0 END START
Директива может быть помечена символическим именем и содержать поле комментария. Символическое имя, стоящее в начале директивы распределения памяти, называется переменной. В отличие от метки команды после символического имени директивы двоеточие не ставиться. Комментарий записывается также как и в случае команды и может занимать целую строку. Программа на языке Ассемблера состоит из программных модулей, содержащихся в различных файлах. Каждый модуль, в свою очередь, состоит из инструкций процессора или директив Ассемблера и заканчивается директивой END. Метка, стоящая после кода псевдооперации END, определяет адрес, с которого должно начаться выполнение программы и называется точкой входа в программу. Каждый модуль также разбивается на отдельные части директивами сегментации, определяющими начало и конец сегмента. Каждый сегмент начинается директивой начала сегмента – SEGMENT и заканчивается директивой конца сегмента – ENDS. В начале директив ставится имя сегмента. Оператор SEGMENT может указывать выравнивание сегмента в памяти, способ, которым он объединяется с другими сегментами, а также имя класса. Существует два типа выравнивания сегмента: тип PARA, когда сегмент будет расположен начиная с границы параграфа, и тип BYTE, когда сегмент расположен начиная с любого адреса. Различные виды взаимной связи сегментов определяют параметры сборки, например, при модульном программировании. Объявление PUBLICвызывает объединение всех сегментов с одним и тем же именем в виде одного большого сегмента. Объявление AT с адресным выражением располагает сегмент по заданному абсолютному адресу. Каждый сегмент может быть также разбит на части. В общем случае информационные сегменты SS, ES и DS состоят из определений данных, а программный сегмент CS – из инструкций и директив, группирующих инструкции в блоки. Программный сегмент может разбиваться на части директивами определения процедур – некоторых выделенных блоков программы. Как и для определения сегмента, имеются две директивы определения процедуры (подпрограммы) – директива начала PROC и директива конца ENDP. Процедура имеет имя, которое должно включаться в обе директивы. В сегменте процедуры могут располагаться последовательно одна за другой или могут быть вложенными одна в другую. Директива ASSUME С помощью директивы ASSUME Ассемблеру сообщается информация о соответствии между сегментными регистрами, и программными сегментами. Директива имеет следующий формат:
ASSUME пара [[, <пара>]] ASSUME NOTHING где пара - это <сегментный регистр> : <имя сегмента> либо <сегментный регистр> : NOTHING
Например, директива
ASSUME ES:A, DS:B, CS:C
сообщает Ассемблеру, что для сегментирования адресов из сегмента А выбирается регистр ES, для адресов из сегмента В – регистр DS, а для адресов из сегмента С – регистр CS. Таким образом, директива ASSUME дает право не указывать в командах (по крайней мере, в большинстве из них) префиксы – опущенные префиксы будет самостоятельно восстанавливать Ассемблер. В качестве особенностей директивы прежде всего следует отметить, что директива ASSUME не загружает в сегментные регистры начальные адреса сегментов. Этой директивой автор программы лишь сообщает, что в программе будет сделана такая загрузка. Директиву ASSUME можно размещать в любом месте программы, но обычно ее указывают в начале сегмента команд, так как информация из нее нужна только при трансляции инструкций. При этом в директиве обязательно должно быть указано соответствие между регистром CS и данным сегментом кода, иначе при появлении первой же метки Ассемблер зафиксирует ошибку. Если в директиве ASSUME указано несколько пар с одним и тем же сегментным регистром, то последняя из них «отменяет» предыдущие, так как каждому сегментному регистру, можно поставить в соответствие только один сегмент. В то же время на один и тот же сегмент могут указывать разные сегментные регистры. Если в директиве ASSUME в качестве второго элемента пары задано служебное слово NOTHING (ничего), например, ASSUME ES: NOTHING, то это означает, что с данного момента сегментный регистр не указывает ни на какой сегмент, что Ассемблер не должен использовать этот регистр при трансляции команд. В связи с тем, что директивой ASSUME автор программы лишь сообщает, что все имена из таких-то программных сегментов должны сегментироваться по таким-то сегментным регистрам (в начале выполнения программы в этих регистрах ничего нет), то выполнение программы необходимо начинать с команд, которые загружают в сегментные регистры адреса соответствующих сегментов памяти. Загрузка производится следующим образом. Пусть регистр DS необходимо установить на начало сегмента В. Для загрузки регистра необходимо выполнить присваивание вида DS:=B. Однако сделать это командой MOV DS,B нельзя, поскольку имя сегмента – это константное выражение, то есть непосредственный операнд, а по команде MOV запрещена пересылка непосредственного операнда в сегментный регистр (см. ниже). Поэтому такую пересылку следует делать через другой, несегментный регистр, например, через АХ:
MOV АХ,В MOV DS,AX ;DS:=B
Аналогичным образом загружается и регистр ES. Регистр CS загружать нет необходимости, так как к началу выполнения программы этот регистр уже будет указывать на начало сегмента кода. Такую загрузку выполняет операционная система, прежде чем передает управление программе. Загрузить регистр SS можно двояко. Во-первых, его можно загрузить в самой программе так же, как DS или ES. Во-вторых, такую загрузку можно поручить операционной системе. Для этого в директиве SEGMENT, открывающей описание сегмента стека, надо указать специальный параметр STACK, например,
S SEGMENT STACK ... S ENDS
В таком случае загрузка S в регистр SS будет выполнена автоматически до начала выполнения программы. Директива INCLUDE Встречая директиву INCLUDE, Ассемблер весь текст, хранящийсяв указанном файле, подставит в программу вместо этой директивы. В общем случае обращение к директиве INCLUDE (включить) имеет следующий вид
INCLUDE имя_файла
Директиву INCLUDE можно указывать любое число раз и в любых местах программы. В ней можно указать любой файл, причем название файла записывается по правилам операционной системы. Директива полезна, когда в разных программах используется один и тот же фрагмент текста; чтобы не выписывать этот фрагмент в каждой программе заново, его записывают в какой-то файл, а затем подключают к программам с помощью данной директивы. |
|
|