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 ;создаём и завершаем нулём
}
string_table 'aaaaa','bbbbbb','5'
получим:
dd addr?00000001
dd addr?00000002
dd addr?00000003
addr?00000001 db 'aaaaa',0
addr?00000002 db 'bbbbbb',0
addr?00000003 db '5',0
Другой пример с блоком REVERSE
:
macro a [x]
{
forward
local here
here db x
reverse
dd here
}
a 1,2,3
будет:
here?00000001 db 1
here?00000002 db 2
here?00000003 db 3
dd here?00000003
dd here?00000002
dd here?00000001
Как видно, метки используется с соответствующими аргументами и в FORWARD
и в REVERSE
блоках.
6.7. Макросы с несколькими групповыми аргументами
Возможно использовать и несколько групповых аргументов. В этом случае определение макроса не будет выглядеть как:
macro a [grp1],[grp2]
так как тут не ясно какой аргумент какой группе принадлежит. Исходя из этого делают так:
Синтаксис:
macro a [grp1,grp2]
В этом случае каждый нечётный аргумент относится к группе grp1
, а каждый чётный — к grp2
:
macro a [grp1,grp2]
{
forward
l_#grp1:
forward
l_#grp2:
}
a 1,2,3,4,5,6
будет:
l_1:
l_3:
l_5:
l_2:
l_4:
l_6:
Или ещё:
macro ErrorList [name,value]
{
forward
ERROR_#name = value
}
ErrorList \
NONE,0,\
OUTOFMEMORY,10,\
INTERNAL,20
получим:
ERROR_NONE = 0
ERROR_OUTOFMEMORY = 10
ERROR_INTERNAL = 20
Конечно же, может быть больше 2х групп аргументов:
macro a [g1,g2,g3]
{
common
db g1
db g2
db g3
}
a 1,2,3,4,5,6,7,8,9,10,11
будет:
db 1,4,7,10
db 2,5,8,11
db 3,6,9
7. Условный препроцессинг
В действительности, FASM не имеет директив для условного препроцессинга. Но директива ассемблера if
может быть использована совместно с возможностями препроцессора для получения тех же результатов, что и при условном препроцессинге. (Но в этом случае увеличивается расход памяти и времени).
Как известно, оператор if
обрабатывается во время ассемблирования. Это значит, что условие в этом операторе проверяется после обработки исходного текста препроцессором. Именно это обеспечивает работу некоторых логических операций.
Я не буду рассказывать о деталях времени ассемблирования (логических операциях вроде &
, |
и т. п.) — RTFM. Я лишь расскажу об операторах проверки условия используемых препроцессором.
7.1. Оператор EQ
Простейший логический оператор — это EQ
. Он всего лишь сравнивает два идентификатора — одинаковы ли их значение. Значение abcd eq abcd
— истина, а abcd eq 1
— ложь и так далее… Это полезно для сравнения символов, которые будут обработаны препроцессором:
STRINGS equ ASCII
if STRINGS eq ASCII
db 'Oh yeah',0
else if STRINGS eq UNICODE
du 'Oh yeah',0
else
display 'unknown string type'
end if
после обработки препроцессором, это примет вид:
if ASCII eq ASCII
db 'Oh yeah',0
else if ASCII eq UNICODE
du 'Oh yeah',0
else
display 'unknown string type'
end if
Здесь только первое условие (ASCII eq ASCII
) выполняется, так что будет ассемблировано только db 'Oh yeah',0
Другой вариант:
STRINGS equ UNICODE ;разница здесь, UNICODE вместо ASCII
if STRINGS eq ASCII
db 'Oh yeah',0
else if STRINGS eq UNICODE
du 'Oh yeah',0
else
display 'unknown string type'
end if
получим:
if UNICODE eq ASCII
db 'Oh yeah',0
else if UNICODE eq UNICODE
du 'Oh yeah',0
else
display 'unknown string type'
end if
Тут уже первое условие (UNICODE eq ASCII
) будет ложно, второе (UNICODE eq UNICODE
) — верно, будет ассемблироваться du 'Oh yeah',0
.
Несколько лучшее применение этого — проверка аргументов макросов, вроде:
macro item type,value
{
if type eq BYTE
db value
else if type eq WORD
dw value
else if type eq DWORD