Макрокоманды

Обычно переменные в программах, например, написанных на ФОРТРАНе, ассоциируются с ячейками памяти, содержимое которых (значения переменных) вычисляется в процессе выполнения программы. Аналогично имена, которыми обозначаются отдельные команды или области памяти, представляют адреса, которые используются машинной программой во время выполнения.

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

Все символические переменные начинаются со знака & (амперсанд). Далее следует имя, состоящее не более чем из семи элементарных символов, первый из которых должен быть буквой. Например, &А, &А1, &ALLIGTR являются доступными символическими переменными, а &1А и &TOOOLONG— нет. При желании сам амперсанд(&) можно использовать в качестве символа, но для этого нужно в соответствующем месте вместо одного амперсанда писать строку вида &&. Например, по команде

RAINING DC C'CATS && DOGS'

заводится символьная константа, выглядящая в кодах EBCDIC как CATS&DOGS.

Перейдем теперь к вопросу использования символических параметров при определении и использовании макрокоманд.

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

Все макро, которые предполагается использовать в программе, необходимо определить в самом ее начале. Для этого можно задать макроопределение вместе с программой в качестве части входных данных для ассемблера. Можно также макроопределения предварительно поместить в макробиблиотеку. Определения всех системных макрокоманд, а также макрокоманд, использованных нами ранее, вводилось в систему вторым способом.

Любое макроопределение состоит из четырех основных частей:

1. Предложение MACRO, говорящее ассемблеру о том, что далее следует макроопределение.

2. Предложение-прототип, с помощью которого указывается имя макро и способ задания его параметров.

3. Модельные предложения, или тело макроопределения, т. е. последовательность управляющих предложений ассемблера и машинных команд, определяющих порядок выполнения макрогенерации.

4. Предложение MEND (Macro END), указывающее на окончание макроопределения.

Предложение MACRO содержит пробелы в поле имени и поле операндов. В поле операции пишется просто MACRO. Предложение MEND также не имеет операндов и комментариев. Поле имени в нем может быть оставлено пустым, а может содержать метку следования (см. разд. 18.3).

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

метка имя - операнды комментарий

Поле операции в данном случае превращается в поле имени, которое может содержать произвольное допустимое в языке ассемблера имя, такое, например, как GET, RWD или MYMAC1. Это имя в дальнейшем используется для задания макрокоманды в программе. Поле метки может быть пустым, а может содержать некоторую метку, обычно символический параметр, который превращается в имя некоторого предложения при получении макрорасширения. В поле операндов перечисляются параметры, которые затем будут использованы в макрокоманде. Параметры разделяются запятыми. В качестве параметров могут выступать символические имена и константы, однако чаще всего ими являются символические параметры.

В макроопределении и макрорасширении символические параметры занимают основное место. Указывая символические параметры в предложении-прототипе, мы фактически говорим ассемблеру о том, что присвоение ему каких-то определенных значений нужно отложить до момента использования макро. Значения будут заданы операндами макрокоманды. Модельные предложения определяют порядок использования этих значений. Например, если символический параметр &VAR является третьим в списке параметров предложения-прототипа, а имя XQW является третьим в списке операндов соответствующей макрокоманды, то ассемблер заменяет строку &VAR везде, где она встречается в теле макроопределения, на XQW. Это означает, что определение фактического значения символического параметра откладывается до момента, когда макро, содержащее этот параметр в качестве метки или операнда, будет использовано в программе.

Приведем несколько примеров, связанных с использованием символических параметров. Вспомним, что в процессе выполнения макро-команды RWD производится вызов подпрограммы $$IO, которая вводит образ перфокарты в память, преобразует его в двоичное представление и помещает полученное значение в регистр 1. Затем RWD производит пересылку содержимого регистра 1 в какой-либо другой регистр или ячейку памяти в соответствии со значением операнда макрокоманды.

Рис. 18.1. Определение макро RWD с регистровым операндом.

Предположим, что в качестве операнда выступает регистр. При составлении макроопределения нам не известно, какой именно регистр программист захочет использовать для ввода информации с карты. Таким образом, в предложении-прототипе вместо конкретного номера некоторого регистра пишется символический параметр. Позже, когда программист выберет конкретный номер регистра, он укажет его в поле операндов макрокоманды RWD. На рис. 18.1 приведено макроопределение RWD.

В качестве операнда предложения-прототипа выступает параметр &LOC. Можно, например, написать

RWD 5

В распоряжении ассемблера находится предложение-прототип

RWD&LOC

Значение переменной &LOC присваивается при использовании макро. В данном случае &LOC получит значение 5. В процессе построения макрорасширения ассемблер всюду заменяет &LOC на его значение, т. е. на 5. Макрорасширение RWD, следовательно, выглядит так:

L 15, = V($$IO)

BALR 14,15

LTR 5,1

Эти три команды будут вставлены в программу вместо макрокоманды RWD.

Заметим, что само предложение-прототип ни в каком виде не входит в макрорасширение. Это является общим правилом; предложение-прототип фактически играет лишь роль схемы будущей макрокоманды.

Этот факт становится непосредственно важным, если необходимо пометить какое-либо предложение макрорасширения. Приведенное на рис. 18.1 макроопределение не позволяет использовать макрокоманду RWD, например, в следующем цикле:

Рис. 18.2. Макроопределение RWD, допускающее использование метки при записи макрокоманды.

Макрорасширение, полученное при использовании старого макроопределения

L 15, = V($$IO)

BALR14,15

LTR 7,1

не содержит метки NEXTCARD.

Модифицированное в соответствии с возникшей потребностью макроопределение RWD приведено на рис. 18.2. На первый взгляд может показаться, что метка &LAB определена здесь дважды. Но это не так, поскольку прототип не входит в макрорасширение. Если теперь договориться использовать в качестве макроопределения RWD определение, приведенное на рис. 18.2, то при расширении макрокоманды

NEXTCARD RWD 7

получится

NEXTCARD L 15, = V($$I0)

BALR 14,15

LTR7,1

что и является желаемым результатом. Ассемблер просто подставил вместо символических параметров &LAB и &LOC их фактические значения, соответственно NEXTCARD и 7.

В качестве еще одного параметра рассмотрим макроопределение, изображенное на рис. 18.3. Макрокоманда BEGIN выполняет следующую последовательность действий: сохранение содержимого регистров, определение базового регистра (регистра, номер которого задан в качестве операнда), сохранение содержимого регистра 13, загрузку регистра 13 адресом области сохранения, также заданным в качестве операнда макрокоманды.

Ассемблер позволяет производить конкатенацию, т. е. присоединение значения символического параметра к началу или концу некоторой символьной строки модельного предложения. Для присоединения значения к концу некоторой строки необходимо просто поместить имя этого символического параметра сразу же после строки. Если параметр &LET имеет значение BAGE, то строка

Рис. 18.3. Определение макро BEGIN.

в модельном предложении заменится в макрорасширении на

GARBAGE

Присоединить значение к началу строки можно почти так же, только теперь перед началом исходной строки нужно поставить точку. Если написать просто

&LETGAR

то ассемблер решит, что мы использовали новую символическую переменную

&LETGAR

Итак, написав

&LET.GAR

мы получим

BAGEGAR

-в макрорасширении.

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

В данном случае осуществляется конкатенация символов &TYPE иС для получения мнемоники требуемой команды сравнения. Если нам хочется сравнить два полуслова, то в качестве типа операндов необходимо указать Н. Макрорасширение для макрокоманды

LOOP COMPARE 3, HW, H, EQUAL, NOTEQUAL

выглядиттак:

LOOPCH3,HW

BEEQUAL

ВNOTEQUAL

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

Рис. 18.4. МакроопределениеCOMPARE.

Расширениемакрокоманды

NEXT COMPARE 6,FULWD,,EQ,NEQ

выглядит тик:

NEXTС 6.FULWD

BEEQ

ВNEQ

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

имя = значение

где «имя» — это имя параметра, а «значение» — значение, которое будет присвоено этому параметру по умолчанию, т. е. если мы не укажем значение этого параметра в макрокоманде.

На рис. 18.5 приведен другой вариант макро COMPARE, в котором параметр &TYPE является ключевым. По умолчанию параметру &TYPE присваивается значение пробел, поэтому если в макрокоманде значение &TYPE не определять, то будет сгенерирована команда с мнемоникойС, т. е. команда сравнения полных слов. Для того чтобы определить значение ключевого параметра, нужно в списке операндов макрокоманды написать комбинацию: имя параметра без знака & впереди, знак равенства и желаемое значение. Таким образом, если нам хочется, воспользовавшись последним определением макро COMPARE, организовать сравнение чисел с плавающей точкой (тип Е), нужно

Рис. 18.5. Макроопределение COMPARE с ключевым параметром &TYPE.

написать

FP COMPARE 2,FPNO,FPEQ,FPNEQ,TYPE = E

Полученное при этом макрорасширение будет содержать команду с мнемоникой СЕ, т. е. команду сравнения чисел с плавающей точкой.

 
Статьи раздела