@WritToFil MACRO ;;определить макро
mov ah,40h ;;функция DOS по записи в файл
int 21h ;;вызов DOS
ENDM ;;конец макро
Теперь вместо того, чтобы каждый раз, когда мы хотим записать
символ в файл, переписывать команды MOV и INT, мы можем использо-
вать макро WritToFil.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ Сравнительные характеристики макросов и подпрограмм ¦
¦ ¦
¦ При помощи подпрограмм мы можем сделать то же самое, что и¦
¦при помощи макро, однако оформление небольших программных кус-¦
¦ков в подпрограммы не эффективно. Разница между макро и под- ¦
¦программой заключается в том, что макро вставляет нужный прог-¦
¦раммный код непосредственно в файл исходного текста, в то время¦
¦как подпрограмма размещается где-нибудь в другом месте, и для¦
¦выполнения ее программного кода мы должны осуществлять переход¦
¦по месту ее расположения. Другими словами, использование макро-¦
¦сов для создания повторяющегося линейного кода ликвидирует нак-¦
¦ладные расходы при выполнении программы, обусловленные вызовом¦
¦и возвратом из подпрограмм. ¦
¦ Мы используем макросы вместо подпрограмм из тех же соображе-¦
¦ний, что для короткого разговора обращаемся к кому-либо по те-¦
¦лефону вместо поездки к нему через весь город - потери времени¦
¦при переходе по другому адресу не оправдываются краткостью на-¦
¦шей задачи. Кроме того, макрокод имеет тенденцию к уменьшению¦
¦своих размеров, так как он добавляется к программе всякий раз,¦
¦когда должен использоваться. Если он получается слишком длин-¦
¦ным, его следует оформить в подпрограмму. Что значит "Слишком¦
¦длинный"? Это зависит от накладных расходов, необходимых на вы-¦
¦зов подпрограммы, от того, как часто используется функция, и от¦
¦отношения значения памяти к скорости выполнения программы. ¦
¦ Макросы работают быстрее, так как не требуют сохранения ре-¦
¦гистров,помещения в стек параметров и т.д. Однако частые повто-¦
¦рения коротких макросов могут занимать значительную память в¦
¦объектных и исполнимых файлах. Сначала напишите макрос и, если¦
¦окажется, что он становится неуправляем, перепишите его в под-¦
¦программу. Позже мы увидим,как можно оформить вызов подпрограмм¦
¦в виде макро. ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
- 1-51 -
Условные макросы
Приведенный выше пример макрокода слишком прост, поэтому раз-
решите несколько "приодеть его". Предположим, что для целей от-
ладки мы хотели бы писать наши символы на экран, а не в файл.
Тогда мы можем переписать макро следующим образом:
@WritToFil MACRO EКOFLAG ;;определить INCHRIF с
;;аргументом EKOFLAG
IFIDN , ;;если аргумент EKOFLAG
;;идентичен трем буквам
;;EKO, ассемблировать
;;следующую строку
mov ah,06h ;;функция DOS по записи в
;;стандартный вывод
ELSE ;;если EKOFLAG не идентично трем буквам
;; EKO, ассемблировать следующую строку
mov ah,40h ;;функция DOS по записи в файл
int 21h ;;вызов DOS
ENDM ;;конец макро
В данном случае MASM анализирует аргумент EKOFLAG с целью оп-
ределения, что вставлять: mov ah,06h или mov ah,40h:
@WritToFil EKO ;здесь MASM подставляет MOV AH,06 и
;INT 21H
.
.
. ;так как аргумент идентичен EKO
@WritToFil NOEKO ;MASM подставляет MOV AH,40H и
;INT 21H
.
.
. ;так как аргумент не идентичен EKO
Заметим, что в предыдущем примере вместо NOEKO мы могли бы ис-
пользовать PHUBAH или что-нибудь еще, так как основная мысль зак-
лючается в том, чтобы аргументом не было EKО. Спеллинг параметра
довольно произвольный. Это гарантирует возможность ошибки, если
мы забудем его и напишем @WritToFil ECHO. Такая запись лишает нас
появления эха на экране, так как вместо EKO мы указали ECHO. Мы
можем исключить возможность появления такой ошибки, ограничив се-
бя использованием EKO или NOEKO:
@WritToFil MACRO EKOFLAG ;;определить INCHRIF c аргументом
;;EKOFLAG
IFIDN , ;;если EKOFLAG = EKO,
- 1-52 -
;;ассемблировать след-ую строку
mov ah,06h ;;функция DOS по записи в
;;стандартный файл
ELSE ;;в противном случае
IFIDN , ;;если EKOFLAG = NOEKO,
;;ассемблировать
mov ah,40h ;;функция DOS по записи в файл
ELSE ;;если аргумент не соответствует,
;; то
.ERR ;;выдать ошибку ассемблирования
ENDIF ;;конец проверки условия
int 21h ;; вызов DOS
ENDM ;; конец макро
Вложенные макросы
Рассмотренный нами макрос использует функцию DOS по записи
символов на стандартное устройство вывода или в файл. Однако мы
можем захотеть проверить, была ли нажата клавиша для прерывания
вывода, и если это не так, продолжить обработку. Функция DOS 0Bh
проверяет, была ли нажата клавиша, возвращая AL = 0FFh, если сим-
вол доступен, и AL = 00, если символ не доступен. Мы не можем на-
писать макро chkchr и затем вызывать его из нашего макроса
WritToFil:
@ChkChr MACRO ;;определить макро @ChkChr
mov ah,0Bh ;;проверить стандартный ввод
int 21h ;;вызов DOS
ENDM ;;конец макро
;;
@WritToFil MACRO WAITFLAG,EKOFLAG ;; 2 аргумента
LOCAL bye ;;определить формальный адрес
IFNB ;;если поле для WAITFLAG не
;;пусто, ассемблировать
;;следующее
@ChkChr ;;выявить, ожидается ли символ
cmp al,0 ;; al = 0 => символ не ожидается
je bye ;;если символа нет, продолжить
ENDIF ;;конец проверки условия
IFIDN , ;;если EKOFLAG=EKO, ас-
;;семблировать
MOV AH,06H ;;функция DOS по записи
;;в стандартный вывод
ELSE ;;в противном случае
IFIDN <ЕKOFLAG>, ;;если EKOFLAG=NOEKO,
;;ассемблировать
MOV AH,40H ;;функция DOS по записи в файл
ELSE ;;если аргумент не соответствует
.ERR ;;выдать ошибку ассемблирования
ENDIF ;;конец проверки условия
int 21h ;;вызов DOS
bye:
ENDM ;;конец макро
Обсудим некоторые возможности новой версии WritToFil. Директи-
ва LOCAL сообщает MASM, что метка bye является формальной мет-
- 1-53 -
кой,которую MASM заменяет на реальную всякий раз, когда макро вы-
зывается из программы. Это устраняет проблему использования одной
и той же метки в программе дважды, что вызвало бы ошибку ассемб-
лирования. MASM ассемблирует макро, используя в первый раз метку
??0000, во второй раз - ??0001 и т.д. до ??FFFFh, что позволяет
вызвать макро в одной программе до 65536 раз. Директива LOCAL
должна следовать сразу же за директивой MACRO - перед ней не мо-
жет стоять даже комментарий! Конструкция IFNB WAITFLAG сообщает
MACRO о необходимости ассемблирования следующих трех строк только
тогда, когда аргумент WAIT-FLAG не пуст. В противном случае прог-
раммный код включен не будет, и первой ассемблируемой строкой бу-
дет одна из строк блока IFIDN. Это предоставляет нам возможность
генерации программного кода, который или будет включаться в ито-
говую программу, или после проверки ключей будет опускаться. Опе-
ратор IFNB проверяет существование WAITFLAG не по спеллингу, та-
ким образом мы можем вызвать макро одной из следующих команд:
@WritToFil WAIT,EKO
@WritToFil WAITE,EKO
@WritToFil NoWate,EKO
@WritToFil FOOBAH,EKO
и затем генерировать код, который "не ждет" ввода. Заметим также,
что мы получили вложенность макро, когда одно макро вызывает дру-
гое.
Несколько слов о возможностях макро
Вместо использования параметра WAITFLAG для определения, ас-
семблировать программный код для его включения или нет, мы также
можем использовать глобальную опцию, позволяющую осуществлять
этот выбор во время ассемблирования. Например, мы хотим проверить
нажатие клавиши, если находимся в режиме отладки или если уста-
новлен параметр WAITFLAG, но ни при каких-либо других условиях.
Новое макроописание таково:
@WritToFil MAСRO WAITFLAG,EKOFLAG
LOCAL bye ;;определить формальный адрес
;;макро приема символа из стандартного ввода
;;2 аргумента: WAITFLAG и EKOFLAG определяют,
;;ждать символ или отобразить ввод
.ХCREF ;;подавить выдачу перекрестных ссылок
;;локальных меток и т.д.
x = 0 ;; х - индикатор
IFNDEF DEBUG ;;если параметр DEBUG не опре-
x = 1 ;;делен, установить флаг в 1
ENDIF ;;конец проверки условия
IFNB ;;если поле для WAITFLAG
x = 2 ;;не пусто, флаг = 2
ENDIF ;;конец проверки условия
IF (х EQ 1) or (x eq 2) ;;если не определен
;;DEBUG или не пуст WAITFLAG
@ChkChr ;;проверить, ожидается ли символ
cmp al,0 ;;al = 0 => символ не ожидается