В стародавние времена, в FASMе отсутствовала одна полезная возможность — создавать макросы внутри других макросов. Например, что бы при развёртывании макроса был бы определён новый макрос. Что-то вроде гипотетичного:
macro declare_macro_AAA
{
macro AAA
{
db 'AAA',0
} ;завершаем определение AAA
} ;завершаем определение declare_macro_AAA
Проблема в том, что когда макрос
declare_macro_AAA
обрабатывается препроцессором, первая найденная скобочка }
считается завершением определения его, а не так как хотелось бы. Так же происходит и с другими символами и/или операторами (например, #
, `
, forward
, local
).
Но со временем, была добавлена новая директива. Она работает подобно
EQU
, но обрабатывается до любого другого препроцессинга. (За исключением предварительных операций, про которые говорится в разделе Общие понятия — они выполняются как бы до самого препроцессинга, но это уже внутренние детали, не слишком интересные). Директива эта называется FIX
:
Синтаксис:
name1 fix name2
Видно, что синтаксис такой же как у
EQU
, но как я сказал, когда препроцессор обрабатывает часть кода, он смотрит, есть ли FIX
, а потом уже делает всё остальное. Например код:
a equ 10
b fix 10
mov ax, a
mov bx, b
будет преобразован в:
mov ax, 10
mov bx, 10
Но при обработке такого кода:
equ fix =
a equ 10
mov ax, a
в первой строк директива
FIX
скажет препроцессору поменять все EQU
на =
. Далее, перед обработкой следующей строки, препроцессор проверит, нет ли там пофиксеных идентификаторов. Так что в нашей второй строке equ
будет заменено на =
, и строка примет вид a = 10
. Так что никакой другой обработки этой строки не будет выполнено. А значит, и третья строка не будет преобразовываться препроцессором, так как идентификатор a не будет определён директивой EQU
. Результат всего этого будет такой:
a = 10
mov ax, a
Директива
FIX
может быть использован и для определения макросов в макросах — того, что мы хотели сделать в нашем гипотетичном примере. Делается это подобным образом:
macro declare_macro_AAA
{
macro AAA
%_
db 'aaa',0
_%
}
%_ fix {
_% fix }
declare_macro_AAA
Здесь, препроцессор найдёт объявление макроса
declare_macro_AAA
и определит его, далее будет два FIX
, и потом использование макроса declare_macro_AAA
. Так что он преобразует это в:
macro declare_macro_AAA
{
macro AAA
%_
db 'aaa',0
_%
}
%_ fix {
_% fix }
macro AAA
%_
db 'aaa',0
_%
и теперь уже содержимое нового макроса будет обработано препроцессором. Далее будут заменены аргументы
FIX
ов, и получится:
macro declare_macro_AAA
{
macro AAA
%_
db 'aaa',0
_%
}
macro AAA
{
db 'aaa',0
}
как мы и хотели.
Подобным образом можно пофиксить все остальные проблематичные вещи:
macro declare_macro_TEXT
{
macro TEXT [arg]
%_
%forward
db %x arg
_%
}
%_ fix {
_% fix }
%forward fix forward
declare_macro_TEXT
%x fix `
TEXT abc,def
В этом примере нужно обратить внимание на один момент: строка
%x fix `
должна находиться после declare_macro_TEXT
. Если б она находилась до, то %x
было бы пофиксено во время развёртывания макроса, и тогда `arg
приняло бы вид 'arg'
, следовательно макрос TEXT
был бы объявлен так:
macro TEXT [arg]
{
forward
db 'arg' ;строка не зависит от аргументов
}
Но, в нашем случае он будет:
macro TEXT [arg]
{
forward
db `arg ;имена аргументов превращаются в строки
}
Этот пример показывает, как важно местонахождение
FIX
.
Иногда необходимо фиксить идентификаторы дважды:
macro m1
{
macro m2
%_
macro m3 [arg]
%%_
db arg
_%%
_%
}
%%_ fix %_
_%% fix _%
%_ fix {
%_ fix }
m1
m2
m3
Символы фиксятся даже во время препроцессинга других
FIX
, так что код выше не будет работать, если порядок будет такой:
%_ fix {
%_ fix }
%%_ fix %_
_%% fix _%
В этом случае строка
%%_ fix %_
была бы пофиксена сразу же после %_ fix {
, так что все последующие %%_
сразу же преобразовались бы в }
. То же самое и для _%% fix _%
.
Я знаю,
FIX
ы могут смутить, и хорошо бы понимать внутренние детали работы препроцессора, но они предоставляют очень большие возможности. Privalov делает FASM настолько мощным, на сколько это возможно, даже за счёт некоторого ущерба удобопонятности.