Руководство по препроцессору FASM - страница 3
или >mov
). Всё потому, что основное предназначение макросов — имитировать инструкции. Единственное исключение из этого правила — макросы недопустимы после префиксов инструкций (>rep
).
Пример:
>macro CheckErr
>{
> cmp eax, -1
> jz error
>}
> call Something
>a: CheckErr ; здесь макросу предшествует метка, всё Ок.
получим:
> call Something
>a: cmp eax,-1
> jz error
Пример № 2:
>macro stos0
>{
> mov al, 0
> stosb
>}
> stos0 ;это место инструкции, будет замена.
>here: stos0 ;это тоже место инструкции.
> db stos0 ;здесь инструкции не место, замены не будет.
получим:
> mov al, 0
> stosb
>here: mov al, 0
> stosb
> db stos0
Возможно переопределять (overload) инструкции посредством макросов. Так как препроцессор ничего об инструкциях не знает, он позволяет использовать мнемонику инструкции в качестве имени макроса:
>macro pusha
>{
> push eax ebx ecx edx ebp esi edi
>}
>macro popa
>{
> pop edi esi ebp edx ecx ebx eax
>}
эти 2 новые инструкции будут экономить по 4 байта в стеке, так как не сохраняют >ESP
(правда, занимают побольше места, чем реальные инструкции:). Всё же, переопределение инструкций не всегда хорошая идея — кто-нибудь читая Ваш код может быть введён в заблуждение, если он не знает, что инструкция переопределена.
Также, возможно переопределять директивы ассемблера:
>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,
>safe_declare string,
получим:
>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
>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
— какой-то шестнадцатеричный номер генерируемый препроцессором. Последний пример может быть преобразован к чему-то вроде: