Поделиться Поделиться

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

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

Инструкция условного перехода может осуществлять или нет переход на целевую (указанную в ней) метку, в зависимости от состояния регистра флагов. Рассмотрим следующий пример:

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, но над ними гораздо удобнее выполнять арифметические операции. Существуют специальные команды коррекции, но о них вы узнаете позже.

Похожие статьи