Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
программирование и основы алгоритмизации.doc
Скачиваний:
34
Добавлен:
21.08.2019
Размер:
4.84 Mб
Скачать

Макрокоманды

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

  • макроассемблер;

  • механизм процедур;

  • механизм прерываний.

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

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

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

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

endm

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

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

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

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

Есть три варианта размещения макроопределений:

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

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

.586

.model flat, stdcall

include show.inc ;сюда вставляется текст файла show.inc

  • в макробиблиотеке. Универсальные макрокоманды, которые используются практически во всех программах целесообразно записать в так называемую макробиблиотеку. Сделать актуальными макрокоманды из этой библиотеки можно также с помощью директивы include. Недостаток этого и предыдущего способов в том, что в исходный текст программы включаются абсолютно все макроопределения. Для исправления ситуации можно использовать директиву purge, в качестве операндов которой через запятую перечисляются имена макрокоманд, которые не должны включаться в текст программы. К примеру:

include iomac.inc

purge outstr,exit

В данном случае в исходный текст программы перед началом трансляции MASM вместо строки include iomac.inc вставит строки из файла iomac.inc. Но вставленный текст будет отличаться от оригинала тем, что в нем будут отсутствовать макроопределения outstr и exit.

Каждый фактический аргумент представляет собой строку символов, для формирования которой применяются следующие правила:

  1. Строка может состоять:

    • из последовательности символов без пробелов, точек, запятых, точек с запятой;

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

  2. Для того чтобы указать, что некоторый символ внутри строки, представляющей фактический параметр, является собственно символом, а не чем-то иным, например, некоторым разделителем или ограничивающей скобкой, применяется специальный оператор «!». Этот оператор ставится непосредственно перед описанным выше символом, и его действие эквивалентно заключению данного символа в угловые скобки.

  3. Если требуется вычисление в строке некоторого константного выражения, то в начале этого выражения нужно поставить знак %:

%константное_выражение — значение константное_выражение вычисляется и подставляется в текстовом виде в соответствии с текущей системой счисления.

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

имя_формального_аргумента[: тип]

где тип может принимать значения:

  • REQ – требуется обязательное явное задание фактического аргумента при вызове макрокоманды;

  • =<любая_строка> — если аргумент при вызове макрокоманды не задан, то в соответствующие места в макрорасширении будет вставлено значение по умолчанию, соответствующее значению любая_строка. Символы, входящие в любая_строка, должны быть заключены в угловые скобки.

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

.686p

.model flat, stdcall

def_table macro t:REQ, len:=<1>

tabl_&t d&t len dup(5)

endm

.data

def_table d, 10

def_table b

.code

main proc

mov al, [tabl_b]

mov ah, [tabl_b+1]

mov ebx, [tabl_d]

ret

main endp

end main

После трансляции текста программы, содержащего строки секции данных, получится

def_table d, 10 ;tabl_d dd 10 dup(5)

def_table b ;tabl_b db 1 dup(5)

Заметим, что строка программы mov ah, [tabl_b+1] поместит в ah число отличное от 5, поскольку память для tabl_b распределена только под 1 элемент массива длиной 1 байт со значением 5.

Символ & можно применять и для распознавания формального аргумента в строке, заключенной в кавычки "".

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

local список_идентификаторов

Эту директиву необходимо задавать непосредственно за заголовком макроопределения. Результатом работы этой директивы будет генерация в каждом экземпляре макрорасширения уникальных имен для всех идентификаторов, перечисленных в список_идентификаторов. Эти уникальные имена имеют вид ??хххх, где хххх — шестнадцатеричное число. Для первого идентификатора в первом экземпляре макрорасширения хххх=0000, для второго — хххх=0001 и т. д. Контроль за правильностью размещения и использования этих уникальных имен берет на себя ассемблер.

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

.686P

.MODEL FLAT, STDCALL

PrintName macro Name

local STR1, STR2, METKA

jmp METKA

STR1 DB "Программа",0

STR2 DB "Меня зовут: &Name ",0

METKA:

PUSH 0

PUSH OFFSET STR1

PUSH OFFSET STR2

PUSH 0

CALL MessageBoxA@16

endm

Init macro

EXTERN MessageBoxA@16:NEAR

endm

Init

.CODE

START:

PrintName <Лена>

PrintName <Таня>

PrintName <Алёша>

RET

END START

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

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

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

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