Реклама полностью отключится, после прочтения нескольких страниц!



1. Об этом документе

Я написал это потому что вижу, как многие задают вопросы на форуме FASM, связанные с непониманием идей или особенностей препроцессора. (Я не отговариваю Вас задавать такие вопросы, непонимание чего-то — это вполне нормально, и если Ваш вопрос не чересчур сложен, кто-нибудь наверняка на него ответит).

Если Вам что-нибудь из туториала покажется непонятным, пожалуйста, напишите на форум FASM, форум WASM, автору или переводчику.

2.1. Что такое препроцессор

Препроцессор — это программа (или чаще — часть компилятора), которая преобразует исходный текст непосредственно перед компиляцией. К примеру, если Вы используете какой-либо кусок кода довольно часто, можно дать ему некое имя и заставить препроцессор повсеместно заменять это имя в исходном тексте на соответствующий ему код.

Другой пример — Вы хотите имитировать инструкцию, которая на самом деле не существует. В таком случае препроцессор может заменять её последовательностью инструкций дающих желаемый эффект.

Препроцессор просматривает исходный текст и заменяет некоторые вещи другими. Но как объяснить препроцессору, что именно он должен делать? Для этих целей существуют директивы препроцессора. О них мы и будем говорить.

Препроцессор понятия не имеет о инструкциях, директивах компилятора и прочих подобных вещах. Для него существуют собственные команды, и он игнорирует всё остальное.

2.2. Комментарии

Подобно большинству ассемблеров, комментарии в FASM начинаются с точки с запятой ;. Всё, что следует за этим символом до конца строки игнорируется и удаляется из исходника.

К примеру, исходный текст

; заполним 100h байтов адресуемых EDI нулями

xor eax, eax ; обнуляем eax

mov ecx, 100h/4

rep stosd

после препроцессора превращается в

xor eax,eax

mov ecx,100h/4

rep stosd

ПРИМЕЧАНИЕ: ; можно рассматривать как директиву препроцессора, удаляющую текст начиная с этого символа до конца строки.

ПРИМЕЧАНИЕ: Строка, полностью состоящая из комментария не будет удалена. Она становится пустой строкой (см. пример выше). Это будет важно в дальнейшем.

2.3. Перенос строки (Line Break)

Если строка выглядит слишком длинной, возможно разделить её на несколько, используя символ \. При обработке препроцессором следующая строка будет добавлена к текущей.

Например:

db 1, 2, 3,\

 4, 5, 6,\

 7, 8, 9

будет преобразовано в:

db 1,2,3,4,5,6,7,8,9

Конечно, \ в составе текстовой строки или комментария не вызовет объединения строк. Внутри текстовой строки этот символ воспринимается как обычный ASCII символ (как и всё остальное заключённое между кавычками ' или "). Комментарии же удаляются без анализа того, что в них написано.

В строке после символа \ могут быть только пробелы или комментарии.

Ранее, я упоминал, что строка, состоящая только из комментария не удаляется, а заменяется на пустую строку. Это значит, что код, подобный этому:

db 1, 2, 3,\

; 4,5,6,\   - закомментировано

 7, 8, 9

преобразуется в:

db 1, 2, 3

 7, 8, 9

и вызовет ошибку. Выход из положения — помещать символ \ до комментария:

db 1, 2, 3,\

\; 4,5,6     - правильно закомментировано

 7, 8, 9

в результате будет:

db 1, 2, 3, 7, 8, 9

как мы и хотели.

2.4. Директива INCLUDE

Синтаксис:

include 'file name'

Эта директива вставляет содержимое файла file name в исходный текст. Вставленный текст, естественно, тоже будет обработан препроцессором. Имя файла (и путь к нему, если он есть) должны быть заключены в кавычки " или апострофы '.

Например:

include 'file.asm'

include 'HEADERS\data.inc'

include '..\lib\strings.asm'

include 'C:\config.sys'

Можно также использовать переменные окружения ОС, помещая их имена между символами %:

include '%FASMINC%\win32a.inc'

include '%SYSTEMROOT%\somefile.inc'

include '%myproject%\headers\something.inc'

include 'C:\%myprojectdir%\headers\something.inc'

3.1. Директива EQU

Простейшая команда препроцессора. 

Синтаксис:

name1 equ name2

Это команда говорит препроцессору, что необходимо заменить все последующие name1 на name2.

Например:

count equ 10 ; это команда препроцессора

mov ecx, count

преобразуется в:

mov ecx, 10

Ещё пример:

mov eax, count

count equ 10

mov ecx, count

преобразуется в:

mov eax, count

mov ecx,10

потому что препроцессор заменит count только после директивы equ.

Даже это работает:

10 equ 11

mov ecx, 10

после обработки препроцессором, получим:

mov ecx, 11

Обратите внимание, name1 может быть любым идентификатором. Идентификатор — это всего лишь набор символов, завершаемый пробелом (space), символом табуляции (tab), концом строки (EOL), комментарием ;, символом переноса строки \ или оператором, включая операторы ассемблера и/или специальные символы вроде , или }.

name2 может быть не только единичным идентификатором, берутся все символы до конца строки. name2 может и отсутствовать, тогда name1 будет заменен на пустое место.

Например:

10 equ 11, 12, 13

db 10

получим:

db 11, 12, 13

3.2. Директива RESTORE

Можно заставить препроцессор прекратить заменять идентификаторы, определённые директивой EQU. Это делает директива RESTORE.

Синтаксис:

restore name1

name1 — это идентификатор определённый ранее в директиве EQU. После этой команды name больше не будет заменяться на name2.

Например:

mov eax, count

count equ 10

mov eax, count

restore count

mov eax, count

получим:

mov eax, count

mov eax, 10

mov eax, count

Обратите внимание, что для определений сделанных директивой EQU работает принцип стека. То есть, если мы два раза определим один и тот же идентификатор используя EQU, то после однократного использования RESTORE значение идентификатора будет соответствовать определённому первой директивой EQU.

Например:

mov eax, count

count equ 1

mov eax, count

count equ 2

mov eax, count

count equ 3

mov eax, count

restore count

mov eax, count

restore count

mov eax,count

restore count

mov eax,count

получим:

mov eax, count

mov eax, 1

mov eax, 2

mov eax, 3

mov eax, 2

mov eax, 1

mov eax, count

Если попытаться выполнить RESTORE большее количество раз, чем было сделано EQU, никаких предупреждений выдано не будет. Значение идентификатора будет неопределенно.

Например:

mov eax, count

restore count

mov eax, count

получим:

mov eax, count

mov eax, count

4.1. Определение простых макросов

Использую EQU можно делать наиболее простые замены в исходном тексте при обработке препроцессором. Большими возможностями обладают макросы. Командой MACRO можно создавать собственные инструкции.

Синтаксис:

macro name

{

; тело макроса

}

Когда препроцессор находит директиву macro, он определяет макрос с именем name. Далее, встретив в исходном тексте строку, начинающуюся с name, препроцессор заменит name на тело макроса — то, что указано в определении между скобочками { и }. Имя макроса может быть любым допустимым идентификатором, а тело макроса — всё, что угодно, за исключением символа }, который означает завершение тела макроса.

Например:

macro a

{

 push eax

}

xor eax, eax

 a

будет заменено на:

xor eax, eax

push eax

Или:

macro a

{

 push eax

}

macro b

{

 push ebx

}

b

a

получим:

push ebx

push eax

Разумеется, макросы не обязательно оформлять так, как выше, можно делать и так:

macro push5 {push dword 5}

push5

получим:

push dword 5

Или:

macro push5 {push dword 5

}

с тем же самым результатом. Скобочки можете размещать как хотите.

4.2. Вложенные макросы

Макросы могут быть вложенными один в другой. То есть, если мы переопределим макрос, будет использовано последнее определение. Но если в теле нового определения содержится тот же макрос, то будет использовано предыдущее определение. Посмотрите пример:

macro a {mov ax, 5}

macro a

{

 a

 mov bx, 5

}

macro a

{

 a

 mov cx, 5

}

a

в результате получим:

mov ax, 5

mov bx, 5

mov cx, 5

Или такой пример:

macro a {1}

a

macro a {

 a

 2 }

a

macro a {

 a

 3 }

a

получим:

1

1

2

1

2

3

4.3. Директива PURGE. Отмена определения макроса

Как и в случае с директивой EQU, можно отменить определение макроса. Для этого используется директива PURGE с указанием имени макроса.

Синтаксис:

purge name

Пример:

a

macro a {1}

a

macro a {2}

a

purge a

a

purge a

a

получим:

a

1

2

1

a

Если применить PURGE к несуществующему макросу, ничего не произойдёт.

4.4. Поведение макросов

Имя макроса будет заменено его телом не только в том случае, если оно расположено в начале строки. Макрос может находиться в любом месте исходного текста, где допустима мнемоника инструкции (например, add или mov). Всё потому, что основное предназначение макросов — имитировать инструкции. Единственное исключение из этого правила — макросы недопустимы после префиксов инструкций (rep).

Читать книгу онлайн Руководство по препроцессору FASM - автор или скачать бесплатно и без регистрации в формате fb2. Книга написана в году, в жанре Программирование, программы, базы данных. Читаемые, полные версии книг, без сокращений - на сайте Knigism.online.