Сравнение и условный переход
Безусловный переход
Безусловный переход осуществляется с помощью следующей команды:
JMP op
Здесь операнд тем или иным способом указывает адрес перехода, т.е. адрес команды, которая должна быть выполнена следующей. Существуют два варианта безусловного перехода: прямой и косвенный.
Прямой переход
В данном случае в качестве операнда указывается метка той команды, на которую надо передать управление:
JMP <метка>
Пример:
JMP L
…
L: MOV AX, 0
В машинной команде адрес перехода рассчитывается как разность между адресом команды, на которую надо перейти, и адресом самой команды перехода (т.е. как бы относительный адрес относительно содержимого регистра IP. При выполнении команды это смещение складывается с содержимым IP). Под смещение в команде отводится либо 1 байт (короткий переход), либо 2 байта (длинный переход). Это делается для экономии памяти. Ассемблер сам решает, какой сгенерировать.
Косвенный переход
JMP <регистр> или JMP <слово памяти>
Содержимое указанного в команде регистра или слова памяти рассматривается как адрес, по которому нужно делать переход (“настоящий”, а не смещение от IP).
Пример:
B DW L ; слово памяти (переменная),
; проинициализированное значением
; метки L, т.е. адресом
…
MOV DX, B ; в DX пересылается значение метки L
JMP DX ; goto [DX] = goto L Переход по адресу,
; записанному в регистр DX
…
JMP B ; goto [B] = goto L
; переход по адресу, записанному в B
………
L: MOV AX, 0
Существует проблема, с которой сталкивается ассемблер при трансляции команд безусловного перехода. Предположим, что существует команда JMP Z, где Z – некоторое имя (но не имя регистра). По самому имени Z нельзя догадаться, какой переход будет осуществлен – прямой или косвенный. Если имя Z описано до команды JMP, то проблемы нет: если именем Z помечена команда, то осуществляется переход по метке, а если имя Z описано в директиве DW, то осуществляется косвенный переход.
Прямой переход назад
Z: INC AX
…
JMP Z ; goto Z
Косвенный переход назад
Z DW L
…
JMP Z ; goto L
Но если Z – ссылка вперед, т.е. это имя описывается позже, то ассемблер не будет знать, какой здесь переход. Для решения проблемы неоднозначности принято следующее правило: при ссылке вперед ассемблер считает, что Z – метка и формирует команду прямого перехода. Если Z не является меткой, то будет зафиксирована ошибка.
Если все же необходимо использовать косвенный переход, то следует применить оператор PTR. Вместо имени Z следует записать конструкцию WORD PTR Z, которая сообщает ассемблеру, чтобы он рассматривал Z как имя переменной размером в слово, чтобы он формировал машинную команду косвенного перехода.
Прямой переход вперед
JMP Z ; goto Z
…
Z: INC AX
Косвенный переход вперед
JMP WORD PTR Z ; goto L
…
Z DW L
Сравнение и условный переход
Если переход осуществляется только при выполнении некоторого условия и не осуществляется в противном случае, то такой переход называется условным. Условный переход обычно реализуется в два шага: сначала сравниваются некоторые величины, в результате чего соответствующим образом формируются флаги (ZF, SF и т.д.), а затем выполняется условный переход в зависимости от значений флагов.
Команда сравнения записывается следующим образом:
CMP op1, op2
Данная команда идентична команде вычитания, но ее результат никуда не записывается. Основной результат выполнения команды CMP – установка флагов, характеризующих полученную разность, по значению которой можно понять соотношение сравниваемых величин. Рассматриваются значения флагов ZF, SF, CF, OF.
Существует достаточно большое количество команд перехода, записываемых по следующей схеме:
JXX <метка>
Операнд указывает метку той команды программы, на которую надо сделать переход в случае выполнения некоторого условия, а мнемокод начинается буквой J (от jump – прыжок), за которой следует одна или несколько букв, в сокращенном виде описывающих это условие.
Все команды условного перехода можно разделить на три группы.
Группа 1. Команды, ставящиеся после команды сравнения.
В мнемокодах данных команд с помощью определенных букв описывается тот результат сравнения, при котором надо делать переход. Это следующие буквы:
E – equal (равно);
N – not (отрицание);
G – greater (больше) – для чисел со знаком;
L – less (меньше) – для чисел со знаком;
A – above (выше, больше) – для чисел без знака;
B – below (ниже, меньше) – для чисел без знака.
Для условий «меньше» и «больше» введены две системы обозначений. Это связано с тем, что после сравнения чисел со знаком и сравнения чисел без знака надо реагировать на разные значения флагов.
В табл. 1 приведены названия всех команд условного перехода, используемых после команды сравнения (через косую черту указаны названия-синонимы).
Таблица 1
Команды условного перехода, ставящиеся после команды сравнения
Мнемокод | Условие перехода | Состояние флагов |
для любых чисел | ||
JE | op1 = op2 | ZF = 1 |
JNE | op1 <> op2 | ZF = 0 |
для чисел со знаком | ||
JL / JNGE | op1 < op2 | SF <> OF |
JLE / JNG | op1 <= op2 | SF <> OF или ZF = 1 |
JG / JNLE | op1 > op2 | SF = OF и ZF = 0 |
JGE / JNL | op1 >= op2 | SF = OF |
для чисел без знака | ||
JB / JNAE | op1 < op2 | CF = 1 |
JBE / JNA | op1 <= op2 | CF = 1 или ZF = 1 |
JA / JNBE | op1 > op2 | CF = 0 и ZF = 0 |
JAE / JNB | op1 >= op2 | CF = 0 |
Как видно из таблицы, некоторые команды условных переходов могут иметь два синонимичных названия, так как одно и то же условие перехода может быть сформулировано по разному. Например: условие «равно» и условие «не меньше и не больше» - это одно и то же. Автор программы может выбрать любое из них.
Пример использования:
Пусть X,Y,Z – переменные размером в слово. Записать в Z максимальное из чисел X и Y.
Для чисел со знаком | Для чисел без знака |
MOV AX,X CMP AX,Y JGE L1 MOV AX,Y L1: MOV Z,AX | MOV AX,X CMP AX,Y JAE L1 MOV AX,Y L1: MOV Z,AX |
Группа 2. Команды условного перехода по значению флага.
В мнемокодах данных команд указывается первая буква проверяемого флага, если переход должен быть выполнен при значении 1 у флага, либо эта буква указывается с буквой N (not), если переход надо сделать при нулевом значении флага.
В табл. 2 приведен список данных команд.
Таблица 2
Команды условного перехода по значению флагов
Мнемокод | Условие перехода | Мнемокод | Условие перехода |
JZ | ZF=1 | JNZ | ZF=0 |
JS | SF=1 | JNS | SF=0 |
JC | CF=1 | JNC | CF=0 |
JO | OF=1 | JNO | OF=0 |
JP | PF=1 | JNP | PF=0 |
Следующие пары мнемокодов команд перехода эквивалентны: JE и JZ, JNE и JNZ, JB и JC, JNB и JNC.
Пример использования:
Пусть X,Y,Z – беззнаковые байтовые переменные. Вычислить Z = X*X + Y, но если Z сумма не помещается в байт, то управление передать на метку ERROR и выдать сообщение об ошибке.
MOV AL, X
MUL AL
JC ERROR ; X*X > 255 (CF=1) –> ERROR
ADD AL, Y
JC ERROR ; CF=1 –> ERROR
MOV Z, AL
Группа 3. Команда условного перехода по значению счетчика (регистра CX).
Команда записывается следующим образом:
JCXZ <метка>
Действие команды JCXZ (jump if CX is zero) можно описать так:
if CX = 0 then goto <метка>
Используется, например, для организации цикла вместе с командой LOOP (см. ниже).
Все команды условного перехода осуществляют только короткий переход (не более, чем на 128 байт – примерно 30-40 команд). Для реализации длинного условного перехода необходимо использовать конструкции вида:
CMP AX,BX
JNE L ; if AX<>BX then goto L
JMP M ; goto M
L: ……..
Данный фрагмент программы задает переход на метку M при условии равенства содержимого регистров AX и BX.
1.2.2. Команды управления циклом
С помощью команд перехода можно реализовать любые разветвления и циклы. Например, следующие операторы языка Паскаль:
а) if X>0 then S1 else S2
б) while X>0 do S
в) repeat S until X>0
где S, S1 и S2 – некоторые операторы, а X – знаковая переменная, реализуются по схемам, приведенным в табл. 3.
Таблица 3
Варианты реализации ветвлений и циклов с неизвестным числом повторений в ассемблере
if X>0 then S1 else S2 | while X>0 do S | repeat S until X>0 | ||||||||||||
CMP X, 0 JLE L1
JMP L2
L2: | BEG: CMP X, 0 JLE FIN
JMP BEG FIN: …………. |
CMP X, 0 JG BEG |
Для реализации цикла с известным числом повторений можно воспользоваться командой LOOP записываемой следующим образом:
LOOP <метка>
Действие этой команды можно записать следующим образом (пользуясь операторами языка Паскаль):
CX:=CX–1 ; уменьшение счетчика повторений цикла
; на единицу (предварительно число повторений
; цикла надо записать в CX)
IF CX<>0 THEN GOTO <метка> ;
С помощью команды LOOP цикл, который необходимо повторить N раз можно записать следующим образом:
MOV CX, N ; в CX записывается число
; повторений цикла N (N>0)
L: ; начало цикла
… ; тело цикла
…
LOOP L
Данная команда обладает рядом особенностей:
1) В качестве счетчика цикла обязательно должен использоваться регистр CX.
2) Начальное значение для CX должно быть присвоено до цикла, причем данному регистру надо присваивать ровно столько, сколько раз должен повториться цикл (Если цикл повторяется 100 раз, то в CX следует записать 100).
3) Так как команда LOOP ставится в конце цикла, тело цикла выполниться хотя бы один раз. Поэтому для того, чтобы предусмотреть вариант нулевого повторения цикла, необходимо сделать обход цикла:
MOV CX, N ; N>=0
JCXZ L1 ;CX=0 -> L1
L: …
… ;
…
LOOP L
L1: …
Именно для осуществления подобных обходов и была введена команда условного перехода JCXZ.
1.2.3. Работа с массивами