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


Так как ассемблер позволяет ссылаться на некоторые метки и константы перед тем, как они фактически определены, приходится прогнозировать значения этих меток и если есть даже подозрение, что прогноз окажется неверным хотя бы один раз, делается еще один проход, ассемблирующий весь код, и в это время делается лучший прогноз, базирующееся на значениях меток, полученных в предыдущий проход.
Изменение значений меток может быть причиной того, что некоторые инструкции перекодируются с другими длинами и это снова повлечет изменение меток. И так как метки и константы ещё могут использоваться внутри выражений, которые влияют на поведение директив управления, весь блок инструкций в новый проход может ассемблироваться абсолютно по-другому. Поэтому ассемблер делает проходы снова и снова, каждый раз пытаясь создать лучшие прогнозы, чтобы приблизиться к финальному решению, когда все значения спрогнозированны правильно. Для прогнозов используются разные методы, которые выбираются с тем, чтобы найти с как можно меньшим количеством проходов решение наименьшей возможной длины для большинства программ.
О некоторых ошибках, таких как непопадание значений в заданные границе, не сигнализируется во время этих промежуточных проходов, пока может случиться такое, что если какие-то значения будут спрогнозированы лучше, эти ошибки исчезнут сами собой. Однако, если ассемблер встречает какую-то недопустимую синтаксическую конструкцию или неизвестную инструкцию, он всегда останавливается немедленно. Такую же ошибку вызывает определение метки более, чем один раз, так как это делает прогнозы необоснованными.
Если в коде встречается директива "display", фактически отображаются только сообщения, созданные в последний совершённый проход. В случае, если ассемблер остановился из-за ошибки, эти сообщения могут отражать спрогнозированные значения, которые еще не разрешены правильно.
Разрешение иногда может не создаться и в таких случаях ассемблер никогда не сумеет создать правильные прогнозы - по этой причине существует предел количества походов, и когда ассемблер исчерпает этот лимит, он остановится отобразит сообщение, что невозможно сгенерировать корректный вывод. Рассмотрим следующий пример:

Assembler
1
2
3
    if ~ defined alpha
        alpha:
    end if
Если оператор "defined" выдает значение истина, если выражение, следующее за ним, в этом месте может быть вычислено, что в данном случае означает, что метка "alpha" где-то определена. Но блок выше определяет эту метку только, если значение, данное оператором "defined" ложь, что ведет к противоречию и делает невозможным разрешить такой код. Если, обрабатывая директиву "if" ассемблер должен прогнозировать, будет ли где-нибудь определена метка "alpha" (этого делать не приходится только если метка уже определена раньше), то какой бы ни был прогноз, всегда происходит противоположное. Поэтому ассемблирование остановится,если только метка "alpha" не определена где-то в коде перед вышеукзанным блоком - в этом случае, как уже было отмечено прогнозирование не требуется и блок просто будет пропущен.
Предыдущий пример может быть создан как попытка определить метку, только если этого все ещё не сделано. Эти строк неправильны, поскольку оператор "defined" проверяет определена ли метка где-либо вообще, и это включает определение внутри этого условного блока. Однако есть способ обойти эту проблему:

Assembler
1
2
3
4
    if ~ defined alpha | defined @f
        alpha:
        @@:
    end if
"@f" это всегда та же метка, что ближайший следующий за ним символ "@@", поэтому предыдущий пример значит то же, как если бы вместо анонимной метки было определено любое уникальное имя. Если метка "alpha" ещё не определена, ассемблер спрогнозирует значение "defined alpha" как ложь, это будет однако значить, что будут определены обе метки. Но на следующем проходе ассемблер спрогнозирует, что определены обе метки, что заставит определить их вновь - так прогноз будет совпадать с результатом и процесс ассемблирования придет к правильному решению. Анонимная метка выступает здесь как маркер того, что метка "alpha" определена в этом месте.
Из этого примера вы можете заключить, что прогноз для оператора "defined" очень прямолинейный - метка прогнозируется как определенная только если она была определена в предыдущий проход (а если она была определена в текущий проход, прогноз не требуется). То же самое относится к оператору "used". Однако прогнозы для значений меток не так просты и вам никогда не следует полагать, что ассемблер работает таким способом.


Все директивы препроцессора выполняются перед основным ассемблированием, и таким образом директивы управления на них никак не влияют. В это время также удаляются все комментарии.


"include" включает указанный файл-исходник туда, где эта директива используется. За ней должно следовать в кавычках имя файла, который должен быть включен, например:

Assembler
1
    include 'macros.inc'
Весь включенный файл обрабатывается препроцессором перед обработкой строк, следующих за содержащей директиву "include". Нет предела для количества включаемых файлов, пока они умещаются в память.
Путь, заключенный в скобки, может содержать окружающие переменные, заключенные в знаки "%", они будут заменены на их значения внутри пути. Знаки "\" и "/" трактуются как разделители пути. Если не указан абсолютный путь, сначала файл ищется в директории, содержащей файл, в который он включается, и, далее, если его там нет, в директории, содержащей главный файл-исходник (указанный в командной строке). Эти правила так же относятся к путям, которые указываются в директиве "file".


Символьные константы отличаются от числовых констант тем, что перед процессом ассемблирования они заменяются на их значения во всех строках кода, следующих за их определением, и все может стать их значением.
Определение символьных констант состоит из имени константы, за которой следует директива "equ". Все, что следует за этой директивой, станет значением константы. Если значение символьной константы содержит другие символьные константы, они заменяются на их значения перед присвоением значения новой константе. Например:

Assembler
1
2
3
    d equ dword
    NULL equ d 0
    d equ edx
После этих трех определений значение "NULL" будет "dword 0", а значение "d" будет "edx". Так, например, "push NULL" будет сассемблировано как "push dword 0", а "push d" как "push edx". А, например, в такой строке:
Assembler
1
    d equ d,eax
константе "d" будет присвоено новое значение "edx,eax". Таким образом могут определяться растущие списки символов.
"restore" позволяет присвоить назад предыдущее значение переопределенной константы. За ней должно следовать одно или больше имен символьных констант, разделенных запятыми. Так, "restore d" после предыдущего переопределения вернет константе значение "edx", следующее применение этой директивы вернет ей значение "dword", а ещё одно применение восстановит первоначальное значение, как будто такая константа не определялась. Если не константа с заданным именем не определена, то "restore" не вызовет ошибку, а будет просто проигнорирована.
Символьные константы могут использоваться для адаптации синтаксиса ассемблера к персональным предпочтениям. Например, следующие определения создают удобные ярлыки для всех операторов размера:

Assembler
1
2
3
4
5
6
7
8
    b equ byte
    w equ word
    d equ dword
    p equ pword
    f equ fword
    q equ qword
    t equ tword
    x equ dqword
Так как символьная константа может так же иметь пустое значение, она может использоваться для того, чтобы допустить синтаксис со словом "offset" перед каким-нибудь значением адреса:

Assembler
1
    offset equ
После такого определения "mov ax, offset char" будет правильной конструкцией, которая будет копировать смещение переменной "char" в регистр "ax",так как "offset" заменяется пустым значением, и поэтому игнорируется.
Символьные константы могут также быть определены директивой "fix", которая имеет такой же синтаксис, как "equ", но определяет константы высшего приоритета - они заменяются их символическим значением даже перед совершением директив препроцессора и макроинструкций. Исключением является сама директива "fix", которая имеет наивысший возможный приоритет, и поэтому допускает переопределение констант, заданных таким путем. Но если такие константы высшего приоритета находятся внутри значения, следующего за директивой "fix", они заменяются их значениями перед присвоением этого значения новой константе.
Директива "fix" может использоваться для адаптирования директив препроцессора, что нельзя сделать директивой "equ". Например:
Assembler
1
    incl fix include
определяет короткое имя для директивы "include", тогда как такое же определение директивой "equ" не даст такого результата, так как стандартные символьные константы заменяются на из значения после поиска строк с директивами препроцессора.
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
29.08.2014, 06:27
Ответы с готовыми решениями:

Неофициальная разработка Flat assembler версии 2.0.0
Разработчик Flat assembler'a Tomasz Grysztar в одном из блогов сообщил о разработке новой неофициальной версии флэт асма. Вскоре через...

Flat assembler ругается на PROC
Доброго времени суток. Есть программа, собственно вот что она делает: "На экране инициализировать 2 локальных окна. Каждое окно со...

Как подключить include к flat компилятору
Здравствуйте,как подключить include к flat компилятору?Требуется подключить include 'win32a.inc' к проекту.win32a.inc лежит в папке...

50
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
29.08.2014, 06:30  [ТС]


"macro" позволяет вам определить собственный комплекс инструкций, называемых макроинструкциями. Их использование может существенно упростить процесс программирования. В своей простейшей форме директива похожа на описание символьной константы. Например, следующая строка определяет ярлык для инструкции "test al,0xFF":
Assembler
1
    macro tst {test al,0xFF}
После директивы "macro" должно идти имя макроинструкции и далее её содержание, заключенное между знаками "{" и "}". Вы можете использовать инструкцию "tst" в любом месте после её определения и она будет ассемблирована как "test al,0xFF". Определение символьной константы с таким значением даст похожий результат, различие лишь в том, что имя макроинструкции будет распознаваться только как мнемоник инструкции. Также, макроинструкции заменяются соответствующим кодом даже перед заменой символьных констант на их значения. То есть, если вы вы определите макроинструкцию и символьную константу под одним и тем же именем и используете это имя как мнемоник инструкции, оно будет заменено на содержание макроинструкции, но если вы используете его внутри операндов, имя будет заменено на значение символьной константы.
Определение макроинструкции может состоять из нескольких строк, потому что знаки "{" и "}" не обязательно должны находиться на одной строке директивой "macro". Например:
Assembler
1
2
3
4
5
    macro stos0
     {
        xor al,al
        stosb
     }
Макроинструкция "stos0" будет заменена на эти две инструкции ассемблера, где бы он не использовался.
Как и инструкции, которым требуются несколько операндов, для макроинструкции можно задать требование нескольких аргументов, разделяя их запятыми. Имена этих аргументов должны следовать за именем макроинструкции на строке с директивой "macro". В любом месте в макроинструкции, где эти имена появятся, они будут заменены соответствующими значениями, указанными там, где макроинструкция используется. Вот пример макроинструкции, которая делает выравнивание данных для двоичного формата вывода:
Assembler
1
    macro align value { rb (value-1)-($+value-1) mod value }
Когда инструкция "align 4" встречается после этого задания макроинструкции, она заменяется на его содержание, и здесь "value" станет 4, а результат будет "rb (4-1)-($+4-1) mod 4".
Если в определении макроинструкции встречается её же имя, то используется предыдущее значение этого имени. Таким образом могут быть сделаны полезные переопределения макросинструкций, например:

Assembler
1
2
3
4
5
6
7
8
9
    macro mov op1,op2
     {
      if op1 in <ds,es,fs,gs,ss> & op2 in <cs,ds,es,fs,gs,ss>
        push  op2
        pop   op1
      else
        mov   op1,op2
      end if
     }
Эта макроинструкция расширяет синтаксис инструкции "mov", позволяя обоим операндам быть сегментными регистрами. Например, "mov ds,es" будет ассемблировано как "push es" и "pop ds". Во всех других случаях будет использована стандартная инструкция "mov". Синтаксис этого "mov" может быть расширен далее определением следующей макроинструкции с таким именем, который будет использовать предыдущий:

Assembler
1
2
3
4
5
6
7
8
9
    macro mov op1,op2,op3
     {
      if op3 eq
        mov   op1,op2
      else
        mov   op1,op2
        mov   op2,op3
      end if
     }
Это позволяет инструкции "mov" иметь три операнда, но она так же все ещё может иметь два операнда, так как если макроинструкции задается меньше аргументов, чем ему требуется, оставшиеся заполняются пустми значениями. Если заданы три операнда, то макроинструкция превратится в две ранее определенных, то есть "mov es,ds,dx" будет ассемблировано как "push ds", "pop es" и "mov ds,dx".
Если требуется создать макроинструкцию с аргументом, который содержит запятые, этот аргумент следует заключить между "<" и ">". Если он содержит больше одного знака "<", то для окончания его описания должно быть использовано такое же количество ">".
"purge" позволяет отменить последнее определение указанной макроинструкции. За директивой должно следовать одно или больше имен макроинструкций, разделенных запятыми. Если указанная макроинструкция не определена, это не вызовет ошибку. Например, после расширения синтаксиса "mov" вышеуказанными макроинструкциями вы можете отключить синтаксис с тремя операндами, используя директиву "purge mov". Следующее "purge mov" отключит синтаксис для сегментных регистров, а дальнейшее применение этой директивы не возымеет эффекта.
Если после директивы "macro" вы заключаете некоторую группу аргументов в квадратные скобки, это позволит при использовании макроинструкции задать данной группе аргументов больше значений. Любой следующий аргумент данный после последнего аргумента данной группы начнет новую группу и станет её первым членом. Поэтому после закрытия квадратных скобок не должно быть имен аргументов. Содержание макроинструкции будет обрабатываться для каждой такой группы аргументов отдельно. Простейший пример - это заключение одного имени аргумента в квадратные скобки:
Assembler
1
2
3
4
5
    macro stoschar [char]
     {
        mov al,char
        stosb
     }
Эта макроинструкция допускает неограниченное число аргументов, и каждый будет обработан этими двумя инструкциями отдельно. Например, "stoschar 1,2,3" будет ассемблирован как следующие инструкции:

Assembler
1
2
3
4
5
6
    mov al,1
    stosb
    mov al,2
    stosb
    mov al,3
    stosb
Существуют некоторые специальные директивы, возможные только внутри определений макроинструкций. Директива "local" задает локальные имена, которые будут заменены уникальными значениями каждый раз, когда используется макроинструкция. За ней должны следовать имена, разделенные запятыми. Эта директива обычно требуется для внутренних констант или меток макроинструкции. Например:
Assembler
1
2
3
4
5
6
7
8
9
    macro movstr
     {
        local move
      move:
        lodsb
        stosb
        test al,al
        jnz move
     }
Каждый раз, когда используется эта макроинструкция, "move" заменяется новым уникальным именем. То есть вы не получите ошибку, это обычный случай, когда метка определяется больше, чем один раз.
"forward", "reverse" и "common" делят макроинструкцию на блоки, каждый из которых обрабатывается после окончания обработки предыдущего. Они различаются в поведении только если макроинструкция поддерживает много групп аргументов. Блок инструкций, следующий за "forward" будет обрабатываться для каждой группы аргументов от первой до последней, как блок по умолчанию (без этих директив). Блок, идущий за "reverse" будет обрабатываться для каждой группы аргументов в обратном порядке - от последней до первой. Блок за директивой "common" обрабатывается лишь один раз, просто для всех групп аргументов. Локальное имя, определенное в одном блоке, доступно во всех следующих блоках при обработке той же группы аргументов. Если оно было определено в блоке "common", оно доступно во всех следующих блоках, независимо от обрабатываемой группы.
Вот пример макроинструкции, которая создает таблицу адресов строк и следующих за ними строк.

Assembler
1
2
3
4
5
6
7
8
9
10
    macro strtbl name,[string]
     {
      common
        label name dword
      forward
        local label
        dd label
      forward
        label db string,0
     }
Первый аргумент, задаваемый этой макроинструкции, станет меткой для таблицы адресов, следующими аргументами должны быть строки. Первый блок обрабатывается однажды и определяет метку, второй блок назначает локальную метку для каждой строки и определяет запись в таблице, содержащий адрес этой строки. Третий блок определяет данные каждой строки с соответствующей меткой.
Первая инструкция, следующая за директивой, начинающей блок в макроинструкции, может идти с ней на той же строке, как на следующем примере:
Assembler
1
2
3
4
5
    macro stdcall proc,[arg]
     {
      reverse push arg
      common call proc
     }
Это макрос может применяться для вызова процедур, используя соглашение STDCALL, аргументы сохраняются в стеке в обратном порядке. На пример, "stdcall foo,1,2,3" будет ассемблировано так:
Assembler
1
2
3
4
    push 3
    push 2
    push 1
    call foo
Если некоторое имя внутри макроинструкции имеет несколько значений (это либо один из аргументов, заключенных в квадратные скобки, либо локальное имя, определенное в блоке, следующем за директивой "forward" или "reverse") и используется в блоке, следующем за директивой "common", оно будет заменено на все значения, разделенные запятыми. Например, следующий макрос передать все дополнительные аргументы ранее определенной макроинструкции "stdcall":
Assembler
1
2
    macro invoke proc,[arg]
     { common stdcall [proc],arg }
Он может применяться для непрямого вызова (через указатель в памяти) процедуры, используя соглашение STDCALL.
0
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
29.08.2014, 06:31  [ТС]
Внутри макроинструкции также может быть использован специальный оператор "#". Этот оператор сцепляет два имени в одно. Это может быть полезно, так как делается после того, как аргументы и локальные имена заменяются на свои значения. Следующая макроинструкция генерирует условный переход в зависимости от аргумента "cond":

Assembler
1
2
3
4
5
    macro jif op1,cond,op2,label
     {
        cmp op1,op2
        j#cond label
     }
Например, "jif ax,ae,10h,exit" будет ассемблировано как инструкции "cmp ax,10h" и "jae exit".
Оператор "#" может также использоваться для объединения двух строк, заключенных в кавычки.
Возможно преобразование имени в строку в кавычках с помощью оператора "`", который также может быть использован внутри макроинструкции. Он конвертирует следующее за ним имя в строку, заключенную в скобки, но имейте в виду, что если за ним следует аргумент, который заменяется на значение, содержащее больше, чем один символ, будет преобразован только первый из них, так как оператор "`" конвертирует только символ, идущиий непосредственно за ним. Здесь пример использования этих двух свойств:
Assembler
1
2
3
4
5
6
7
    macro label name
     {
        label name
        if ~ used name
          display `name # " is defined but not used.",13,10
        end if
     }
Если метка, определенная таким макросом, не используется в коде, он известит вас об этом сообщением, указывающим, к какой метке это относится.
Чтобы создать макроинструкцию, ведущую себя по-разному в зависимости от типа аргументов, например если это строки в кавычках, вы можете использовать оператор сравнения "eqtype". Вот пример его использования для отделения строки в кавычках от других типов аргументов:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    macro message arg
     {
      if arg eqtype ""
        local str
        jmp   @f
        str   db arg,0Dh,0Ah,24h
        @@:
        mov   dx,str
      else
        mov   dx,arg
      end if
        mov   ah,9
        int   21h
     }
Вышеописанный макрос создан для показа сообщений в программах DOS. Если аргумент этого макроса некоторое число, метка или переменная, показывается строка из этого адреса, но если аргумент - это строка в кавычках, то созданный код покажет её после ... и ... .
Также возможно объявить макроинструкцию внутри другой макроинструкции, то есть один макрос может определить другой, но с такими определениями есть проблема, вызванная тем, что знак "}" не может появляться внутри макроинструкции, он всегда означает конец его определения. Чтобы обойти эту проблему, можно избавиться от мешающих символов. Это делается путем подстановки одного или больше обратных слэшей перед любыми другими символами (даже специальными знаками). Препроцессор видит эту последовательность как один символ, но каждый раз, когда он видит такой символ во время обработки макроса, он обрезает обратные слэши с его начала. Например, "\{" трактуется как один символ, но во время обработки макроса он станет символом "\{". Это позволит вам определить одну макроинструкцию внутри другой:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    macro ext instr
     {
      macro instr op1,op2,op3
       \{
        if op3 eq
          instr op1,op2
        else
          instr op1,op2
          instr op2,op3
        end if
       \}
     }
 
    ext add
    ext sub
Макрос "ext" определен корректно, но когда он используется, символы "\{" и "\}" становятся "{" и "}". То есть когда обрабатывается "ext add", содержание макроса становится действительным определением макроинструкции, и таким образом определяется макрос "add". Так же "ext sub" определяет макрос "sub". Использование символа "\{" не было здесь действительно необходимо, но сделано таким образом для того, чтобы определение было более ясным.
Если некоторые директивы, специфические для макроинструкций, такие как "local" или "common", требуются в некотором макросе, включенном таким образом, то их можно избежать таким же путем. Исключение символа больше чем одним обратным слэшем так же поддерживается, это позволяет допустить моножественные уровни вложения определений макросов.
Другая техника определения макроинструкций внутри других состоит в использовании директивы "fix", которая становится полезной, когда некоторый макрос только начинает определение другого, без его закрытия. Например:
Assembler
1
2
3
4
5
6
7
    macro tmacro params
     {
      macro params {
     }
 
    MACRO fix tmacro
    ENDM fix }
определяет альтернативный синтаксис определения макросов, который выглядит как:

Assembler
1
2
3
4
    MACRO stoschar char
        mov al,char
        stosb
    ENDM
Имейте в виду, что таким образом заданное определение должно быть создано с применением директивы "fix", так как перед тем, как процессор ищет знак "}" во время определения макроса, обрабатываются только символьные константы высшего приоритета! Может возникнуть проблема, если требуется выполнить некоторые дополнительные задания в конце такого определения, но есть еще одно свойство, которое в таких случаях поможет вам. А именно возможно поместить любую директиву, инструкцию или макроинструкцию сразу после символа "}", который заканчивает макроинструкцию и она будет обработана так же, как если бы была на следующей строке.


2.3.4 Структуры


"struc" - это специальный вариант директивы "macro", который используется для определения структур данных. Макроинструкции, определенные директивой "struc", когда используются, должны предваряться меткой (как директивы определения данных). Эта метка будет также присоединена к началу каждого имени, начинающегося с точки, в содержании макроинструкции. Макроинструкция, определенная с использованием директивы "struc", может иметь такое же имя, как макросы, определенные с использованием директивы "macro". Структурная макроинструкция не будет мешать обычному макросу, выполняющемуся без метки перед ним и наоборот. Все правила и свойства, касающиеся стандартных макросов, применимы к структурным макроинструкциям.
Вот пример структуры:
Assembler
1
2
3
4
    struc point x,y
     {
        .x dw x
        .y dw y
Например "my point 7,11" определит структуру, помеченную "my", содержащую две переменные: "my.x" со значением 7 и "my.y" со значением 11.
Если где-то в определении структуры находится имя, состоящие из одной лишь точки, оно заменяется на имя метки для данного примера структуры и эта метка таким образом не будет определена автоматически, позволяя полностью задать определение. Следующий пример использует это свойство, чтобы расширить определение директивы "db" с возможностью вычисления размера определяемых данных:
Assembler
1
2
3
4
5
6
    struc db [data]
     {
       common
        . db data
        .size = $ - .
     }
Таким образом строка "msg db 'Hello!',13,10" определит так же константу "msg.size", равную размеру определяемых данных в байтах.
Определение структур данных, адресованных регистрами или абсолютными значениями может быть сделано структурными макроинструкциями с использованием директивы "virtual" (смотрите 2.2.3).
"restruc" удаляет последнее определение структуры, так же как "purge" делает с макросами и "restore" с символьными константами. Директива имеет тот же синтаксис - за ней должно следовать одно или несколько имен структурных макросов, разделенных запятыми.
0
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
29.08.2014, 06:31  [ТС]
2.3.5 Повторение макроинструкций


Директива "rept" - это специальный вид макроинструкций, который делает заданное число дубликатов блока, заключенного в фигурные скобки. Простой синтаксис - число, следующее за "rept" (это не может быть выражение, так как препроцессор не совершает вычисления, если вам нужны повторения, базирующиеся на выражениях, вычисленных ассемблером, используйте одну из директив, обрабатываемых ассемблером, смотрите 2.2.2), и блок кода, заключенный между знаками "{" и "}". Простейший пример:

Assembler
1
    rept 5 { in al,dx }
создает пять дубликатов строки "in al,dx". Блок инструкций определяется таким же образом, как для стандартных макросов, и допускаются все специальные операторы и директивы, которые могут использоваться только внутри макроинструкций. Если заданное число равно нулю, блок просто пропускается, как если бы вы определили макрос, но не использовали его. За количеством повторений может следовать имя символа-счетчика, который символьно будет заменяться на номер текущего повторения. Таким образом:

Assembler
1
2
3
4
    rept 3 counter
     {
        byte#counter db counter
     }
Сгенерирует строки:
Assembler
1
2
3
    byte1 db 1
    byte2 db 2
    byte3 db 3
Механизм повторения, применяемый к блокам "rept" такой же, как тот, что используется для обработки множественных групп аргументов макросов, то есть директивы, такие как "forward", "common" и "reverse" могут использоваться их обычном значении.
Итак, такой макрос:
Assembler
1
    rept 7 num { reverse display `num }
покажет символы от 7 до 1 как текст. Директива "local" работает так же, как внутри макросов с несколькими группами аргументов, то есть:
Assembler
1
2
3
4
5
    rept 21
     {
       local label
       label: loop label
     }
сгенерирует уникальную метку для каждого дубликата. Символ-счетчик обычно начинает с 1, но вы можете объявить другое базовое значение, предваренное запятой, сразу же после имени счетчика. Например:

Assembler
1
    rept 8 n:0 { pxor xmm#n,xmm#n }
Сгенерирует код, очищающий содержимое регистров SSE. Вы можете определить несколько счетчиков, разделенных запятыми, и каждый может иметь свою базу.
"irp" итерирует один аргумент через данный список параметров. Синтаксис такой: за "irp" следует имя аргумента, далее запятая и далее список параметров. Параметры определяются таким же образом, как в вызове стандартного макроса, то есть они должны разделяться запятыми и каждый может быть заключен между знаками "<" и ">". Так же за именем аргумента может следовать "*" для обозначения того, что он не может иметь пустое значение. Такой блок:
Assembler
1
2
   irp value, 2,3,5
    { db value }
сгенерирует строки:
Assembler
1
2
3
   db 2
   db 3
   db 5
"irps" итерирует через данный список символов, за директивой должно следовать имя аргумента, далее запятая и далее последовательность любых символов. Каждый символ в последовательности, независимо от того, символы ли это имен, знаки символов или строки в кавычках, становится значением аргумента на одну итерацию. Если за запятой никаких символов не следует, то итераций не производится вообще. Этот пример:
Assembler
1
2
   irps reg, al bx ecx
    { xor reg,reg }
FASM генерирует строки:
Assembler
1
2
3
   xor al,al
   xor bx,bx
   xor ecx,ecx
Блоки, определенные директивами "irp" и "irps", обрабатываются так же, как макросы, то есть операнды и директивы, специфичные для макросов могут в них свободно использоваться.
0
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
01.09.2014, 06:00  [ТС]

При применении директивы "match" некоторый блок кода обрабатывается препроцессором и передается ассемблеру, только если заданная последовательность символов совпадает с образцом. Образец идет первым, заканчивается запятой, далее идут символы, которые должны подходить под образец, и далее блок кода, заключенный в фигурные скобки, как макроинструкция.
Есть несколько правил для построения выражения для сравнения, первое - это любые символьные знаки и строки в кавычках должны соответствовать абсолютно точно. В этом примере:

Assembler
1
2
    match +,+ { include 'first.inc' }
    match +,- { include 'second.inc' }
Первый файл будет включен, так как "+" после запятой соответствует "+" в образце, а второй файл не будет включен, так как совпадения нет.
Чтобы соответствовать любому другому символу буквально, он должен предваряться знаком "=" в образце. Также чтобы привести в соответствие сам знак "=", или запятую должны использоваться конструкции "==" и "=,". Например, образец "=a==" будет соответствовать последовательности "a=".
Если в образце стоит некоторый символ имени, он соответствует любой последовательности, содержащей по крайней мере один символ и его имя заменяется на поставленную в соответствие последовательность везде в следующем блоке, аналогично параметрам в макроинструкции. На пример:
Assembler
1
2
    match a-b, 0-7
     { dw a,b-a }
сгенерирует инструкцию "dw 0, 7-0". Каждое имя всегда ставится в соответствие как можно меньшему количеству символов, оставляя оставшиеся, то есть:
Assembler
1
    match a b, 1+2+3 { db a }
имя "a" будет соответствовать символу "1", оставляя последовательность "+2+3" в соответствие с "b". Но, таким образом:
Assembler
1
    match a b, 1 { db a }
для "b" ничего не остается, и блок вообще не будет обработан.
Блок кода, определенный директивой "match" обрабатывается так же, как любая макроинструкция, поэтому здесь могут использоваться любые операторы, специфичные для макроинструкций.
Что делает директиву "match" очень полезной, так это тот факт, что она заменяет символьные константы на их значения в поставленной в соответствие последовательности символов (то есть везде после запятой до начала блока кода) перед началом сопоставления. Благодаря этому директива может использоваться, например, для обработки некоторого блока кода в зависимости от выполнения условия, что данная символьная константа имеет нужное значение, например:

Assembler
1
    match =TRUE, DEBUG { include 'debug.inc' }
здесь файл будет включен, только если символьная константа "DEBUG" определена со значением "TRUE".



При сочетании разных свойств препроцессора важно знать порядок, в котором они обрабатываются. Как уже было отмечено, высший приоритет имеет директива "fix" и замены, ею определенные. Это полностью делается перед совершением любого другого препроцессинга, поэтому такой кусок кода:
Assembler
1
2
3
4
5
    V fix {
      macro empty
       V
    V fix }
       V
делает допустимое определение пустого макроса. Можно сказать, что директива "fix" и приоритетные константы обрабатываются на отдельной стадии, и весь остальной препроцессинг делается на результирующем коде.
Стандартный препроцессинг, который начинается после, на каждой строке начинается с распознавания первого символа. Сначала идет проверка на директивы препроцессора, и если ни одна из них не опознана, препроцессор проверяет, является ли первый символ макроинструкцией. Если макроинструкция не найдена, препроцессор переходит ко второму символу на строке, и снова начинает с проверки на директивы, список которых в этом случае ограничивается лишь "equ", так как только она может оказать вторым символом на строке. Если нет директивы, второй символ проверяется на структурную макроинструкцию, и если ни одна из этих проверок не дала положительного результата, символьные константы заменяются на их значения, и строка передается ассемблеру.
Продемонстрируем это на примере. Пусть "foo" - это макрос, а "bar" - это структура. Эти строки:

Assembler
1
2
    foo equ
    foo bar
обе будут интерпретированы как вызовы макроса "foo", так как значение первого символа берет верх над значением второго.
Макроинструкции генерируют новые строки от их блоков определения, заменяя параметры на их значения и далее обрабатывая операторы "#" и "`". Оператор конверсии имеет высший приоритет, чем оператор сцепления.
После завершения этого, заново сгенерированная строка проходит через стандартный препроцессинг, как описано выше.
Хотя обычно символьные константы заменяются исключительно в строках, нет ни директив препроцессора, ни макроинструкций, встречается несколько особых ситуаций, где замены проводятся в частях строк, содержащих директивы. Первая - это определение символьной константы, где замены производятся везде после слова "equ" и результирующее значение присваивается новой константе (смотрите 2.3.2). Вторая такая ситуация - это директива "match", где замены производятся в символах, следующих за запятой перед сопоставлением их с образцом. Эти свойства могут использоваться, например, для сохранения списков, как,например, эта совокупность определений:

Assembler
1
2
3
4
5
6
7
    list equ
 
    macro append item
     {
       match any, list \{ list equ list,item \}
       match , list \{ list equ item \}
     }
Здесь константа "list" инициализируется с пустым значением, и макрос "append" может использоваться для добавления новых пунктов к списку, разделяя их запятыми. Первое сопоставление в этом макросе происходит, только если значение списка не пусто (смотрите 2.3.6), таким образом новое его значение - это предыдущее с запятой и новым пунктом, добавленным в конец. Второе сопоставление происходит, только если список все еще пуст, и таким образом список определяется как содержащий только лишь новый пункт. Так, начиная с пустого списка, "append 1" определит "list equ 1", а "append 2", следующий за ним, определит "list equ 1,2". Может потребоваться использовать этот список как параметры к некоторому макросу. Но это нельзя сделать прямо - если "foo" это макрос, то в строке "foo list" символ "list" просто прошел бы как параметр к макросу, поскольку символьные константы на этой стадии ещё не развернуты. Для этой цели снова оказывается удобна директива "match":

Assembler
1
    match params, list { foo params }
Значение "list", если оно не пустое, соответствует ключевому слову "params", которое далее во время генерации строк, заключенных в фигурные скобки, заменяется на соответственное значение. Так, если "list" имеет значение "1,2", строка, указанная выше, сгенерирует строку, содержащую "foo 1,2", которая далее пройдет стандартный препроцессинг.
Есть ещё один особый случай - когда препроцессор собирается проверить второй символ и натыкается на двоеточие (что далее интерпретируется асемблером как определение метки), он останавливается в этом месте и заканчивает препроцессинг первого символа (то есть если это символьная константа, она развертывается) и если это все еще выглядит меткой, совершается стандартный препроцессинг, начиная с места после метки. Это позволяет поместить директивы препроцессора и макроинструкции после меток, аналогично инструкциям и директивам, обрабатываемым ассемблером, например:
Assembler
1
    start: include 'start.inc'
Однако если метка во время препроцессинга разрушается (например, если у символьной константы пустое значение), происходит только замена символьных констант до конца строки.
0
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
01.09.2014, 06:02  [ТС]

"format" со следующим за ним идентификатором формата позволяет выбрать формат вывода. Эта директива должна стоять в начале кода. Формат вывода по умолчанию - это простой двоичный файл, он может быть также выбран директивой "format binary".
"use16", "use32" и "use64" указывают ассемблеру генерировать 16-/32-битный или 64-битный код, пренебрегая настройкой по умолчанию для выбранного формата вывода. "use64" включает генерирование кода для длинного режима (long mode) процессоров x86.
Ниже описаны разные форматы вывода со специфичными для них директивами.



Чтобы выбрать формат вывода MZ, используйте директиву "format MZ". По умолчанию код для этого формата 16-битный.
  • "segment" определяет новый сегмент, за ним должна следовать метка, чьим значением будет номер определяемого сегмента. Опционально за этой директивой может следовать "use16" или "use32", чтобы указать битность кода в сегменте. Начало сегмента выровнено по параграфу (16 байт). Все метки, определенные далее, будут иметь значения относительно начала этого сегмента.
  • "entry" устанавливает точку входа для формата MZ, за ней должен следовать дальний адрес (имя сегмента, двоеточие и смещение в сегменте) желаемой точки входа.
  • "stack" устанавливает стек для MZ. За директивой может следовать числовое выражение, указывающее размер стека для автоматического создания, либо дальний адрес начального стекового фрейма, если вы хотите установить стек вручную. Если стек не определен, он будет создан с размером по умолчанию в 4096 байт.
  • "heap" со следующим за ней значением определяет максимальный размер дополнительного места в параграфах (это место в добавление к стеку и для неопределенных данных). Используйте "heap 0", Чтобы всегда отводить только память, которая программе действительно нужна.



Чтобы выбрать формат вывода PE, используйте директиву "format PE", за ней могут следовать дополнительные настройки формата: используйте "console", "GUI" или оператор "native", чтобы выбрать целевую субсистему (далее может следовать значение с плавающей точкой, указывающее версию субсистемы), "DLL" помечает файл вывода как динамическую связывающую библиотеку. Далее может следовать оператор "at" и числовое выражение, указывающее базу образа PE и далее опционально оператор "on" со следующей за ним строкой в кавычках, содержащей имя файла, выбирающей заглушку MZ для PE программы (если указанный файл не в формате MZ, то он трактуется как простой двоичный исполняемый файл и конвертируется в формат MZ). По умолчанию код для этого формата 32-битный. Пример объявления формата PE со всеми свойствами:
Assembler
1
    format PE GUI 4.0 DLL at 7000000h on 'stub.exe'
"section" определяет новую секцию, за ней должна следовать строка в кавычках, определяющая имя секции, и далее могут следовать один или больше флагов секций. Возможные флаги такие: "code", "data", "readable", "writeable", "executable", "shareable", "discardable", "notpageable". Начало секции выравнивается по странице (4096 байт). Пример объявления секции PE:

Assembler
1
    section '.text' code readable executable
Вместе с флагами также может быть определен один из специальных идентификаторов данных PE, отмечающий всю секцию как специальные данные, возможные идентификаторы: "export", "import", "resource" и "fixups". Если секция помечена для содержания настроек адресов, они генерируются автоматически, и никаких данных определять больше не требуется. Также данные ресурсов могут быть сгенерированы автоматически из файлов ресурсов, этого можно добиться, написав после идентификатора "resourse" оператор "from" и имя файла в кавычках. Ниже вы можете увидеть примеры секций, содержащих некоторые специальные данные:

Assembler
1
2
    section '.reloc' data discardable fixups
    section '.rsrc' data readable resource from 'my.res'
"entry" создает точку входа для PE, далее должно следовать значение точки входа.
"stack" устанавливает размер стека для PE, далее должно следовать значение зарезервиронного размера стека, опционально может следовать отделенное запятой значение начала стека. Если стек не определен, ему присваивается размер по умолчанию, равный 4096 байт.
"heap" выбирает размер дополнительного места для PE, далее должно следовать значение для зарезервированного для него места, опционально ещё может быть значение его начала, отделенное запятой. Если дополнительное место не определено, оно ставится по умолчанию равным 65536 байт, если не указано его начало, то оно устанавливается равным 0.
"data" начинает определение специальных данных PE, за директивой должен следовать один из идентификаторов данных ("export", "import", "resource" или
"fixups") или номер записи данных в заголовке PE. Данные должны быть определены на следующих строках и заканчиваться директивой "end data". Если выбрано определение настроек адресов, они генерируются автоматически, и никаких данных определять больше не требуется. То же самое относится к ресурсам, если за идентификатором "resourse" следует оператор "from" и имя файла в кавычках - в этом случае данные берутся из этого файла ресурсов.



Чтобы выбрать COFF (Common Object File Format), используйте директиву "format COFF" или "format MS COFF", если вы хотите создать классический мелкософтофский файл COFF. По умолчанию код для этого формата 32-битный. Чтобы создать микросФтовский формат COFF для архитектуры x86-64, используйте установку "format MS64 COFF", в этом случае автоматически будет генерироваться код длинного режима.
  • "section" определяет новую секцию, за директивой должна следовать строка в кавычках, определяющая имя новой секции, и ещё может следовать один или более флагов секций. Возможные флаги такие: "code" и "data" для обоих вариантов COFF, "readable", "writeable", "executable", "shareable", "discardable" и "notpageable" только для микросовтофского варианта COFF. По умолчанию секция выровнена по двойному слову (четыре байта), но микросовтовский вариант COFF можно выровнять еще как-нибудь по-другому с помощью оператора "align" и следующим за ним значением выравнивания (любая степень двойки от двух до 8192) среди фагов секций.
  • "extrn" определяет внешний символ, за ним должно следовать имя символа и опционально оператор размера, указывающий размер данных, помеченных этим символом. Имя символа также может предваряться строкой в кавычках, содержащей имя внешнего символа и оператор "as". Пара примеров объявления внешних символов:
Assembler
1
2
     extrn exit
    extrn '__imp__MessageBoxA@16' as MessageBox:dword
  • "public" объявляет существующий символ как общедоступный, за ним должно следовать имя символа, и далее опционально оператор "as" и строка в кавычках, содержащая имя, под которым символ будет действителен как общедоступный. Пара примеров объявления общедоступных символов:
Assembler
1
2
    public main
    public start as '_start'


Чтобы выбрать формат вывода ELF, используйте директиву "format ELF". По умолчанию код для этого формата 32-битный. Чтобы создать формат ELF для архитектуры x86-64, используйте установку "format ELF", в этом случае автоматически будет генерироваться код длинного режима.
"section" определяет новую секцию, за директивой должна следовать строка в кавычках, определяющая имя новой секции, и ещё может следовать один или оба флага "executable" и "writeable", опционально также может идти оператор "align" со следующим за ним числом, определяющим выравнивание секции (это должна быть степень двойки), если выравнивание не указано, используется значение по умолчанию, которое равно 4 или 8, в зависимости от варианта выбранного формата.
"extrn" и "public" имеют те же значения и синтаксис у них, как в случае формата COFF (описанного в предыдущем параграфе).
Чтобы создать исполняемый файл, придерживайтесь директивы выбора формата со словом "executable".Это позволяет использовать директиву "entry" со следующим за ним значением, чтобы создать точку входа в программу. С другой стороны это делает недоступными директивы "extrn" и "public". За директивой "section" в этом случае может следовать только один или более флагов секций и начало секции будет выровнено по странице (4096 байт). Доступные флаги секций такие: "readable", "writable" и "executable".
0
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
01.09.2014, 06:02  [ТС]

Версия FASM'а для Windows включает пакет стандартных файлов разработанных, чтобы помочь создавать программы для Windows. Этот пакет содержит заголовочные файлы для программирования под Win32/Win64 находящиеся в корневой папке а также спецализированные файлы находящиеся в поддиректориях. Вообще, заголовочные файлы Win32 включают все требуемые специализированные файлы для Вас, хотя иногда Вы могли бы предпочесть включать часть макросов самостоятельно (так как некоторые из них не включены в заголовочные файлы). Есть двенадцать заголовочных файлов Win32/Win64, которые Вы можете выбрать , с названиями, начинающимися с "win32/win64", и продолжающихся символом "a" чтобы использовать кодирование ASCII, или символом "w" для Unicode.

win32a.inc, win32w.inc, win64a.inc и win64w.inc ― основные заголовочные файлы, win32ax.inc, win32wx.inc, win64ax.inc и win64wx.inc ― расширенные заголовочные файлы, они содержат более расширенные макросы, эти расширения будут обсуждены отдельно. Наконец win32axp.inc, win32wxp.inc, win64axp.inc и win64wxp.inc ― те же самые расширенные заголовочные файлы с особенностью проверки параметров при вызовах процедур.

Вы можете включить заголовочные файлы, любым путем которые предпочитаете, указывая полный путь или используя переменную окружения, но самый простой метод состоит в том, чтобы определить переменную окружения INCLUDE, должным образом указывающую на каталог, содержащий заголовочные файлы и затем включить их например:
Assembler
1
include ’win32a.inc
Важно обратить внимание, что все макросы, в противоположность внутренним директивам FASM, являются чувствительными к регистру, и строчные буквы используются для большинства из них. Если Вы желаете использовать их по другому чем по умолчанию, Вы должны сделать соответствующие корректировки директивой "fix".


Основные заголовочные файлы win32a.inc, win32w.inc, win64a.inc и win64w.inc включают константы и структуры Win32/Win64 и обеспечивают стандартный набор макросов.


Все заголовки допускают макрос "struct", который позволяет определять структуры более традиционно, подобно другим ассемблерам чем "struc" директива. Определение структуры должно быть начато с макроинструкции "struct" , сопровождаемой именем, и заканчиваться "ends". В строках определения структуры разрешены только метки, являющимися названиями для ее членов:
Assembler
1
2
3
4
struct POINT
    x dd ?
    y dd ?
ends
Строка с таким определением:
Assembler
1
point1 POINT
объявит структуру point1 с членами point1.x и point1.y задавая им значения по умолчанию ― те же самые что предусмотрены при определении структуры (в этом случае, значения по умолчанию являются неинициализированными). Но при объявлении структура также принимает, такое же количество параметров сколько и членов в структуре, отменяя значения определенные по умолчанию для этих членов. Например:
Assembler
1
point2 POINT 10,20
инициализирует члена point2.x значением 10, и point2.y значением 20. "struct" макрокоманда не только допускает, объявление структуры данного типа, но также и определяет метки для смещений членов в структуре и константы определяющие размер каждого члена и структуры в целом. Например вышеупомянутое определение структуры POINT определяет метки POINT.x и POINT.y, которые являются смещениями членов в структуре, и sizeof.POINT.x, sizeof.POINT.y и sizeof.POINT как размеры соответствующих членов и целой структуры. Метки смещения могут использоваться чтобы обратиться к структурам, к которым обращаются косвенно, например:

Assembler
1
mov eax,[ebx+POINT.x]
когда регистр EBX содержит указатель на структуру POINT. Обратите внимание, также что проверка размера членов будет выполнена.

Сами структуры также могут быть в определениях структуры, так что структуры могут содержать некоторые другие структуры как члены:
Assembler
1
2
3
4
struct LINE
    start POINT
    end POINT
ends
Когда никакие значения по умолчанию для членов вложенной структуры не определены, как в этом примере, значения по умолчанию берутся из определения вложенной структуры.

Так как значение для каждого члена ― отдельный параметр в объявлении структуры, при инициализации вложенной структуры параметры для каждой из них должны быть сгруппированы в отдельную группу например:
Assembler
1
line1 LINE <0,0>,<100,100>
Вышеупомянутое объявление инициализирует каждого члена из line1.start.x и line1.start.y 0, и line1.end.x и line1.end.y 100.

Когда структура инициализируется данными меньшими чем размер соответствующего члена, она дополняется до размера неопределенными байтами (а когда большими, случается ошибка). Например:
Assembler
1
2
3
struct FOO
    data db 256 dup (?)
ends
Assembler
1
some FOO <"ABC",0>
заполняет первые четыре байта some.data определенными значениями и резервирует остальное.

В структурах также могут быть определены "union" ― объединения а также безымянные вложенные структуры. Определение объединения должно начаться с "union" и оканчиваться "ends", например:
Assembler
1
2
3
4
5
6
7
struct BAR
    field_1 dd ?
    union
        field_2 dd ?
        field_2b db ?
    ends
ends
Каждый из членов, определенных в объединении имеет то же самое смещение, и они совместно используют одну и ту же область памяти. Только первый член объединения инициализируется данным значением, значения для остальных членов игнорируются (однако, если один из других членов требует большего количества памяти чем первый, объединение дополняется до требуемого размера неопределенными байтами). Целое объединение будет инициализировано единственным параметром, данным в объявлении структуры, и этот параметр дает значение первому члену объединения.

Безымянные вложенные структуры объявляются также как "union", только начинаются со строки "struct" например:
Assembler
1
2
3
4
5
6
7
struct WBB
    word dw ?
    struct
        byte1 db ?
        byte2 db ?
    ends
ends
Такая вложенная структура принимает только один параметр при объявлении структуры, чтобы инициализировать ее значения, этим параметром может быть группа параметров, определяющих каждого члена вложенной структуры. Так что вышеупомянутый тип структуры можно объявить так:
Assembler
1
my WBB 1,<2,3>
К членам объединений и безымянных вложенных структур обращаются так же, как если бы они были непосредственно члены родительской структуры. Например с вышеупомянутым объявлением my.byte1 и my.byte2 ― являются правильными метками для членов вложенной структуры.

Вложенные структуры и объединения могут быть вложены без пределов для глубины вложения:

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct LINE
    union
        start POINT
        struct
        x1 dd ?
        y1 dd ?
        ends
    ends
    union
        end POINT
        struct
        x2 dd ?
        y2 dd ?
        ends
    ends
ends
Определение структуры может также быть основано на некоторых из уже определенных типов структур, и она наследует всех членов этой структуры, например:
Assembler
1
2
3
struct CPOINT POINT
    color dd ?
ends
определяет ту же самую структуру как:
Assembler
1
2
3
4
5
struct CPOINT
    x dd ?
    y dd ?
    color dd ?
ends
Все заголовочные файлы определяют тип данных CHAR, который может использоваться, чтобы определить символьные строки в данных структур.
0
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
01.09.2014, 06:04  [ТС]

Макросы импорта помогают формировать данные импорта для PE файла (обычно составляющие отдельную секцию). Есть два макроса для этой цели.
  • Первый library, должен быть помещен непосредственно в начале объявления данных импорта, и он определяет, из каких библиотек будут импортированы функции. Он должен сопровождаться любым количеством пар параметров, каждая пара, является меткой для таблицы импорта из данной библиотеки, и символьной строки, определяющей название библиотеки. Например:
    Assembler
    1
    2
    
    library kernel32,’KERNEL32.DLL’,\
        user32,’USER32.DLL’
    объявляет, импорт из этих двух библиотек.
  • Для каждой из библиотек, тогда должна быть объявлена таблица импорта где-нибудь в данных импорта. Это делается макроинструкцией import, которая нуждается в первом параметре, чтобы определить метку для таблицы (такую же самую как объявлено ранее в макрокоманде library), и затем пары параметров которые содержат метку для импортированного указателя и символьной строки, определяющей название функции точно с таким же именем которое экспортируется библиотекой. Например, вышеупомянутое объявление библиотек может быть закончено со следующими объявлениями импорта:
    Assembler
    1
    2
    3
    4
    5
    
    import kernel32,\
        ExitProcess,’ExitProcess’
    import user32,\
        MessageBeep,’MessageBeep’,\
        MessageBox,’MessageBoxA’
    Метки, определенные первыми параметрами в каждой паре переданные макрокоманде import, адресуют указатели на двойные слова, которые после загрузки PE заполняются адресами экспортируемых процедур.

Вместо символьной строки для названия процедуры, можно задать номер, чтобы определить импорт по ординалу, например:
Assembler
1
2
3
import custom,\
    ByName,’FunctionName’,\
    ByOrdinal,17
Макрос import оптимизирует данные импорта, поэтому только те функции, которые используются где-нибудь в программе, будут помещены в таблицы импорта, и если некоторая таблица импорта была бы пуста, то на нее, целая библиотека не будет ссылаются вообще. По этой причине удобно иметь законченную таблицу импорта для каждой библиотеки ― пакет содержит такие таблицы для некоторых из стандартных библиотек, они сохранены в подкаталогах APIA И APIW и импортируют ASCII и Unicode варианты функций API. Каждый файл содержит одну таблицу импорта, с меткой в нижнем регистре с тем же самым именем как название файла. Так законченные таблицы для того, чтобы импортировать из библиотек KERNEL32.DLL и USER32.DLL могут быть определены, вот так (если ваша переменная среда INCLUDE указывает на каталог содержащий пакет стандартных файлов):

Assembler
1
2
3
4
library kernel32,’KERNEL32.DLL’,\
    user32,’USER32.DLL’
include ’apia\kernel32.inc’
include ’apiw\user32.inc
Добавлено через 41 секунду

Есть четыре макроинструкции для того, чтобы вызывать процедуры с параметрами, переданными в стек.
  • stdcall вызывает непосредственно процедуру указанную первым параметром, используя STDCALL соглашение о вызовах. Остальная часть параметров переданных макроинструкции, определяет параметры для процедуры и сохраняется в стеке в обратном порядке.
  • Макроинструкция invoke делает то же самое, однако процедура вызывается косвенно, через указатель, помеченный первым параметром. Таким образом, invoke может использоваться, чтобы вызвать процедуры с помощью указателей, определенных в таблицах импорта. Эта строка:
    Assembler
    1
    
    invoke MessageBox,0,szText,szCaption,MB_OK
    является эквивалентной этой:
    Assembler
    1
    
    stdcall [MessageBox],0,szText,szCaption,MB_OK
    и они обе преобразуются в этот код:
    Assembler
    1
    2
    3
    4
    5
    
        push MB_OK
        push szCaption
        push szText
        push 0
        call [MessageBox]
  • ccall и cinvoke походят на stdcall и invoke, но они должны использоваться, чтобы вызвать процедуры, использующих C соглашение о вызовах, где стековый фрейм должен быть восстановлен вызывающей программой.
Чтобы определить процедуру, использующую стек для параметров и локальных переменных, Вы должны использовать макроинструкцию proc. В самой простой форме она должна сопровождаться названием для процедуры и затем именами для всех параметров которые требуется, например:
Assembler
1
proc WindowProc,hwnd,wmsg,wparam,lparam
Запятая между названием процедуры и первым параметром является дополнительной. Код процедуры должен следовать в следующих строках, и заканчиваться макроинструкцией endp. Стековый фрейм устанавливается автоматически на входе в процедуру, регистр EBP используется как основание, при обращении к параметрам, так что Вы должны избегать использовать этот регистр для других целей. Названия, указанные для параметров используются, чтобы определить метки, базирующиеся на значении в регистре EBP, которые Вы можете использовать, чтобы обратиться к параметрам как к переменным. Например:
Assembler
1
mov eax, [hwnd]
инструкции в процедуре, определенные как в вышеупомянутом примере, являются эквивалентом
Assembler
1
mov eax, [ebp+8]
Область видимости этих меток ограничена процедурой, так что Вы можете использовать те же самые названия для других целей вне данной процедуры. Так как любые параметры помещаются в стек как двойные слова при вызове таких процедур, метки для параметров определяются как двойные данные слова по умолчанию, однако Вы можете определять размеры для параметров с помощью двоеточия и оператора размера следующего за названием параметра . Предыдущий пример может быть перезаписан, который является снова эквивалентным:
Assembler
1
2
proc WindowProc,hwnd:DWORD,wmsg:DWORD,\
    wparam:DWORD,lparam:DWORD
Если Вы определяете размер меньший чем двойное слово, данная метка обращается к младшей части целого двойного слова, сохраненного в стеке. Если Вы, определяете больший размер, подобно указателю из учетверенного слова, два параметра размером с двойное слово будут определены, чтобы содержать это значение, но помечены как одна переменная.

Название процедуры может также сопровождаться stdcall или ключевым словом, которое определит используемое соглашение о вызовах. Когда никакой такой тип не определен, значение по умолчанию используется, которое является эквивалентным STDCALL. Также ключевое слово uses может следовать, и после него, список регистров (разделенных пробелами), которые будут автоматически сохранены на входе в процедуру и восстановлены на выходе. В этом случае должна быть запятая между списком регистров и первым параметром. Так что полностью показанная инструкция процедуры могла бы выглядеть следующим образом:
Assembler
1
2
proc WindowProc stdcall uses ebx esi edi,\
    hwnd:DWORD,wmsg:DWORD,wparam:DWORD,lparam:DWORD
Чтобы объявлять локальную переменную, Вы можете использовать макроинструкцию local, сопровождаемую одним или более объявлениями, разделенными запятыми, каждое, состоящее из названия для переменной, сопровождаемое двоеточием и типом переменной ― любым из стандартных типов (должен быть в верхнем регистре) или названием структуры данных. Например:
Assembler
1
local hDC:DWORD,rc:RECT
Чтобы объявлять локальный массив, Вы должны за названием переменной указать размер массива, заключенный в квадратные скобки, например:
Assembler
1
local str[256]:BYTE
Другой способ определять локальные переменные состоит в том, чтобы объявить их в блоке, начатом с макроинструкции "locals" и законченным "endl", в этом случае, они могут быть определены точно так же как допустимые данные. Это объявление ― эквивалент примера приведенного выше:
Assembler
1
2
3
4
locals
    hDC dd ?
    rc RECT
endl
Локальные переменные могут быть объявлены где-нибудь в процедуре, с единственным ограничением, что они должны быть объявлены прежде, чем они используются. Область видимости меток для переменных, определенных как локальные ограничена внутренней частью процедуры, Вы можете использовать те же самые имена для других целей вне процедуры. Если Вы даете немного инициализированных значений переменным, объявленным как локальные, макроинструкция генерирует команды, которые инициализируют эти переменные с заданными значениями и помещает их в ту же самую позицию в процедуре, где это объявление помещено.

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

Полное определение процедуры может выглядеть следующим образом:
Assembler
1
2
3
4
5
proc WindowProc uses ebx esi edi,hwnd,wmsg,wparam,lparam
    local hDC:DWORD,rc:RECT
    ; здесь ваш код
    ret
endp
0
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
01.09.2014, 06:04  [ТС]

In 64-bit Windows there is only one calling convention, and thus only two macroin- structions for calling procedures are provided. The fastcall calls directly the proce- dure specified by the first argument using the standard convention of 64-bit Windows system. The invoke macro does the same, but indirectly, through the pointer labelled by the first argument. Parameters are provided by the arguments that follow, and they can be of any size up to 64 bits. The macroinstructions use RAX register as a temporary storage when some parameter value cannot be copied directly into the stack using the mov instruction. If the parameter is preceded with addr word, it is treated as an address and is calculated with the lea instruction ― so if the address is absolute, it will get calculated as RIP-relative, thus preventing generating a relocation in case of file with fixups.

Because in 64-bit Windows the floating-point parameters are passed in a dierent way, they have to be marked by preceding each one of them with float word. They can be either double word or quad word in size. Here is an example of calling some OpenGL procedures with either double-precision or single-precision parameters:

Assembler
1
2
invoke glVertex3d,float 0.6,float -0.6,float 0.0
invoke glVertex2f,float dword 0.1,float dword 0.2
The stack space for parameters are allocated before each call and freed immediately after it. However it is possible to allocate this space just once for all the calls inside some given block of code, for this purpose there are frame and endf macros provided. They should be used to enclose a block, inside which the RSP register is not altered between the procedure calls and they prevent each call from allocating stack space for parameters, as it is reserved just once by the frame macro and then freed at the end by the endf macro.

Assembler
1
2
3
4
frame ; allocate stack space just once
invoke TranslateMessage,msg
invoke DispatchMessage,msg
endf
The proc macro for 64-bit Windows has the same syntax and features as 32-bit one (though stdcall and c options are of no use in its case). It should be noted however that in the calling convention used in 64-bit Windows first four parameters are passed in registers (RCX, RDX, R8 and R9), and therefore, even though there is a space reserved for them at the stack and it is labelled with name provided in the procedure definition, those four parameters will not initially reside there. They should be accessed by directly reading the registers. But if those registers are needed to be used for some other purpose, it is recommended to store the value of such parameter into the memory cell reserved for it. The beginning of such procedure may look like:

Assembler
1
2
3
4
5
6
7
proc WindowProc hwnd,wmsg,wparam,lparam
mov [hwnd],rcx
mov [wmsg],edx
mov [wparam],r8
mov [lparam],r9
; now registers can be used for other purpose
; and parameters can still be accessed later

It is possible to create a custom code for procedure framework when using proc macroin- struction. There are three symbolic variables, prologue@proc, epilogue@proc and close@proc, which define the names of macroinstructions that proc calls upon entry to the procedure, return from procedure (created with ret macro) and at the end of proce- dure (made with endp macro). Those variables can be re-defined to point to some other macroinstructions, so that all the code generated with proc macro can be customized. Each of those three macroinstructions takes five parameters. The first one provides a label of procedure entry point, which is the name of procedure aswell. The second one is a bitfield containing some

ags, notably the bit 4 is set when the caller is supposed to restore the stack, and cleared otherwise. The third one is a value that specifies the number of bytes that parameters to the procedure take on the stack. The fourth one is a value that specified the number of bytes that should be reserved for the local variables. Finally, the fifth an last parameter is the list of comma-separated registers, which procedure declared to be used and which should therefore be saved by prologue and restored by epilogue.

The prologue macro apart from generating code that would set up the stack frame and the pointer to local variables has to define two symbolic variables, parmbase@proc and localbase@proc. The first one should provide the base address for where the parameters reside, and the second one should provide the address for where the local variables reside ― usually relative to EBP/RBP register, but it is possible to use other bases if it can be ensured that those pointers will be valid at any point inside the procedure where parameters or local variables are accessed. It is также up to the prologue macro to make any alignments necessary for valid procedure implementation; the size of local variables provided as fourth parameter may itself be not aligned at all. The default behavior of proc is defined by prologuedef and epiloguedef macros (in default case there is no need for closing macro, so the close@proc has an empty value). If it is needed to return to the defaults after some customizations were used, it should be done with the following three lines:

Assembler
1
2
3
prologue@proc equ prologuedef
epilogue@proc equ epiloguedef
close@proc equ
As an example of modified prologue, below is the macroinstruction that implements stack-probing prologue for 32-bit Windows. Such method of allocation should be used every time the area of local variables may get larger than 4096 bytes.

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
macro sp_prologue procname,flag,parmbytes,localbytes,reglist
- local loc
loc = (localbytes+3) and (not 3)
parmbase@proc equ ebp+8
localbase@proc equ ebp-loc
if parmbytes | localbytes
push ebp
mov ebp,esp
if localbytes
repeat localbytes shr 12
mov byte [esp-%*4096],0
end repeat
sub esp,loc
end if
end if
irps reg, reglist \{ push reg \} }
prologue@proc equ sp_prologue
It can be easily modified to use any other stack probing method of the programmer's preference.

The 64-bit headers provide an additional set of prologue/epilogue macros, which allow to define procedure that uses RSP to access parameters and local variables (so RBP register is free to use for any other by procedure) and также allocates the common space for all the procedure calls made inside, so that fastcall or invoke macros called do not need to allocate any stack space themselves. It is an eect similar to the one obtained by putting the code inside the procedure into frame block, but in this case the allocation of stack space for procedure calls is merged with the allocation of space for local variables. The code inside such procedure must not alter RSP register in any way. To switch to this behavior of 64-bit proc, use the following instructions:

Assembler
1
2
3
prologue@proc equ static_rsp_prologue
epilogue@proc equ static_rsp_epilogue
close@proc equ static_rsp_close

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

Assembler
1
2
3
export ’MYLIB.DLL’,\
    MyStart,Start,\
    MyStop,’Stop’
определяет таблицу, экспортирующую две функции, которые определены под названиями MyStart и MyStop в исходниках, но экспортируются библиотекой под более короткими названиями. Макрокоманда заботится об алфавитной сортировке таблицы, которая требуется форматом PE.


Макрос interface позволяет объявлять интерфейс для объекта типа СОМ, первый параметр ― название интерфейса, и затем последовательность из имен методов, как в этом примере:

Assembler
1
2
3
4
5
6
7
8
9
interface ITaskBarList,\
    QueryInterface,\
    AddRef,\
    Release,\
    HrInit,\
    AddTab,\
    DeleteTab,\
    ActivateTab,\
    SetActiveAlt
Тогда может использоваться макрос comcall, чтобы вызвать методы данного объекта. Первым параметром для этого макроса должен быть хендл объекта, второй должен быть названием интерфейса COM, осуществленного этим объектом, и затем название метода и параметры для этого метода. Например:

Assembler
1
comcall ebx,ITaskBarList,ActivateTab,[hwnd]
здесь регистр EBX содержит хендл объекта COM с интерфейсом ITaskBarList, и вызывает метод этого объекта ActivateTab с параметром [hwnd].

Вы можете также использовать название интерфейса COM тем же самым способом как название структур, объявляя переменную, которая будет содержать хендл объекта данного типа например:

Assembler
1
ShellTaskBar ITaskBarList
Вышеупомянутая строка объявляет переменную размером в двойное слово, в которой может быть сохранен хендл COM объекта. После сохранения там хендла объекта, его методы можно вызывать с cominvk. Эта макроинструкция нуждается только в названии обьявленной переменной с назначенным интерфейсом и названием метода в качестве первых двух параметров, и затем параметры для метода. Так что метод ActivateTab объекта, хендл которого сохранен в переменной ShellTaskBar как определено выше, можно вызвать вот так:

Assembler
1
cominvk ShellTaskBar,ActivateTab,[hwnd]
который делает то же самое, как и этот:

Assembler
1
comcall [ShellTaskBar],ITaskBarList,ActivateTab,[hwnd]
0
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
01.09.2014, 06:06  [ТС]
Есть два способа создать ресурсы, первый ― Вы должны включить внешний файл, сделанного любым компилятором ресурсов например:

Assembler
1
section '.rsrc' data readable resource from 'my.res'
И Вы не должны что-нибудь еще помещать в такую секцию. В случае, если Вы не хотите отдельную секцию для ресурсов, Вы можете поместить их в любую другую секцию с помощью директивы data:

Assembler
1
2
   data resource from 'my.res'
   end data
Другой способ ― Вы должны создать раздел ресурсов вручную. Последний метод, хотя не нуждается ни в какой дополнительной программе, которая будет использована, более трудоемкий, но стандартные заголовочные файлы обеспечивают помощь ― набор макросов, которые служат кирпичами, чтобы составить секцию ресурсов.

макроинструкция directory должна быть помещена непосредственно в начале вручную написанных ресурсов, и это определит, какие типы ресурсов используются. Это должно сопровождаться парами значений, первый в каждой паре, является идентификатором типа ресурса, а второй метка подкаталога ресурсов данного типа. Это может выглядеть следующим образом:

Assembler
1
2
3
directory RT_MENU,menus,\
    RT_ICON,icons,\
    RT_GROUP_ICON,group_icons
Подкаталоги могут быть помещены где-нибудь в секции ресурсов после основного каталога, и они должны быть определены макроинструкцией resource, которая требует, чтобы первый параметр был меткой подкаталога (соответствующий входу в основном каталоге) сопровождаемый тремя параметров ― в каждом таком входе, первый параметр определяет идентификатор ресурса (это значение выбранное программистом и используется, чтобы обратиться к данному ресурсу в программе), второй определяет язык, а третий ― метка ресурса. Стандартный equates, должен использоваться, чтобы задавать идентификаторы языка. Например подкаталог меню может быть определен вот так:

Assembler
1
2
3
resource menus,\
    1,LANG_ENGLISH+SUBLANG_DEFAULT,main_menu,\
    2,LANG_ENGLISH+SUBLANG_DEFAULT,other_menu
Если этот вид ресурсов для которого язык не имеет значения, должен использоваться идентификатор языка LANG_NEUTRAL

Для определения ресурсов различных типов есть специализированные макроинструкции, которые должны быть помещены в секции ресурсов

Точечные рисунки ― ресурсы с идентификатором типа RT_BITMAP.

Объявляя растровый ресурс используют макроинструкцию bitmap с первым параметром, являющейся меткой ресурса (соответствующей входу в подкаталоге точечных рисунков) и вторым параметром символьной строкой, содержащей путь к bmp-файлу, например:

Assembler
1
bitmap program_logo,’logo.bmp’
Два типа ресурса, связанные со значками ― RT_GROUP_ICON ― тип ресурса, который должен быть связан с одним или более ресурсам типа RT_ICON, каждый содержащий отдельное изображение. Это позволяет объявлять значки различных размеров и разрядностей цвета под общим идентификатором ресурса. Этот идентификатор, данный ресурсу типа RT_GROUP_ICON можно тогда передать к функции LoadIcon, и она выберет изображение подходящего размера из группы. Чтобы объявлять значок, используйте макроинструкцию icon, с первым параметром, являющимся меткой ресурса RT_GROUP_ICON , сопровождаемого парами параметров, объявляющих изображения. Первым параметром в каждой паре должна быть метка ресурса RT_ICON , и второй символьная строка, содержащая путь к файлу значка. В самом простом варианте, когда группа значков содержит только одно изображение, это будет:

Assembler
1
icon main_icon,icon_data,’main.ico’
где main_icon ― метка для входа в подкаталоге ресурса для типа RT_GROUP_ICON, и icon_data ― метка для входа типа RT_ICON.

Курсоры объявляют очень похожим способом на значки, на сей раз с типами RT_GROUP_CURSOR и RT_CURSOR и макроинструкцией cursor, которая берет параметры, аналогичные принимаемые макрокомандой icon. Так что определение курсора может выглядеть следующим образом:

Assembler
1
cursor my_cursor,cursor_data,’my.cur’
Меню имеют тип ресурса RT_MENU и объявляется макроинструкцией menu, сопровождаемой несколькими другими макросами определяющими элементы в меню. Само меню берет только один параметр ― метка ресурса. menuitem определяет элемент в меню, требуется до пяти параметров, но только два обязательны ― первый ― символьная строка, содержащая текст пункта меню, и второй ― значение его идентификатора (который является значением, которое будет возвращено, когда пользователь выбирает данный элемент в меню). menuseparator определяет разделитель в меню и не требует никаких параметров.

Дополнительный третий параметр menuitem определяет флажки ресурса меню. Есть два таких флажка
  • MFR_END ― флажок последнего элемента в данном меню, и метках
  • MFR_POPUP ― флажок что данный элемент ― подменю, и следующие элементы будут элементами, составляющими то подменю, пока элемент с флажком MFR_END не найден.
Флажок MFR_END можно также передать как параметр для menuseparator и это единственный параметр, который эта макрокоманда может взять. Каждое подменю должно быть закрыто элементом с флажком MFR_END, и полностью меню должно также быть закрыто этим флагом. Вот ― пример законченного объявления меню:
Assembler
1
2
3
4
5
6
7
menu main_menu
    menuitem ’&File’,100,MFR_POPUP
    menuitem ’&New’,101
    menuseparator
    menuitem ’E&xit’,109,MFR_END
menuitem ’&Help’,900,MFR_POPUP + MFR_END
    menuitem ’&About...,901,MFR_END
Дополнительный четвертый параметр menuitem определяет флажки состояния за данного элемента, эти флажки те же самые как те что используются функциями API, подобно MFS_CHECKED или MFS_DISABLED. Точно так же пятый параметр может определить типа флажка. Например, это определит элемент отмеченный с отметкой переключателя:

Assembler
1
menuitem ’Selection’,102, ,MFS_CHECKED,MFT_RADIOCHECK
Диалоговые окна имеют тип ресурса RT_DIALOG и объявляются макроинструкцией dialog, сопровождаемой любым количеством элементов, начатых dialogitem и законченных enddialog.

dialog может взять до одиннадцати параметров, минимум требуется семь.
  • Первый параметр, как обычно, определяет метку ресурса,
  • второй ― символьная строка, содержащая заголовок диалогового окна,
  • следующие четыре параметра определяют горизонтальные и вертикальные координаты, ширину и высоту диалогового окна соответственно.
  • Седьмой параметр определяет флажки стиля для диалогового окна,
  • дополнительный восьмой определяет расширенные флажки стиля.
  • Девятый параметр может определить меню для окна ― это должен быть идентификатор ресурса меню, такой же самый как один из указанных в подкаталоге ресурсов с типом RT_MENU.
  • Наконец десятый и одиннадцатый параметр может использоваться, чтобы определить шрифт для диалогового окна ― сначала , должна быть символьная строка, содержащая название шрифта, и после нее номер, определяющий размер шрифта. Когда эти дополнительные параметры не определены, используется значение по умолчанию, MS Sans Serif размером 8 кеглей.
Этот пример показывает макрокоманду dialog со всеми параметрами кроме меню (который оставляют с пустым значением), дополнительные параметры находятся во второй строке:

Assembler
1
2
dialog about,’About’,50,50,200,100,WS_CAPTION+WS_SYSMENU,\
    WS_EX_TOPMOST, ,Times New Roman’,10
dialogitem имеет восемь обязательных параметров и один дополнительный
  • Первым параметром должна быть символьная строка, содержащая имя класса элемента управления.
  • Вторым параметром может быть или символьная строка содержащий текст на элементе, или идентификатор ресурса в случае, если, когда содержание элемента должно быть определено некоторым дополнительным ресурсом (подобно элементу класса STATIC с стилем SS_BITMAP).
  • Третий параметр ― идентификатор элемента, который идентифицирует элемент для функций API.
  • Затем четыре параметра определяют горизонтальные, вертикальные координаты, ширину и высоту элемента соответственно.
  • Восьмой параметр определяет, стиль элемента,
  • и дополнительный девятый определяет расширенный стиль.
Пример определения элемента диалога:

Assembler
1
dialogitem 'BUTTON','OK',IDOK,10,10,45,15,WS_VISIBLE+WS_TABSTOP
И пример статического элемента, содержащего точечный рисунок, предполагая, что там существует растровый ресурс с идентификатором 7:

Assembler
1
dialogitem ’STATIC’,7,0,10,50,50,20,WS_VISIBLE+SS_BITMAP
Определение ресурса диалога может содержать любое количество элементов или ни одного вообще, и должно всегда заканчиваться макроинструкцией enddialog.

Ресурсы типа RT_ACCELERATOR создаются с макроинструкцией accelerator. Первый параметр, традиционно является меткой ресурса, далее должны следовать за три параметра ― флажки акселератора, сопровождаемые кодом виртуальной клавиши или символом ASCII и значением идентификатора (который подобен идентификатору пункта меню). Простое определение акселератора может выглядеть следующим образом:

Assembler
1
2
3
accelerator main_keys,\
    FVIRTKEY+FNOINVERT,VK_F1,901,\
    FVIRTKEY+FNOINVERT,VK_F10,109
Информация версии ― ресурс типа RT_VERSION и создается макросом version. После метки ресурса, второй параметр определяет операционную систему PE файла (обычно VOS __ WINDOWS32), третий параметр, тип файла (наиболее общий является VFT_APP для программы и VFT_DLL для библиотеки), четвертый подтип (обычно VFT2_UNKNOWN), пятый идентификатор языка, шестой кодовая страница и затем пары из символьных строк ― названия свойств и определяющие их значения. Самая простая информация версии может быть определена вот так:

Assembler
1
2
3
4
5
6
versioninfo vinfo,VOS__WINDOWS32,VFT_APP,VFT2_UNKNOWN,\
    LANG_ENGLISH+SUBLANG_DEFAULT,0,\
    ’FileDescription’,’Description of program’,\
    ’LegalCopyright’,’Copyright et cetera’,\
    ’FileVersion’,1.0,\
    ’ProductVersion’,1.0
Другие виды ресурсов могут быть определены макроинструкцией resdata, которая берет только один параметр ― метка ресурса, и может сопровождаться любыми командами, определяющими данные,а заканчиваться макроинструкцией endres, например:

Assembler
1
2
3
resdata manifest
    file ’manifest.xml’
endres
0
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
01.09.2014, 06:06  [ТС]

Макросы ресурсов используют du директиву, чтобы определить любые строки Unicode в ресурсах . Так как эта директива просто расширяет символы, обнуляя 16-разрядные значения, для строк содержащих некоторые символы не ASCII, du возможно, должна быть переопределена. Для части кодировок макросы, переопределяющие du, чтобы генерировать тексты должным образом в Unicode находятся в подкаталоге ENCODING. Например, если исходный текст закодирован с кодовой страницей Windows 1251 (русская кодовая страница в Windows), такая строка должна быть помещена где-нибудь в начале источника:

Assembler
1
include ’encoding\win1251.inc

Расширенные заголовочные файлы win32ax.inc, win32wx.inc, win64ax.inc и win64wx.inc обеспечивают все функциональные возможности основных и включают еще несколько особенностей, вовлекающих более сложные макрокоманды. Также, если формат PE не объявлен прежде, чем включены расширенные заголовочные файлы, то они объявляют это автоматически. win32axp.inc, win32wxp.inc, win64axp.inc и win64wxp.inc ― варианты расширенных заголовочных файлов которые дополнительно выполняют проверку количества параметров при вызовах процедур.


С расширенными заголовочными файлами макросы позволяют вызывать процедуры с большими типами параметров а не только с двойными словами. Прежде всего, когда символьная строка ― передается как параметр для процедуры, это используется, чтобы определить строковые данные, размещенные среди кода, и передать процедуре указатель на эту строку. Это позволяет легко определять символьные строки которые не должны многократно использоваться только в строке вызывающей процедуру которая требует указателей на эти строки, например:

Assembler
1
invoke MessageBox,HWND_DESKTOP,"Message","Caption",MB_OK
Если параметр ― группа, содержащая некоторые значения, разделенные запятыми, это будет обработано тем же самым способом как простой символьный строковый параметр.

Если параметру предшествует слово addr это означает, что это значение ― адрес двойного слова, и этот адрес нужно передать процедуре, даже если это не может быть сделано непосредственно ― как в случае локальных переменных, которые имеют адреса относительно регистра EBP, в таком случае, регистр EDX используется временно, чтобы вычислить значение адреса и передать его процедуре. Например:

Assembler
1
    invoke RegisterClass,addr wc
в случае, если, когда wc ― локальная переменная с адресом EBP-100h, это генерирует такую последовательность команд:

Assembler
1
2
3
    lea edx,[ebp-100h]
    push edx
    call [RegisterClass]
Однако, когда данный адрес ― не соотносится ни с каким регистром, это будет сохранено непосредственно.

Если параметру предшествует слово double, это будет обработано как значение на 64 бита и передано процедуре как два 32-разрядных параметра. Например:

Assembler
1
invoke glColor3d,double 1.0,double 0.1,double 0.1
передаст процедуре три параметра на 64 бита как шесть двойных слов. Если параметр после double ― операнд в памяти, он не должен иметь оператора размера, double уже работает как переопределение размера.

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

Assembler
1
2
invoke MessageBox,<invoke GetTopWindow,[hwnd]>,\
    "Message","Caption",MB_OK
Нет никаких пределов для глубины вложения вызовов процедур.


Расширенные заголовочные файлы включают некоторые макроинструкции, которые помогают с простым структурированием в программе. .data и .code ― только ярлыки для объявления разделов для данных и для кода. Макрос .end должен быть помещен в конце программы, с одним параметром, определяющим точку входа программы, и это также автоматически сгенерирует раздел импорта, используя все стандартные таблицы импорта.

Макрос .if генерирует часть кода, который проверяет некоторое простое условие во время выполнения, и в зависимости от результата продолжает выполнение следующего блока или пропускает его. Блок должен быть закончен .endif, но ранее также макрос .elseif может использоваться один или более раз и начинать код, который будет выполнен при некотором дополнительном условии, когда предыдущие условия не были выполнены, и .else как последний блок перед .endif, который будет выполнен, когда все условия были ложные.

Другой способ определить условие состоит в том, чтобы обеспечить два значения и оператор сравнения между ними ― один из =,<,>, <=, >=, и <>. Значения сравниваются как беззнаковое. Также, если Вы запишете только единственное значение как отдельный параметр, это будет проверено, на равенство нулю, и условие будет истинно, только если это не ноль. For example:

Assembler
1
2
3
.if eax
    ret
.endif
generates the instructions, which skip over the ret when the EAX is zero. There are также some special symbols recognized as conditions: the ZERO? is true when the ZF flag is set, in the same way the CARRY?, SIGN?, OVERFLOW? and PARITY? correspond to the state of CF, SF, OF and PF flags.

The simple conditions like above can be composed into complex conditional expressions using the &, | operators for conjunction and alternative, the ~ operator for negation, and parenthesis. For example:

Assembler
1
2
3
.if eax<=100 & ( ecx | edx )
    inc ebx
.endif
will generate the compare and jump instructions that will cause the given block to get executed only when EAX is below or equal 100 and at the same time at least one of the ECX and EDX is not zero.

Макрос .while генерирует команды, которые повторят выполнение данного блока (заканчивающегося .endw), пока условие истинно. Условие должно следовать за .while и может быть определено тем же самым способом что и для .if.

Пара макроинструкций .repeat и .until определяет блок, который будет выполняться неоднократно, пока данное условие не будет выполнено ― на сей раз условие должно следовать за .until макрокомандой, помещенной в конце блока, подобно:

Assembler
1
2
3
.repeat
    add ecx,2
.until ecx>100
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
01.09.2014, 06:06
Помогаю со студенческими работами здесь

Flat Assembler
Со временем задачи стали нерешаемыми из-за ужасно медленной скорости. Уже давно хочу перейти на FASM, но понятных по нему статей так и не...

Ошибка в flat assembler
начал изучать ассемблер сталкнулся с такой проблемой: перепечатываю пример из книги: org 100h mov ax,0B800h mov es,ax ...

flat assembler массив
У меня есть задание &quot;Упорядочить по убыванию элементы каждого столбцу матрицы&quot; Числа произвольные . Если кому не сложно будет...

Массив в Flat Assembler
Всем добрый день! Подскажите, почему не работает массив в Flat Assembler? org 100h jmp start massiv dw 1, 3, 5, 7, 5 start: ...

Как использовать Flat Assembler в Free Pascal?
Я недавно хотел разработать так ради прикола мини ОС с использованием в связке Free Pasal и Flat Assembler. Один вопрос как можно Flat...


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

Или воспользуйтесь поиском по форуму:
51
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
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 . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru