Обратите внимание, нельзя использовать #
совместно с идентификаторами, определёнными local
, так как local
обрабатывается препроцессором раньше, чем #
. Из-за этого подобный код работать не будет:
macro a arg
{
local name_#arg
}
a foo
5.5. Оператор `
Существует оператор, преобразующий идентификатор в символьную строку. Он так же может быть использован только внутри макросов:
macro proc name
{
name:
log `name ;log - макрос, принимающий параметр-строку
}
proc DummyProc
получим:
DummyProc:
log 'DummyProc'
Пример посложнее, с использованием #
macro proc name
{
name:
log 'начинается подпрограмма: '#`name
}
proc DummyProc
retn
proc Proc2
retn
будет:
DummyProc:
log 'начинается подпрограмма: DummyProc'
retn
Proc2:
log 'начинается подпрограмма: Proc2'
retn
6. Макросы с групповыми аргументами
6.1. Определение макросов с групповым аргументом
У макросов могут быть так называемые групповые аргументы. Это позволяет использовать переменное количество аргументов. При определении макроса, групповой аргумент заключается в квадратные скобочки [
и ]
:
Синтаксис:
macro name arg1, arg2, [grouparg]
{
; тело макроса
}
Среди аргументов в определении макроса, групповой аргумент должен быть последним. Групповой аргумент может содержать несколько значений:
macro name arg1,arg2,[grouparg] {}
name 1,2,3,4,5,6
В этом примере значение arg1
будет 1
, arg2
— 2
, а grouparg
— 3,4,5,6
.
6.2. Директива COMMON
Для работы с групповыми аргументами применяются специальные директивы препроцессора. Они могут быть использованы только внутри тела макроса имеющего групповой аргумент. Первая такая директива — это COMMON
. Она означает, что после неё имя группового аргумента будет замещаться всеми аргументами сразу:
macro string [grp]
{
common
db grp,0
}
string 'aaaaaa'
string 'line1',13,10,'line2'
string 1,2,3,4,5
получим:
db 'aaaaaa',0
db 'line1',13,10,'line2',0
db 1,2,3,4,5,0
6.3. Директива FORWARD
Аргументы можно обрабатывать и по-отдельности. Для этого служит директива FORWARD
. Часть тела макроса после этой директивы обрабатывается препроцессором для каждого аргумента из группы:
macro a arg1,[grparg]
{
forward
db arg1
db grparg
}
a 1,'a','b','c'
a -1, 10, 20
будет:
db 1
db 'a'
db 1
db 'b'
db 1
db 'c'
db -1
db 10
db -1
db 20
Директива FORWARD
работает по умолчанию для макросов с групповыми аргументами, так что предыдущий пример можно сделать так:
macro a arg1,[grparg]
{
db arg1
db grparg
}
6.4. Директива REVERSE
REVERSE
— это аналог FORWARD
, но обрабатывает группу аргументов в обратном порядке — от последнего к первому:
macro a arg1,[grparg]
{
reverse
db arg1
db grparg
}
a 1,'a','b','c'
получим:
db 1
db 'c'
db 1
db 'b'
db 1
db 'a'
6.5. Комбинирование директив управления группами
3 вышеупомянутые директивы могут разделять тело макроса на блоки. Каждый блок обработается препроцессором после предыдущего. Например:
macro a [grparg]
{
forward
f_#grparg: ;оператор объединения
common
db grparg
reverse
r_#grparg:
}
a 1,2,3,4
будет:
f_1:
f_2:
f_3:
f_4:
db 1,2,3,4
r_4:
r_3:
r_2:
r_1:
6.6. Директива LOCAL в макросах с групповыми аргументами
У локальных меток в макросах есть ещё одно полезное свойство. Если директива LOCAL
находится внутри блока FORWARD
или REVERSE
, то уникальное имя метки сгенерируется для каждого аргумента из группы, и в последующих блоках FORWARD
и/или REVERSE
для каждого аргумента будет использована соответствующая ему метка:
macro string_table [string]
{
forward ;таблица указателей на строки
local addr ;локальная метка для строки
dd addr ;указатель на строку
forward ;строки
addr db string,0 ;создаём и завершаем нулём
}