Продолжая использовать наш сайт, вы даете согласие на обработку файлов cookie, которые обеспечивают правильную работу сайта. Благодаря им мы улучшаем сайт!
Принять и закрыть

Читать, слущать книги онлайн бесплатно!

Электронная Литература.

Бесплатная онлайн библиотека.

Читать: Flat Assembler 1.64. Мануал программера - Tomasz Grysztar на бесплатной онлайн библиотеке Э-Лит


Помоги проекту - поделись книгой:

mov al,1

stosb

mov al,2

stosb

mov al,3

stosb

Существуют некоторые специальные директивы, возможные только внутри определений макроинструкций. Директива «local» задает локальные имена, которые будут заменены уникальными значениями каждый раз, когда используется макроинструкция. За ней должны следовать имена, разделенные запятыми. Эта директива обычно требуется для внутренних констант или меток макроинструкции. Например:

macro movstr

{

 local move

 move:

 lodsb

 stosb

 test al,al

 jnz move

}

Каждый раз, когда используется эта макроинструкция, «move» заменяется новым уникальным именем. То есть вы не получите ошибку, это обычный случай, когда метка определяется больше, чем один раз.

«forward», «reverse» и «common» делят макроинструкцию на блоки, каждый из которых обрабатывается после окончания обработки предыдущего. Они различаются в поведении только если макроинструкция поддерживает много групп аргументов. Блок инструкций, следующий за «forward» будет обрабатываться для каждой группы аргументов от первой до последней, как блок по умолчанию (без этих директив). Блок, идущий за «reverse» будет обрабатываться для каждой группы аргументов в обратном порядке — от последней до первой. Блок за директивой «common» обрабатывается лишь один раз, просто для всех групп аргументов. Локальное имя, определенное в одном блоке, доступно во всех следующих блоках при обработке той же группы аргументов. Если оно было определено в блоке «common», оно доступно во всех следующих блоках, независимо от обрабатываемой группы.

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

macro strtbl name,[string]

{

 common

 label name dword

 forward

 local label

 dd label

 forward

 label db string,0

}

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

Первая инструкция, следующая за директивой, начинающей блок в макроинструкции, может идти с ней на той же строке, как на следующем примере:

macro stdcall proc,[arg]

{

 reverse push arg

 common call proc

}

Это макрос может применяться для вызова процедур, используя соглашение STDCALL, аргументы сохраняются в стеке в обратном порядке. Например, «stdcall foo,1,2,3» будет ассемблировано так:

push 3

push 2

push 1

call foo

Если некоторое имя внутри макроинструкции имеет несколько значений (это либо один из аргументов, заключенных в квадратные скобки, либо локальное имя, определенное в блоке, следующем за директивой «forward» или «reverse») и используется в блоке, следующем за директивой «common», оно будет заменено на все значения, разделенные запятыми. Например, следующий макрос передать все дополнительные аргументы ранее определенной макроинструкции «stdcall»:

macro invoke proc,[arg]

{ common stdcall [proc],arg }

Он может применяться для непрямого вызова (через указатель в памяти) процедуры, используя соглашение STDCALL.

Внутри макроинструкции также может быть использован специальный оператор «#». Этот оператор сцепляет два имени в одно. Это может быть полезно, так как делается после того, как аргументы и локальные имена заменяются на свои значения. Следующая макроинструкция генерирует условный переход в зависимости от аргумента «cond»:

macro jif op1,cond,op2,label

{

 cmp op1,op2

 j#cond label

}

Например, «jif ax,ae,10h,exit» будет ассемблировано как инструкции «cmp ax,10h» и «jae exit».

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

Возможно преобразование имени в строку в кавычках с помощью оператора «`», который также может быть использован внутри макроинструкции. Он конвертирует следующее за ним имя в строку, заключенную в скобки, но имейте в виду, что если за ним следует аргумент, который заменяется на значение, содержащее больше, чем один символ, будет преобразован только первый из них, так как оператор «`» конвертирует только символ, идущий непосредственно за ним. Здесь пример использования этих двух свойств:

macro label name

{

 label name

 if ~ used name

  display `name # "is defined but not used.",13,10

 end if

}

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

Чтобы создать макроинструкцию, ведущую себя по-разному в зависимости от типа аргументов, например если это строки в кавычках, вы можете использовать оператор сравнения «eqtype». Вот пример его использования для отделения строки в кавычках от других типов аргументов:

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. Если аргумент этого макроса некоторое число, метка или переменная, показывается строка из этого адреса, но если аргумент — это строка в кавычках, то созданный код покажет её после … и … .

Также возможно объявить макроинструкцию внутри другой макроинструкции, то есть один макрос может определить другой, но с такими определениями есть проблема, вызванная тем, что знак «}» не может появляться внутри макроинструкции, он всегда означает конец его определения. Чтобы обойти эту проблему, можно избавиться от мешающих символов. Это делается путем подстановки одного или больше обратных слэшей перед любыми другими символами (даже специальными знаками). Препроцессор видит эту последовательность как один символ, но каждый раз, когда он видит такой символ во время обработки макроса, он обрезает обратные слэши с его начала. Например, «\{» трактуется как один символ, но во время обработки макроса он станет символом «\{». Это позволит вам определить одну макроинструкцию внутри другой:

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», которая становится полезной, когда некоторый макрос только начинает определение другого, без его закрытия. Например:

macro tmacro params

{

macro params {

}



Поделиться книгой:

На главную
Назад