Существует несколько команд условного перехода, предназначенных для
управления циклами в программах. Поскольку программые циклы
используются часто, желательно эффективное управление циклом. На
Фиг. 4.30 показаны четыре команды, созданные для того, чтобы
облегчить программирование циклов на языке ассемблера
микропроцессора 8088.
Так же, как строковые команды используют регистр CX в качестве
счетчика, команды цикла LOOP используют регистр CX в качестве
счетчика цикла. Все эти команды неявно рассматривают регистр CX как
счетчик итераций цикла. Простейшая команда среди них - команда
LOOP. Команда LOOP уменьшает регистр CX и передает управление на
метку, если содержимое регистра CX не равно 0. Если вычитание
единицы из регистра CX не привело к нулевому результату, команда
LOOP не делает перехода, и выполняется следующая команда.
Приведенный ниже программный фрагмент демонстрирует обычное
использование команды LOOP.
MOV CX,LOOP_COUNT
BEGIN_LOOP:
; ... тело цикла
LOOP BEGIN_LOOP
Microsoft (R) Macro Assembler Version 5.00 1/1/80 04:02:01
Фиг. 4.30 Команды цикла Page 1-1
PAGE ,132
TITLE Фиг. 4.30 Команды цикла
0000 CODE SEGMENT
ASSUME CS:CODE
;----------------------------------------
; В этом примере демонстрируются команды цикла.
; Команды в примере не являются законченной программой.
;----------------------------------------
0000 E3 06 JCXZ END_OF_LOOP ; Конец цикла, если CX равно 0
0002 BEGIN_LOOP:
; .... Тело цикла
0002 E2 FE LOOP BEGIN_LOOP ; Переход пока регистр CX не станет равен 0
; .... Если проверяется какое-либо условие, то
0004 E1 FC LOOPE BEGIN_LOOP ; Переход по равенству в условии и
; значение регистра CX не равно 0
; .... Или
0006 E0 FA LOOPNE BEGIN_LOOP ; Переход по неравенству в условиии и
; значение регистра CX не равно 0
0008 END_OF_LOOP:
0008 CODE ENDS
END
Фиг. 4.30 Команда цикла
Программа помещает число итераций цикла в регистр CX перед
выполнением цикла. Затем выполняется тело цикла, а следом за ним
команда LOOP. Она уменьшает счетчик на единицу, что соответствует
единственной, только что выполненной итерации цикла. Если теперь
счетчик в регистре CX равен 0, программа продолжает выполняться
после команды LOOP. Если счетчик не равен 0, управление
возвращается к началу цикла, чтобы совершить еще один проход по
телу цикла. Тело цикла выполняется столько раз, сколько было
сначала задано содержимым регистра CX. Единственное важное
замечание: если программа внутри цикла изменяет регистр CX, число
итераций цикла не будет соответствовать начальному значению в
регистре CX.
Описанный метод одинаково хорошо работает, когда число циклов
известно во время ассемблирования (как в примере, где LOOP_COUNT -
непосредственно заносимое значение), и когда число циклов
определяется во время выполнения. Если вычисленное число оказалось
равным 0, цикл выполнится 65536 раз. Когда микропроцессор 8088
выполняет первую команду LOOP, он уменьшает CX от 0 до 0FFFFH, и
поскольку теперь регистр CX ненулевой, повторяет цикл. Таким
образом, загрузка нулевого значения счетчика циклов - специальный
случай. Этот специальный случай обрабатывается командой JCXZ
(переход, если содержимое регистра CX равно 0). Эта команда
проверяет текущее содержимое регистра CX, и делает переход, если
оно равно нулю. Команда не проверяет ни одного флага, и не влияет
ни на один из них. Следующий пример аналогичен предыдущему, за
исключением того, что он загружает регистр CX из ячейки памяти,
содержимое которой вычисляется во время выполнения программы. По
этой причине может оказаться, что счетчик циклов нулевой, и пример
использует команду JCXZ, чтобы проверить, нужно ли полностью
пропустить тело цикла.
MOV CX,LOOP_COUNT_WORD
JCXZ END_OF_LOOP
BEGIN_LOOP:
; ... тело цикла
LOOP BEGIN_LOOP
END_OF_LOOP:
В программе не нужно использовать команду JCXZ в каждом цикле с
вычисляемым счетчиком. Если программист знает, что счетчик циклов
никогда не будет равен нулю, проверка не нужна. Однако опыт
показывает, что значение, которое "никогда" не должно появиться,
обычно появляется в первую очередь, как только вы начинаете
выполнять программу.
Оставшиеся две команды цикла предоставляют еще большие
возможностей при управлении циклами. Эти команды аналогичны
префиксам REPE и REPNE. Если команда LOOP выходит из цикла, только
когда в регистре CX оказывается нуль, то команда LOOPE (цикл, пока
равно) выходит из цикла, если установлен флаг нуля, или если в
регистре CX получился 0. Тем самым становится возможным
двойственное завершение цикла. Программа может загрузить в регистр
CX максимальное число итераций цикла, а затем проверять флаг нуля в
конце каждого цикла на условие завершения. Команда LOOPNE (цикл,
пока не равно) выполняет обратную к описанной проверку флага нуля:
цикл здесь завершается, если регистр достиг нуля, или если
установлен флаг нуля.
Следующий пример показывает использование команды LOOPNE. В
примере складываются два списка чисел, чтобы найти пару элементов,
сумма которых точно равна 100. Так как в каждой итерации перед
проверкой складываются два чила, команду REPNE CMPSB использовать
нельзя.
В примере предполагается, что пары регистров DS:SI и ES:DI
инициализированы так, чтобы указывать на эти списки.
MOV CX,MAX_LOOP_COUNT ;максимальное число заходов
BEGIN_LOOP:
LODSB ;чтение числа из первого списка
ADD AL,ES:[DI] ;прибавить из второго списка
INC DI ;указатель на следующий элемент
CMP AL,100 ;проверка на нужное значение
LOOPNE BEGIN_LOOP ;снова, если не равно и не все
JE MATCH_FOUND ;переход сюда, чтобы определить конец