Директивы

Директивой называется команда, которая выполняется ассемблером во время трансляции исходного кода программы. Директивы ассемблера используются для определения логических сегментов, выбора модели памяти, определения переменных, создания процедур и т.п.

Директивы не генерируют машинных кодов. Они представляют собой инструкции клмпилятору, как осуществлять ассемблирование кода. Ассемблеру необходимо задать следующие данные:

· Используемый процессор.

· Условия ассемблирования.

· Условия управления.

· Правила генерации ошибок.

· Выбор процессора

Выбор разрешенного набора инструкций. Для выбранного типа инструкции процессоров старшего уровня запрещены.

· Размещение данных

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

Директива ALIGNE. Начальный номер размещения.

ALIGNE номер ; Задает номер, с которого начинается размещение данных.

Директива EVEN. Правило четного адреса.

EVEN ; Делает четным текущий адрес.

Директива ORG. Создание счетчика для выражения.

ORG выражение ; Размещает в памяти счетчик для выражения

Директива LABEL. Создает метку с именем для типа.

Имя LABEL тип ; Метка с именем для данного типа. Размещение начиная с текущего адреса.

Директивы размещения данных разных типов.

Для численных данных определены типы ниже. Директива определения размещает и инициализирует задаваемое значение.

имя BYTE значение ; беззнаковый байт (1 байт), синоним DB.

имя SBYTE значение ; знаковый байт (1 байт).

имя WORD значение ; беззнаковое слово (2 байта), синоним DD.

имя SWORD значение ; знаковое слово (2 байта).

имя DWORD значение ; беззнаковое двойное слово (4 байта).

имя SDWORD значение ; знаковое двойное слово (4 байта).

имя FWORD значение ; беззнаковое слово (6 байт) , синоним DF.

имя QWORD значение ; беззнаковое квадрослово (8 байт) , синоним DQ.

имя SQWORD значение ; знаковое квадрослово (8 байт).

имя OWORD значение ; беззнаковое октослово (16 байт).

имя REAL4 значение ; переменная с ПТ с одинарной точностью (4 байта).

имя REAL8 значение ; переменная с ПТ с двойной точностью (8 байт).

имя REAL10 значение ; переменная с ПТ с повышенной точностью (10 байт).

имя TBYTE значение ; переменная с ПТ с повышенной точностью (10 байт) , синоним DT.

 

· Строки

Директивы работы со строками.

Мнемоника Действие
Имя CATSTR [ текст1 [ , текст2 ] … Тексты объединяются в текст Имя.
Имя INSTR [ позиция, ] текст1 , текст2 В переменную Имя возвращается позиция первого пояления текст2 в текст1. Сравнение начиянается с задаваемой позиции. Если позиция пропущена, то анализ с начала текст1.
Имя SUBSTR текст, позиция [ ,длина] Возвращает в строку Имя подстроку заданной длины из текста, начиная с задаваемой позиции. Если длина не задана, то возвращается подстрока до конца текста.
Имя SIZESTR текст Возвращает в переменную Имя длину текста.
FORC параметр, <строка> инструкции ENDM Блок, который заменяет в строке символ параметр  

· Структуры и записи

В MASM определены сложные объекты:

· Записи. Это наборы полей переменной длины.

· Структуры. Это наборы иерархически организованных областей.

· Объединения. Это объекты, состоящие из разнородных объектов.

Директива RECORD. Объявление записи с именем ИмяЗаписи, состоящей из поименованных полей с шириной, определяемой выражением.

Синтакис:

ИмяЗаписи RECORD ; Объявление записи с именем ИмяЗаписи

ИмяПоля:Ширина = выражение ; Объявление поля записи

ИмяПоля:Ширина = выражение ; Объявление поля записи

Директива STRUCT.

Синтакис:

ИмяСтруктуры STRUCT ; Объявление структуры с именем ИмяСтруктуры

ДекларацияПоля ; Объявление поля структуры

ДекларацияПоля ; Объявление поля структуры

ИмяСтруктуры ENDS ; Конец структуры

Директива UNION.

Синтакис:

ИмяОбъединения UNION ; Объявление объединения с именем ИмяОбъединения

ДекларацияПоля ; Объявление поля объединения

ИмяОбъединения ENDS ; Конец объединения

Директива TYPEDEF. Объявление нового типа, который эквивалентен заданному типу.

Синтакис:

ИмяТипа TYPEDEF тип ; Объявление типа с именем ИмяТипа, который эквивалентен заданному типу.

· Стандартные директивы сегментации

Для архитектуры Фон-Неймана используется одно общее ОЗУ. В нем нужно размещать коды программ, константы, переменные и стек. Сегментация используеттся для выделения сегментов под эти клмпоненты. Предусмотрены две группы директив – общие и упрощенные.

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

Директива SEGMENT.

Синтаксическое описание сегмента на ассемблере представляет собой конструкцию:

Первое предложение – это определение сегмента и его атрибутов. Последнее предложение – конец сегмента. Внутри размещаются содержательные предложения – директивы, команды, макрокоманды, комментарии.

Имя сегмента. Назначает сегменту символическое имя, определяемое содержанием сегмента (код, данные и др.)

Атрибут выравнивания сегмента (тип выравнивания) сообщает компоновщику о том, что нужно обеспечить размещение начала сегмента на заданной границе. Это важно, поскольку при правильном выравнивании доступ к данным в процессорах выполняется быстрее. Допустимые значения этого атрибута следующие:

BYTE — выравнивание не выполняется. Сегмент может начинаться с любого адреса памяти;

WORD — сегмент начинается по адресу, кратному 2, то есть последний (младший) значащий бит физического адреса равен 0 (выравнивание на границу слова);

DWORD — сегмент начинается по адресу, кратному 4, то есть два последних (младших) значащих бита равны 0 (выравнивание на границу двойного слова);

PARA — сегмент начинается по адресу, кратному 16, то есть последняя шестнадцатеричная цифра адреса должна быть 0h (выравнивание на границу параграфа);

PAGE — сегмент начинается по адресу, кратному 256, то есть две последние шестнадцатеричные цифры должны быть 00h (выравнивание на границу 256-байтной страницы);

MEMPAGE — сегмент начинается по адресу, кратному 4 Кбайт, то есть три последние шестнадцатеричные цифры должны быть 000h (адрес следующей 4-Кбайтной страницы памяти).

По умолчанию тип выравнивания имеет значение PARA.

Атрибут комбинирования сегментов (комбинаторный тип) сообщает компоновщику, как нужно комбинировать сегменты различных модулей, имеющие одно и то же имя. Значениями атрибута комбинирования сегмента могут быть:

PRIVATE — сегмент не будет объединяться с другими сегментами с тем же именем вне данного модуля;

PUBLIC — заставляет компоновщик соединить все сегменты с одинаковыми именами. Новый объединенный сегмент будет целым и непрерывным. Все адреса (смещения) объектов, а это могут быть, в зависимости от типа сегмента, команды и данные, будут вычисляться относительно начала этого нового сегмента;

COMMON — располагает все сегменты с одним и тем же именем по одному адресу. Все сегменты с данным именем будут перекрываться и совместно использовать память. Размер полученного в результате сегмента будет равен размеру самого большого сегмента;

AT xxxx — располагает сегмент по абсолютному адресу параграфа (параграф — объем памяти, кратный 16; поэтому последняя шестнадцатеричная цифра адреса параграфа равна 0). Абсолютный адрес параграфа задается выражением xxx. Компоновщик располагает сегмент по заданному адресу памяти (это можно использовать, например, для доступа к видеопамяти или области ПЗУ), учитывая атрибут комбинирования. Физически это означает, что сегмент при загрузке в память будет расположен, начиная с этого абсолютного адреса параграфа, но для доступа к нему в соответствующий сегментный регистр должно быть загружено заданное в атрибуте значение. Все метки и адреса в определенном таким образом сегменте отсчитываются относительно заданного абсолютного адреса;

STACK — определение сегмента стека. Заставляет компоновщик соединить все одноименные сегменты и вычислять адреса в этих сегментах относительно регистра ss. Комбинированный тип STACK (стек) аналогичен комбинированному типу PUBLIC, за исключением того, что регистр ss является стандартным сегментным регистром для сегментов стека. Регистр sp устанавливается на конец объединенного сегмента стека. Если не указано ни одного сегмента стека, компоновщик выдаст предупреждение, что стековый сегмент не найден. Если сегмент стека создан, а комбинированный тип STACK не используется, программист должен явно загрузить в регистр ss адрес сегмента (подобно тому, как это делается для регистра ds).

По умолчанию атрибут комбинирования принимает значение PRIVATE.

Атрибут класса сегмента (тип класса) — это заключенная в кавычки строка, помогающая компоновщику определить соответствующий порядок следования сегментов при собирании программы из сегментов нескольких модулей. Компоновщик объединяет вместе в памяти все сегменты с одним и тем же именем класса (имя класса, в общем случае, может быть любым, но лучше, если оно будет отражать функциональное назначение сегмента). Типичным примером использования имени класса является объединение в группу всех сегментов кода программы (обычно для этого используется класс “code”). С помощью механизма типизации класса можно группировать также сегменты инициализированных и неинициализированных данных;

Атрибут размера сегмента. Для процессоров i80386 и выше сегменты могут быть 16 или 32-разрядными. Это влияет, прежде всего, на размер сегмента и порядок формирования физического адреса внутри него. Атрибут может принимать следующие значения:

USE16 — это означает, что сегмент допускает 16-разрядную адресацию. При формировании физического адреса может использоваться только 16-разрядное смещение. Соответственно, такой сегмент может содержать до 64 Кбайт кода или данных;

USE32 — сегмент будет 32-разрядным. При формирования физического адреса может использоваться 32-разрядное смещение. Поэтому такой сегмент может содержать до 4 Гбайт кода или данных.

Директива ASSUME.

Все сегменты сами по себе равноправны, так как директивы SEGMENT и ENDS не содержат информации о функциональном назначении сегментов. Для того чтобы использовать их как сегменты кода, данных или стека, необходимо предварительно сообщить транслятору об этом, для чего используют специальную директиву ASSUME, имеющую формат, показанный на рисунке. Эта директива сообщает транслятору о том, какой сегмент к какому сегментному регистру привязан. В свою очередь, это позволит транслятору корректно связывать символические имена, определенные в сегментах. Привязка сегментов к сегментным регистрам осуществляется с помощью операндов этой директивы, в которых имя_сегмента должно быть именем сегмента, определенным в исходном тексте программы директивой SEGMENT или ключевым словом nothing. Если в качестве операнда используется только ключевое слово nothing, то предшествующие назначения сегментных регистров аннулируются, причем сразу для всех шести сегментных регистров. Но ключевое слово nothing можно использовать вместо аргумента имя сегмента; в этом случае будет выборочно разрываться связь между сегментом с именем имя сегмента и соответствующим сегментным регистром.

Директива .ALPHA. Сегменты во внутренней таблице упорядочиваются по алфавиту.

Директива .SEQ. Сегменты во внутренней таблице упорядочиваются поcледовательно. Директива по умолчанию..

Директива .GROUP. Добавляет в поименованную группу перечисленные сегменты.

Синтакис:

Имя GROUP сегмент [ , сегмент [ …

· Упрощенные директивы сегментации

Для простых программ, содержащих по одному сегменту для кода, данных и стека, хотелось бы упростить ее описание. Для этого в трансляторы MASM ввели возможность использования упрощенных директив сегментации. Но здесь возникла проблема, связанная с тем, что необходимо было как-то компенсировать невозможность напрямую управлять размещением и комбинированием сегментов. Для этого совместно с упрощенными директивами сегментации стали использовать директиву указания модели памяти MODEL, которая частично стала управлять размещением сегментов и выполнять функции директивы ASSUME (поэтому при использовании упрощенных директив сегментации директиву ASSUME можно не использовать). Эта директива связывает сегменты, которые в случае использования упрощенных директив сегментации имеют предопределенные имена, с сегментными регистрами (хотя явно инициализировать ds все равно придется).

Синтаксис той или иной директивы зависит от используемой версии ассемблера и никак не связан с системой команд процессоров Intel. Разные ассемблеры могут генерировать идентичный машинный коддля системы команд процессоров Intel, но они могут поддерживать совершенно разный набор директив.

Директивы модели памяти.

Синтаксис директивы MODEL.

Модель памяти. Обязательным параметром директивы MODEL является модель памяти. Этот параметр определяет модель сегментации памяти для программного модуля. В таблице приведены некоторые значения параметра модель памяти директивы MODEL.

Модель Тип кода Тип данных Назначение модели
TINY near near Код и данные объединены в одну группу с именем DGROUP. Используется для создания программ формата .com.
SMALL near near Код занимает один сегмент, данные объединены в одну группу с именем DGROUP. Эту модель обычно используют для большинства программ на ассемблере
MEDIUM far near Код занимает несколько сегментов, по одному на каждый объединяемый программный модуль. Все ссылки на передачу управления — типа far. Данные объединены в одной группе; все ссылки на них — типа near.
COMPACT near far Код в одном сегменте; ссылка на данные — типа far.
LARGE far far Код в нескольких сегментах, по одному на каждый объединяемый программный модуль.

Операнды директивы MODEL используют для задания модели памяти, которая определяет набор сегментов программы, размеры сегментов данных и кода, способ связывания сегментов и сегментных регистров.

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

Директива Назначение
.CODE [имя] Начало или продолжение сегмента кода (имя по умолчанию _TEXT).
.DATA Начало или продолжение сегмента инициализированных данных. Также используется для определения данных типа near (имя _DATA).
.CONST Начало или продолжение сегмента постоянных данных (констант) модуля.
.DATA? Начало или продолжение сегмента неинициализированных данных. Также используется для определения данных типа near (имя _BSS).
.STACK [размер] Начало или продолжение сегмента стека модуля. Параметр [размер] задает размер стека. Размер по умолчанию 1024 байта.
.FARDATA [имя] Начало или продолжение сегмента инициализированных данных типа far (имя FAR_DATA).
.FARDATA? [имя] Начало или продолжение сегмента неинициализированных данных типа far.

Операнд - имя кодового сегмента.

Наличие в некоторых директивах параметра [имя] говорит о том, что возможно определение нескольких сегментов этого типа. С другой стороны, наличие нескольких видов сегментов данных обусловлено требованием обеспечить совместимость с некоторыми компиляторами языков высокого уровня, которые создают разные сегменты данных для инициализированных и неинициализированных данных, а также констант.

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

Имя идентификатора Значение переменной
@code Физический адрес сегмента кода.
@data Физический адрес сегмента данных типа near.
@fardata Физический адрес сегмента данных типа far.
@fardata? Физический адрес сегмента неинициализированных данных типа far.
@curseg Физический адрес сегмента неинициализированных данных типа fa.r
@stack Физический адрес сегмента стека.

Операнд - язык. Применяктся в моделях с вызываемыми процедурами, когда вызывающая программа использует код, созданный в вызываемой программе. Операнд служит для того, чтобы компилятор мог правильно организовать интерфейс (связь) между процедурой на ассемблере и программой на языке высокого уровня. Необходимость такого указания возникает вследствие того, что способы передачи аргументов при вызове процедур различны для разных языков высокого уровня. При вызове подпрограммы аргумент загружаются в стек двумя способами:

· Слева направо.

· Справа налево

После обработки стек очищает одна из двух программ:

· Вызываемая.

· Вызывающая.

В таблице представлены данные о передаче аргументов в языках высокого уровня.

Операнд язык Язык аргументов Направление передачи стека Процедура очистки стека
NOLANGUAGE Ассемблер Слева направо Вызываемая
BASIC Basic Слева направо Вызываемая
PROLOG Prolog Справа налево Вызывающая
FORTRAN Fortran Слева направо Вызываемая
С С Справа налево Вызывающая
С++(СРР) C++ Справа налево Вызывающая
PASCAL Pascal Слева направо Вызываемая

Операнд - модификатор языка.

Операнд – модификатор.

Позволяет уточнить некоторые особенности использования выбранной модели памяти.

Значение модификатора Назначение
use16 Сегменты выбранной модели используются как 16-битные (если соответствующей директивой указан процессор i80386 или i80486)
use32 Сегменты выбранной модели используются как 32-битные (если соответствующей директивой указан процессор i80386 или i80486)
dos Программа будет работать в MS-DOS

Необязательные параметры язык и модификатор языка определяют некоторые особенности вызова процедур. Необходимость в использовании этих параметров появляется при написании и связывании программ на различных языках программирования.

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

Упрощенные директивы целесообразно использовать для простых программ и программ, предназначенных для связывания с программными модулями, написанными на языках высокого уровня. Это позволяет компоновщику эффективно связывать модули разных языков за счет стандартизации связей и управления.

· Наблюдатели

Эти директивы регулируют доступность имен.

Мнемоника Действие
COMM определ1 [ , определ2 ]… Создание общей переменной COMM с индивидуальными атрибутами, укзанными в определениях.
PUBLIC [ язык ] имя … Задание имен переменных, меток или символов, доступных в других модулях программы.
EXTERN [ язык ] имя : тип… Задание имен переменных, меток или символов, имя вызова которых – тип.
EXTERNDEF [ язык ] имя : тип… Задание имен переменных, меток или символов, имя вызова которых – тип с определением доступности. Если на имя есть ссылки в модуле, то оно трактуется, как EXTERN, если нет, то как PABLIC.

· Процедуры

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

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

· в начале программы (до первой исполняемой команды);

· в конце программы (после команды, возвращающей управление операционной системе);

· ii промежуточный вариант — внутри другой процедуры или основной программы (в этом случае необходимо предусмотреть обход процедуры с помощью команды безусловного перехода JМР);

· в другом модуле (библиотеке DLL).

Директива PROC. Создание процедуры. Синтаксис директивы:

имя PROC [ тип вызова ] [ язык ] [ доступность ] [ пролог ] [ список регистров] [ , параметр]…

Инструкции

имя ENDP

Аргументы:

· Тип вызова (NEAR – ближний, FAR - дальний)

· Язык, любой доступный (С, С++, PASCAL и др.).

· Доступность (PRIVATE, PUBLIC или EXPORT)

· Пролог. Аргументы запуска пролога процедуры.

· Список регистров, которые пролог резервирует в стек для восстановления после завершения.

· Параметры, ассмблер транслирует их с размещением в стеке.

Как обратиться к процедуре? Так как имя процедуры обладает теми же атрибутами, что и обычная метка в команде перехода, то обратиться к процедуре, в принципе, можно с помощью любой команды перехода. Но есть одно важное свойство, которое можно использовать благодаря специальному механизму вызова процедур.

Суть состоит в возможности сохранения информации о контексте программы в точке вызова процедуры. Под контекстом понимается информация о состоянии программы в точке вызова процедуры. В системе команд процессора есть две команды для работы с контекстом - CALL и RET.

Команда CALL осуществляет вызов процедуры (подпрограммы). Синтаксис команды:

CALL[модификатор] имя_процедуры

Команда CALL передает управление по адресу с символическим именем имя_процедуры, при этом в стеке сохраняется адрес возврата (то есть адрес команды, следующей после команды CALL).

Команда RET считывает адрес возврата из стека и загружает его в регистры CS и EIP/IP, тем самым возвращая управление на команду, следующую в программе за командой CALL Синтаксис команды:

RET [число]

Необязательный параметр [число] обозначает количество элементов, удаляемых из стека при возврате из процедуры.

Для команды CALL актуальна проблема организации ближних и дальних переходов. Это видно из формата команды, где присутствует параметр [модификатор]. Вызов процедуры командой CALL может быть внутрисегментным и межсегментным.

· При внутрисегментном вызове процедура находится в текущем сегменте кода (имеет тип near), и в качестве адреса возврата команда CALL сохраняет только содержимое регистра IP/EIP.

· При межсегментном вызове процедура находится в другом сегменте кода (имеет тип far), и для осуществления возврата команда CALL должна запомнить содержимое обоих регистров (CS и IP/EIP), при этом в стеке сначала запоминается содержимое регистра CS, затем — регистра IP/EIP.

Директива INVOKE.

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

вершения работы процедуры.

Вызов процедуры по адресу из выражения.

Аргументы заносятся в стек или в регистры в зависимости от условий вызова. Синтаксис:

INVOKE ИмяПроцедуры аргумент1 [ ,аргумент2 ]…

Для INVOKE аргумент ИмяПроцедуры не должен быть опережающей ссылкой на адрес. Чтобы исключить подобные ситуации, существует парная для INVOKE директива PROTO, которая должна предшествовать INVOKE

Директива PROTO. Прототипы функции. Эта директива информирует ассемблер о количестве и типах аргументов, которые принимает процедура. Использование данной директивы позволяет ассемблеру выполнять проверку типов. Обычно все директивы PROTO для процедур собираются в начале исходного текста программы либо в отдельном включаемом файле.

Синтаксис:

имя PROTO [ расстояние ] [ язык ] [ , параметр] [: тип ]…

Директива PROTO принимает аргументы:

· Аргумент расстояние (NEAR, FAR, NEAR16, NEAR32, FAR16 или FAR32) влияет на размер адреса, формируемого ассемблером для вызова процедуры. По умолчанию значение этого параметра определяется, исходя из текущей модели памяти и типа процессора.

· Аргумент язык для определения стиля и соглашения по вызову процедуры в качестве значения принимает имя языка (C, C++, PASCAL и др.).

· Аргумент параметр представляет собой последовательность перечисленных через запятую параметров процедуры. Исходя из этой информации, при вызове процедуры ассемблер преобразует последовательность параметров в последовательность команд Занесения в стек PUSH с формированием соответствующих адресов параметров процедуры в стеке.

· Аргумент тип — один из допустимых ассемблером простых типов данных. В качестве типа может быть указано слово VARARG. Оно предназначено для определения процедур с переменным числом аргументов. Тип VARARG указывается с последним параметром, заданным в директиве PROTO.

· Выражения

Псевдооператоры EQU и =.

К простейшим макросредствам языка ассемблера можно отнести псевдооператоры EQU и "=" (равно). Эти псевдооператоры предназначены для присвоения некоторому выражению символического имени или идентификатора. Впоследствии, когда в ходе трансляции этот идентификатор встретится в теле программы, макроассемблер подставит вместо него соответствующее выражение.В качестве выражения могут быть использованы константы, имена меток, символические имена и строки в апострофах. После присвоения этим конструкциям символического имени его можно использовать везде, где требуется размещение данной конструкции.

Синтаксис псевдооператора EQU:

Имя EQU числовое_выражение

Имя EQU <строка>

Синтаксис псевдооператора =:

имя = числовое_выражение

С помощью EQU идентификатору можно ставить в соответствие как числовые выражения, так и текстовые строки, а псевдооператор “=” может использоваться только с числовыми выражениями.

Ассемблер всегда пытается вычислить значение строки, воспринимая ее как выражение. Для того чтобы строка воспринималась именно как текстовая, необходимо заключить ее в угловые скобки: <строка>. Кстати сказать, угловые скобки являются оператором ассемблера, с помощью которого транслятору сообщается, что заключенная в них строка должна трактоваться как текст, даже если в нее входят служебные слова ассемблера или операторы.

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

TEXTEQU текст. Назначает тексту имя.

Синтаксис псевдооператора TEXTEQU:

имя TEXTEQU текст

· Макрокоманды – макросы

Идейно макрокоманда представляет собой дальнейшее развитие механизма замены текста. С помощью макрокоманд в текст программы можно вставлять последовательности строк (которые логически могут быть данными или командами) и даже более того — привязывать их к контексту места вставки.

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

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

Директива MACRO.

Синтаксис макроопределения следующий:

имя MACRO список_формальных_аргументов

тело макроопределения

ENDM

Где должны располагаться макроопределения? Есть три варианта:

· В начале исходного текста программы до сегмента кода и данных с тем, чтобы не ухудшать читабельноть программы. Этот вариант следует применять в случаях, если определяемые вами макрокоманды актуальны только в пределах одной этой программы.

· В отдельном файле.Этот вариант подходит при работе над несколькими программами одной проблемной области. Чтобы сделать доступными эти макроопределения в конкретной программе, необходимо в начале исходного текста этой программы записать директиву include имя_файла.

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

Функционально макроопределения похожи на процедуры. Сходство их в том, что и те, и другие достаточно один раз где-то описать, а затем вызывать их специальным образом. На этом их сходство заканчивается, и начинаются различия, которые в зависимости от целевой установки можно рассматривать и как достоинства и как недостатки:

· В отличие от процедуры, текст которой неизменен, макроопределение в процессе макрогенерации может меняться в соответствии с набором фактических параметров. При этом коррекции могут подвергаться как операнды команд, так и сами команды. Процедуры в этом отношении объекты менее гибки.

· При каждом вызове макрокоманды ее текст в виде макрорасширения вставляется в программу. При вызове процедуры микропроцессор осуществляет передачу управления на начало процедуры, находящейся в некоторой области памяти в одном экземпляре. Код в этом случае получается более компактным, хотя быстродействие несколько снижается за счет необходимости осуществления переходов.

Макроопределение обрабатывается компилятором особым образом. Для того чтобы использовать описанное макроопределение в нужном месте программы, оно должно быть активизировано с помощью макрокоманды указанием следующей синтаксической конструкции:

имя список_фактических_аргументов

Результатом применения данной синтаксической конструкции в исходном тексте программы будет ее замещение строками из конструкции тела макроопределения. Но это не простая замена. Обычно макрокоманда содержит некоторый список аргументов — список_фактических_аргументов, которыми корректируется макроопределение. Места в теле макроопределения, которые будут замещаться фактическими аргументами из макрокоманды, обозначаются с помощью так называемых формальных аргументов. Таким образом, в результате применения макрокоманды в программе формальные аргументы в макроопределении замещаются соответствующими фактическими аргументами; в этом и заключается учет контекста. Процесс такого замещения называется макрогенерацией, а результатом этого процесса является макрорасширение.

Директива LOCAL. Метки в макросе.

В макросе могут использоваться локальные метки. Синтаксис

LOCAL локал_имя ; Локальная метки, уникальная для каждого вызова макроса

LOCAL метка [ count :type ] ; Метка, в стеке создаются count ячеек размером тип

Директива EXITM. Завершает макроблок и начинает ассемблирование следующей инструкции за пределами макроблока.

EXITM [ текст ] ; Завершить макрос, возможен вывод текста.

Директива PURG. Стирание макросов из памяти.

PURGE Макрос1, макрос2 … ; Стирание перечисленных макросов из памяти

Директива GOTO. Переход на ассемблирование макрометки. Прнименяется внутри блоков MACRO, FOR, FORC, REPEAT, и WHILE. Макрометка – директива.

GOTO [ макрометка ] ; Переход на ассемблирование макрометки.

Директива ENDM. Конец макроса или потовряющего блока.

ENDM ; Конец макроса.

· Макродирективы - циклы

С помощью макросредств ассемблера можно не только частично изменять входящие в макроопределение строки, но и модифицировать сам набор этих строк и даже порядок их следования. Сделать это можно с помощью набора макродиректив (далее — просто директив). Их можно разделить на две группы:

· директивы повторения WHILE, REPEAT (=REPT), IRP и IRPC. Директивы этой группы предназначены для создания макросов, содержащих тело из нескольких строк.

· директивы управления процессом генерации макрорасширения EXITM и GOTO. Они предназначены для управления процессом формирования макрорасширения из набора строк соответствующего макроопределения. С помощью этих директив можно как исключать отдельные строки из макрорасширения, так и вовсе прекращать процесс генерации. Директивы EXITM и GOTO обычно используются вместе с условными директивами компиляции, поэтому они будут рассмотрены вместе с ними.

Директива WHILE применяется для повторения определенное количество раз тела цикла. Это цикл с предусловием. Директива имеют следующий синтаксис:

WHILE выражение

Тело цикла

ENDM

При использовании директивы WHILE макрогенератор транслятора будет повторять тело цикла до тех пор, пока значение выражения не станет равно 0. Это значение вычисляется перед очередной итерацией (и должно подвергаться изменению внутри тела цикла в процессе макрогенерации).

Директива REPEAT. Директива имеют следующий синтаксис:

REPEAT выражение

Тело цикла

ENDM

Отличие от WHILE состоит в том, что она автоматически уменьшает на 1 значение выражени после каждой итерации.

Директива .WHILE. Отличается от WHILE тем, что итерации управляются проверкой не выражения, а условия. Это цикл с предусловием. Директива имеют следующий синтаксис:

.WHILE условие

Тело цикла

.ENDW

При использовании директивы .WHILE макрогенератор транслятора будет повторять тело цикла до тех пор, пока выполняется условие. Условие проверяется перед очередной итерацией (и должно подвергаться изменению внутри тела цикла в процессе макрогенерации).

Директива .REPEAT. Это цикл с постусловием. Директива имеют следующий синтаксис:

.REPEAT

Тело цикла

.UNTIL условие

При использовании директивы .REPEAT макрогенератор транслятора будет повторять тело цикла до тех пор, пока не выполнится условие. Условие проверяется после очередной итерации (и должно подвергаться изменению внутри тела цикла в процессе макрогенерации).

Определены 2 директивы досрочного завершения итераций циклов:

· .BREAK. Завершить цикл.

· .CONTINUE. Завершить итерацию цикла.

Директивы имеют следующий синтаксис:

.BREAK .IF условие ; Завершить цикл, если условие выполняется.

.CONTINUE .IF условие ; Завершить итерацию цикла, если условие выполняется.

· Директивы компиляции по условию

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

Введение в язык ассемблера этих директив значительно повышает его мощь. Всего имеется 10 типов условных директив компиляции. Их логично попарно объединить в четыре группы:

· Директивы IF и IFE — условная трансляция по результату вычисления логического выражения.

· Директивы IFDEF и IFNDEF — условная трансляция по факту определения символического имени.

· Директивы IFB и IFNB — условная трансляция по факту определения фактического аргумента при вызове макрокоманды.

· Директивы IFIDN, IFIDNI, IFDIF и IFDIFI — условная трансляция по результату сравнения строк символов.

Директивы IF и IFE.

Условные директивы компиляции имеют общий синтаксис:

IFxxx логическое_выражение

фрагмент1

ELSE

фрагмент2

ENDIF

Если в директиве IF логическое выражение истинно, то в объектный модуль помещается фрагмент1.
Если логическое выражение ложно, то при наличии директивы ELSE в объектный код помещается фрагмент2. Если же директивы ELSE нет, то вся часть программы между директивами IF и ENDIF игнорируется и в объектный модуль ничего не включается..

Директива IFE аналогично директиве IF анализирует значение логического_выражения. Но теперь для включения фрагмент_программы_1 в объектный модуль требуется, чтобы логическое_выражение имело значение “ложь”.

Директивы IFDEF и IFNDEF

Синтаксис этих директив следующий:

IF(N)DEF символическое_имя

фрагмент1

ELSE

фрагмент2

ENDIF

Данные директивы позволяют управлять трансляцией фрагментов программы в зависимости от того, определено или нет в программе некоторое символическое_имя. Директива IFDEF проверяет, описано или нет в программе символическое_имя, и если это так, то в объектный модуль помещается фрагмент1. В противном случае, при наличии директивы ELSE, в объектный код помещается фрагмент2. Если же директивы ELSE нет (и символическое_имя в программе не описано), то вся часть программы между директивами IF и ENDIF игнорируется и в объектный модуль не включается.

Действие IFNDEF обратно IFDEF. Если символического_имени в программе нет, то транслируется фрагмент1. Если оно присутствует, то при наличии ELSE транслируется фрагмент2. Если ELSE отсутствует, а символическое_имя в программе определено, то часть программы, заключенная между IFNDEF и ENDIF, игнорируется.

Директивы IFB и IFNB

Синтаксис этих директив следующий:

IF(N)B аргумент

фрагмент1

ELSE

фрагмент2

ENDIF

Данные директивы используются для проверки фактических параметров, передаваемых в макрос. При вызове макрокоманды они анализируют значение аргумента, и в зависимости от того, равно оно пробелу или нет, транслируется либо фрагмент1, либо фрагмент2. Какой именно фрагмент будет выбран, зависит от кода директивы:

· Директива IFB проверяет равенство аргумента пробелу. В качестве аргумента могут выступать имя или число.
Если его значение равно пробелу (то есть фактический аргумент при вызове макрокоманды не был задан), то транслируется и помещается в объектный модуль фрагмент1. В противном случае, при наличии директивы ELSE, в объектный код помещается фрагмент2. Если же директивы ELSE нет, то при равенстве аргументапробелу вся часть программы между директивами IFB и ENDIF игнорируется и в объектный модуль не включается.

· Действие IFNB обратно IFB. Если значение аргумента в программе не равно пробелу, то транслируется фрагмент1.
В противном случае, при наличии директивы ELSE, в объектный код помещается фрагмент2. Если же директивы ELSE нет, то вся часть программы (при неравенстве аргументапробелу) между директивами IFNB и ENDIF игнорируется и в объектный модуль не включается.

Директивы IFIDN, IFIDNI, IFDIF и IFDIFI

Эти директивы позволяют не просто проверить наличие или значение аргументов макрокоманды, но и выполнить идентификацию аргументов как строк символов.

Синтаксис этих директив:

IFIDN(I) аргумент1,аргумент2

фрагмент1

ELSE

фрагмент2

ENDIF

 

IFDIF(I) аргумент1,аргумент2

фрагмент1

ELSE

фрагмент2

ENDIF

В директивах аргумент1 и аргумент2 сравниваются, как строки символов.

Директива IFIDN(I) сравнивает символьные значения аргумент1 и аргумент2. Если результат сравнения положительный (строки совпадают), то фрагмент1 транслируется и помещается в объектный модуль.В противном случае, при наличии директивы ELSE, в объектный код помещается фрагмент2. Если же директивы ELSE нет, то вся часть программы между директивами IFIDN(I) и ENDIF игнорируется и в объектный модуль не включается.

Действие IFDIF(I) обратно IFIDN(I). Если результат сравнения отрицательный (строки не совпадают), транслируется фрагмент1. В противном случае все происходит аналогично рассмотренным ранее директивам.

Парность этих директив объясняется тем, что они позволяют учитывать различие строчных и прописных букв. Так, директивы с символом I в конце IFIDNI и IFDIFI игнорируют это различие, а IFIDN и IFDIF — учитывают.

Эти директивы удобно применять для проверки фактических аргументов макрокоманд.

Допускается вложенность условных директив компиляции.

· Директивы генерации ошибок

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

Директивы генерации пользовательской ошибки по принципу работы можно разделить на два типа:

· безусловные директивы, генерирующие ошибку трансляции без проверки каких-либо условий;

· условные директивы, генерирующие ошибку трансляции после проверки определенных условий.

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

Безусловная генерация пользовательской ошибки

К безусловным директивам генерации пользовательской ошибки относится только одна директива — это ERR (.ERR). Данная директива, будучи вставлена в текст программы, безусловно приводит к генерации ошибки на этапе трансляции и удалению объектного модуля.

Условная генерация пользовательской ошибки.

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

Директивы .ERRB (ERRIFB) и .ERRNB (ERRIFNB)

Синтаксис директив:

.ERRB (ERRIFB) <имя_формал_аргумента> ; ошибка, если <имя_формал_аргумента> пропущено;

.ERRNB (ERRIFNB) <имя_формал_аргумента> ; ошибка, если <имя_формало_аргумента> присутствует.

Данные директивы применяются для генерации ошибки трансляции в зависимости от того, задан или нет при вызове макрокоманды фактический аргумент, соответствующий формальному аргументу в заголовке макроопределения с именем <имя_формал_аргумента>. По принципу действия эти директивы полностью аналогичны соответствующим директивам условной компиляции IFB и IFNB. Их обычно используют для проверки задания параметров при вызове макроса.

Строка имя_формального_аргумента должна быть заключена в угловые скобки.

Директивы .ERRDEF (ERRIFDEF) и .ERRNDEF (ERRIFNDEF)

Синтаксис директив:

.ERRDEF (ERRIFDEF) символическое_имя ; ошибка, если имя определено до выдачи директивы в программе

.ERRNDEF(ERRIFNDEF) символическое_имя ; ошибка, если имя не определено до момента обработки транслятором

Данные директивы генерируют ошибку трансляции в зависимости от того, определено или нет некоторое символическое_имя. в программе. Не забывайте о том, что компилятор TASM по умолчанию формирует объектный модуль за один проход исходного текста программы. Следовательно, директивы .ERRDEF (ERRIFDEF) и .ERRNDEF (ERRIFNDEF) отслеживают факт определения символического_имени только в той части исходного текста, которая находится до этих директив.

Директивы .ERRDIF (ERRIFDIF) и .ERRIDN (ERRIFIDN)

Синтаксис директив:

.ERRDIF (ERRIFDIF) <строка_1>,<строка_2> ; ошибка, если две строки посимвольно не совпадают.

.ERRIDN (ERRIFIDN) <строка_1>,<строка_2> ; ошибку, если строки посимвольно идентичны.

Для того чтобы игнорировать различия строчных и прописных букв, существуют аналогичные директивы:

.ERRIFDIFI <строка_1>,<строка_2> ; то же, что и ERRIFDIF, но игнорируется различие строчных и прописных букв

.ERRIFIDNI <строка_1>,<строка_2> ; то же, что и ERRIFIDN, но игнорируется различие строчных и прописных букв

Данные директивы, как и соответствующие им директивы условной компиляции, удобно применять для проверки передаваемых в макрос фактических параметров.

Директивы .ERRE (ERRIFE) и .ERRNZ (ERRIF)

Синтаксис директив:

.ERRE (ERRIFE) константное_выражение ; ошибка, если константное_выражение ложно (равно нулю) .ERRNZ(ERRIF) константное_выражение ; ошибка, если константное_выражение истинно (не равно нулю).

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

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

· Управление листингом

Листинг – это текстовый файл, формируемый ассемблером при трансляции. Команды ниже управляют его содержанием.

Мнемоника Действие
.CREF Разрешает листинг в символьном формате из таблиц символов.
.NOCREF [ [имя [ [, имя ] ]...] ] Запрещает листинг символов из источников с перечисленными именами.
.LIST Старт листинга инструкций. Директива по умолчанию.
.NOLIST Синоним .XLIST Выключает листинг инструкций. Директива по умолчанию.
.LISTIF Синоним .LFCOND Старт листинга из условных блоков , в которых условие не выполнено.
.NOLISTIF Синоним .SFCOND Выключает листинг из условных блоков, в которых условие не выполнено.
.LISTMACRO Синоним .XALL Старт лиситнга инструкций в макросах, которые генерируют коды или данные. Директива по умолчанию.
.NOLISTMACRO Синоним .SALL Выключает листинг макросов
.LISTMACROALL Синоним .LALL Старт листинга всех инструкций в макросах.
.LISTALL Старт листинга всех инструкций. Комбинация .LIST, .LISTIF, LISTMACROALL
PAGE [ [ [ [ длина] ], ширина] ] Определяет размер страницы листинга программного кода (длина и ширина). Если аргументов нет, то создается пустая страница.
PAGE + Увеличивает на 1 номер страницы листнга программного кода.
TITLE текст Определяет заголовок программного листинга.
SUBTITLE текст Синоним SBTTL Определяет подзаголовок программного листинга.
.TFCOND Переключает режим листинга из условных блоков.