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
— какой-то шестнадцатеричный номер генерируемый препроцессором. Последний пример может быть преобразован к чему-то вроде:

 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:

5.4. Оператор объединения #

У макроязыка FASMа есть ещё одна возможность — манипуляции с идентификаторами. Делается это оператором

#
, который объединяет два идентификатора в один. К примеру,
a#b
становится
ab
, а
aaa bbb#ccc ddd
aaa bbbccc ddd
.

Оператор

#
может быть использован только внутри тел макросов, а объединение символов происходит после замены аргументов макроса параметрами. Так что его можно использовать для создания новых идентификаторов из переданных в макрос параметров:

macro string name, data

{

 local ..start

 ..start:

 name db data,0

 sizeof.#name = $ —..start

}

string s1,'нудные макросы'

string s2,<'а вот и я',13,10,'заставлю тебя их видеть во сне'>

получим:

..start?00000001:

s1 db 'нудные макросы',0

sizeof.s1 = $ —..start?00000001

..start?00000002:

s2 db 'а вот и я',13,10,'заставлю тебя их видеть во сне',0

sizeof.s2 = $ —..start?00000002

так что для всех строк, создаваемых этим макросом будет определён идентификатор

sizeof.имя строки
, равный количеству байт строки.

Оператор

#
способен так же объединять символьные строки:

macro debug name

{

 db 'name: '#b,0

}

debug '1'

debug 'foobar'

будет:

db 'name: 1',0

db 'name: foobar',0

Это полезно при передаче аргументов из макроса в макрос:

macro pushstring string

{

 local ..behind

 call ..behind

 db string,0

 ..behind:}

macro debug string

{

 push MB_OK

 push 0 ;empty caption

 pushstring 'debug: '#string ;принимает один аргумент

 push 0           ;нет окна-предка

 call [MessageBox]

}

Обратите внимание, нельзя использовать

#
совместно с идентификаторами, определёнными
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

Загрузка...