0000 dw ?isr8 <--- FALSE
-----------------------------------------------------------------
Синтаксический анализ аргументов макро
С помощью макро, распознающего имена регистров, можно реализо-
вать обобщенное макро PUSH, которое мы назовем @PUSHOP (протолк-
нуть операнд). (Замечание: мы рассматриваем имя pusha для " про-
толкнуть все", но PUSHA является кодом операции для чипов 186,188
и 286 фирмы Intel. Использование его для макро может ограничить
совместимость снизу вверх. Конечно, Вы всегда можете использовать
команду PUSHA в макро pusha для микропроцессоров 8086 и 8088).
Как упоминалось ранее относительно типа операнда, который не
- 1-27 -
определен и не является регистром, необходимо делать некоторые
предположения. Для макро @PUSHOP предположим, что неизвестные
операнды являются ссылками на непосредственные данные. @PUSHOP
ссылается на макро ?reg, и макро ?reg должно быть включено в
программу с @PUSHOР. Макро @PUSHOP см. в Листинге 1-10.
@PUSHOP использует возможность макро ?reg различать 16- и 8-
битовые регистры. Так как команда PUSH не обрабатывает 8-битовый
регистр, для получения первого символа имени регистра использует-
ся директива IRPC. Затем @PUSHOP добавляет х, формируя ,таким об-
разом, имя 16-битового регистра, которое приемлемо для PUSH. За-
метим, что в этом операторе снова необходимо использование
удвоенного амперсанда, причем с обеих сторон формального аргумен-
та, так как сцепление строк возникает с обоих концов.
Для тех случаев, когда предполагается наличие непосредственных
данных, вызывается макро @PushIm. Это макро более сложное , чем
минимально необходимое, так как предполагается, что для передачи
непосредственных данных в стек нельзя использовать регистры.
Вместо этого макро использует указатель базы (BP) на адрес стека.
После сохранения BP и AX в стеке @PushIm заносит непосредственные
данные поверх содержимого AX, обменивая их со старым содержимым
BP. После восстановления содержимого BP макро извлекает содержи-
мое AX, выталкивая его из стека. Макро @PushIm приведено в Лис-
тинге 1-11.
Листинг 1-10. Обобщение макро PUSH - @PushOp
----------------------------------------------------------------
;; **** @PushOp макро с обобщенным операндом команды PUSH
;; Если операнд определен, он может быть:
;; регистром
;; ссылкой на данные
;;
;; Если операнд не определен, он полагается ссылкой на
;; непосредственные данные
@PushOp Macro arg
.SALL
IFDEF &arg ;; операнд определен ...
?argtyp = .type &arg ;; выявить его тип
IF ((?argtyp and 3) EQ 2) ;;операнд - данные
?argsiz = ((type &arg) + 1)/2 ;; получить длину
;; в словах
?argoff = 0 ;; установить смещение в 0
REPT ?argsiz ;; повторить для каждого слова
?argadd = word ptr &arg + ?argoff ;;получить
.XALL тип ptr
push ?argadd ;;продв-ть непоср.в память
.SALL
?argoff = ?argoff + 2 ;;след-ее слово данных
ENDM
ENDIF
IF ((?argtyp AND 3) EQ 1) ;;операнд - программа
@PushImOff &arg ;;продвинуть смещение метки
ENDIF
IFE (?argtyp and 3) ;;операнд - абс.значение
?reg &arg
IF (?isr16) ;;операнд - регистр 16
.XALL
- 1-28 -
push &arg ;;продвинуть непосред.
.SALL
ELSE
IF (?isr8) ;;операнд - регистр 8
IRPC chr1,&arg1
.XALL
push &&chr1&&x ;;сохранить короткий рег.
.SALL
EXITM
ENDM
е ELSE ;;предположить непосред.данны
@PushIm &arg ;;продвинуть непосред. данные
ENDIF
ENDIF
ENDIF
ELSE ;;продвинуть непосред.данные
@PushIm &arg
ENDIF
ENDM ;;конец макроописания
-----------------------------------------------------------------
Листинг 1-11. Макро проталкивания непосредственных данных -
@PushIm
----------------------------------------------------------------
;; **** @PushIm макро проталкивания непосредственных данных
@PushIm Macro arg
.XALL
push bp ;;сохранить указатель базы
mov bp,sp ;;переместить указатель стека в BP
push ax ;;сохранить накопитель
mov ax,&arg ;;получить непосредственные данные
xchg [bp],ax ;;обменять старый BP и непосред. данные
mov bp,ax ;;восстановить старый BP из AX
pop ax ;;восстановить накопитель
.SALL
ENDM ;;конец макроописания
----------------------------------------------------------------
Эта операция свертывания может быть сведена к обмену элемента-
ми внутри стека. Однако ведение подобных игр со стеком опасно.
Если Ваш компьютер поддерживает прерывания, для предохранения це-
лостности данных стека эта операция должна выполняться только че-
рез запрещение прерываний.
Для тех случаев, когда делается попытка протолкнуть в стек
программные адреса, мы предполагаем, что программист желает сох-
ранить действительное смещение метки. Для проталкивания в стек
смещения метки, как непосредственных данных, было использовано
макро @PushImOff. Оно отличается от макро @PushIm только исполь-
зованием команды:
mov ax,offset &arg ,
что противоположно простому перемещению в макро @PushIm. Макро
@PushImOff приведено в Листинге 1-12.
- 1-29 -
Листинг 1-12. Макро продвижения в стек смещения
непосредственных данных - @PushImOff
---------------------------------------------------------------
;; **** @PushImOff макро продвижения смещения непосред.данных
@PushImOff MACRO arg
.XALL
рush bp ;;сохранить указатель базы
mov bp,sp ;;переместить указатель стека в BP
push ax ;;сохранить накопитель
mov ax,offset &arg ;;получить смещение
;;непосред.данных
xchg [bp],ax ;;обменять старый BP и непоср.данные
mov bp,ax ;;восстановить старый BP из AX
pop ax ;;восстановить накопитель
.SALL
ENDM ;;конец макроописания
----------------------------------------------------------------
Последним дискретным случаем, который распознает @PushOp, яв-
ляется попытка прямого проталкивания в стек данных памяти. Слож-
ность в этом случае заключается в том, что стек воспринимает
только 16-битовые данные. Используя директиву перекрытия PTR,
можно убедить MASM сохранять нужные данные пословно. @PushOp со-
держит цикл, который повторяет эту операцию для каждого слова
сохраняемых данных, увеличивая адрес на два при каждом проходе
цикла. Таким образом можно сохранять в стеке двойные слова, чет-
верные слова, 10-байтовые данные и структуры данных.
Наконец, заметим, что макро @PushOp все еще не обрабатывает
ссылки, содержащие сложную адресацию (2[BP] и т.д.). В случае не-
обходимости Вы можете применить подобные проверки, используя мак-
родирективу IRPC для выявления в аргументах квадратных скобок,
адресации "база + индекс" и "база + смещение".
Итоговый тест макро @PushOp приведен в Листинге 1-13, который
содержит результат нескольких вызовов макро @PushOp.
Последняя операция листинга, где @PushOр обрабатывает 4-слов-
ную переменную, не может быть изъята. Каждое проталкивание имеет
один и тот же аргумент. Однако из этого красивого листинга не
видно, что каждая его строка имеет перемещаемый адрес (0000 для
первого слова, 0002 для второго и т.д.). К сожалению, мы не можем
в данной книге привести 132-колоночные листинги, и, если Вы хоти-
те проверить, попробуйте получить такой листинг сами.
Этот пример особенно полезен тем, что демонстрирует одну об-
ласть, где применение макро почти всегда предпочтительнее приме-
нения подпрограмм. Что касается обработки стека (как в @PushIm и
@PushImOff), макросы способны выполнять эти операции без "угро-
зы" влияния команды CALL на стек. Это особенно важно при переме-
щении или изъятии данных из стека, так как подпрограмма не может
изменить вершину стека и осуществить возврат, не вызвав серьезных
осложнений.
Некоторые предупреждения
по использованию условного ассемблирования
и макросов в MASM
Применяя макросы, мы старались забыть, что они вырабатывают
- 1-30 -
построчный код и не вызывают программ. Хотя это и представляет
некоторые преимущества при быстрой генерации программного кода и
освобождает нас от ограничений по использованию стека, результа-
том линейной программы является более объемный код. Как програм-
мист, Вы должны решить, когда целесообразно вызывать быстро рабо-
тающее макро, а когда - подпрограмму, экономящую память, но
имеющую разветвленную структуру. В общем случае используйте мак-
ро, когда программный код невелик, а время критично, или когда Вы
хотите конфигурировать программу, настроив ее на конкретные усло-
вия. Используйте подпрограмму, когда программный код велик и его
нужно расположить в одном месте (так, чтобы его можно было легко
изменить).
Другим тонким моментом работы макро является использование
символов. Вы помните, что символы определяются при помощи опера-