end if
}
Это так же работает для нескольких идентификаторов (как и EQ):
if dword [eax] in <[eax], dword [eax], ptr eax, dword ptr eax>
8. Структуры
В FASM, структуры практически тоже самое, что и макросы. Определяются они посредством директивы STRUC:
Синтаксис:
struc name arguments { тело структуры }
Отличае от макросов заключается в том, что в исходном тексте перед структурой должна находиться метка — имя объекта-структуры. Например:
struc a {db 5}
a
это не будет работать. Структуры распознаются только после меток, как здесь:
struc a {db 5}
name a
подобно макросу, это преобразуется препроцессором в:
db 5
Смысл метки в следующем — она будет добавлена ко всем идентификаторам из тела структуры, которые начинаются с точки… Например:
struc a {.locaclass="underline" }
name1 a
name2 a
будет:
name1.locaclass="underline"
name2.locaclass="underline"
Таким образом можно создавать структуры вроде тех, что есть в других языках:
struc rect left,right,top,bottom ;аргументы как у макроса
{
.left dd left
.right dd right
.top dd top
.bottom dd bottom
}
r1 rect 0,20,10,30
r2 rect ?,?,?,?
получим:
r1.left dd 0
r1.right dd 20
r1.top dd 10
r1.bottom dd 30
r2.left dd ?
r2.right dd ?
r2.top dd ?
r2.bottom dd ?
Поскольку, используемой структуре всегда должна предшествовать метка, препроцессор однозначно отличает их от макросов. Поэтому имя структуры может совпадать с именем макроса — в каждом случае будет выполняться нужная обработка.
Существуют хитрый приём, позволяющий не указывать аргументы, если они равны 0:
struc ymmv arg
{
.member dd arg+0
}
y1 ymmv 0xACDC
y2 ymmv
будет:
y1.member dd 0xACDC+0
y2.member dd +0
Как говорилось ранее, если значение аргумента не указанно, то в теле макроса или структуры вместо него ничего не подставляется. В этом примере + используется или как бинарный (то есть с двумя операндами), или как унарный (с одним операндом) оператор.
ПРИМЕЧАНИЕ: часто используется так же макрос или структура struct, которая определяется для расширения возможностей при определении структур. Не путайте struct и struc.
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 был бы объявлен так: