Смекни!
smekni.com

ЯЗЫК МАКРОАССЕМБЛЕРА IBM PC (стр. 7 из 9)

При записи в MASM команд перехода следует учитывать, что они могут восприниматься неоднозначно. Скажем, как воспринимать команду

JMP A

- как переход по метке A или как переход по адресу, хранящемуся в ячейке с именем A? Кроме того, какой это переход - внутрисегментный или межсегментный? Ответ зависит от того, как описано имя A, и от то­го, когда описано имя A - до или после команды перехода.

Пусть A описано до команды перехода ("ссылка назад"). Если именем A помечена некоторая команда текущего сегмента команда (т.е. A - мет­ка), тогда ассемблер формирует машинную команду внутрисегментного от­носительного перехода. Если же A - имя переменной, тогда ассемблер формирует машинную команду косвенного перехода - внутрисегментного, если A описано в директиве DW, или межсегментного, если A описано в директиве DD.

В случае же, если имя A описано после команды перехода ("ссылка вперед"), ассемблер всегда формирует машинную команду внутрисегментно­го относительного длинного перехода. С учетом этого имя A обязательно должно метить команду из текущего сегмента команд, иначе будет зафиксирована ошибка. Если такая трактовка ссылки вперед не удовлетворяет автора программы, тогда он обязан с помощью оператора SHORT или PTR уточнить тип имени A:

JMP SHORT A ;внутрисегментный короткий переход по метке

JMP WORD PTR A ;внутрисегментный косвенный переход

JMP DWORD PTE A ;межсегментный косвенный переход

Отметим, что переход по метке A из другого сегмента команд всегда должен указываться с помощью FAR PTR (независимо от того, описана мет­ка A до или после команды перехода):

JMP FAR PTR A ;межсегментный переход по метке

1.5.2 Условные переходы.

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

CMP op1,op2

которая вычисляет разность op1-op2, однако результат никуда не записы­вает, а только меняет флаги, на которые и будет реагировать команда

условного перехода.

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

Jxx op

где xx - одна или несколько букв, в сокращенном виде отражающие прове­ряемое условие (обычно в предположении, что перед этой командой нахо­дится команда сравнения). Примеры некоторых мнемоник:

JE - переход "по равно" (jump if equal)

JL - переход "по меньше" (jump if less)

JNL - переход "по неменьше" (jump if not less)

Особеностью всех машинных команд условного перехода является то, что они реализуют внутрисегментный относительный короткий переход, т.е. добавляют к счетчику команд IP свой операнд, рассматриваемый как знаковое число от -128 до 127. В MASM этот операнд всегда должен запи­сываться как метка, которую ассемблер заменит на соответствующий сдвиг (см. выше).

Такая особенность команд условного перехода вызывает неудобство при переходах на "дальние" команды. Например, если надо сделать пере­ход при A<B на команду, помеченную меткой L и расположенную далеко от команды перехода, то приходится использовать команду длинного безус­ловного перехода:

MOV AX,A

CMP AX,B ;сравнение A и B

JNL M ;не меньше --> M (обход команды JMP)

JMP L ;меньше --> L (длинный переход)

M: ...

1.5.3 Команды управление циклом

В ПК есть несколько команд, упрощающих программирование циклов с заранее известным числом повторений. Применение этих команд требует, чтобы к началу цикла в регистр CX было занесено число шагов цикла. Са­ми команды размещаются в конце цикла, они уменьшают значение CX на 1 и, если CX еще не равно 0, передают управление на начало цикла. Напри­мер, найти S - сумму элементов массива X из 10 чисел-слов можно так:

MOV AX,0 ;начальное значение суммы (накапливается в AX)

MOV SI,0 ;начальное значение индексного регистра

MOV CX,10 ;число повторений цикла

L: ADD AX,X[SI] ;AX:=AX+X[i]

ADD SI,2 ;SI:=SI+2

LOOP L ;CX:=CX-1; if CX<>0 then goto L

MOV S,AX ;S:=AX

Помимо команды LOOP есть еще две "циклические" команды - LOOPZ и LOOPNZ (они имеют синонимичные названия LOOPE и LOOPNE), которых кроме регистра CX проверяют еще и флаг нуля ZF; например, команда LOOPZ "вы­ходит" из цикла, если CX=0 или ZF=1. Эту команду можно, например, ис­пользовать при поиске в массиве первого нулевого элемента, где должно быть предусмотрено два условия выхода из цикла: либо будет найден ну-

левой элемент (ZF=1, если перед LOOPZ поставить команду сравнения оче­редного элемента с 0), либо будет исчерпан весь мсассив (CX=0)

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

В MASM есть еще две команды перехода - CALL (переход с возвратом) и RET (возврат из подпрограммы), они рассматриваются в 1.7.

1.6. СТРОКОВЫЕ ОПЕРАЦИИ

В ПК под строкой понимается последовательность соседних байтов или слов. В связи с этим все строковые команды имеют две разновидности ­для работы со строками из байтов (в мнемонику операций входит буква B) и для работы со строками из слов (в мнемонику входит W).

Имеются следующие операции над строками:

· пересылка элементов строк (в память, из памяти, память-память);

· сравнение двух строк;

· просмотр строки с целью поиска элемента, равного заданному.

Каждая из этих операций выполняется только над одним элементом строки, однако одновременно происходит автоматическая настройка на следующий или предыдущий элемент строки. Имеются специальные команды повторения (REP и др.), которые заставляют следующую за ними строковую команду многократно повторяться (до 2^16 раз), в связи с чем такая па­ра команд позволяет обработать всю строку, причем намного быстрее, чем запрограммированный цикл.

Кроме того, строки можно просматривать вперед (от их начала к кон­цу) и назад. Направление просмотра зависит от флага направления DF, значение которого можно менять с помощью команд STD (DF:=1) и CLD (DF:=0). При DF=0 все последующие строковые команды программы просмат­ривают строки вперед, а при DF=1 - назад.

В строковых командах операнды явно не указываются, а подразумева­ются. Если команда работает с одной строкой, то адрес очередного, об­рабатываемого сейчас элемента строки задается парой регистров DS и SI или парой ES и DI, а если команда работает с двумя строками, то адрес элемента одной из них определяется парой DS:SI, а адрес элемента дру­гой - парой ES:DI. После выполнения операции значение регистра SI и/или DI увеличивается (при DF=0) или уменьшается (при DF=1) на 1 (для байтовых строк) или на 2 (для строк из слов).

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

LEA SI,<начальный/конечный адрес строки>

Если же надо загрузить сразу оба регистра DS и SI, тогда можно вос­пользоваться командой

LDS SI,m32

которая в регистр SI заносит первое слово, а в регистр DS - второе слово из двойного слова, имеющего адреc m32 (таким образом, по адресу m32+2 должен храниться сегмент, а по адресу m32 - смещение начального или конечного элемента строки). Начальную загрузку регистров ES и DI обычно осуществляют одной командой

LES DI,m32

которая действует аналогично команде LDS.

Перечислим вкратце строковые команды ПК.

Команда загрузки элемента строки в аккумулятор (LODSB или LODSW) пересылает в регистр AL или AX очередной элемент строки, на который указывает пара DS:SI, после чего увеличивает (при DF=0) или уменьшает (при DF=1) регистр SI на 1 или 2.

Команда записи аккумулятора в строку (STOSB или STOSW) заносит со­держимое регистра AL или AX в тот элемент строки, на который указывает пара ES:DI, после чего изменяет регистр DI на 1 или 2.

Команда пересылки строк (MOVSB или MOVSW) считывает элемент первой строки, определяемый парой DS:SI, в элемент второй строки, определяе­мый парой ES:DI, после чего одновременно меняет регистры SI и DI.

Команда сравнения строк (CMPSB или CMPSW) сравнивает очередные элементы строк, указываемые парами DS:SI и ES:DI, и результат сравне­ния (равно, меньше и т.п.) фиксирует в флагах, после чего меняет реги­стры SI и DI.

Команда сканирования строки (SCASB или SCASW) сравнивает элемент строки, адрес которого задается парой ES:DI, со значением регистра AL или AX и результат сравнения фиксирует в флагах, после чего меняет со­держимое регистра DI.

Перед любой строковой командой можно поставить одну из двух ко­манд, называемых "префиксами повторения", которая заставит многократно повториться эту строковую команду. Число повторений (обычно это длина строки) должно быть указано в регистре CX. Префикс повторения REPZ (синонимы - REPE, REP) сначала заносит 1 в флаг нуля ZF, после чего, постоянно уменьшая CX на 1, заставляет повторяться следующую за ним строковую команду до тех пор, пока в CX не окажется 0 или пока флаг ZF не изменит свое значение на 0. Другой префикс повторения REPNZ (сино­ним - REPNE) действует аналогично, но только вначале устанавливает флаг ZF в 0, а при при изменении его на 1 прекращает повторение стро­ковой команды.

Пример. Пусть надо переписать 10000 байтов начиная с адреса A в другое место памяти начиная с адреса B. Если оба этих имени относятся к сегменту данных, на начало которого указывает регистр DS, тогда эту пересылку можно сделать так:

CLD ;DF:=0 (просмотр строки вперед)

MOV CX,1000 ;CX - число повторений