foo equ
foo bar
обе будут интерпретированы как вызовы макроса « foo
», так как значение первого символа берет верх над значением второго.
Макроинструкции генерируют новые строки от их блоков определения, заменяя параметры на их значения и далее обрабатывая операторы « #
» и « `
». Оператор конверсии имеет высший приоритет, чем оператор сцепления.
После завершения этого, заново сгенерированная строка проходит через стандартный препроцессинг, как описано выше.
Хотя обычно символьные константы заменяются исключительно в строках, нет ни директив препроцессора, ни макроинструкций, встречается несколько особых ситуаций, где замены проводятся в частях строк, содержащих директивы. Первая — это определение символьной константы, где замены производятся везде после слова « equ
» и результирующее значение присваивается новой константе (смотрите 2.3.2). Вторая такая ситуация — это директива « match
», где замены производятся в символах, следующих за запятой перед сопоставлением их с образцом. Эти свойства могут использоваться, например, для сохранения списков, как, например, эта совокупность определений:
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
»:
match params, list { foo params }
Значение « list
», если оно не пустое, соответствует ключевому слову « params
», которое далее во время генерации строк, заключенных в фигурные скобки, заменяется на соответственное значение. Так, если « list
» имеет значение « 1,2
», строка, указанная выше, сгенерирует строку, содержащую « foo 1,2
», которая далее пройдет стандартный препроцессинг.
Есть ещё один особый случай — когда препроцессор собирается проверить второй символ и натыкается на двоеточие (что далее интерпретируется ассемблером как определение метки), он останавливается в этом месте и заканчивает препроцессинг первого символа (то есть если это символьная константа, она развертывается) и если это все еще выглядит меткой, совершается стандартный препроцессинг, начиная с места после метки. Это позволяет поместить директивы препроцессора и макроинструкции после меток, аналогично инструкциям и директивам, обрабатываемым ассемблером, например:
start: include 'start.inc'
Однако если метка во время препроцессинга разрушается (например, если у символьной константы пустое значение), происходит только замена символьных констант до конца строки.
2.4 Директивы форматирования
« format
» со следующим за ним идентификатором формата позволяет выбрать формат вывода. Эта директива должна стоять в начале кода. Формат вывода по умолчанию — это простой двоичный файл, он может быть также выбран директивой « format binary
».
« use16
» и « use32
» указывают ассемблеру генерировать 16-битный или 32-битный код, пренебрегая настройкой по умолчанию для выбранного формата вывода. « use64
» включает генерирование кода для длинного режима процессоров x86.
Ниже описаны разные форматы вывода со специфичными для них директивами.
Чтобы выбрать формат вывода MZ, используйте директиву « format MZ
». По умолчанию код для этого формата 16-битный.
« segment
» определяет новый сегмент, за ним должна следовать метка, чьим значением будет номер определяемого сегмента. Опционально за этой директивой может следовать « use16
» или « use32
», чтобы указать битность кода в сегменте. Начало сегмента выровнено по параграфу (16 байт). Все метки, определенные далее, будут иметь значения относительно начала этого сегмента.
Читать дальше