Также, возможно переопределять директивы ассемблера:
macro use32
{
align 4
use32
}
macro use16
{
align 2
use16
}
5. Макросы с фиксированным количеством аргументов
5.1. Макросы с одним аргументом
Макросы могут иметь аргумент. Аргумент представляет собой какой-либо идентификатор, который будет повсюду заменён в теле макроса тем, что будет указанно при использовании.
Синтаксис:
macro name argument { тело макроса }
Например:
macro add5 where
{
add where, 5
}
add5 ax
add5 [variable]
add5 ds
add5 ds+2
получим:
add ax, 5
add [variable], 5
add ds, 5 ;такой инструкции не существует
;но препроцессор это не волнует.
;ошибка появится на стадии ассемблирования.
add ds+2,5 ;ошибка синтаксиса, как и ранее
;определится при анализе синтаксиса (parsing).
(разумеется, комментарии в результате работы препроцессора не появятся:)
5.2. Макросы с несколькими аргументами
У макросов может быть несколько аргументов, разделённых запятыми,
macro movv where, what
{
push what
pop where
}
movv ax, bx
movv ds, es
movv [var1], [var2]
преобразуется в:
push bx
pop ax
push es
pop ds
push [var2]
pop [var1]
Если несколько аргументов имеют одно и тоже имя, то будет использован первый из них:).
Если при использовании макроса указать меньше аргументов, чем при определении, то значения неуказанных будет пустым:
macro pupush a1, a2, a3, a4
{
push a1 a2 a3 a4
pop a4 a3 a2 a1
}
pupush eax, dword [3]
получим:
push eax dword [3]
pop dword [3] eax
Если в аргументе макроса необходимо указать запятую, необходимо аргумент заключить в скобочки из символов < и >.
macro safe_declare name, what
{
if used name
name what
end if}
safe_declare var1, db 5
safe_declare array5, <dd 1,2,3,4,5>
safe_declare string, <db "привет, я просто строка",0>
получим:
if used var1
var1 db 5
end if
if used array5
array5 dd 1,2,3,4,5
end if
if used string
string db "привет, я просто строка",0
end if
Конечно же, можно использовать символы < и > и внутри тела макроса:
macro a arg {db arg}
macro b arg1,arg2 {a <arg1,arg2,3>}
b <1,1>,2
получим:
db 1,1,2,3
5.3. Директива LOCAL
Возможно, появится необходимость объявить метку внутри тела макроса:
macro pushstr string
{
call behind ; помещаем в стек адрес string и переходим к behind
db string, 0
behind:
}
но если использовать такой макрос 2 раза, то и метка behind будет объявлена дважды, что приведёт к ошибке. Эта проблема решается объявлением локальной метки behind. Это и делает директива LOCAL.
Синтаксис:
local label_name
Директива должна применяться внутри тела макроса. Все метки label_name внутри макроса становятся локальными. Так что, если макрос используется дважды никаких проблем не появляется:
macro pushstr string
{
local behind
call behind
db string,0
behind:
}
pushstr 'aaaaa'
pushstr 'bbbbbbbb'
call something
На самом деле, behind заменяется на behind?XXXXXXXX, где XXXXXXXX — какой-то шестнадцатеричный номер генерируемый препроцессором. Последний пример может быть преобразован к чему-то вроде:
call behind?00000001
db 'aaaaa', 0
behind?00000001:
call behind?00000002
db 'bbbbbbbb', 0
behind?00000002:
call something
Заметьте, Вы не сможете напрямую обратиться к метке содержащей ? так как это специальный символ в FASM, поэтому он и используется в локальных метках. К примеру, aa?bb рассматривается как идентификатор aa, специальный символ ? и идентификатор bb.
Если Вам нужно несколько локальных меток — не проблема, их можно указать в одной директиве LOCAL, разделив запятыми ,:
macro pushstr string ; делает то же, что и предыдущий макрос
{
local addr, behind
push addr
jmp behind
addr db string,0
behind:
}
Всегда хорошо бы начинать все локальные метки макросов с двух точек .. — это значит, что они не будут менять текущую глобальную метку. К примеру:
macro pushstr string
{
local behind
call behind
db string, 0
behind:
}
MyProc:
pushstr 'aaaa'
.a:
будет преобразовано в:
MyProc:
call behind?00000001
db 'aaaa', 0
behind?00000001:
.a:
в результате получим метку behind?00000001.a вместо MyProc.a. Но если в примере выше behind заменить на ..behind, текущая глобальная метка не изменится и будет определена метка MyProc.a:
macro pushstr string
{
local ..behind
call ..behind
db string,0
..behind:
}
MyProc:
pushstr 'aaaa'
.a: