macro movstr
{
local move
move:
lodsb
stosb
test al,al
jnz move
}
Каждый раз, когда используется эта макроинструкция, « move
» заменяется новым уникальным именем. То есть вы не получите ошибку, это обычный случай, когда метка определяется больше, чем один раз.
« forward
», « reverse
» и « common
» делят макроинструкцию на блоки, каждый из которых обрабатывается после окончания обработки предыдущего. Они различаются в поведении только если макроинструкция поддерживает много групп аргументов. Блок инструкций, следующий за « forward
» будет обрабатываться для каждой группы аргументов от первой до последней, как блок по умолчанию (без этих директив). Блок, идущий за « reverse
» будет обрабатываться для каждой группы аргументов в обратном порядке — от последней до первой. Блок за директивой « common
» обрабатывается лишь один раз, просто для всех групп аргументов. Локальное имя, определенное в одном блоке, доступно во всех следующих блоках при обработке той же группы аргументов. Если оно было определено в блоке « common
», оно доступно во всех следующих блоках, независимо от обрабатываемой группы.
Вот пример макроинструкции, которая создает таблицу адресов строк и следующих за ними строк.
macro strtbl name,[string]
{
common
label name dword
forward
local label
dd label
forward
label db string,0
}
Первый аргумент, задаваемый этой макроинструкции, станет меткой для таблицы адресов, следующими аргументами должны быть строки. Первый блок обрабатывается однажды и определяет метку, второй блок назначает локальную метку для каждой строки и определяет запись в таблице, содержащий адрес этой строки. Третий блок определяет данные каждой строки с соответствующей меткой.
Первая инструкция, следующая за директивой, начинающей блок в макроинструкции, может идти с ней на той же строке, как на следующем примере:
macro stdcall proc,[arg]
{
reverse push arg
common call proc
}
Это макрос может применяться для вызова процедур, используя соглашение STDCALL, аргументы сохраняются в стеке в обратном порядке. Например, « stdcall foo,1,2,3
» будет ассемблировано так:
push 3
push 2
push 1
call foo
Если некоторое имя внутри макроинструкции имеет несколько значений (это либо один из аргументов, заключенных в квадратные скобки, либо локальное имя, определенное в блоке, следующем за директивой « forward
» или « reverse
») и используется в блоке, следующем за директивой « common
», оно будет заменено на все значения, разделенные запятыми. Например, следующий макрос передать все дополнительные аргументы ранее определенной макроинструкции « stdcall
»:
macro invoke proc,[arg]
{ common stdcall [proc],arg }
Он может применяться для непрямого вызова (через указатель в памяти) процедуры, используя соглашение STDCALL.
Внутри макроинструкции также может быть использован специальный оператор « #
». Этот оператор сцепляет два имени в одно. Это может быть полезно, так как делается после того, как аргументы и локальные имена заменяются на свои значения. Следующая макроинструкция генерирует условный переход в зависимости от аргумента « cond
»:
macro jif op1,cond,op2,label
{
cmp op1,op2
j#cond label
}
Например, « jif ax,ae,10h,exit
» будет ассемблировано как инструкции « cmp ax,10h
» и « jae exit
».
Оператор « #
» может также использоваться для объединения двух строк, заключенных в кавычки.
Возможно преобразование имени в строку в кавычках с помощью оператора « `
», который также может быть использован внутри макроинструкции. Он конвертирует следующее за ним имя в строку, заключенную в скобки, но имейте в виду, что если за ним следует аргумент, который заменяется на значение, содержащее больше, чем один символ, будет преобразован только первый из них, так как оператор « `
» конвертирует только символ, идущий непосредственно за ним. Здесь пример использования этих двух свойств:
macro label name
{
label name
if ~ used name
display `name # "is defined but not used.",13,10
end if
}
Если метка, определенная таким макросом, не используется в коде, он известит вас об этом сообщением, указывающим, к какой метке это относится.
Чтобы создать макроинструкцию, ведущую себя по-разному в зависимости от типа аргументов, например если это строки в кавычках, вы можете использовать оператор сравнения « eqtype
». Вот пример его использования для отделения строки в кавычках от других типов аргументов:
Читать дальше