В стародавние времена, в 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 настолько мощным, на сколько это возможно, даже за счёт некоторого ущерба удобопонятности.