Форум программистов, компьютерный форум, киберфорум
FASM
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.84/103: Рейтинг темы: голосов - 103, средняя оценка - 4.84
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757

Руководство по препроцессору FASM

09.09.2014, 12:53. Показов 21685. Ответов 7
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Руководство по препроцессору FASM
перевод TAJGA FASM Tutorial by vid - FASM preprocessor guide
перевел S.T.A.S. | последняя редакция: 22 июня 2004г.

Содержание
  1. Об этом документе
  2. Общие понятия
  3. Присваивания
  4. Простые макросы без аргументов
  5. Макросы с фиксированным количеством аргументов
  6. Макросы с групповыми аргументами
  7. Условный препроцессинг
  8. Структуры
  9. Оператор FIX и макросы внутри макросов
  10. Заключение
4
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
09.09.2014, 12:53
Ответы с готовыми решениями:

Вызываю dll (написанную на vc++2008) из Fasm. Через 40 секунд вылет из программы.Без вызова dll из Fasm программа не вылетает.
Программа на vc++2008: #include "MathFuncsDll.h" #include <stdexcept> using namespace std; namespace MathFuncs { ...

Вопрос по препроцессору C
Хотел спросить по поводу вычислений на этапе компиляции. Допустим, есть вот такой код #defyme A 185 #defyme B 66 Как...

Директива препроцессору #pragma
Что это за директива такая? Для чего предназначена? Если не затруднит, можно привести примеры?

7
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
09.09.2014, 12:54  [ТС]
1. Об этом документе

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

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

2. Общие понятия

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

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

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

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

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

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

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

К примеру, исходный текст
Assembler
1
2
3
4
; заполним 100h байтов адресуемых EDI нулями
xor eax, eax    ; обнуляем eax
mov ecx, 100h/4
rep stosd
после препроцессора превращается в
Assembler
1
2
3
xor eax,eax
mov ecx,100h/4
rep stosd
ПРИМЕЧАНИЕ: ; можно рассматривать как директиву препроцессора, удаляющую текст начиная с этого символа до конца строки.

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

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

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

Например:
Assembler
1
2
3
db  1, 2, 3,\
    4, 5, 6,\
    7, 8, 9
будет преобразовано в:
Assembler
1
db  1,2,3,4,5,6,7,8,9
Конечно, \ в составе текстовой строки или комментария не вызовет объединения строк. Внутри текстовой строки этот символ воспринимается как обычный ASCII символ (как и всё остальное заключённое между кавычками ' или "). Комментарии же удаляются без анализа того, что в них написано.

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

Ранее, я упоминал, что строка, состоящая только из комментария не удаляется, а заменяется на пустую строку. Это значит, что код, подобный этому:
Assembler
1
2
3
db  1, 2, 3,\
;   4,5,6,\   - закомментировано
    7, 8, 9
преобразуется в:
Assembler
1
2
db  1, 2, 3
    7, 8, 9
и вызовет ошибку. Выход из положения - помещать символ \ до комментария:
Assembler
1
2
3
db  1, 2, 3,\
\;  4,5,6     - правильно закомментировано
    7, 8, 9
в результате будет:
Assembler
1
db  1, 2, 3, 7, 8, 9
как мы и хотели.
2.4. Директива INCLUDE

Синтаксис:
Assembler
1
include <некая строка содержащая имя файла file_name>
Эта директива вставляет содержимое файла file_name в исходный текст. Вставленный текст, естественно, тоже будет обработан препроцессором. Имя файла (и путь к нему, если он есть) должны быть заключены в кавычки " или апострофы '.
Например:
Assembler
1
2
3
4
include 'file.asm'
include 'HEADERS\data.inc'
include '..\lib\strings.asm'
include 'C:\config.sys'
Можно также использовать переменные окружения ОС, помещая их имена между символами %:
Assembler
1
2
3
4
include '%FASMINC%\win32a.inc'
include '%SYSTEMROOT%\somefile.inc'
include '%myproject%\headers\something.inc'
include 'C:\%myprojectdir%\headers\something.inc'
2.5. Strings preprocessing
You may have problem to include ' in string declared using 's or " in string declared using "s. For this reason you must place the character twice into string, in that case it won't end string and begin next as you may think, but it will include character into string literaly.

For example:
Assembler
1
db 'It''s okay'
will generate binary containing string It's okay.
It's same for ".

3. Присваивания (Equates)

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


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

Синтаксис:
Assembler
1
<name1> equ <name2>
Это команда говорит препроцессору, что необходимо заменить все последующие <name1> на <name2>.
Например:
Assembler
1
2
count   equ 10  ; это команда препроцессора
mov ecx, count
преобразуется в:
Assembler
1
mov ecx, 10
Ещё пример:
Assembler
1
2
3
mov eax, count
count   equ 10
mov ecx, count
преобразуется в:
Assembler
1
2
mov eax, count
mov ecx,10
потому что препроцессор заменит count только после директивы equ.
Даже это работает:
Assembler
1
2
10  equ 11
mov ecx, 10
после обработки препроцессором, получим:
Assembler
1
mov ecx, 11
Обратите внимание, name1 может быть любым идентификатором. Идентификатор - это всего лишь набор символов, завершаемый пробелом (space), символом табуляции (tab), концом строки (EOL), комментарием ;, символом переноса строки \ или оператором, включая операторы ассемблера и/или специальные символы вроде , или }.
name2 может быть не только единичным идентификатором, берутся все символы до конца строки. name2 может и отсутствовать, тогда name1 будет заменен на пустое место.

Например:
Assembler
1
2
10  equ 11, 12, 13
db  10
получим:
Assembler
1
db  11, 12, 13
3.2. Директива RESTORE

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

Синтаксис:
Assembler
1
restore <name1>
name1 - это идентификатор определённый ранее в директиве EQU. После этой команды name1 больше не будет заменяться на name2.
Например:
Assembler
1
2
3
4
5
mov eax, count
count   equ 10
mov eax, count
restore count
mov eax, count
получим:
Assembler
1
2
3
mov eax, count
mov eax, 10
mov eax, count
Обратите внимание, что для определений сделанных директивой EQU работает принцип стека. То есть, если мы два раза определим один и тот же идентификатор используя EQU, то после однократного использования RESTORE значение идентификатора будет соответствовать определённому первой директивой EQU.
Например:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
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
получим:
Assembler
1
2
3
4
5
6
7
mov eax, count
mov eax, 1
mov eax, 2
mov eax, 3
mov eax, 2
mov eax, 1
mov eax, count
Если попытаться выполнить RESTORE большее количество раз, чем было сделано EQU, никаких предупреждений выдано не будет. Значение идентификатора будет неопределенно.
Например:
Assembler
1
2
3
mov eax, count
restore count
mov eax, count
получим:
Assembler
1
2
mov eax, count
mov eax, count
2
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
09.09.2014, 12:56  [ТС]
4. Простые макросы без аргументов

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


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

Синтаксис:
Assembler
1
2
3
4
macro   name
{
; тело макроса
}
Когда препроцессор находит директиву macro, он определяет макрос с именем name. Далее, встретив в исходном тексте строку, начинающуюся с name, препроцессор заменит name на тело макроса - то, что указано в определении между скобками { и }. Имя макроса может быть любым допустимым идентификатором, а тело макроса - всё, что угодно, за исключением символа }, который означает завершение тела макроса.
Например:
Assembler
1
2
3
4
5
6
macro   a
{
    push eax
}
xor eax, eax
    a
будет заменено на:
Assembler
1
2
xor eax, eax
push    eax
Или:
Assembler
1
2
3
4
5
6
7
8
9
10
macro   a
{
push    eax
}
macro   b
{
push    ebx
}
b
a
получим:
Assembler
1
2
push    ebx
push    eax
Разумеется, макросы не обязательно оформлять так, как выше, можно делать и так:
Assembler
1
2
macro   push5 {push dword 5}
push5
получим:
Assembler
1
push    dword 5
Или:
Assembler
1
2
macro   push5 {push dword 5
}
с тем же самым результатом. Скобочки можете размещать как хотите.

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

Макросы могут быть вложенными один в другой. То есть, если мы переопределим макрос, будет использовано последнее определение. Но если в теле нового определения содержится тот же макрос, то будет использовано предыдущее определение. Посмотрите пример:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
macro   a { mov ax, 5}
 
macro   a
{
    a
    mov bx, 5
}
 
macro   a
{
    a
    mov cx, 5
}
    a
в результате получим:
Assembler
1
2
3
    mov ax, 5
    mov bx, 5
    mov cx, 5
Или такой пример:

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
macro   a {1}
a
 
macro   a {
    a
    2 }
a
 
macro   a {
    a
    3 }
 
a
получим:
Assembler
1
2
3
4
5
6
7
8
    1
 
    1
    2
 
    1
    2
    3
4.3. Директива PURGE. Отмена определения макроса

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

Синтаксис:
Assembler
1
purge   name
Пример:
Assembler
1
2
3
4
5
6
7
8
9
a
macro   a {1}
a
macro   a {2}
a
purge   a
a
purge   a
a
получим:
Assembler
1
2
3
4
5
a
1
2
1
a
Если применить PURGE к несуществующему макросу, ничего не произойдёт.
4.4. Поведение макросов

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

Пример:
Assembler
1
2
3
4
5
6
7
8
macro   CheckErr
{
    cmp eax, -1
    jz  error
}
 
    call    Something
a:  CheckErr    ; здесь макросу предшествует метка, всё Ок.
получим:
Assembler
1
2
3
    call    Something
a:  cmp eax,-1
    jz  error
Пример #2:
Assembler
1
2
3
4
5
6
7
8
macro   stos0
{
    mov al, 0
    stosb
}
    stos0       ;это место инструкции, будет замена.
here:   stos0       ;это тоже место инструкции.
    db  stos0   ;здесь инструкции не место, замены не будет.
получим:
Assembler
1
2
3
4
5
    mov al, 0
    stosb
here:   mov al, 0
    stosb
    db  stos0
Возможно переопределять (overload) инструкции посредством макросов. Так как препроцессор ничего об инструкциях не знает, он позволяет использовать мнемонику инструкции в качестве имени макроса:
Assembler
1
2
3
4
5
6
7
8
macro   pusha
{
    push eax ebx ecx edx ebp esi edi
}
macro   popa
{
    pop edi esi ebp edx ecx ebx eax
}
эти две новые инструкции будут экономить по четыре байта в стеке, так как не сохраняют ESP (правда, занимают побольше места, чем реальные инструкции . Всё же, переопределение инструкций не всегда хорошая идея - кто-нибудь читая Ваш код может быть введён в заблуждение, если он не знает, что инструкция переопределена.
Также, возможно переопределять директивы ассемблера:
Assembler
1
2
3
4
5
6
7
8
9
10
11
macro   use32
{
    align   4
    use32
}
 
macro   use16
{
    align   2
    use16
}
5. Макросы с фиксированным количеством аргументов

5.1. Макросы с одним аргументом


Макросы могут иметь аргумент. Аргумент представляет собой какой-либо идентификатор, который будет повсюду заменён в теле макроса тем, что будет указанно при использовании.

Синтаксис:
Assembler
1
macro <name> <argument> { <тело макроса> }
Например:
Assembler
1
2
3
4
5
6
7
8
9
macro add5 where
{
    add where, 5
}
 
    add5    ax
    add5    [variable]
    add5    ds
    add5    ds+2
получим:
Assembler
1
2
3
4
5
6
7
    add ax, 5
    add [variable], 5
    add ds, 5   ;такой инструкции не существует
            ;но препроцессор это не волнует.
            ;ошибка появится на стадии ассемблирования.
    add ds+2,5  ;ошибка синтаксиса, как и ранее
            ;определится при анализе синтаксиса (parsing).
(разумеется, комментарии в результате работы препроцессора не появятся
5.2. Макросы с несколькими аргументами

У макросов может быть несколько аргументов, разделённых запятыми ",":

Assembler
1
2
3
4
5
6
7
8
9
macro movv where, what
{
    push    what
    pop where
}
 
movv    ax, bx
movv    ds, es
movv    [var1], [var2]
преобразуется в:
Assembler
1
2
3
4
5
6
7
8
    push    bx
    pop ax
 
    push    es
    pop ds
    
    push    [var2]
    pop [var1]
Если несколько аргументов имеют одно и тоже имя, то будет использован первый из них .
Если при использовании макроса указать меньше аргументов, чем при определении, то значения неуказанных будет пустым:
Assembler
1
2
3
4
5
6
7
macro   pupush a1, a2, a3, a4
{
    push    a1 a2 a3 a4
    pop a4 a3 a2 a1
}
 
    pupush  eax, dword [3]
получим:
Assembler
1
2
    push    eax dword [3]
    pop dword [3] eax
Если в аргументе макроса необходимо указать запятую как символ (","), тогда необходимо аргумент заключить в скобочки из символов < и >.
Assembler
1
2
3
4
5
6
7
8
9
macro   safe_declare name, what
{
    if used name
        name    what
    end if}
    
safe_declare    var1, db 5
safe_declare    array5, <dd 1,2,3,4,5>
safe_declare    string, <db "привет, я просто строка",0>
получим:
Assembler
1
2
3
4
5
6
7
8
9
10
11
if used var1
    var1    db 5
end if
    
if used array5
    array5  dd 1,2,3,4,5
end if
    
if used string
    string  db "привет, я просто строка",0
end if
Конечно же, можно использовать символы < и > и внутри тела макроса:

Assembler
1
2
3
4
5
macro   a arg {db arg}
macro   b arg1,arg2 {a <arg1,arg2,3>}
b   <1,1>,2
получим:
db  1,1,2,3
5.3. Директива "LOCAL"

Возможно, появится необходимость объявить метку внутри тела макроса:
Assembler
1
2
3
4
5
6
macro   pushstr string
{
    call    behind ;помещаем в стек адрес string и переходим к behind
    db  string, 0
behind:
}
но если использовать такой макрос 2 раза, то и метка behind будет объявлена дважды, что приведёт к ошибке. Эта проблема решается объявлением локальной метки behind. Это и делает директива LOCAL.
Синтаксис:
Assembler
1
local       label_name
Директива должна применяться внутри тела макроса. Все метки label_name внутри макроса становятся локальными. Так что, если макрос используется дважды никаких проблем не появляется:
Assembler
1
2
3
4
5
6
7
8
9
10
11
macro   pushstr string
{
  local behind
    call    behind
    db  string,0
behind:
}
 
pushstr 'aaaaa'
pushstr 'bbbbbbbb'
call    something
На самом деле, behind заменяется на behind?XXXXXXXX, где XXXXXXXX - какой-то шестнадцатеричный номер генерируемый препроцессором. Последний пример может быть преобразован к чему-то вроде:
Assembler
1
2
3
4
5
6
7
    call    behind?00000001
    db  'aaaaa', 0
behind?00000001:
    call    behind?00000002
    db  'bbbbbbbb', 0
behind?00000002:
    call    something
Заметьте, Вы не сможете напрямую обратиться к метке содержащей ?, так как это специальный символ в FASM, поэтому он и используется в локальных метках. К примеру, aa?bb рассматривается как идентификатор aa, специальный символ ? и идентификатор bb.

Если Вам нужно несколько локальных меток - не проблема, их можно указать в одной директиве LOCAL, разделив запятыми:
Assembler
1
2
3
4
5
6
7
8
macro   pushstr string  ;делает то же, что и предыдущий макрос
{
  local addr, behind
    push    addr
    jmp behind
addr    db  string,0
behind:
}
Всегда хорошо бы начинать все локальные метки макросов с двух точек .. - это значит, что они не будут менять текущую глобальную метку. К примеру:
Assembler
1
2
3
4
5
6
7
8
9
10
11
macro   pushstr string
{
  local behind
    call    behind
    db  string, 0
behind:
}
 
MyProc:
    pushstr 'aaaa'
.a:
будет преобразовано в:
Assembler
1
2
3
4
5
MyProc:
    call    behind?00000001
    db  'aaaa', 0
behind?00000001:
.a:
в результате получим метку behind?00000001.a вместо MyProc.a. Но если в примере выше behind заменить на ..behind, текущая глобальная метка не изменится и будет определена метка MyProc.a:
Assembler
1
2
3
4
5
6
7
8
9
10
11
macro   pushstr string
{
  local ..behind
    call    ..behind
    db  string,0
..behind:
}
 
MyProc:
    pushstr 'aaaa'
.a:
2
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
09.09.2014, 12:57  [ТС]
5.4. Оператор объединения #

У макроязыка FASMа есть ещё одна возможность - манипуляции с идентификаторами. Делается это оператором #, который объединяет два идентификатора в один. К примеру, a#b становится ab, а aaa bbb#ccc ddd -- aaa bbbccc ddd.

Оператор # может быть использован только внутри тел макросов, а объединение символов происходит после замены аргументов макроса параметрами. Так что его можно использовать для создания новых идентификаторов из переданных в макрос параметров:

Assembler
1
2
3
4
5
6
7
8
9
10
macro   string name, data
{
  local ..start
..start:
name db data,0
sizeof.#name =  $ - ..start
}
 
string  s1,'нудные макросы'
string  s2,<'а вот и я',13,10,'заставлю тебя их видеть во сне'>
получим:
Assembler
1
2
3
4
5
6
7
..start?00000001:
s1  db 'нудные макросы',0
sizeof.s1 = $ - ..start?00000001
 
..start?00000002:
s2  db 'а вот и я',13,10,'заставлю тебя их видеть во сне',0
sizeof.s2 = $ - ..start?00000002
так что для всех строк, создаваемых этим макросом будет определён идентификатор sizeof.имя строки, равный количеству байт строки.
Оператор # способен так же объединять символьные строки:
Assembler
1
2
3
4
5
6
macro   debug name
{
    db 'name: '#b,0
}
debug   '1'
debug   'foobar'
будет:
Assembler
1
2
db  'name: 1',0
db  'name: foobar',0
Это полезно при передаче аргументов из макроса в макрос:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
macro   pushstring string
{
  local ..behind
    call    ..behind
    db  string,0
..behind:
}
macro   debug string
{
    push    MB_OK
    push 0 ;empty caption
    pushstring  'debug: '#string    ;принимает один аргумент
    push    0               ;нет окна-предка
    call    [MessageBox]
}
Обратите внимание, нельзя использовать # совместно с идентификаторами, определёнными local, так как local обрабатывается препроцессором раньше, чем #. Из-за этого подобный код работать не будет:
Assembler
1
2
3
4
5
macro   a arg
{
  local name_#arg
}
a   foo
5.5. Оператор "`"

Существует оператор, преобразующий идентификатор в символьную строку. Он так же может быть использован только внутри макросов:
Assembler
1
2
3
4
5
6
macro   proc name
{
   name:
   log  `name   ;log - макрос, принимающий параметр-строку
}
proc    DummyProc
получим:
Assembler
1
2
DummyProc:
    log 'DummyProc'
Пример посложнее, с использованием "#"
Assembler
1
2
3
4
5
6
7
8
9
macro   proc name
{
     name:
     log 'начинается подпрограмма: '#`name
}
proc    DummyProc
retn
proc    Proc2
retn
будет:
Assembler
1
2
3
4
5
6
DummyProc:
log 'начинается подпрограмма: DummyProc'
retn
Proc2:
log 'начинается подпрограмма: Proc2'
retn
6. Макросы с групповыми аргументами

6.1. Определение макросов с групповым аргументом


У макросов могут быть так называемые групповые аргументы. Это позволяет использовать переменное количество аргументов. При определении макроса, групповой аргумент заключается в квадратные скобочки "[" и "]":

Синтаксис:
Assembler
1
2
3
4
macro   name arg1, arg2, [grouparg]
{
 <тело макроса>
}
Среди аргументов в определении макроса, групповой аргумент должен быть последним. Групповой аргумент может содержать несколько значений:
Assembler
1
2
macro   name arg1,arg2,[grouparg] {}
name    1,2,3,4,5,6
В этом примере значение arg1 равно 1, arg2 равно 2, а grouparg равно 3,4,5 и 6
6.2. Директива "COMMON"

Для работы с групповыми аргументами применяются специальные директивы препроцессора. Они могут быть использованы только внутри тела макроса имеющего групповой аргумент. Первая такая директива - это "COMMON". Она означает, что после нее имя группового аргумента будет замещаться всеми аргументами сразу:

Assembler
1
2
3
4
5
6
7
8
9
macro   string [grp]
{
  common
  db    grp,0
}
 
string  'aaaaaa'
string  'line1',13,10,'line2'
string  1,2,3,4,5
получим:
Assembler
1
2
3
db  'aaaaaa',0
db  'line1',13,10,'line2',0
db  1,2,3,4,5,0
6.3. Директива "FORWARD"

Аргументы можно обрабатывать и по-отдельности. Для этого служит директива "FORWARD". Часть тела макроса после этой директивы обрабатывается препроцессором для каждого аргумента из группы:
Assembler
1
2
3
4
5
6
7
8
9
macro   a arg1,[grparg]
{
forward
    db  arg1
    db  grparg
}
 
a   1,'a','b','c'
a   -1, 10, 20
будет:
Assembler
1
2
3
4
5
6
7
8
9
10
11
db  1
db  'a'
db  1
db  'b'
db  1
db  'c'
 
db  -1
db  10
db  -1
db  20
Директива "FORWARD" работает по умолчанию для макросов с групповыми аргументами, так что предыдущий пример можно сделать так:
Assembler
1
2
3
4
5
macro   a arg1,[grparg]
{
    db  arg1
    db  grparg
}
6.4. Директива "REVERSE"

"REVERSE" - это аналог "FORWARD", но обрабатывает группу аргументов в обратном порядке - от последнего к первому:
Assembler
1
2
3
4
5
6
7
8
macro   a arg1,[grparg]
{
reverse
    db  arg1
    db  grparg
}
 
a   1,'a','b','c'
получим:
Assembler
1
2
3
4
5
6
db  1
db  'c'
db  1
db  'b'
db  1
db  'a'
6.5. Комбинирование директив управления группами

Три вышеупомянутые директивы могут разделять тело макроса на блоки. Каждый блок обработается препроцессором после предыдущего. Например:
Assembler
1
2
3
4
5
6
7
8
9
10
11
macro   a [grparg]
{
  forward
    f_#grparg:  ;оператор объединения
  common
    db  grparg
  reverse
    r_#grparg:
}
 
a   1,2,3,4
будет:
Assembler
1
2
3
4
5
6
7
8
9
f_1:
f_2:
f_3:
f_4:
db  1,2,3,4
r_4:
r_3:
r_2:
r_1:
6.6. Директива LOCAL в макросах с групповыми аргументами

У локальных меток в макросах есть ещё одно полезное свойство. Если директива "LOCAL" находится внутри блока "FORWARD" или "REVERSE", то уникальное имя метки сгенерируется для каждого аргумента из группы, и в последующих блоках "FORWARD" и/или "REVERSE" для каждого аргумента будет использована соответствующая ему метка:
Assembler
1
2
3
4
5
6
7
8
9
macro   string_table [string]
{
  forward       ;таблица указателей на строки
    local   addr    ;локальная метка для строки
    dd  addr        ;указатель на строку
  forward       ;строки
    addr    db string,0 ;создаём и завершаем нулём
}
string_table    'aaaaa','bbbbbb','5'
получим:
Assembler
1
2
3
4
5
6
dd  addr?00000001
dd  addr?00000002
dd  addr?00000003
addr?00000001   db 'aaaaa',0
addr?00000002   db 'bbbbbb',0
addr?00000003   db '5',0
Другой пример с блоком "REVERSE":
Assembler
1
2
3
4
5
6
7
8
9
macro   a [x]
{
forward
  local here
  here  db x
reverse
  dd    here
}
a   1,2,3
будет:
Assembler
1
2
3
4
5
6
here?00000001   db 1
here?00000002   db 2
here?00000003   db 3
dd  here?00000003
dd  here?00000002
dd  here?00000001
Как видно, метки используется с соответствующими аргументами и в "FORWARD"- и в "REVERSE"-блоках.
6.7. Макросы с несколькими групповыми аргументами

Возможно использовать и несколько групповых аргументов. В этом случае определение макроса не будет выглядеть как:
Assembler
1
macro   a [grp1],[grp2]
так как тут не ясно какой аргумент какой группе принадлежит. Исходя из этого делают так:
Assembler
1
macro   a [grp1,grp2]
В этом случае каждый нечётный аргумент относится к группе grp1, а каждый чётный - к grp2:
Assembler
1
2
3
4
5
6
7
8
9
macro   a [grp1,grp2]
{
  forward
    l_#grp1:
  forward
    l_#grp2:
}
 
a   1,2,3,4,5,6
будет:
Assembler
1
2
3
4
5
6
l_1:
l_3:
l_5:
l_2:
l_4:
l_6:
Или ещё:
Assembler
1
2
3
4
5
6
7
8
9
macro   ErrorList [name,value]
{
  forward
    ERROR_#name = value
}
ErrorList \
    NONE,0,\
    OUTOFMEMORY,10,\
    INTERNAL,20
получим:
Assembler
1
2
3
ERROR_NONE = 0
ERROR_OUTOFMEMORY = 10
ERROR_INTERNAL = 20
Конечно же, может быть больше двух групп аргументов:
Assembler
1
2
3
4
5
6
7
8
macro   a [g1,g2,g3]
{
  common
    db  g1
    db  g2
    db  g3
}
a   1,2,3,4,5,6,7,8,9,10,11
будет:
Assembler
1
2
3
db  1,4,7,10
db  2,5,8,11
db  3,6,9
7. Условный препроцессинг

В действительности, FASM не имеет директив для условного препроцессинга. Но директива ассемблера "if" может быть использована совместно с возможностями препроцессора для получения тех же результатов, что и при условном препроцессинге. (Но в этом случае увеличивается расход памяти и времени).
Как известно, оператор "if" обрабатывается во время ассемблирования. Это значит, что условие в этом операторе проверяется после обработки исходного текста препроцессором. Именно это обеспечивает работу некоторых логических операций.

Я не буду рассказывать о деталях времени ассемблирования (логических операциях вроде "&", "|" и тому подобном) - читайте об этом в документации FASM. Я лишь расскажу об операторах проверки условия используемых препроцессором.
2
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
10.09.2014, 04:59  [ТС]
7.1. Оператор "EQ"

Простейший логический оператор - это "EQ". Он всего лишь сравнивает два идентификатора - одинаковы ли их значение. Значение abcd eq abcd - истина, а abcd eq 1 - ложь и так далее... Это полезно для сравнения символов, которые будут обработаны препроцессором:
Assembler
1
2
3
4
5
6
7
8
STRINGS equ ASCII
if STRINGS eq ASCII
    db  'Oh yeah',0
else if STRINGS eq UNICODE
    du  'Oh yeah',0
else
    display 'unknown string type'
end if
после обработки препроцессором, это примет вид:
Assembler
1
2
3
4
5
6
7
if ASCII eq ASCII
    db  'Oh yeah',0
else if ASCII eq UNICODE
    du  'Oh yeah',0
else
    display 'unknown string type'
end if
Здесь только первое условие (ASCII eq ASCII) выполняется, так что будет ассемблировано только
Assembler
1
db 'Oh yeah',0
Другой вариант:
Assembler
1
2
3
4
5
6
7
8
STRINGS equ UNICODE   ;разница здесь, UNICODE вместо ASCII
if STRINGS eq ASCII
    db  'Oh yeah',0
else if STRINGS eq UNICODE
    du  'Oh yeah',0
else
    display 'unknown string type'
end if
получим:
Assembler
1
2
3
4
5
6
7
if UNICODE eq ASCII
    db  'Oh yeah',0
else if UNICODE eq UNICODE
    du  'Oh yeah',0
else
    display 'unknown string type'
end if
Тут уже первое условие (UNICODE eq ASCII) будет ложно, второе (UNICODE eq UNICODE) - верно, будет ассемблироваться
Assembler
1
du 'Oh yeah',0
Несколько лучшее применение этого - проверка аргументов макросов, вроде:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
macro   item type,value
{
   if type eq BYTE
    db  value
   else if type eq WORD
    dw  value
   else if type eq DWORD
    dd  value
   else if type eq STRING
    db  value,0
   end if
}
item    BYTE,1
item    STRING,'aaaaaa'
будет:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if BYTE eq BYTE
    db  1
else if BYTE eq WORD
    dw  1
else if BYTE eq DWORD
    dd  1
else if BYTE eq STRING
    db  1,0
end if
if STRING eq BYTE
    db  'aaaaaa'
else if STRING eq WORD
    dw  'aaaaaa'
else if STRING eq DWORD
    dd  'aaaaaa'
else if STRING eq STRING
    db  'aaaaaa',0
end if
ассемблироваться будут только две команды:
Assembler
1
2
db  1
db  'aaaaaa',0
Подобно всем другим операторам препроцессора, "EQ" может работать с пустыми аргументами. Это значит, что, например, "if eq" верно, а if 5 eq - ложно и т.п.

Пример макроса:
Assembler
1
2
3
4
5
6
7
8
9
macro   mov dest,src,src2
{
  if src2 eq
    mov dest, src
  else
    mov dest, src
    mov src, src2
  end if
}
здесь, если есть третий аргумент, то будут ассемблироваться две последних команды, если нет - то только первая.
7.2. Оператор "EQTYPE"

Ещё один оператор - "EQTYPE". Он определяет, одинаков ли тип идентификаторов.
Существующие типы:
  • отдельные строки символов, заключённые в кавычки (те, которые не являются частью численных выражений)
  • вещественные числа
  • любые численные выражения, например, 2+2 (любой неизвестный символ будет рассматриваться как метка, так что он будет считаться подобным выражением)
  • адреса - численные выражения в квадратных скобках (учитывая оператор размерности и префикс сегмента)
  • мнемоники инструкций
  • регистры
  • операторы размерности
  • операторы NEAR и FAR
  • операторы USE16 и USE32
  • пустые аргументы (пробелы, символы табуляции)
Пример макроса, который позволяет использовать переменную в памяти в качестве счётчика в инструкции SHL (например shl ax, [myvar]):
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
macro   shl dest, count
{
  if count eqtype [0]       ;если count - ячейка памяти
    push    cx
    mov cl, count
    shl dest, cl
    pop cx
  else              ;если count другого типа
    shl dest, count ;просто используем обычную shl
  end if
}
shl ax, 5
byte_variable   db 5
shl ax, [byte_variable]
получится:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if 5 eqtype [0]
    push    cx
    mov cl, 5
    shl ax, cl
    pop cx
else
    shl ax, 5
end if
byte_variable   db 5
 
if [byte_variable] eqtype [0]
    push    cx
    mov cl, [byte_variable]
    shl ax, cl
    pop cx
else
    shl ax, [byte_variable]
end if
в результате обработки условий конечный результат будет:
Assembler
1
2
3
4
5
6
    shl ax, 5
byte_variable   db 5
    push    cx
    mov cl, [byte variable]
    shl ax, cl
    pop cx
Заметьте, что shl ax, byte [myvar] не будет работать с этим макросом, так как условие byte [variable] eqtype [0] не выполняется. Читаем дальше.
Когда мы сравниваем что-то посредством "EQTYPE", то это что-то может быть не только единичным идентификатором, но и их комбинацией. В таком случае, результат eqtype истина, если не только типы, но и порядок идентификаторов совпадают. К примеру, if eax 4 eqtype ebx name - верно, так как name - это метка, и её тип - численное выражение.

Пример расширенной инструкции mov, которая позволяет перемещать данные между двумя ячейками памяти:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
macro   mov dest,src
{
  if dest src eqtype [0] [0]
    push    src
    pop dest
  else
    mov dest,src
  end if
}
 
mov [var1], 5
mov [var1], [var2]
преобразуется препроцессором в:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
if [var1] 5 eqtype [0] [0]  ;не верно
    push    5
    pop [var1]
else
    mov [var1],5
end if
 
if [var1] [var2] eqtype [0] [0]  ;верно
    push    [var2]
    pop [var1]
else
    mov [var1], [var2]
end if
и будет ассемблировано в:
Assembler
1
2
3
    mov [var1], 5
    push    [var2]
    pop [var1]
Хотя более удобно для восприятия реализовать макрос используя логический оператор И - &:
Assembler
1
2
3
4
5
6
7
8
9
macro   mov dest,src
{
  if (dest eqtype [0]) & (src eqtype [0])
    push    src
    pop dest
  else
    mov dest, src
  end if
}
Пример с использованием "EQTYPE" с четырьмя аргументами приведён для демонстрации возможностей, обычно проще использовать в таких случаях "&". Кстати, в качестве аргументов, возможно использовать некорректные выражения - достаточно, чтобы лексический анализатор распознал их тип. Но это не является документированным, так что не будем этот обсуждать.
7.3. Оператор "IN"

Бывают случаи, когда в условии присутствует слишком много "EQ":
Assembler
1
2
3
4
5
6
7
8
9
10
macro   mov a,b
{
  if (a eq cs) | (a eq ds) | (a eq es) | (a eq fs) | \
     (a eq gs) | (a eq ss)
    push    b
    pop a
  else
    mov a, b
  end if
}
Вместо применения множества логических операторов ИЛИ - |, можно использовать специальный оператор "IN". Он проверяет, присутствует ли идентификатор слева, в списке идентификаторов справа. Список должен быть заключён в скобочки "<" и ">", а идентификаторы в нём разделяются запятыми:
Assembler
1
2
3
4
5
6
7
8
9
macro   mov a,b
{
  if a in <cs,ds,es,fs,gs,ss>
    push    b
    pop a
  else
    mov a, b
  end if
}
Это так же работает для нескольких идентификаторов (как и "EQ"):
Assembler
1
if dword [eax] in <[eax], dword [eax], ptr eax, dword ptr eax>
8. Структуры

В FASM, структуры практически тоже самое, что и макросы. Определяются они посредством директивы STRUC:
Синтаксис:
Assembler
1
struc   <name> <arguments>  { <тело структуры> }
Отличие от макросов заключается в том, что в исходном тексте перед структурой должна находиться некое "имя" - имя объекта-структуры. Например:
Assembler
1
2
struc   a {db 5}
a
это не будет работать. Структуры распознаются только после имен, как здесь:
Assembler
1
2
struc   a {db 5}
name    a
подобно макросу, это преобразуется препроцессором в:
Assembler
1
db  5
Смысл имени в следующем - оно будет добавлена ко всем идентификаторам из тела структуры, которые начинаются с точки. Например:
Assembler
1
2
3
struc   a {.local:}
name1   a
name2   a
будет:
Assembler
1
2
name1.local:
name2.local:
Таким образом можно создавать структуры вроде тех, что есть в языках высокого уровня абстракции:
Assembler
1
2
3
4
5
6
7
8
9
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 ?,?,?,?
получим:
Assembler
1
2
3
4
5
6
7
8
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:
Assembler
1
2
3
4
5
6
struc   ymmv arg
{
    .member dd arg+0
}
y1  ymmv 0xACDC
y2  ymmv
будет:
Assembler
1
2
y1.member   dd 0xACDC+0
y2.member   dd +0
Как говорилось ранее, если значение аргумента не указанно, то в теле макроса или структуры вместо него ничего не подставляется. В этом примере "плюс" ("+") используется или как бинарный оператор (то есть с двумя операндами), или как унарный (с одним операндом) оператор.
ПРИМЕЧАНИЕ: часто используется так же макрос или структура struct, которая определяется для расширения возможностей при определении структур. Не путайте struct и struc.
3
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
10.09.2014, 04:59  [ТС]
9. Оператор FIX и макросы внутри макросов

В стародавние времена, в FASMе отсутствовала одна полезная возможность - создавать макросы внутри других макросов. Например, что бы при развёртывании макроса был бы определён новый макрос. Что-то вроде гипотетичного:
Assembler
1
2
3
4
5
6
7
macro   declare_macro_AAA
{
  macro AAA
  {
    db  'AAA',0
  } ;завершаем определение AAA
}   ;завершаем определение declare_macro_AAA
Проблема в том, что когда макрос
Assembler
1
declare_macro_AAA
обрабатывается препроцессором, первая найденная скобочка "}" считается завершением определения его, а не так как хотелось бы. Так же происходит и с другими символами и/или операторами (например, "#", "`", "forward", "local").

9.1. Explaination of fixes
Но со временем, была добавлена новая директива. Она работает подобно "EQU", но обрабатывается до любого другого препроцессинга. (За исключением предварительных операций, про которые говорится в разделе "Общие понятия" - они выполняются как бы до самого препроцессинга, но это уже внутренние детали, не слишком интересные). Директива эта называется FIX:

Синтаксис :
Assembler
1
<name1> <fix name2>
Видно, что синтаксис такой же как у "EQU", но как я сказал, когда препроцессор обрабатывает часть кода, он смотрит, есть ли "FIX", а потом уже делает всё остальное. Например код:
Assembler
1
2
3
a equ 1
b equ a
a b
Then preprocesisng happens like this:

Preprocessing line 1:
a - Preprocessor finds unknown word, skips it.
equ - "equ" is second word of line, so it remembers "a" equals rest of line ("b") and deletes line
Preprocessing line 2:
b - Preprocessor finds unknown word, skips it.
equ - "equ" is second word of line, so it remembers "b" equals rest of line ("a") and deletes line
Preprocessing line 3:
a - Preprocessor replaces "a" with "1"
b - Preprocessor replaces "b" with "a"

So it becomes:
Assembler
1
1 a
But if we have
Assembler
1
2
3
a fix 1
b fix a
a b
then it looks like:

Fixing line 1: No symbols to be fixed
Preprocessing line 1:
a - Preprocessor finds unknown word, skips it.
fix - "fix" is second word of line, so it remembers "a" is fixed to rest of line ("b") and deletes line
Fixing line 2: "a" is fixed to "1", so line becomes "b fix 1"
Preprocessing line 2:
b - Preprocessor finds unknown word, skips it.
fix - "fix" is second word of line, so it remembers "b" is fixed to rest of line ("1") and deletes line
Fixing line 3: "a" is fixed to "1", "b" is fixed to "1" so line becomes "1 1"
Preprocessing line 3:
1 - Preprocessor finds unknown word, skips it.
1 - Preprocessor finds unknown word, skips it.

This was only example to see how fixing works, usually it isn't used in this manner.



9.2. Using fixes for nested macro declaration
Now back to declaring macro inside macro - First, we need to know how are macros preprocessed. You can quite easily make it out yourself - on macro declaration macro body is saved, and when macro is being expanded preprocessor replaces line with macro usage by macro body and internally declares equates to handle arguments and continues with preprocessing of macro body. (of course it is more complicated but this is enough for understanding fixes).

So where was problem with declaring macro inside macro? First time compiler found "}" inside macro body it took it as end of macro body declaration, so there wasn't any way to include "}" in macro body. So we can easily fix this
Assembler
1
2
3
4
5
6
7
8
9
10
11
macro a
{
  macro b
  %_
     display 'Never fix before something really needs to be fixed'
  _%
}
%_ fix {
_% fix }
a
b
Now preprocessing looks like (simplified)
  1. Preprocessor loads declaration of macro "a"
  2. Preprocessor loads declaration of fixes "%_" and "_%"
  3. Preprocessor expands macro "a"
  4. Preprocessor loads macro "b" declaration ("_%" and "%_" are fixed in each line before being handled by rest of preprocessor)
  5. Preprocessor expands macro "b"

Here you see how important is placing of declaration of fixes, because macro body is fixed too before it's loaded by preprocessor. For example this won't work:
Assembler
1
2
3
4
5
6
7
8
9
10
11
%_ fix {
_% fix }
macro a
{
  macro b
  %_
     display 'Never fix before something really needs to be fixed, here you see it'
  _%
}
a
b
Because "%_" and "_%" will be fixed before loading macro "a", so loading macro body will end at "_%" fixed to "}" and second "}" will remain there.

NOTE: Character "%" isn't special character for FASM's preprocessor, so you use it just like any normal character, like "a" or "9". It has special meaning AFTER preprocessing, and only when it is only char in whole word ("%" not "anything%anything").

We also need to fix other macro-releated operators:
Assembler
1
2
3
4
5
6
7
%_ fix {
_% fix }
%local fix local
%forward fix forward
%reverse fix revese
%common fix common
%tostring fix `
Only # is special case, you can fix it, but there is a easier way. Every time preprocessor finds multiple #s, it removes one, so it is something like (this won't really work)
Assembler
1
2
3
4
5
6
etc...
###### fix #####
#####  fix ####
####   fix ###
###    fix ##
##     fix #
So instead of using symbol fixed to "#" you can just use "##" etc.


9.3. Using fixes for moving parts of codes
You can also use fixes to move parts of code. In assembly programming is this useful especially when you break code into modules, but you want to have data and code grouped in separate segment/section, but defined in one file.

Right now this part of tutorial is TODO, I hope I will write it soon, for now you can look at JohnFound's Fresh's macro library, file
Assembler
1
INCLUDE\MACRO\globals.inc
Я знаю, FIXы могут смутить, и хорошо бы понимать внутренние детали работы препроцессора, но они предоставляют очень большие возможности. Создатель FASM'a сделал его настолько мощным, на сколько это возможно, даже за счёт некоторого ущерба удобочитаемости.



Assembler
1
2
3
4
a   equ 10
b   fix 10
mov ax, a
mov bx, b
будет преобразован в:
Assembler
1
2
mov ax, 10
mov bx, 10
Но при обработке такого кода:
Assembler
1
2
3
 equ fix =
a   equ 10
mov ax, a
в первой строк директива FIX скажет препроцессору поменять все EQU на =. Далее, перед обработкой следующей строки, препроцессор проверит, нет ли там пофиксеных идентификаторов. Так что в нашей второй строке equ будет заменено на =, и строка примет вид a = 10. Так что никакой другой обработки этой строки не будет выполнено. А значит, и третья строка не будет преобразовываться препроцессором, так как идентификатор a не будет определён директивой EQU. Результат всего этого будет такой:
Assembler
1
2
a   = 10
mov ax, a
Директива FIX может быть использован и для определения макросов в макросах - того, что мы хотели сделать в нашем гипотетичном примере. Делается это подобным образом:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
macro   declare_macro_AAA
{
  macro AAA 
  %_
    db  'aaa',0
  _%
}
 
%_ fix {
_% fix }
 
declare_macro_AAA
Здесь, препроцессор найдёт объявление макроса declare_macro_AAA и определит его, далее будет два FIX, и потом использование макроса declare_macro_AAA. Так что он преобразует это в:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
macro   declare_macro_AAA
{
  macro AAA 
  %_
    db  'aaa',0
  _%
}
 
%_ fix {
_% fix }
 
macro   AAA
%_
    db  'aaa',0
_%
и теперь уже содержимое нового макроса будет обработано препроцессором. Далее будут заменены аргументы FIXов, и получится:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
macro   declare_macro_AAA
{
  macro AAA 
  %_
    db  'aaa',0
  _%
}
 
macro   AAA
{
    db  'aaa',0
}
как мы и хотели.
Подобным образом можно пофиксить все остальные проблематичные вещи:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 был бы объявлен так:
Assembler
1
2
3
4
5
macro   TEXT [arg]
{
  forward
    db 'arg'    ;строка не зависит от аргументов
}
Но, в нашем случае он будет:
Assembler
1
2
3
4
5
macro   TEXT [arg]
{
  forward
    db `arg     ;имена аргументов превращаются в строки
}
Этот пример показывает, как важно местонахождение FIX.
Иногда необходимо фиксить идентификаторы дважды:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
macro m1
{
  macro m2
  %_
    macro m3 [arg]
    %%_
      db arg
    _%%
  _%
}
 
%%_ fix %_
_%% fix _%
%_ fix {
%_ fix }
 
m1
m2
m3
Символы фиксятся даже во время препроцессинга других FIX, так что код выше не будет работать, если порядок будет такой:
Assembler
1
2
3
4
%_ fix {
%_ fix }
%%_ fix %_
_%% fix _%
В этом случае строка
Assembler
1
%%_ fix %_
была бы пофиксена сразу же после
Assembler
1
%_ fix {
, так что все последующие %%_ сразу же преобразовались бы в }. То же самое и для
Assembler
1
_%% fix _%
.

Заключение

Не забывайте читать документацию FASM. Практически всё, что есть в туториале, можно найти там. Может быть написано и немного сложнее для изучения, но лучше подойдёт в качестве справочной информации. Не так сложно запомнить - 99% пользователей FASM научились его использовать по этой документации и при помощи форума.
3
Эксперт быдлокодинга
 Аватар для Полный 30h
2094 / 528 / 70
Регистрация: 04.11.2010
Сообщений: 1,314
18.09.2015, 01:41
Mikl___, не знаю, в тему ли и быть может об этом тут уже где то писалось, но я не встречал, а поделиться хочется. Если где то уже есть, не сочти за труд удали мою телегу.
По локальным переменным
В отличие от простых переменных, адрес локальной переменной просто так в регистры не пишется.

Assembler
1
2
3
4
5
6
7
8
9
locals
  Var_loc rq 1
endl
 
mov rax,Var_loc ;выдаст ошибку компиляции
lea rax,[Var_loc] ; а так нет
 
invoke ReadFile,[hFile],[Adr],[Size], Var_loc,0 ; выдаст ошибку компиляции
invoke ReadFile,[hFile],[Adr],[Size], addr Var_loc,0 ; а так нет
Второй момент касается 64 битного FASM макроса invoke
Если по какой либо причине (с оказией) удалось запихнуть один (или все первые четыре) параметры в положенные регистры, то при подстановке этих регистров "масла масляного" не происходит.
Т.е. при написании
Assembler
1
2
3
4
xor rcx,rcx
mov rdx,String
xor r8,r8
invoke MessageBox, rcx, rdx, r8,  MB_OK
Макрос "понимает" где родные регистры и не пытается их загрузить самих в себя тем самым создавая избыточный код.
Кактотаг
1
Эксперт быдлокодинга
 Аватар для Полный 30h
2094 / 528 / 70
Регистрация: 04.11.2010
Сообщений: 1,314
10.04.2016, 11:01
Очередная мелочёвка из цикла "хозяйке на заметку"
В интернетах читал что помимо инициированных данных
Assembler
1
ABC db 1
имеются так же не инициированные
Assembler
1
ABC db ?
или вовсе резервирование (или что то типа того)
Assembler
1
ABC rb 1
По поводу первого типа представления мне всегда всё было менее ясно - исходный код программы несёт в себе какие то данные пользуемые по мере необходимости. По поводу второго и третьего типа интернеты уверяли что дескать программа только декларирует свои намерения. Не занимая при этом лишние байты под этот тип данных. Однако это не так. Вернее не совсем так. Потому что как выяснилось, что интернеты скромно умолчали о том, что неинициированные данные остаются таковыми только в том случае если они находятся в самом конце секции данных и не подпёрты инициированными переменными. В этом не трудно убедится скомпилировав программу следующим образом
Assembler
1
2
3
4
section '.data' data readable writeable
  INI       DB 1  
  MASSIFF_1 RB 1000h
  MASSIFF_2 DB 1000h dup ?
а потом вот так
Assembler
1
2
3
4
section '.data' data readable writeable
  MASSIFF_1 RB 1000h
  MASSIFF_2 DB 1000h dup ?
  INI       DB 1
И оценить размеры полученных файлов в первом и во втором случаях.

З.Ы. Вполне допускаю что данное наблюдение всем давно известно и прописано во всех мануалах. Однако я человек темный и ленивый, мануалов не читаю, ассемблер учу из под палки. Поэтому вот только дозрел. Короче если инфа не актуальна, то снесите что бы не захламлять ветку.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
10.04.2016, 11:01
Помогаю со студенческими работами здесь

Требуется директива препроцессору
у меня проблема такого плана (опишу все действия сначала, т.к. не уверен в их правильности): создаю пустой проект, добавляю &quot;файл с...

Требуется директива препроцессору
Создаю проект &quot;Консольное приложение&quot; на Visual C#. Код : #include &lt;stdio.h&gt; int main(void) { puts(&quot;Hello,...

Видимость переменных и директивы препроцессору, не видит поле
Есть поле public float zoomSpeed = 0; Есть метод, в нем строки для разных платформ. void LateUpdate() { #if...

При создании файла заголовка в Code::Blocks вставляются какие-то команды препроцессору.
Вот что появляется при создании файла rectangle.hpp: #ifndef RECTANGLE_HPP_INCLUDED #define RECTANGLE_HPP_INCLUDED //Здесь...

Руководство
Как ообще по spring 4 его руководство читать, может кто-нибудь переведет или че путное есть а не эта оттирка


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
Новые блоги и статьи
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
Фото: Daniel Greenwood
kumehtar 13.11.2025
Расскажи мне о Мире, бродяга
kumehtar 12.11.2025
— Расскажи мне о Мире, бродяга, Ты же видел моря и метели. Как сменялись короны и стяги, Как эпохи стрелою летели. - Этот мир — это крылья и горы, Снег и пламя, любовь и тревоги, И бескрайние. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru