Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Финогенов-основы_языка_ассемблера.doc
Скачиваний:
26
Добавлен:
17.09.2019
Размер:
3.35 Mб
Скачать

Глава 2

Основы программирования

89

jmp S+2 ;выполнения

jrap S+2 ;трех команд jmp

in AL,301h ;Следующее обращение к оборудованию

Здесь используется обозначение счетчика текущего адреса S- При транс­ляции любой команды в счетчике текущего адреса содержится адрес этой команды (смещение ее первого байта). Команда короткого перехода зани­мает 2 байт, поэтому команда jmp S+2 осуществляет переход на команду, идущую следом.

Часто в подобных случаях ограничиваются одной командой jmp, ко­торая создает необходимую задержку в доли микросекунды. В тех случаях, однако, когда устройство сопряжения с оборудованием работает заметно медленнее процессора, приходится включать между командами обраще­ния к портам 5-6 команд jmp. Такой фрагмент можно оформить в виде блока повторения:

rept 6 jmp S+2 end m

Это, пожалуй, проще, чем писать 6 команд jmp. Макросы повторения имеют несколько разновидностей, которые мы не будем здесь рассматривать.

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

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

Макроопределение должно начинаться строкой с именем макроопре­деления и директивой macro, в поле аргументов которой указывается спи­сок формальных аргументов. Заканчивается макроопределение директи­вой endm.

Пусть в программе требуется неоднократно сохранять в стеке содержи­мое трех регистров, но в каждом конкретном случае номера регистров и их порядок отличаются. Оформим эти действия в виде макроопределения:

psh

macroa,b,c push a push b push с endm

Появление в исходном тексте программы строки psh АХ, ВХ, СХ

приведет к генерации следующего фрагмента текста:

АХ ВХ СХ

push push push

Если же в исходном тексте имеется строка psh DX, ES, ВР то соответствующее макрорасширение будет иметь вид:

DX

ES

BP

push push push

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

push

push push

psh mem,[BX],ES:[17h] приведет к следующему макрорасширению:

mem [ВХ] ES:[17h]

Если какие-то строки макроопределения должны быть помечены (на­пример, с целью организации циклов), то обозначения меток следует объявить локальными с помощью оператора local. В этом случае ассемб­лер, генерируя макрорасширения, будет создавать собственные обозначе­ния меток, не повторяющиеся при повторных вызовах одной и той же макрокоманды:

delay macro

local point

mov CX,200 point: loop point

endm

Макрос delay создает задержку фиксированной длительности. Если в текст программы включить две макрокоманды delay

delay

delay

to их макрорасширения, подставленные в текст программы, будут выгля­деть следующим образом:

'Основы программирования

91

raov loop

mov loop

??0000:

CX.20000 ??0000

CX.20000 ??0001

??0001:

При повторных подстановках макроопределения транслятор заменяет

обозначение метки point на различающиеся обозначения "0000, "0001 и т.д., обеспечивая тем самым правильное выполнение команд циклов и переходов.

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

Подпрограммы позволяют сократить объем выполнимого файла за счет описания повторяющихся участков программы лишь однажды. При каж­дом вызове подпрограммы командой call происходит переход на один и тот же фрагмент программы, содержащий подпрограмму, а после выпол­нения подпрограммы — возврат назад в точку вызова. Текст подпрограм­мы полностью определяется на этапе ее написания, и изменения в ходе выполнения подпрограммы возможны только за счет передачи ей тех или иных конкретных значений.

Механизм использования макроса иной. Каждая макрокоманда, встре­тившаяся транслятору в тексте программы, заменяется им на полный текст макроопределения. Если макрокоманда содержит параметры, то в про­цессе этой замены происходит подстановка параметров в текст макрооп­ределения. Образованное таким образом макрорасширение составляет часть текста программы, неотличимо от остальных предложений программы и не нуждается в каких-либо вызовах. В силу этих обстоятельств макроко­манды оказываются несколько эффективнее подпрограмм по скорости выполнения, особенно, если учесть время, требуемое для подготовки параметров перед вызовом подпрограммы (например, проталкивание их в стек). Вряд ли стоит, однако, проводить такое сравнение. Подпрограммы и макрокоманды имеют различные области применения.

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

Смысл использования макрокоманд совсем иной. Макрокоманды по­зволяют упростить процесс написания программы и, можно сказать, яв­ляются средством автоматизации программирования. При этом язык мак­рокоманд предоставляет большие возможности по изменению текста мак­рорасширения в зависимости от указываемых в макрокоманде параметров. Проиллюстрируем эти возможности на простом примере макрокоманды

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

;Пример 2-1. Использование макрокоманды

sym

macro с ;

push

AX ;

push

DX ;

mov

AH,02h ;<

mov

DL,c ;:

int

21h ;

pop

DX ;

pop

AX ;i

cndm

?•

code

segment

assume cs:code

main

proc

sym

V ;<

sym

ES:0 ;:

sym

CS:msg ;:

lea

BX,msg+l ;,

sym

[BX] ;]

mov

AX,40h ;;

mov

DS,AX ;i

sym

DS:49h ;]

mov

AX,4COOh ;:

int

21h

main

cndp

msg

db 'OK'

code

ends

;Имя и формальный аргумент

;Сохраним используемые

;в макроопределении регистры

;функция DOS вывода символа

;3аберем символ

; Вызов DOS

восстановим

;регистры

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

;Символ указан непосредственно

; Вывод первого байта PSP

; Вывод первой буквы из nisg

;Адрес второй буквы из nisg

;Вывод второй буквы

; Настроим DS

;на начало памяти

;Вывод номера видеорежима

;3авершение программы

Тексты макроопределений обычно размещаются в самом начале про­граммы, что дает возможность вызывать макрокоманды из любых точек программы. Содержательная часть макроса syni состоит в вызове функции 021i DOS, которая выводит на экран символ из регистра DL. Поскольку макрос использует регистры АХ и DX, они в начале макроса сохраняются в стеке, а перед его завершением восстанавливаются. В качестве параметра макрокоманды можно использовать любое обозначение ассемблера, ко­торое может интерпретироваться, как адрес символа.

Сама программа умышленно построена несколько нестандартным об­разом. В ней имеется единственный сегмент с текстом программы, в кон­це которого помещена строка данных (слово 'ОК'). Такое расположение данных допустимо, однако для обращения к ним необходимо использо­вать замену сегмента (как это сделано в третьей строке программы), так как программный сегмент адресуется через регистр CS. Сегмент стека в Программе отсутствует, что не очень хорошо, но для небольших программ Допустимо. Фактически под стек будет использован самый низ сегмента

92