Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Флоренсов А.Н. УП Системное программное обеспечение.docx
Скачиваний:
46
Добавлен:
28.06.2021
Размер:
148.95 Кб
Скачать

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 накладывается то же ограничение, что и на простейшие команды Jcc, а именно с помощью их нельзя перейти достаточно далеко от места самой команды (не далее чем на 127 байтов вперед или –128 назад).

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

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

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

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

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

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

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

PUSH ecx

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

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

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

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

POPecx

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

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