Условные переходы
Описанные в предыдущем разделе инструкции переходов - это только часть того, что вам потребуется для написания полезных программ. В действительности необходима возможность писать такие программы, которые могут принимать решения. Именно это можно делать с помощью операций условных переходов.
Инструкция условного перехода может осуществлять или нет переход на целевую (указанную в ней) метку, в зависимости от состояния регистра флагов. Рассмотрим следующий пример:
mov ah,1 ;функция DOS ввода с клавиатуры
int 21h ; получить следующую нажатую клавишу
cmp al,'A' ; была нажата буква "A"?
je AWasTyped ; да, обработать ее
mov [TampByte],al ; нет, сохранить символ
.
.
.
AWasTyped:
push ax ; сохранить символ в стеке
Сначала в данной программе с помощью функции операционной системы DOS воспринимается нажатая клавиша. Затем для сравнения введенного символа с символом A используется инструкция CMP. Эта инструкция аналогична инструкции SUB, только ее выполнение ни на что не влияет, поскольку назначение данной инструкции состоит в том, чтобы можно было сравнить два операнда, установив флаги так же, как это делается в инструкции SUB. Поэтому в предыдущем примере флаг нуля устанавливается в значение 1 только в том случае, если регистр AL содержит символ A.
Теперь мы подошли к основному моменту. Инструкция JE представляет инструкцию условного перехода, которая осуществляет передачу управления только в том случае, если флаг нуля равен 1. В противном случае выполняется инструкция, непосредственно следующая за инструкцией JE (в данном случае - инструкция MOV). Флаг нуля в данном примере будет установлен только в случае нажатия клавиши A; и только в этом случае процессор 8086 перейдет к выполнению инструкции с меткой AWasTyped, то есть инструкции PUSH.
Набор инструкций процессора 8086 предусматривает большое разнообразие инструкций условных переходов, что позволяет вам осуществлять переход почти по любому флагу или их комбинации. Можно осуществлять условный переход по состоянию нуля, переноса, по знаку, четности или флагу переполнения и по комбинации флагов, показывающих результаты операций чисел со знаками.
Перечень инструкций условных переходов приводится в таблице 1.
Таблица 1 - Инструкции условных переходов
Название | Значение | Проверяемые флаги |
JB/JNAE | Перейти, если меньше / перейти, если не больше или равно | CF = 1 |
JAE/JNB | Перейти, если больше или равно / перейти, если не меньше | CF = 0 |
JBE/JNA | Перейти, если меньше или равно / перейти, если не больше | CF = 1 или ZF = 1 |
JA/JNBE | Перейти, если больше / перейти, если не меньше или равно | CF = 0 и ZF = 0 |
JE/JZ | Перейти, если равно | ZF = 1 |
JNE/JNZ | Перейти, если не равно | ZF = 0 |
JL/JNGE | Перейти, если меньше чем / перейти, если не больше чем или равно | SF = OF |
JGE/JNL | Перейти, если больше чем или равно /перейти, если не меньше чем | SF = OF |
JLE/JNLE | Перейти, если меньше чем или равно / перейти, если не больше, чем | ZF = 1 или SF = OF |
JG/JNLE | Перейти, если больше чем / перейти, если не меньше чем или равно | ZF = 0 или SF = OF |
JP/JPE | Перейти по четности | PF = 1 |
JNP/JPO | Перейти по нечетности | PF = 0 |
JS | Перейти по знаку | SF = 1 |
JNS | Перейти, если знак не установлен | SF = 0 |
JC | Перейти при наличии переноса | CF = 1 |
JNC | Перейти при отсутствии переноса | CF = 0 |
JO | Перейти по переполнению | OF = 1 |
JNO | Перейти при отсутствии переполнения | OF = 0 |
CF - флаг переноса, SF - флаг знака, OF - флаг переполнения, ZF - флаг нуля, PF - флаг четности.
Не смотря на свою гибкость, инструкции условного перехода имеют также серьезные ограничения, поскольку переходы в них всегда короткие. Другими словами, целевая метка, указанная в инструкции условного перехода, должна отстоять от инструкции перехода не более, чем на 128 байт. Например, Турбо Ассемблер не может ассемблировать:
JumpTarget:
.
.
.
DB 1000 DUP (?)
.
.
.
dec ax
jnz JumpTarget
так как метка JumpTarget отстоит от инструкции JNZ более чем на 1000 байт. В данном случае нужно сделать следующее:
JumpTarget:
.
.
.
DB 1000 DUP (?)
.
.
.
dec ax
jnz SkipJump
jmp JumpTarget
SkipJump:
где условный переход применяется для того, чтобы определить, нужно ли выполнить длинный безусловные переход.
Командам условного перехода может предшествовать любая команда, изменяющая состояние флагов.
Пример
Рассмотрим простейший пример с использованием ввода, вывода и различных видов переходов.
Необходимо ввести с клавиатуры значение двух переменных a и x. Если а<x, то сложить их значения, а иначе из х отнять а.
model small
.386
.stack 100h
.data
a db ?
x db ?
per db 10,13,'$'
mesa db 10,13,'Input a: $'
mesx db 10,13,'Input x: $',10,13
.code
start:
mov ax, @data
mov ds, ax
lea dx, mesa
mov ah,9 ;Приглашение на ввод а
int 21h
mov ah,1 ;Считывание нажатого символа
int 21h
mov a,al ;Запись считанного символа в а
lea dx, mesx
mov ah,9 ;Приглашение на ввод х
int 21h
mov ah,1 ;Считывание нажатого символа
int 21h
mov x,al ;Запись считанного символа в х
lea dx, per
mov ah,9 ;Перевод строки
int 21h
mov al,x
cmp a,al
jl Lower ;Если а<х,то перейти на метку Lower. Иначе на метку Higher
Higher:
mov al,a
sub al,x
add al,30h ;Коррекция по вычитанию
jmp short l1
lower:
mov al,x ;В регистр al записываем результат сложения а и х
add al,a
sub al,30h ;Корекция по сложению
l1:
mov dl,al
mov ah,2 ;Вывод содержимого dl на экран
int 21h
mov ah,0 ;Ожидание нажатия клавиши
int 16h
mov ah,4ch
int 21h
end start
Остановимся подробнее на строках, в которых происходит коррекция по сложению и вычитанию. Так как Ассемблер не способен обрабатывать просто десятичные числа, то необходимо придумывать алгоритмы обработки самостоятельно. Удобнее всего приводить их к нераспакованному десятичному виду.
При чтении с клавиатуры происходит чтение именно символа, и в регистр записывается его код. Например у чисел 1 и 5 коды соответственно 31h и 35h. Чтобы привести к нераспакованному десятичному виду необходимо привести их к виду 01h и 05h. Эти коды имеют символы отличные от 1 и 5, но над ними гораздо удобнее выполнять арифметические операции. Существуют специальные команды коррекции, но о них вы узнаете позже.