Макрокоманда - это программный инструмент, который позволяет вам
создавать собственные операции ассемблера. На самом деле макро-
определения относятся к механизму препроцессора. Макропроцессор
позволяет определять новые коды операций для процессора. В этом
определении вы, в частности, сообщаете ассемблеру текст выполняемой
операции. Когда ассемблер встречает этот вновь определенный код
операции, он обращается к сохраненному определению макрокоманды и
помещает в транслируемый участок программы текст из этого
определения. Например, в программе могут быть определены в качестве
макрокоманд часто используемые последовательности команд. Каждый
раз, когда эти команды должны быть вставлены в текст программы,
программист может вместо этого воспользоваться макрокомандой.
В использовании макрокоманды можно выделить два шага. На первом
шаге макрокоманда определяется в программе. Программист присваивает
ей имя и определение. Определение состоит из из операций ассемблера
и команд, которые будут генерироваться каждый раз при появлении
имени макрокоманды. Второй шаг - применение макрокоманды. Это
происходит когда ассемблер встречает ее имя в качестве кода
операции. Ассемблер заменяет это имя указанными в определении
командами.
Возьмем в качестве примера команды сопроцессора 8087, который
мы обсудим в глве 7. В написании программ с использованием команд
числового процессора 8087 возникают некоторые трудности. В
макроассемблере отсутствуют коды операций 8087. Для использования
8087 вы должны сформировать его команды с помощью либо оператора
определения данных, либо кодов операций WAIT и ESC. Лучше всего это
делать через определение макрокоманды, что позволит вам писать
команды 8087. После этого программа может пользоваться командами
8087, хотя они и не входят в язык ассемблера.
В программировании на языке ассемблера макрокоманды исполь-
зуются наиболее часто. Хотя видимых причин не применять макропро-
цессор в языках высокого уровня нет, там макрокоманды встречаются
довольно редко. Макроассемблер для IBM PC поддерживает
макрокоманды. Как мы уже отмечали, существует две версии
ассемблера. Малый ассемблер, ASM, не поддерживает это средство.
Полный ассемблер, MASM, допускает все макрооперации, которые обсуж-
даются в этой главе. Для использования MASM ваш персональный
компьютер должен иметь как миимум 96K оперативной памяти.
Простейшая макрокоманда, которую можно использовать как код
операции 8087, - FENI. Макроассеблер 8088 не распознает ключевого
слова FENI, которое в действительности является командой для 8087.
Фиг. 6.1 показывает два шага макро-процесса: определение
макрокоманды FENI и ее последующий вызов в программе. Фиг.6.1
состоит из двух частей: часть (a) - это исходный файл для
программы, а часть (b) содержит листинг ассемблера для нее. Два
варианта на Фиг. 6.1 разделены, чтобы показать, какой из них
написан программистом, а какой сгенерирован макропроцессором.
Программа определяет макрокоманду с помощью ключевого слова
MACRO. На Фиг. 6.1 макроопределение выглядит так:
FENI MACRO
;---- Тело макрокоманды
ENDM
Оператор MACRO является кодом псевдооперации. Эта конкретная псев-
дооперация сообщает ассемблеру, что начинается определение макроко-
манды. В поле имени операции указано это имя, которое программа
приписывает определяемой макрокоманде, в нашем случае FENI. Команды
PAGE ,132
TITLE Фиг. 6.1 Макрокоманда
FENI MACRO
DB 0DBH, 0E0H
ENDM
CODE SEGMENT
ASSUME CS:CODE
FENI
CODE ENDS
END
Фиг. 6.1 (a) Исходный файл для программы
Microsoft (R) Macro Assembler Version 5.00 1/1/80 04:02:38
Фиг. 6.1 Макрокоманда Page 1-1
PAGE ,132
TITLE Фиг. 6.1 Макрокоманда
FENI MACRO
DB 0DBH, 0E0H
ENDM
0000 CODE SEGMENT
ASSUME CS:CODE
FENI
0000 DB E0 1 DB 0DBH, 0E0H
0002 CODE ENDS
END
Фиг. 6.1 (b) Листинг ассмблера программы
Фиг.6.1 Макроопределение.(a) исходный файл; (b) листинг ассемблера.
(или действия ассемблера), которые будут заменять имя макрокоманды,
следуют за строкой заголовка. Наконец, ключевое слово ENDM
указывает ассемблеру на конец определения. Текст между операторами
MSCRO и ENDM называется телом макрокоманды. На Фиг. 6.1 телом
макрокоманды FENI является оператор определения байтов. Поскольку в
8088 нет команды, которая соответствовала бы команде FENI код
машинного языка для этой команды должен состять из операторов DB.
Важно заметить, что во время определения макрокоманды код
машинного языка еще не генерируется. Это можно утверждать, потому
что колонки адреса и данных в листинге ассемблера пусты. Когда
ассемблер впервые встречае макроопределение, он сохраняет его для
дальнейшего использования. Затем программа на Фиг. 6.1 привлекает
макрокоманду FENI. Программист использует имя макрокоманды FENI как
если бы это был код оперции ассемблера типа CLD или DAA, а
ассемблер обращается к сохраненному определению макрокоманды FENI.
Ассемблер берет текст из тела макроопределения и помещает его в той
же позиции транслируемой программы. Знак "+", появляющийся слева от
оператора DB в распечатке ассемблера, указывает на то, что эта
строка вставлена макропроцессором. Если сравнить исходный текст с
ассемблируемым, вы увидите в исходном тексте только команду FENI, в
то время как на листинге ассемблера за командой FENI следует тело
макрокоманды. В данном случае оно представлено одним оператором DB.
Этот простой пример демонстрирует большие возможности
макропроцессора. Возникла необходимость в коде операции FENI,
который не предусмотрен ассемблером. При отсутствии механизма
макрокоманд программист был бы вынужден вместо операции FENI каждый
раз записывать ее код:
DB 0DBH, 0E0H
Имея же в распоряжении такой механизм, можно определить
макрокоманду FENI и в дальнейшем в этой же программе использовать
как код операции только ее. Для использования подобных макрокоманд
есть две серьезные причины. Во-первых, они облегчают написание
программы. Во-вторых, при чтении текста программы оператор FENI,
выглядит гораздо более осмысленным, чем DB 0DBH,0E0H.
Макрокоманду можно сравнить с подпрограммой. Подпрограмма - это
участок программы, определяемый в единственном месте программы,
Программа может передать управление подпрограмме из любой своей
точки. Использование подпрограмм экономит время написания и объем
памяти, занимаемый программой. Вместо того, чтобы каждый раз
переписывать команды подпрограммы, когда необходимо ее выполнение,
вы вставляете ее вызов. Подпрограмма выполняет свою определенную
функцию, и управление возвращантся в точку вызова.
Макрокоманда точно также определяется в ассемблируемой
программе в единственном месте. После того, как макрокоманда
определена, ее можно привлечь ("вызвать") в любой точке
транслируемой программы. Использование макрокоманды экономит время
составления программ и место, занимаемое исходным файлом. Вместо
того, чтобы всякий раз, когда в них возникает необходимость,
переписывать входящие в макрокоманду команды, в программу
вставляется вызов макрокоманды. Ассемблер генерирует заданные в
определении команды и переходит к обработке следующего кода
операции.
Разница между макрокомандой и подпрограммой заключается в
моменте их использования. Макрокоманда является операцией текстовой
обработки. Она определяется и "выполняется" во время
ассемблирования. Выполнение макрокоманды состоит в замене имени
макрокоманды текстом, составляющим тело макрокоманды. Подпрограмма
же хотя и определяется при ассемблировании, но выполняется не
раньше, чем сама программа. Мы будем говорить, что макрокоманда
выполняется при ассемблировании, а подпрограмма - во время
выполнения программы.
Лучший способ отличить макрокоманду от подпрограммы - это
запомнить, когда они проявляются. На самом деле, макропроцессор не
является атрибутом языка программирования. Допустим, вы - адвокат,
и составляете завещания для множества разных людей. Так как
завещания часто похожи, то вы могли бы задать набор
"макрозавещаний", которые будут содержать совпадающие части
составляемых завещаний. Первая часть завещания, где перечислены
участвующие в нем стороны, будет уникальной. Оставшаяся часть будет
состоять из различных макрозавещаний, охватывающих стандартные
куски завещаний. Результатом работы "процессора завещаний" будет
текстовый документ. Макрозавещания раскрываются и образуют
стандартную часть завещания. Вам остается только заполнить
переменные фрагменты документа между макрозавещаниями.
Но если макрокоманды и подпрограммы во многих отношениях так
похожи, то зачем использовать вместо процедуры макрокоманду?
Действительно, во многих случаях применима любая из них.
Последовательность команд может быть определена и как макрокоманда,
и как подпрограмма. Когда вам потребуется эта последовательность,
вы можете вызвать соответственно макрокоманду или подпрограмму.
Какую именно - зависит от того, как вы определили данную
последовательность команд. Выбор последнего задается соображениями
времени выполнения программы и объема занимаемой ею памяти. В
большинстве случаев использование макрокоманд приводит к более
длинным программа, т.е. для реализации одной и той же функции
требуется больше байтов объектного кода. Однако такая программа
выполняется быстрее так как отсутствуют временные издержки,
связанные с вызовом подпрограммы и возвратом в программу каждый
раз, когда требуется данная последовательность команд. Для
минимизации размера программы слудет использовать подпрограммы.
Чтобы иметь программу с максимальным быстродействием, вы
пользуетесь макрокомандами.
В случае макрокоманды FENI на Фиг. 6.1, выбор в пользу
макрокоманды очевиден. Здесь соответствующий участок программы в
качестве макрокоманды не только выполняется быстрее нежели, в
качестве подпрограммы, но и занимает меньше памяти. Команда CALL
для близкой процедуры требует три байта. Макрокоманды FENI - только
два байта. В случае макрокоманд для процессора 8087 для реализации
тех же функций через процедуры потребовалось бы больше байтов
объектного кода. Кроме того использование макрокоманд сокращает
время выполнения программы.