Иллюстрированный самоучитель по Assembler

Циклы и условные переходы

Циклы, позволяющие выполнить некоторый участок программы многократно, в любом языке являются одной из наиболее употребительных конструкций. В системе команд МП 86 циклы реализуются, главным образом, с помощью команды loop (петля), хотя имеются и другие способы организации циклов. Во всех случаях число шагов в цикле определяется содержимым регистра СХ, поэтому максимальное число шагов составляет 64 К.

Рассмотрим простой пример организации цикла. Пусть в программе зарезервировано место для массива размером 10000 слов, и этот массив надо заполнить натуральным рядом чисел от 0 до 9999. Эти числа, заполняющие последовательные элементы массива, иногда называют числами-заполнителями.

Соответствующий фрагмент программы будет выглядеть следующим образом:

;В сегменте данных
array dw 10000 dup(0)
;В программном сегменте
mov BX,offset array; Адрес массива
mov SI,0; Индекс
mov AX,0; Начальное значение заполнителя
mov CX,10000; Счетчик цикла
fill: mov [BX] [SI],AX; Заполнитель пошлем в массив
inc AX; Инкремент заполнителя
add SI,2; модификация индекса
loop fill; Команда цикла

На этапе подготовки мы заносим в регистр ВХ относительный адрес начала массива, отождествляемый с его именем array, устанавливаем начальное значение индекса элемента массива в регистре SI (с таким же успехом можно было взять DI) и начальное значение числа-заполнителя. Сам цикл состоит из трех команд – единственной содержательной команды засылки числа-заполнителя в очередной элемент массива (по адресу, который вычисляется, как сумма содержимого регистров ВХ и SI), а также модификации числа-заполнителя и индекса очередного элемента массива. Завершающей командой loop управление передается на метку fill, и цикл повторяется столько раз, каково содержимое СХ, в данном случае 10000 шагов.

Следует обратить внимание на команду модификации индекса – в каждом шаге к содержимому SI добавляется 2, так как массив состоит из двухбайтовых слов. Если бы нужно было заполнить байтовый массив, то в каждом шаге содержимое регистра цикла SI следовало увеличивать на 1.

Стоит отметить некоторые детали, связанные с механизмом выполнения команды loop. При реализации этой команды процессор сначала уменьшает содержимое регистра СХ на 1, а затем сравнивает полученное число с нулем. Если СХ > 0, переход на указанную метку выполняется. Если СХ = 0, цикл разрывается и процессор переходит на команду, следующую за командой loop. Поэтому после нормального выхода из цикла содержимое СХ всегда равно 0.

Другое обстоятельство связано с кодированием команды loop. В ее коде под смещение к точке перехода отводится всего 1 байт. Поскольку смещение должно являться величиной со знаком, максимальное расстояние, на которое можно передать управление командой loop, составляет от -128 до +127 байт (хотя довольно трудно представить себе цикл, в котором переход осуществляется вперед). Другими словами, тело цикла ограничивается всего 128 байтами. Если циклически повторяемый фрагмент программы имеет большую длину, цикл придется организовать другим, более сложным способом:

;Организация длинного цикла
mov CX,10000; Счетчик цикла
fill:; Метка начала цикла
…; Тело длинного цикла
dec CX; Декремент счетчика цикла
cmp CX,0; Отработано заданное число шагов?
je finish; Да, на метку продолжения программы
jmp fill; Нет, на начало цикла
finish:; Продолжение программы

В этом, весьма типичном фрагменте мы "вручную" уменьшаем содержимое счетчика цикла и сравниваем полученное значение с 0. Если СХ = 0, это значит, что в цикле выполнено заданное число шагов, и командой условного перехода je осуществляется переход на продолжение программы (метка finish). Если СХ еще не равно нулю, командой безусловного перехода jmp осуществляется возврат в начало цикла. Как было показано в гл. 2, команда jmp позволяет перейти в любую точку сегмента, и ограничение на размер тела цикла снимается.

При необходимости организовать вложенные циклы, для сохранения счетчика внешнего цикла на время выполнения внутреннего удобно воспользоваться стеком. В следующем фрагменте организуется временная задержка длительностью несколько секунд (конкретная величина задержки зависит от скорости работы процессора).

mov CX,2000; Счетчик внешнего цикла
outer: push CX; Сохраним его в стеке
mov CX,0; Счетчик внутреннего цикла
inner: loop inner; loop внутреннего цикла
pop CX; Восстановим внешний счетчик
loop outher; loop внешнего цикла

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

Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.