|
|
|
|
Синтаксический анализ аргументов макро
С помощью макро, распознающего имена регистров, можно реализовать обобщенное макро PUSH, которое мы назовем @PUSHOP (протолкнуть операнд).
(Замечание: мы рассматриваем имя pusha для " протолкнуть все", но PUSHA является кодом операции для чипов 186,188 и 286 фирмы Intel. Использование его
для макро может ограничить совместимость снизу вверх. Конечно, Вы всегда можете использовать команду PUSHA в макро pusha для микропроцессоров 8086 и
8088).
Как упоминалось ранее относительно типа операнда, который не определен и не является регистром, необходимо делать некоторые предположения. Для
макро @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
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 только использованием команды:
что противоположно простому перемещению в макро @PushIm. Макро @PushImOff приведено в Листинге 1-12.
Листинг 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 на стек. Это
особенно важно при перемещении или изъятии данных из стека, так как подпрограмма не может изменить вершину стека и осуществить возврат, не вызвав
серьезных осложнений.
|
|