9. Оператор FIX и макросы внутри макросов

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

Загрузка...