Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
flor_apparato-orientirovnnoe_prog.doc
Скачиваний:
89
Добавлен:
15.06.2014
Размер:
926.72 Кб
Скачать

3.2. Средства организации циклов

После условных управляющих конструкций следующими по значимости оказываются средства организации циклов. Заметим, что циклы можно реализовать уже на основе команд условных переходов, если в качестве параметра цикла использовать именованное поле или зарезервировать за ним некоторый регистр. Тогда, учитывая замеченное свойство указанных команд осуществлять переход «куда хотим», можно построить цикл по схеме

MOV параметр, число_повторений_цикла

метка_тела_цикла:

команды тела цикла

SUB параметр, 1

JG метка_тела_цикла

или по схеме

MOV параметр, 0

метка_тела_цикла:

команды тела цикла

ADD параметр, 1

CMP параметр, число_повторений_цикла

JNE метка_тела_цикла

В связи с очень частым использованием элементарных действий увеличения на единицу и уменьшения на единицу для этих операций введены отдельные команды. Они имеют мнемонику INC и DEC (от английских слов increment - увеличить и decrement - уменьшить). Обе эти команды имеют единственный операнд, задающий регистр или место в памяти. При использовании таких команд последние схемы перепишутся в виде

MOV параметр, число_повторений_цикла

метка_тела_цикла:

команды тела цикла

DEC параметр

JG метка_тела_цикла

или по схеме

MOV параметр, 0

метка_тела_цикла:

команды тела цикла

INC параметр

CMP параметр, число_повторений_цикла

JNE метка_тела_цикла

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

Простейшая из этих команд в мнемонике ассемблера изображается с помощью служебного слова LOOP и имеет общий вид

LOOP метка_тела_цикла

По выполняемым действиям эта команда равносильна двум последним командам первой схемы с тем дополнением, что значение параметра предполагается размещенным стандартно в регистре ECX. (Практически для команд результирующего объектного файла, предназначенного для 32-битной архитектуры, строится код машинной команды для регистра ECX, а для результирующего файла, предназначенного для 16-битной архитектуры, строится код машинной команды для регистра CX.)

Поэтому использование команды LOOP в общем случае имеет вид

MOV параметр, число_повторений_цикла

метка_тела_цикла:

команды тела цикла

LOOP метка_тела_цикла

Для сознательного применения этой команды нужно четко представлять, что в ходе выполнения команды LOOP вначале значение регистра для параметра цикла (обычно регистра ecx) уменьшается на единицу, а только затем осуществляется проверка полученного значения на нуль. Следствием такого хода внутренних действий является несколько неожиданное поведение цикла при нулевом начальном параметре. Цикл при этом не будет повторяться "нуль раз", как мог бы подумать поверхностно мыслящий учащийся - поскольку команда LOOP организует цикл с проверкой после тела цикла, то получается цикл с постусловием, который должен выполняться не менее одного раза. При начальном значении параметра цикла, равного нулю, цикл будет выполняться максимально возможное число раз! (Это число равно 232  4 млр. ) Казалось бы, нормально думающий программист и не задаст с помощью команды MOV нулевое число повторений цикла. Но, в действительности, начальное значение для параметра цикла может определяться не константой, а событиями в предшествующей части программы. В частности, и достаточно характерно, может определяться числом действительно введенных символов. Тогда не исключена возможность ввода нулевого числа символов с последующими непредвиденными последствиями.

Поэтому разработчики даже ввели специальные команды для предшествующей циклу проверки на нуль регистра ecx и регистра cx. Они имеют мнемонику JECXZ и JCXZ, а записываются на ассемблере в виде

JECXZ метка_перехода

и

JCXZ метка_перехода

где в качестве метки перехода метки_перехода предполагается использовать метку сразу за концом цикла, использующего команду LOOP.

Заметим, что команда LOOP предполагает ее использование в двух основных модификациях: с регистром ecx и с регистром cx. Эти различия на ассемблере NASM могут задаваться дополнительным операндом в одном из вариантов

LOOP метка_тела_цикла, ecx

LOOP метка_тела_цикла, cx

явно подчеркивающим использование того или иного регистра. На ассемблерах MASM и TASM выбор варианта использования регистра ecx задается расширенным мнемокодом в виде LOOPD, а применение обычного обозначения LOOP влечет использование регистра cx в качестве места хранения параметра цикла.

На использование команды LOOP накладывается то же ограничение, что и на простейшие команды Jcc, а именно с помощью их нельзя перейти достаточно далеко от места самой команды (не далее чем на 127 байтов вперед или -128 назад).

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

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

Для простейшего применения встроенного стека достаточно знать две команды для манипуляций со стеком. Они называются PUSH и POP, обе из них имеют единственный операнд. В зависимости от используемого варианта (32-битного или 16-битного) эти команды вставляют в стек или выдают из него 32-битные двоичные данные или 16-битные. Манипуляции для 8-битных данных со стеком в рассматриваемой архитектуре не предусмотрены (просто нет таких команд в процессорах семейства Intel и совместимых с ним).

В качестве операнда команды PUSH может быть задан регистр (но только не 8-битный), а также места в памяти, где помещаются запоминаемое слово. Допускается также использование числовой константы в качестве такого операнда. В последних двух случаях для компилятора с ассемблера возникает неопределенность - какой вариант команды PUSH использовать: для 32-битных или для 16-битных данных. Здесь также вынуждено используются модификаторы размера операндов, рассмотренные в разделе 2.6.1.

Построение вложенных циклов на изложенной основе демонстрирует следующая схема

MOV ecx, число_повторений_внешнего_цикла

метка_начала_внешнего_цикла:

PUSH ecx

MOV параметр, число_повторений_внутреннего_цикла

метка_начла_внутреннего_цикла:

команды тела цикла

LOOP метка_начала_внутреннего_цикла

POP ecx

LOOP метка_начала_внешнего_цикла

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

Кроме простейшего варианта команды цикла имеется два ее несколько более сложных варианта. Они используют в качестве мнемокода обозначения LOOPE и LOOPNE, которым также равносильны обозначения LOOPZ и LOOPNZ. Отличие этих вариантов от простой команды LOOP заключается в том, что переход по метке, заданной в этих командах, производится не просто по условию отличия от нуля текущего значения регистра параметра (регистра ecx или регистра cx), а только в том случае, когда текущее условие, зафиксированное в регистре EFLAGS, отвечает дополнительному условию "равно" или "не равно" (или, что равносильно, результат "равен нулю" или "не равен нулю"). Таким образом, условие перехода по метке можно записать в виде

(ecx  0) И (флаг ZF установлен) для команд LOOPE и LOOPZ,

(ecx  0) И (флаг ZF не установлен) для команд LOOPNE и LOOPNZ.

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

Соседние файлы в предмете Системное программное обеспечение