Выбрать главу

 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 был бы объявлен так: