Ассемблер для процессоров Intel Pentium
Шрифт:
– обнаружено условие, согласно которому должен произойти выход из цикла.
Рассмотрим следующий пример: пусть необходимо подсчитать количество символов в строке. Условимся, что такая строка оканчивается нулем, и будем использовать этот факт как признак конца цикла. Вот фрагмент программного кода, реализующий этот алгоритм:
. . .
.data
s1 DB «ABCDFEG», 0
.code
. . .
mov a1, 0
lea s1 , s1
next:
cmp byte ptr [SI], 0
je exit
inc SI
inc AL
jmp next
exit:
. . .
Проанализируем
cmp byte ptr [SI], G
Если признак конца строки обнаружен, то происходит выход из цикла. Если элемент строки не равен 0, то к счетчику элементов в регистре a1 прибавляется 1, а в регистр s1 загружается адрес следующего элемента строки при помощи команды
inc SI
Далее цикл повторяется. Как видно из примера, цикл заканчивается по условию (достигнут конец строки).
Если известно заранее количество итераций в цикле, то признаком окончания цикла является выполнение всех итераций. В следующем примере подсчитывается количество вхождений символа А в строку si. Размер строки определяется константой 1 en, поэтому можно использовать это значение для инициализации счетчика цикла:
. . .
.data
s1 DB «ABCAEFGAGEBA»
len EQU $-sl
.code
. . .
mov DX, len
mov a1, 'A'
xor BL, BL
lea s1 , s1
next:
cmp byte ptr [SI], AL
je inc_counter
continue:
dec DX
jz exit
inc SI
jmp next
inc_counter:
inc BL
jmp continue
. . .
exit:
. . .
Посмотрим, как работает этот код. Поскольку количество итераций заранее известно и равно 1 en, можно загрузить это значение в регистр DX и по окончании каждой итерации уменьшать содержимое DX на 1. Выход из цикла произойдет при значении DX, равном 0. Количество обнаруженных в строке символов А запоминается в счетчике символов, в качестве которого используется регистр BL (начальное значение равно 0).
В самом цикле выполняется сравнение значения текущего символа с содержимым регистра AL. Если обнаружено совпадение, то есть проверяемый элемент равен А, то регистр BL инкрементируется:
cmp byte ptr [SI], a1
je inc_counter
. . .
inc_counter:
inc BL
В нашем последнем примере использовался счетчик цикла на регистре BL. В языке ассемблера для организации циклов с заранее определенным количеством итераций очень удобно применять команду loop, специально
Команда loop выполняет декремент содержимого регистра СХ (ЕСХ), и если оно не равно нулю, то осуществляется переход на указанную метку вперед или назад в диапазоне от -128 до +127 байт. Содержимое регистра СХ (ЕСХ) рассматривается как целое число без знака. Перед использованием команды loop в регистр СХ (ЕСХ) нужно поместить счетчик итераций. Команда loop является последней в цикле и анализирует содержимое счетчика: как только оно становится равным нулю, происходит выход из цикла.
Следующий пример демонстрирует в общих чертах методику использования команды loop:
. . .
.data
counter DW 5
.code
. . .
xor AX, AX
mov CX, counter ; счетчик итераций -> СХ
next:
inc AX ; инкремент регистра АХ
loop next ; следующая итерация
. . .
После окончания цикла регистр АХ будет содержать значение 5. Команду loop можно представить ее функциональным аналогом, состоящим из других команд, как показано в этом примере:
. . .
.data
counter DW 5
.code
. . .
xor AX, AX
mov CX, counter ; счетчик итераций -> СХ
next:
inc AX ; инкремент регистра АХ
dec CX ; декремент регистра СХ
jcxz skip ; если CX = 0, выйти из цикла
jmp next ; следующая итерация
. . .
skip:
. . .
Если вместо команды jcxz в этом фрагменте кода применить jz, то исходный текст будет выглядеть так:
. . .
.data
counter DW 5
.code
. . .
xor AX, AX
mov CX, counter ; счетчик итераций -> CX
next:
inc AX ; инкремент регистра АХ
dec CX ; декремент регистра СХ
jnz next ; если CX = 0, выйти из цикла,
; иначе следующая итерация
. . .
Модификациями команды loop являются команды loope/loopz и 1 oopne/1 oopnz. Рассмотрим вначале команду loope/loopz. Обозначения loope и loopz представляют собой синонимы и относятся к одной и той же команде. Эта команда обладает дополнительными возможностями по обработке циклов. Она выполняет декремент содержимого регистра СХ (ЕСХ), и если оно не равно 0 и флаг ZF установлен в 1, то выполняется переход на указанную метку вперед или назад.
Рассмотрим пример использования команды loope. Это простое 16-разрядное приложение, которое выводит на экран дисплея строку без начальных пробелов (листинг 5.3).
Листинг 5.3. Вывод строки без начальных пробелов на экран
.model small
.data
s1 DB « String with leading blanks !$»
len EQU $-s1
msg DB «Blank string!$»
.code
start:
mov AX, @data
mov DS, AX
lea s1 , s1 ; адрес строки -> SI
dec SI ; декремент адреса для организации цикла