Если после директивы «macro
» вы заключаете некоторую группу аргументов в квадратные скобки, это позволит при использовании макроинструкции задать данной группе аргументов больше значений. Любой следующий аргумент данный после последнего аргумента данной группы начнет новую группу и станет её первым членом. Поэтому после закрытия квадратных скобок не должно быть имен аргументов. Содержание макроинструкции будет обрабатываться для каждой такой группы аргументов отдельно. Простейший пример — это заключение одного имени аргумента в квадратные скобки:
macro stoschar [char]
{
mov al,char
stosb
}
Эта макроинструкция допускает неограниченное число аргументов, и каждый будет обработан этими двумя инструкциями отдельно. Например, «stoschar 1,2,3
» будет ассемблирован как следующие инструкции:
mov al,1
stosb
mov al,2
stosb
mov al,3
stosb
Существуют некоторые специальные директивы, возможные только внутри определений макроинструкций. Директива «local
» задает локальные имена, которые будут заменены уникальными значениями каждый раз, когда используется макроинструкция. За ней должны следовать имена, разделенные запятыми. Эта директива обычно требуется для внутренних констант или меток макроинструкции. Например:
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
»: