Макросредства

------------------------------------------------------------

Макросредства

Цель: Объяснить определение и использование ассемблерных макрокоманд.

ВВЕДЕНИЕ

------------------------------------------------------------

Для каждой закодированной команды ассемблер генерирует одну команду на машинном языке. Но для каждого закодированного оператора компиляторного языка Pascal или C генерируется один или более (чаще много) команд машинного языка. В этом отношении можно считать, что компиляторный

язык состоит из макро операторов.

Ассемблер MASM также имеет макросредства, но макросы здесь определяются программистом. Для этого задается имя макроса, директива MACRO, различные ассемблерные команды, которые должен генерировать данный макрос и для завершения макроопределения - директива MEND. Затем в любом месте программы, где необходимо выполнение определенных в макрокоманде команд, достаточно закодировать имя макроса. В результате ассемблер сгенерирует необходимые команды.

Использование макрокоманд позволяет:

- упростить и сократить исходный текст программы;

- сделать программу более понятной;

- уменьшить число возможных ошибок кодирования.

Примерами макрокоманд могут быть операции ввода-вывода, связанные с инициализацией регистров и выполнения прерываний преобразования ASCII и двоичного форматов данных, арифметические операции над длинными полями, обработка строковых данных, деление с помощью вычитания.

ПРОСТОЕ МАКРООПРЕДЕЛЕНИЕ

------------------------------------------------------------

Макроопределение должно находиться до определения сегмента. Рассмотрим пример простого макроопределения по имени INIT1, которое инициализирует сегментные регистры для EXE-программы:

INIT1 MACRO ;Начало

ASSUME CS:CSEG, DS:DSEG, SS:STACK, ES:DSEG

PUSH DS ;

SUB AX,AX ;

PUSH AX ;Тело

MOV AX,DSEG ;макро/

MOV DS,AX ;

MOV ES,AX ;

ENDM ;Конец

Директива MACRO указывает ассемблеру, что следующие команды до директивы ENDM являются частью макроопределения.

Имя макрокоманды - INIT1, хотя здесь возможны другие правильные уникальные ассемблерные имена.

Директива ENDM завершает макроопределение.

Семь команд между директивами MACRO и ENDM составляют тело макроопределения.

Имена, на которые имеются ссылки в макроопределении, CSEG, DSEG и STACK должны быть определены где-нибудь в другом месте программы.

Макрокоманда INIT1 может использоваться в кодовом сегменте там, где необходимо инициализировать регистры.

Когда ассемблер анализирует команду INIT1, он сначала просматривает таблицу мнемокодов и, не обнаружив там соответствующего элемента, проверяет макрокоманды. Так как программа содержит определение макрокоманды INIT1 ассемблер подставляет тело макроопределения, генерируя необходимые команды - макрорасширение. Программа использует рассматриваемую макрокоманду только один раз, хотя имеются другие макрокоманды, предназначенные на любое число применений и для таких макрокоманд ассемблер генерирует одинаковые макрорасширения.

На рис.1 показана ассемблированная программа. В листинге макрорасширения каждая команда, помеченная слева знаком плюс (+), является результатом генерации макрокоманды. Кроме того, в макрорасширении отсутствует директива ASSUME, так как она не генерирует объектный код.

Предусмотрена возможность каталогизации макрокоманд в

библиотеки макроопределений и автоматическое включение их в любые программы.

------------------------------------------------------------

TITLE MACRO1 (EXE) Макрос для инициализации

; --------------------------------------------

INIT1 MACRO

ASSUME CS:CSEG,DS:DSEG,SS:STACK,ES:DSEG

PUSH DS

SUB AX,AX

PUSH AX

MOV AX,DSEG

MOV DS,AX

MOV ES,AX

ENDM ;Конец макрокоманды

; --------------------------------------------

0000 STACK SEGMENT PARA STACK 'Stack'

0000 20 [ ???? ] DW 32 DUP(?)

0040 STACK ENDS

; --------------------------------------------

0000 DSEG SEGMENT PARA 'Data'

0000 54 65 73 74 20 6F MESSGE DB 'Test of macro-instruction', 13

66 20 6D 61 63 72

6F 2D 69 6E 73 74

72 65 73 74 69 6F

6E 0D

001A DSEG ENDS

; --------------------------------------------

0000 CSEG SEGMENT PARA 'Code'

0000 BEGIN PROC FAR

INIT1 ;Макрокоманда

0000 1E + PUSH DS

0001 2B C0 + SUB AX,AX

0003 50 + PUSH AX

0004 B8 ---- R + MOV AX,DSEG

0007 8E D8 + MOV DS,AX

0009 8E C0 + MOV ES,AX

000B B4 40 MOV AH,40H ;Вывод на экран

000D BB 0001 MOV BX,01 ;Номер

0010 B9 001A MOV CX,26 ;Длина

0013 8D 16 0000 R LEA DX,MESSGE ;Сообщение

0017 CD 21 INT 21H

0019 CB RET

001A BEGIN ENDP

001A CSEG ENDS

END BEGIN

Macros:

N a m e Length

INIT1. . . . . . . . . . . . . . . . 0004

Segments and Groups:

N a m e Size Align Combine Class

CSEG . . . . . . . . . . . . . . . . 001A PARA NONE 'CODE'

DSEG . . . . . . . . . . . . . . . . 001A PARA NONE 'DATA'

STACK. . . . . . . . . . . . . . . . 0040 PARA STACK 'STACK'

Symbols:

N a m e Type Value Attr

BEGIN. . . . . . . . . . . . . . . . F PROC 0000 CSEG Length=001A

MESSAGE. . . . . . . . . . . . . . . L BYTE 0000 DSEG

------------------------------------------------------------

Рис.1. Пример ассемблирования макрокоманды.

ИСПОЛЬЗОВАНИЕ ПАРАМЕТРОВ В МАКРОКОМАНДАХ

------------------------------------------------------------

В предыдущем макроопределении требовались фиксированные имена сегментов: CSEG, DSEG и STACK. Для того, чтобы макро команда была более гибкой и могла принимать любые имена сегментов, определим эти имена, как формальные параметры:

INIT2 MACRO CSNAME,DSNAME,SSNAME ;Формальные параметры

ASSUME CS:CSNAME,DS:DSNAME,SC:SSNAME,ES:DSNAME

PUSH DS

SUB AX,AX

PUSH AX

MOV AX,DSNAME

MOV DS,AX

MOV ES,AX

ENDM ;Конец макроопределения

Формальные параметры в макроопределении указывают ассемблеру на соответствие их имен любым аналогичным именам в теле макроопределения. Все три формальных параметра CSNAME, DSNAME и SSNAME встречаются в директиве ASSUME, а параметр DSNAME еще и в последующей команде MOV.

Формальные параметры могут иметь любые правильные ассемблерные имена, не обязательно совпадающими именами в сегменте данных.

Теперь при использовании макрокоманды INIT2 необходимо указать в качестве параметров действительные имена трех сегментов в соответствующей последовательности.

Например, следующая макрокоманда содержит три параметра, которые соответствуют формальным параметрам в исходном макроопределении:

Макроопределение:

INIT2 MACRO CSNAME,DSNAME,SSNAME (формальные параметры)

Макрокоманда: | | |

INIT2 CSEG, DSEG, STACK (параметры)

Так как ассемблер уже определил соответствие между формальными параметрами и операторами в макроопределении, то теперь ему остается подставить параметры макрокоманды в макрорасширении:

- Параметр 1: CSEG ставится в соответствие с CSNAME в макроопределении. Ассемблер подставляет CSEG вместо CSNAME в директиве ASSUME.

- Параметр 2: DSEG ставится в соответствие с DSNAME в макроопределении. Ассемблер подставляет DSEG вместо двух DSNAME: в директиве ASSUME и в команде MOV.

- Параметр 3: STACK ставится в соответствие с SSNAME в макроопределении. Ассемблер подставляет STACK вместо SSNAME в директиве ASSUME.

Макроопределение с формальными параметрами и соответствующее макрорасширение приведены на рис.2.

------------------------------------------------------------

TITLE MACRO2 (EXE) Использование параметров

; ------------------------------------------------

INIT2 MACRO CSNAME,DSNAME,SSNAME

ASSUME CS:CSNAME,DS:DSNAME,SS:SSNAME,ES:DSNAME

PUSH DS

SUB AX,AX

PUSH AX

MOV AX,DSNAME

MOV DS,AX

MOV ES,AX

ENDM ;Конец макрокоманды

; ------------------------------------------------

0000 STACK SEGMENT PARA STACK 'Stack'

0000 20 [ ???? ] DW 32 DUP(?)

0040 STACK ENDS

; ------------------------------------------------

0000 DSEG SEGMENT PARA 'Data'

0000 54 65 73 74 20 6F MESSAGE DB 'Test of macro', '$'

66 20 6D 61 63 72

6F 24

000E DSEG ENDS

; ------------------------------------------------

0000 CSEG SEGMENT PARA 'Code'

0000 BEGIN PROC FAR

INIT2 CSEG,DSEG,STACK

0000 1E + PUSH DS

0001 2B C0 + SUB AX,AX

0003 50 + PUSH AX

0004 B8 ---- R + MOV AX,DSEG

0007 8E D8 + MOV DS,AX

0009 8E C0 + MOV ES,AX

000B B4 09 MOV AH,09 ;Вывод на экран

000D 8D 16 0000 R LEA DX,MESSGE ;Сообщение

0011 CD 21 INT 21H

0013 CB RET

0014 BEGIN ENDP

0014 CSEG ENDS

END BEGIN

------------------------------------------------------------

Рис.2. Использование параметров в макрокомандах.

Формальный параметр может иметь любое правильное ассемблерное имя (включая имя регистра, например, CX), которое в процессе ассемблирования будет заменено на параметр макрокоманды. Отсюда следует, что ассемблер не распознает регистровые имена и имена, определенные в области данных, как таковые. В одной макрокоманде может быть определено любое число формальных параметров, разделенных запятыми, вплоть до 120 колонки в строке.

КОММЕНТАРИИ

-----------------------------------------------------------

Для пояснений назначения макроопределения в нем могут находиться комментарии. Директива COMMENT или символ точка с запятой указывают на строку комментария, как это показано в следующем макроопределении PROMPT:

PROMPT MACRO MESSGE

; Эта макрокоманда выводит сообщения на экран

MOV AH,09H

LEA DX,MESSGE

INT 21H

ENDM

Так как по умолчанию в листинг попадают только команды генерирующие объектный код, то ассемблер не будет автоматически выдавать и комментарии, имеющиеся в макроопределении.

Если необходимо, чтобы в расширении появлялись комментарии, следует использовать перед макрокомандой директиву .LALL

("list all" - выводить все), которая кодируется вместе с лидирующей точкой:

.LALL

PROMPT MESSAG1

Макроопределение может содержать несколько комментариев, причем некоторые из них могут выдаваться в листинге, а другие - нет. В первом случае необходимо использовать директиву .LALL. Во втором - кодировать перед комментарием два символа точка с запятой (;;) - признак подавления вывода комментария в листинг.

По умолчанию в ассемблере действует директива .XALL, которая выводит в листинг только команды, генерирующие объектный код.

И, наконец, можно запретить появление в листинге ассемблерного кода в макрорасширениях, особенно при использовании макрокоманды в одной программе несколько раз. Для этого служит директива .SALL ("suppress all" - подавить весь вывод), которая уменьшает размер выводимого листинга, но не оказывает никакого влияния на размер объектного модуля.

Директивы управления листингом .LALL, .XALL, .SALL сохраняют свое действие по всему тексту программы, пока другая директива листинга не изменит его. Эти директивы можно размещать в программе так, чтобы в одних макрокомандах распечатывались комментарии, в других - макрорасширения, а в третьих подавлялся вывод в листинг.

Программа на рис.3 демонстрирует описанное выше свойство директив листинга. В программе определено два макроопределения INIT2 и PROMPT, рассмотренные ранее. Кодовый сегмент содержит директиву .SALL для подавления распечатки INIT2 и первого расширения PROMPT. Для второго расширения PROMPT директива .LALL указывает ассемблеру на вывод в листинг комментария и макрорасширения. Заметим, однако, что комментарий, отмеченный двумя символами точка с запятой (;;) в макроопределении PROMPT, не распечатывается в макрорасширениях независимо от действия директив управления листингом.

------------------------------------------------------------

TITLE MACRO3 (EXE) Директивы .LALL и .SALL

; ------------------------------------------------

INIT2 MACRO CSNAME,DSNAME,SSNAME

ASSUME CS:CSNAME,DS:DSNAME,SS:SSNAME,ES:DSNAME

PUSH DS

SUB AX,AX

PUSH AX

MOV AX,DSNAME

MOV DS,AX

MOV ES,AX

ENDM

; ------------------------------------------------

PROMPT MACRO MESSAGE

; Макрокоманда выводит на экран любые сообщения

;; Генерирует команды вызова DOS

MOV AH,09 ;Вывод на экран

LEA DX,MESSAGE

INT 21H

ENDM

; ------------------------------------------------

0000 STACK SEGMENT PARA STACK 'Stack'

0000 20 [ ???? ] DW 32 DUP (?)

0040 STACK ENDS

; ------------------------------------------------

0000 DATA SEGMENT PARA 'Data'

0000 43 75 73 74 6F 6D MESSG1 DB 'Customer name?', '$'

65 72 20 6E 61 6D

65 3F 24

000F 43 75 73 74 6F 6D MESSG2 DB 'Customer address?', '$'

65 72 20 61 64 64

72 65 73 73 3F 24

0021 DATA ENDS

; ------------------------------------------------

0000 CSEG SEGMENT PARA 'Code'

0000 BEGIN PROC FAR

.SALL

INIT2 CSEG,DATA,STACK

PROMPT MESSG1

.LALL

PROMPT MESSG2

+ ; Макрокоманда выводит на экран любые сообщения

0013 B4 09 + MOV AH,09 ;Вывод на экран

0015 8D 16 000F R + LEA DX,MESSG2

0019 CD 21 + INT 21H

001B CB RET

001C BEGIN ENDP

001C CSEG ENDS

END BEGIN

------------------------------------------------------------

Рис.3. Распечатка и подавление макрорасширений в листинге.

ИСПОЛЬЗОВАНИЕ МАКРОКОМАНД В МАКРООПРЕДЕЛЕНИЯХ

------------------------------------------------------------

Макроопределение может содержать ссылку на другое макроопределение.

Рассмотрим простое макроопределение DOS21, которое заносит в регистр AH номер функции DOS и выполняет INT 21H:

DOS21 MACRO DOSFUNC

MOV AH,DOSFUNC

INT 21H

ENDM

Для использования данной макрокоманды при вводе с клавиатуры необходимо закодировать:

LEA DX,NAMEPAR

DOS21 0AH

Предположим, что имеется другое макроопределение, использующее функцию 02 в регистре AH для вывода символа:

DISP MACRO CHAR

MOV AH,02

MOV DL,CHAR

INT 21H

ENDM

Для вывода на экран, например, звездочки достаточно закодировать макрокоманду DISP '*'. Можно изменить макроопределение DISP, воспользовавшись макрокомандой DOC21:

DISP MACRO CHAR

MOV DL,CHAR

DOS21 02

ENDM

Теперь, если закодировать макрокоманду DISP в виде DISP '*', то ассемблер сгенерирует следующие команды:

MOV DL,'*'

MOV AH,02

INT 21H

ДИРЕКТИВА LOCAL

------------------------------------------------------------

В некоторых макрокомандах требуется определять элементы данных или метки команд. При использовании такой макрокоманды в программе более одного раза происходит также неоднократное определение одинаковых полей данных или меток.

В результате ассемблер выдаст сообщения об ошибке из-за дублирования имен. Для обеспечения уникальности генерируемых в каждом макрорасширении имен используется директива LOCAL, которая кодируется непосредственно после директивы MACRO, даже перед комментариями. Общий формат имеет следующий вид:

LOCAL dummy-1,dummy-2,... ;Формальные параметры

Рис.4. иллюстрирует использование директивы LOCAL. В приведенной на этом рисунке программе выполняется деление вычитанием; делитель вычитается из делимого и частное увеличивается на 1 до тех пор, пока делимое больше делителя.

Для данного алгоритма необходимы две метки: COMP - адрес цикла, OUT - адрес выхода из цикла по завершению. Обе метки COMP и OUT определены как LOCAL и могут иметь любые правильные ассемблерные имена.

В макрорасширении для COMP генерируется метка ??0000, а для OUT - ??0001. Если макрокоманда DIVIDE будет использована в этой программе еще один раз, то в следующем макрорасширении будут сгенерированы метки ??0002 и ??0003 соответственно. Таким образом, с помощью директивы LOCAL обеспечивается уникальность меток в макрорасширениях в одной программе.

------------------------------------------------------------

TITLE MACRO4 (COM) Использование директивы LOCAL

; -------------------------------------------------

DIVIDE MACRO DIVIDEND,DIVISOR,QUOTIENT

LOCAL COMP

LOCAL OUT

; AX=делимое, BX=делитель, CX=частное

MOV AX,DIVIDEND ;Загрузить делимое

MOV BX,DIVISOR ;Загрузить делитель

SUB CX,CX ;Регистр для частного

COMP:

CMP AX,BX ; Делимое < делителя?

JB OUT ; да - выйти

SUB AX,BX ;Делимое - делитель

INC CX ;Частное + 1

JMP COMP

OUT:

MOV QUOTIENT,CX ;Записать результат

ENDM

; ------------------------------------------------

0000 CSEG SEGMENT PARA 'Code'

ASSUME CS:CSEG,DS:CSEG,SS:CSEG,ES:CSEG

0100 ORG 100H

0100 EB 06 BEGIN: JMP SHORT MAIN

; ------------------------------------------------

0102 0096 DIVDND DW 150 ;Делимое

0104 001B DIVSOR DW 27 ;Делитель

0106 ???? QUOTNT DW ? ;Частное

; ------------------------------------------------

0108 MAIN PROC NEAR

.LALL

DIVIDE DIVDND,DIVSOR,QUOTNT

+ ; AX=делимое, BX=делитель, CX=частное

0108 A1 0102 R + MOV AX,DIVDND ;Загрузить делимое

010B 8B 1E 0104 R + MOV BX,DIVSOR ;Загрузить делитель

010F 2B C9 + SUB CX,CX ;Регистр для частного

0111 + ??0000:

0111 3B C3 + CMP AX,BX ;Делимое < делителя?

0113 72 05 + JB ??0001 ; да - выйти

0115 2B C3 + SUB AX,BX ;Делимое - делитель

0117 41 + INC CX ;Частное + 1

0118 EB F7 + JMP ??0000

011A + ??0001:

011A 89 0E 0106 R + MOV QUOTNT,CX ;Записать результат

011E C3 RET

011F MAIN ENDP

011F CSEG ENDS

END BEGIN

------------------------------------------------------------

Рис.4. Использование директивы LOCAL.

ИСПОЛЬЗОВАНИЕ БИБЛИОТЕК МАКРООПРЕДЕЛЕНИЙ

------------------------------------------------------------

Определение таких макрокоманд, как INIT1 и INIT2 и одноразовое их использование в программе кажется бессмысленным. Лучшим подходом здесь является каталогизация собственных макрокоманд в библиотеке на магнитном диске, используя любое описательное имя, например, MACRO.LIB:

INIT MACRO CSNAME,DSNAME,SSNAME

.

.

ENDM

PROMPT MACRO MESSGE

.

.

ENDM

Теперь для использования любой из каталогизированных макрокоманд вместо MACRO определения в начале программы следует применять директиву INCLUDE:

INCLUDE C:MACRO.LIB

.

.

INIT CSEG,DATA,STACK

В этом случае ассемблер обращается к файлу MACRO.LIB (в нашем примере) на дисководе C и включает в программу оба макроопределения INIT и ROMPT. Хотя в нашем примере требуется только INIT. Ассемблерный листинг будет содержать копию макроопределения, отмеченного символом C в 30 колонке

LST-файла. Следом за макрокомандой идет ее расширение с объектным кодом и с символом плюс (+) в 31 колонке.

Так как транслятор с ассемблера является двухпроходовым, то для обеспечения обработки директивы INCLUDE только в первом проходе (а не в обоих) можно использовать следующую конструкцию:

IF1

INCLUDE C:MACRO.LIB

ENDIF

IF1 и ENDIF являются условными директивами. Директива IF1 указывает ассемблеру на необходимость доступа к библиотеке только в первом проходе трансляции. Директива ENDIF завершает IF-логику. Таким образом, копия макроопределений не появится в листинге - будет сэкономлено и время и память.

Программа на рис. 5 содержит рассмотренные выше директивы IF1, INCLUDE и ENDIF, хотя в LST-файл ассемблер выводит только директиву ENDIF. Обе макрокоманды в кодовом сегменте INIT и PROMPT за каталогизированы в файле MACRO.LIB, т.е. просто записаны друг за другом на дисковый файл по имени MACRO.LIB с помощью текстового редактора.

Расположение директивы INCLUDE не критично, но она должна появиться ранее любой макрокоманды из включаемой библиотеки.

------------------------------------------------------------

TITLE MACRO5 (EXE) Проверка директивы INCLUDE

EDIF

; ------------------------------------------------

0000 STACK SEGMENT PARA STACK 'Stack'

0000 20 [????] DW 32 DUP(?)

0040 STACK ENDS

; ------------------------------------------------

0000 DATA SEGMENT PARA 'Data'

0000 54 65 73 74 20 6F MESSGE DB 'Test of macro','$'

66 20 6D 61 63 72

6F 24

000E DATA ENDS

; ------------------------------------------------

0000 CSEG SEGMENT PARA 'Code'

0000 BEGIN PROC FAR

INIT CSEG,DATA,STACK

0000 1E + PUSH DS

0001 3B C0 + SUB AX,AX

0003 50 + PUSH AX

0004 B8 ---- R + MOV AX,DATA

0007 8E D8 + MOV DS,AX

0009 8E C0 + MOV ES,AX

PROMPT MESSGE

000B B4 09 + MOV AH,09 ;Вывод на экран

000D 8D 16 0000 R + LEA DX,MESSGE

0011 CD 21 + INT 21H

0013 CB RET

0014 BEGIN ENDP

0014 CSEG ENDS

END BEGIN

------------------------------------------------------------

Рис.5. Использование библиотеки макроопределений.

Директива очистки

Директива INCLUDE указывает ассемблеру на включение всех макроопределений из специфицированной библиотеки. Например, библиотека содержит макросы INIT, PROMPT и DIVIDE, хотя программе требуется только INIT. Директива PURGE позволяет "удалить" нежелательные макросы PROMPT и DIVIDE в текущем ассемблировании:

IF1

INCLUDE MACRO.LIB ;Включить всю библиотеку

ENDIF

PURGE PROMRT, DIYIDE ;Удалить ненужные макросы

...

INIT CSEG, DATA, STACK ;Использование оставшейся

; макрокоманды

Директива PURGE действует только в процессе ассемблирования и не оказывает никакого влияния на макрокоманды, находящиеся в библиотеке.

КОНКАТЕНАЦИЯ (&)

------------------------------------------------------------

Символ амперсанд (&) указывает ассемблеру на сцепление (конкатенацию) текста или символов. Следующая макрокоманда MOVE генерирует команду MOVSB или MOVSW:

MOVE MACRO TAG

REP MOVS&TAG

ENDM

Теперь можно кодировать макрокоманду в виде MOVE B или MOVE W. В результате макрорасширения ассемблер сцепит параметр с командой MOVS и получит REP MOVSB или REP MOVSW. Данный пример весьма тривиален и служит лишь для иллюстрации.

ДИРЕКТИВЫ ПОВТОРЕНИЯ: REPT, IRP, IRPC

------------------------------------------------------------

Директивы повторения заставляют ассемблер повторить блок операторов, завершаемых директивой ENDM. Эти директивы не обязательно должны находится в макроопределении, но если они там находятся, то одна директива ENDM требуется для завершения повторяющегося блока, а вторая ENDM - для завершения макроопределения.

REPT: Повторение

Операция REPT приводит к повторению блока операторов до директивы ENDM в соответствии с числом повторений, указанным в выражении:

REPT выражение

В следующем примере происходит начальная инициализация значения N=0 и затем повторяется генерация DB N пять раз:

N = 0

REPT 5

N = N + 1

DB N

ENDM

В результате будут сгенерированы пять операторов DB от DB 1 до DB 5.

Директива REPT может использоваться таким образом для определения таблицы или части таблицы.

Другим примером может служить генерация пяти команд MOVSB, что эквивалентно REP MOVSB при содержимом CX равном 05:

REPT 5

MOVSB

ENDM

IRP: Неопределенное повторение

Операция IRP приводит к повторению блока команд до директивы ENDM. Основной формат:

IRP dummy,<arguments>

Аргументы, содержащиеся в угловых скобках, представляют собой любое число правильных символов, строк, числовых или арифметических констант. Ассемблер генерирует блок кода для каждого аргумента.

В следующем примере ассемблер генерирует DB 3, DB 9, DB 17, DB 25 и DB 28:

IRP N,<3, 9, 17, 25, 28>

DB N

ENDM

IRPC: Неопределенное повторение символа

Операция IRPC приводит к повторению блока операторов до

директивы ENDM. Основной формат:

IRPC dummy,string

Ассемблер генерирует блок кода для каждого символа в строке "string".

В следующем примере ассемблер генерирует DW 3, DW 4 ... DW 8:

IRPC N,345678

DW N

ENDM

УСЛОВНЫЕ ДИРЕКТИВЫ

------------------------------------------------------------

Ассемблер поддерживает ряд условных директив. Ранее нам уже приходилось использовать директиву IF1 для включения библиотеки только в первом проходе ассемблирования. Условные директивы наиболее полезны внутри макроопределений, но не ограничены только этим применением. Каждая директива IF должна иметь спаренную с ней директиву ENDIF для завершения IF-логики и возможную директиву ELSE для альтернативного действия:

IFxx (условие)

. }

. } Условный

ELSE (не обязательное действие) }

. } блок

. }

ENDIF (конец IF-логики)

Отсутствие директивы ENDIF вызывает сообщение об ошибке: "Undeterminated conditional" (незавершенный условный блок).

Если проверяемое условие истинно, то ассемблер выполняет условный блок до директивы ELSE или при отсутствии ELSE – до директивы ENDIF. Если условие ложно, то ассемблер выполняет условный блок после директивы ELSE, а при отсутствии ELSE вообще обходит условный блок.

Ниже перечислены различные условные директивы:

IF выражение Если выражение не равно нулю,

ассемблер обрабатывает операторы в

условном блоке.

IFE выражение Если выражение равно нулю, ассемблер

обрабатывает операторы в условном

блоке.

IF1 (нет выражения) Если осуществляется первый проход

ассемблирования, то обрабатываются

операторы в условном блоке.

IF2 (нет выражения) Если осуществляется второй проход

ассемблирования, то обрабатываются

операторы в условном блоке.

IFDEF идентификатор Если идентификатор определен в

программе или объявлен как EXTRN, то

ассемблер обрабатывает операторы в

условном блоке.

IFNDEF идентификатор Если идентификатор не определен в

программе или не объявлен как EXTRN,

то ассемблер обрабатывает операторы в

условном блоке.

IFB <аргумент> Если аргументом является пробел,

ассемблер обрабатывает операторы в

условном блоке. Аргумент должен быть в

угловых скобках.

IFNB <аргумент> Если аргументом является не пробел, то

ассемблер обрабатывает операторы в

условном блоке. Аргумент должен быть в

угловых скобках.

IFIDN <арг-1>, <арг-2> Если строка первого аргумента

идентична строке второго аргумента, то

ассемблер обрабатывает операторы в

условном блоке. Аргументы должны быть

в угловых скобках.

IFDIF <арг-1>, <арг-2> Если строка первого аргумента

отличается от строки второго

аргумента, то ассемблер обрабатывает

операторы в условном блоке. Аргументы

должны быть в угловых скобках.

Ниже приведен простой пример директивы IFNB (если не пробел). Для DOS INT 21H все запросы требуют занесения номера функции в регистр AH, в то время как лишь некоторые из них используют значение в регистре DX. Следующее макроопределение учитывает эту особенность:

DOS21 MACRO DOSFUNC,DXADDRES

MOV AN,DOSFUNC

IFNB <DXADDRES>

MOV DX,OFFSET DXADDRES

ENDIF

INT 21H

ENDM

Использование DOS21 для простого ввода с клавиатуры

требует установки значения 01 в регистр AH:

DOS21 01

Ассемблер генерирует в результате команды MOV AH,01 и INT 21H. Для ввода символьной строки требуется занести в регистр AH значение 0AH, а в регистр DX - адрес области ввода:

DOS21 0AH,IPFIELD

Ассемблер генерирует в результате обе команды MOV и INT 21H.

ДИРЕКТИВА ВЫХОДА ИЗ МАКРОСА EXITM.

------------------------------------------------------------

Макроопределение может содержать условные директивы, которые проверяют важные условия. Если условие истинно, то ассемблер должен прекратить дальнейшее макрорасширение. Для этой цели служит директива EXITM:

IFxx [условие]

.

. (неправильное условие)

.

EXITM

.

.

ENDIF

Как только ассемблер попадает в процессе генерации макрорасширения на директиву EXITM, дальнейшее расширение прекращается и обработка продолжается после директивы ENDM.

Можно использовать EXITM для прекращения повторений по директивам REPT, IRP и IRPC даже если они находятся внутри макроопределения.

МАКРОКОМАНДЫ, ИСПОЛЬЗУЮЩИЕ IF И IFNDEF УСЛОВИЯ

------------------------------------------------------------

Программа на рис. 6 содержит макроопределение DIVIDE, которая генерирует подпрограмму для выполнения деления вычитанием. Макрокоманда должна кодироваться с параметрами в следующей последовательности: делимое, делитель, частное.

Макрокоманда содержит директиву IFNDEF для проверки наличия параметров. Для любого неопределенного элемента макрокоманда увеличивает счетчик CNTR. Этот счетчик может иметь любое корректное имя и предназначен для временного использования в макроопределении. После проверки всех трех параметров, макрокоманда проверяет CNTR:

IF CNTR

; Макрорасширение прекращено

EXITM

Если счетчик CNTR содержит ненулевое значение, то ассемблер генерирует комментарий и прекращает по директиве EXITM дальнейшее макрорасширение. Заметим, что начальная команда устанавливает в счетчике CNTR нулевое значение и, кроме того, блоки IFNDEF могут устанавливать в CNTR единичное значение, а не увеличивать его на 1.

Если ассемблер успешно проходит все проверки, то он генерирует макрорасширение. В кодовом сегменте первая макрокоманда DIVIDE содержит правильные делимое и частное и, поэтому генерирует только комментарии. Один из способов улучшения рассматриваемой макрокоманды - обеспечить проверку на ненулевой делитель и на одинаковый знак делимого и делителя; для этих целей лучше использовать коды ассемблера, чем условные директивы.

------------------------------------------------------------

TITLE MACRO6 (COM) Проверка директив IF и IFNDEF

; ------------------------------------------------

DIVIDE MACRO DIVIDEND,DIVISOR,QUOTIENT

LOCAL COMP

LOCAL OUT

CNTR = 0

; AX-делимое, BX-делитель, CX-частное

IFNDEF DIVIDEND

; Делитель не определен

CNTR = CNTR +1

ENDIF

IFNDEF DIVISOR

; Делимое не определено

CNTR = CNTR +1

ENDIF

IFNDEF QUOTIENT

; Частное не определено

CNTR = CNTR +1

ENDIF

IF CNTR

; Макрорасширение отменено

EXITM

ENDIF

MOV AX,DIVIDEND ;Загрузка делимого

MOV BX,DIVISOR ;Загрузка делителя

SUB CX,CX ;Регистр для частного

COMP:

CMP AX,BX ;Делимое < делителя?

JB OUT ; да - выйти

SUB AX,BX ;Делимое - делитель

INC CX ;Частное + 1

JMP COMP

OUT:

MOV QUOTIENT,CX ;Запись результата

ENDM

; ------------------------------------------------

0000 CSEG SEGMENT PARA 'Code'

ASSUME CS:CSEG,DS:CSEG,SS:CSEG,ES:CSEG

0100 ORG 100H

0100 EB 06 BEGIN: JMP SHORT MAIN

; ------------------------------------------------

0102 0096 DIVDND DW 150

0104 001B DIVSOR DW 27

0106 ???? QUOTNT DW ?

; ------------------------------------------------

0108 MAIN PROC NEAR

.LALL

DIVIDE DIVDND,DIVSOR,QUOTNT

= 0000 + CNTR = 0

+ ; AX-делимое, BX-делитель, CX-частное

+ ENDIF

+ ENDIF

+ ENDIF

+ ENDIF

0108 A1 0102 R + MOV AX,DIVDND ;Загрузка делимого

0108 8B 1E 0104 R + MOV BX,DIVSOR ;Загрузка делителя

010F 2B C9 + SUB CX,CX ;Регистр для частного

0111 + ??0000:

0111 3B C3 + CMP AX,BX ;Делимое < делителя?

0113 72 05 + JB ??0001 ; да - выйти

0115 2B C3 + SUB AX,BX ;Делимое - делитель

0117 41 + INC CX

0118 EB F7 + JMP ??0000

011A + ??0001:

011A 89 0E 0106 R + MOV QUOTNT,CX ;Запись результата

DIVIDE DIDND,DIVSOR,QUOT

= 0000 + CNTR = 0

+ ; AX-делимое, BX-делитель, CX-частное

+ IFNDEF DIDND

+ ; Делитель не определен

= 0001 + CNTR = CNTR +1

+ ENDIF

+ ENDIF

+ IFNDEF QUOT

+ ; Частное не определено

= 0002 + CNTR = CNTR +1

+ ENDIF

+ IF CNTR

+ ; Макрорасширение отменено

+ EXITM

011E C3 RET

011F MAIN ENDP

011F CSEG ENDS

END BEGIN

------------------------------------------------------------

Рис. 6. Использование директив IF и IFNDEF.

МАКРОС, ИСПОЛЬЗУЮЩИЙ IFIDN-УСЛОВИЕ

------------------------------------------------------------

Программа на рис. 7 содержит макроопределение по имени MOVIF, которая генерирует команды MOVSB или MOVSW в зависимости от указанного параметра. Макрокоманду можно кодировать с параметром B (для байта) или W (для слова) для генерации команд MOVSB или MOVSW из MOVS.

Обратите внимание на первые два оператора в макроопределении:

MOVIF MACRO TAG

IFIDN <&TAG>,<B>

Условная директива IFIDN сравнивает заданный параметр (предположительно B или W) со строкой B. Если значения идентичны, то ассемблер генерирует REP MOVSB.

Обычное использование амперсанда (&) - для конкатенации, но в данном примере операнд <TAG> без амперсанда не будет работать.

Если в макрокоманде не будет указан параметр B или W, то ассемблер сгенерирует предупреждающий комментарий и команду MOVSB (по умолчанию).

Примеры в кодовом сегменте трижды проверяют макрокоманду MOVIF: для параметра B, для параметра W и для неправильного параметра. Не следует делать попыток выполнения данной программы в том виде, как она приведена на рисунке, так как регистры CX и DX не обеспечены правильными значениями.

Предполагается, что рассматриваемая макрокоманда не является очень полезной и ее назначение здесь - проиллюстрировать условные директивы в простой форме. К данному моменту, однако, вы имеете достаточно информации для составления больших полезных макроопределений.

------------------------------------------------------------

TITLE MACRO7 (COM) Проверка директивы IFIDN

; --------------------------------------------

MOVIF MACRO TAG

IFIDN <&TAG>,<B>

REP MOVSB

EXITM

ENDIF

IFIDN <&TAG>,<W>

REP MOVSW

ELSE

; Не указан параметр B или W,

; по умолчанию принято B

REP MOVSB

ENDIF

ENDM

; --------------------------------------------

0000 CSIG SEGMENT PARA 'Code'

ASSUME CS:CSEG,DS:CSEG,SS:CSEG,ES:CSEG

0100 ORG 100H

0100 EB 00 BEGIN: JMP SHORT MAIN

; ...

0102 MAIN PROC NEAR

.LALL

MOVIF B

+ IFIDN <B>,<B>

0102 F3/A4 + REP MOVSB

+ EXITM

MOVIF W

+ ENDIF

+ IFIDN <W>,<W>

0104 F3/A5 + REP MOVSW

+ ENDIF

MOVIF

+ ENDIF

+ ELSE

+ ; Не указан параметр B или W,

+ ; по умолчанию принято B

0106 F3/A4 + REP MOVSB

+ ENDIF

0108 C3 RET

0109 MAIN ENDP

0109 CSEG ENDS

END BEGIN

------------------------------------------------------------

Рис. 7. Использование директивы IFIDN

ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ

------------------------------------------------------------

· Макросредства возможны только для полной версии ассемблера (MASM).

· Использование макрокоманд в программах на ассемблере дает в результате более удобочитаемые программы и более производительный код.

· Макроопределение состоит из директивы MACRO, блока из одного или нескольких операторов, которые генерируются при макрорасширениях и директивы ENDM для завершения определения.

· Код, который генерируется в программе по макрокоманде, представляет собой макрорасширение.

· Директивы .SALL,.LALL и .XALL позволяют управлять распечаткой комментариев и генерируемого объектного кода в макрорасширении.

· Директива LOCAL позволяет использовать имена внутри макроопределений. Директива LOCAL кодируется непосредственно после директивы MACRO.

· Использование формальных параметров в макроопределении позволяет кодировать параметры, обеспечивающие большую гибкость макросредств.

· Библиотека макроопределений дает возможность использовать макрокоманды для различных ассемблерных программ.

· Условные директивы позволяют контролировать параметры макрокоманд.

КОНТРОЛЬНЫЕ ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ

------------------------------------------------------------

1. Напишите необходимые директивы: а) для подавления всех команд, которые генерирует макрокоманда и б) для распечатки только команд, генерирующих объектный код.

2. Закодируйте два макроопределения для умножения:
а) MULTBY должна генерировать код для умножения байта на байт;
б) MULTWD должна генерировать код для умножения слова на слово.
Для множителя и множимого используйте в макроопределении формальные параметры. Проверьте выполнение макрокоманд на небольшой программе, в которой также определены необходимые области данных.

3. Запишите макроопределения из вопроса 2 в "макробиблиотеку". Исправьте программу для включения элементов библиотеки по директиве INCLUDE в первом проходе ассемблирования.

4. Напишите макроопределение BIPRINT, использующей BIOS INT 17H для печати. Макроопределение должно включать проверку состояния принтера и обеспечивать печать любых строк любой длины.

5. Измените макроопределение на рис. 6 для проверки делителя на ноль (для обхода деления).