Непосредственный 20 локально 0 неверно
Регистровый 20 локально 0 неверно
Метка данных 22 локально Х количество
байтов
"Близкая" метка 21 локально FFFF "близкая"
метка
"Далекая" метка 21 локально FFFE "далекая"
метка
Код операции MASM 00 неверно 0 неверно
Нонсенс 00 неверно 0 неверно
-------------------------------------------------------------
Список примеров можно продолжить. Хотя .TYPE и распознает име-
на различных регистров, он не распознает регистровые конструкции
типа [BX] или ARRAY[BX][SI]. Односимвольная константа типа A рас-
познается оператором .TYPE как переменная, определенная локально.
Во время первого прохода ассемблера ссылка вперед никак не
распознается. IFDEF в качестве результата возвращает "не опреде-
ленно", .TYPE возвращает "неверно", а TYPE возвращает нулевую
длину. К ссылкам вперед может быть применено только одно правило:
если возможно, не применять их вообще.
Фазовые ошибки и некоторые особенности MASM
С использованием операторов MASM связана одна важная особен-
ность. MASM является двухпроходным ассемблером, назначающим зна-
чения символам на первом проходе и затем вычисляющим символы при
втором проходе. Программные метки и метки данных являются симво-
лами. Их значения определяются во время первого прохода, а затем
используются на втором проходе для генерации программного кода.
Рассмотрим следующее дерево событий. При обнаружении ссылки
вперед MASM не распознает метку на первом проходе и не способен
определить ее тип. Попытка ссылки на этот символ вызывает появле-
ние сообщения об ошибке "Символ не определен". MASM обнаруживает
эту ошибку на первом проходе, но подавляет ее и продолжает ас-
семблирование. MASM способен подавить ошибку, предположив тип
символа из контекста, в котором он появился. Если это предположе-
ние неверно, MASM может закончить свою работу, выдав сообщение
"фазовая ошибка между проходами", или может укоротить команду и
поместить после нее команду nop (нет операции), как заполнитель.
Существует два способа устранения фазовых ошибок при нормаль-
ной работе MASM. В большинстве случаев MASM способен определить
тип операндов из контекста. Программисты редко применяют переходы
в сегменте данных и обычно не добавляют программных адресов. Для
тех специальных случаев, когда MASM делает ошибочные предположе-
ния, программист может определить порядок работы ассемблера, ис-
пользуя оператор перекрытия PTR (указатель). С помощью оператора
- 1-24 -
PTR программист может явно указать тип ссылки вперед, и, следова-
тельно, MASM не сделает ошибочного предположения. Однако, пытаясь
выработать многоцелевые команды макро, мы значительно увеличиваем
вероятность ошибочного выражения. Если наши многоцелевые команды
предназначены для обработки любых классов операндов, точное зна-
чение такого класса трудно определить из контекста. Кроме того,
хотя использование PTR и может помочь в некоторых подобных случа-
ях (как мы увидим в макро @PushOP), оно не достигает цели осво-
бождения программиста от излишних деталей.
Проанализировав то, как ошибочное предположение вырабатывает
фазовую ошибку, мы с большей вероятностью можем устранить ее по-
явление. Так как фазовые ошибки являются результатом смены неко-
торыми символами (например, меток) своих значений между прохода-
ми, важно, чтобы макросы вырабатывали один и тот же объем
программного кода на каждом проходе. Это предохраняет значения
меток, размещенных после макро, и объясняет, почему MASM забивает
укороченные команды инструкциями NOP. От одного прохода к другому
должны также оставаться постоянными программные метки.
Сравнение строк. Пример
К сожалению, способность оператора .TYPE распознавать непос-
редственные операнды и регистры значительно уменьшается при выяс-
нении типа операндов макро. Так как особенно важно знать, являет-
ся ли аргумент макро регистром, мы должны сконструировать способ
выявления этого. Определение того, является ли аргумент регист-
ром, обычно полезно только вместе с неявным предположением, что
если он не является регистром и не является определенной адресной
ссылкой, то предположительно является ссылкой на непосредственные
данные.
Это хорошо сочетается с использованием условного ассемблирова-
ния на базе директив IRP и IRPC. Целью в данном случае является
определение принадлежности аргумента макро к какому-либо набору.
Для решения задачи - является ли аргумент регистром, используется
сопоставление строк. Так как оператор .TYPE может определить
только то, что эти регистры объявлены локально и абсолютны, для
явной проверки имени регистра применяется макро сопоставления
строк. Эту функцию выполняет приведенное в Листинге 1-8 макро
?reg.
Листинг 1-8. Макро сопоставления имен регистров - ?reg
-----------------------------------------------------------------
FALSE EQU 0
TRUE EQU 0FFFFh
;;
;;**** ?REG - Определить, является ли аргумент регистром
;;
?reg MACRO arg
?isr8 = FALSE
?isr16 = FALSE
IRP reg,
IFIDN <&®>,<&arg>
?isr16 = TRUE
EXITM
- 1-25 -
ENDIF
ENDM ;; конец секции IRP
;; Если сравнились, остановиться здесь
IF (?isr16)
EXITM
ENDIF
;; Если еще не сравнились, продолжить далее
IRP reg,
IFIDN <&®>,<&arg>
?isr8 = TRUE
EXITM
ENDIF
ENDM ;; конец секции IRP
;; Если сравнились, остановиться здесь
IF (?isr8)
EXITM
ENDIF
;; Если не сравнились, попробовать прописные имена регистров
IRP reg,
IFIDN <&®>,<&arg>
?sir16 = TRUE
EXITM
ENDIF
ENDM ;; конец секции IRP
;; Если сравнились, остановиться здесь
IF (?isr16)
EXITM
ENDIF
;; Если еще не сравнились, попробовать еще
IRP reg,
IFIDN <&®>,<&arg>
?isr8 = TRUE
EXITM
ENDIF
ENDM ;; конец секции IRP
ENDM ;; конец макроописания
-----------------------------------------------------------------
Cердцевиной этого макро, как и любого макро сопоставления, яв-
ляются команды:
IRP reg,
IFIDN <&®>,<&arg1>
?isr16 % TRUE
Интерпретировать эти строки можно так:
Для reg равного ax...ss выполнить . . .
Если reg равно аргументу arg . . .
Аргумент есть регистр!
Здесь следует остановиться на двух интересных моментах.
Во-первых, необходимо явно проверять имена регистров, написанные
как малыми, так и большими буквами. Директива условного ассембли-
рования IFIDN сравнивает строки на точное соответствие. Несмотря
на все усилия, макро ?reg не полно. Оно не сопоставляет имена ре-
гистров, состоящие из одной большой и одной малой буквы (например
- 1-26 -
"aL"). Во-вторых, необходимо проводить две проверки: одну для
16-битовых регистров и одну для 8-битовых регистров. В данной ре-
ализации наличие двух отдельных проверок не приносит нам никакой
выгоды, однако такие проверки окажутся полезными в следующем при-
мере.
Макро ?reg имеет два дополнительных синтаксических элемента.
Один - директива завершения макро EXITM. Эта директива использу-
ется для завершения работы макро ?reg при обнаружении совпадения.
Менее очевидно использование двойного амперсанда в операторе
IFIDN. Согласно Руководству по MASM фирмы Майкрософт пользователь
должен "указывать столько амперсандов, сколько имеется уровней
вложенности". Столь лаконичное выражение не вносит ясности в ре-
шение проблемы. "Уровни вложенности" относятся не к глубине бло-
ков, где появляется ссылка, а к глубине блоков, где находится ее
описание. Таким образом, arg1 приводится только с одним амперсан-
дом, в то время как reg, описание которого находится во вложенном
блоке, требует наличия двух амперсандов. Фирма Майкрософт не ут-
верждает, что это предел разрешенного количества уровней вложен-
ности или количества требуемых амперсандов. В тех случаях, когда
казалось бы необходимо указывать множество амперсандов, попытки
написания примеров, позволяющих выявить правильное функционирова-
ние, не увенчались успехом.
Приведенный Листинг 1-9 с макро ?reg показывает, что это макро
выполняет возложенную на него функцию. Заметьте,что регистр bР,
который распознается MASM, отбрасывается макро ?reg. Это может
быть истолковано, как необходимость соблюдения строгого правила
ввода исходного текста программы с клавиатуры.
Листинг 1-9. Тест макро сравнения имен регистров - ?reg
-----------------------------------------------------------------
?reg ax ; "AX" - регистр ?
FFFF dw ?isr16 <--- TRUE
?reg CS ; "CS" - регистр ?
FFFF dw ?isr16 <--- TRUE
?reg zork ; "ZORK" - регистр ?
0000 dw ?isr16 <--- FALSE
0000 dw ?isr8 <--- FALSE
?reg 01234h ; "1234" - регистр ?
0000 dw ?isr16 <--- FALSE
0000 dw ?isr8 <--- FALSE
?reg bР ; "BP" - регистр ?
0000 dw ?isr16 <--- FALSE