- •Вычислительные системы, сети и телекоммуникации: руководство к лабораторным и практическим занятиям
- •1. Арифметические команды
- •2. Логические команды
- •3. Сдвиговые команды
- •4. Команды передачи управления и управления флагами
- •5. Формат строк ассемблера. Метки и их использование
- •6. Директивы распределения памяти
- •7. Сегменты
- •8. Прерывания
- •9. Директивы порядка загрузки сегментов
- •10. Директивы управления программным счетчиком
- •11. Условия
- •12. Процедуры
- •13. Глобальные объявления
- •14. Макроопределения
- •15. Выражения
- •16. Директивы управления файлами
- •17. Резидентные программы
- •Библиографический список
- •Вычислительные системы, сети и телекоммуникации: руководство к лабораторным и практическим занятиям
- •400005, Волгоград, просп. Ленина, 28, корп.1
- •400005, Волгоград, ул. Советская, 35, корп.7
4. Команды передачи управления и управления флагами
В этом разделе изучим команды передачи управления и управления флагами.
Простейшими командами передачи управления являются команды безусловного и условного переходов. Команда JMP (джампер, от слова прыжок), является командой безусловного (обязательного) перехода. имеющей формат
JMP операнд.
JMP передает управление в другую точку программы, не сохраняя какой-либо информации для возврата. Операндом в большей части случаев является метка, установленная перед командой, на которую выполняется переход. Иногда используют регистр или переменную, содержащие адрес перехода.
Необходимо помнить, что переход может быть нескольких видов:
- short (короткий переход) – если адрес перехода находится в пределах от -127 до +128 байт от команды JMP;
- near (ближний переход) – если адрес перехода находится в том же сегменте памяти, что и команда JMP;
- far (дальний переход) – если адрес перехода находится в другом сегменте. Дальний переход может выполняться и в тот же самый сегмент, если в сегментной части операнда указано число, совпадающее с текущим значением сегментного регистра кода CS.
На синтаксис тип перехода, конечно же, не влияет, но знание о них необходимо для понимания процесса. В противном случае не исключены ошибки во вроде бы правильно написанной программе.
Безусловный переход представлен лишь одной командой, в то время как команд условно перехода гораздо больше. Все команды условного перехода выполняют переход (типа short или near), если выполняется соответствующее условие. Условием в каждом случае реально является состояние тех или иных флагов. Если же условие не выполняется, то команда игнорируется и мы идем дальше по программе. Формат любой из команд условного перехода имеет следующий вид:
Jxx метка.
В тексте программы это может выглядеть так:
m1:
…
…
JNZ m1
Иными словами, если результат предидущих действий не равен нулю, то выполняется переход на метку m1. Если же наблюдается равенство нулю, то игнорируем команду условного перехода и идем дальше по программе.
Следует помнить, что команды типа Jxx не поддерживают дальних переходов, так что, если требуется выполнить условный переход на дальнюю метку, необходимо использовать команду из набора Jxx с обратным условием и дальний JMP.
Теперь перечислим команды условного перехода:
- JA – переход, если выше, то есть первый операнд больше второго. Выполняется, если CF=0, ZF=0.
- JAE – переход, если выше или равно, то есть первый операнд больше либо равен второму. Выполняется, если CF=0.
- JB – переход, если ниже, то есть первый операнд меньше второго. Выполняется, если CF=1.
- JBE – переход, если ниже или равно, то есть первый операнд меньше либо равен второму. Выполняется, если CF=1, ZF=1.
- JC – переход, если осуществляется перенос, то есть CF=1.
- JCXZ – переход, если регистр счетчик становится равным нулю (CX=0). Применяется в тех случаях, когда отслеживается число шагов, проведенных действий и т.д.
- JE – переход, если равно, то есть первый операнд равен второму. Выполняется, если ZF=1.
- JZ – переход если ноль. Полный аналог JE.
- JG – переход, если больше, то есть первый операнд больше второго. Выполняется, если SF=OF, ZF=0. Очень похожа на команду JA, различие лишь в одном из двух условий по флагам.
- JGE - переход, если больше, , то есть первый операнд больше либо равен второму. Выполняется, если SF=OF. Очень похожа на команду JAE.
- JL – переход, если меньше, то есть первый операнд меньше второго. Выполняется, если SF≠OF. Очень похожа на команду JВ.
- JLE – переход, если меньше или равно, то есть первый операнд меньше либо равен второму. Выполняется, если SF≠OF или ZF=1. Очень похожа на команду JВЕ.
- JNA – переход, если не выше. Полный аналог JBE.
- JNAE – переход, если не выше и не равно. Полный аналог JB.
- JNB – переход, если не ниже. Полный аналог JAE.
- JNВE – переход, если не ниже и не равно. Полный аналог JА.
- JNC – переход осуществляется, если флаг переноса сброшен (CF=0).
- JNE – переход, если не равно, то есть первый операнд не равен второму. Выполняется, если ZF=0.
- JNL – переход, если не меньше. Полный аналог JGE.
- JNLЕ – переход, если не меньше и не равно. Полный аналог JG.
- JNO – переход осуществляется, если сброшен флаг переполнения (OF=0).
- JO – переход осуществляется, если установлен флаг переполнения (OF=1).
- JNP – переход, если нечетно, то есть флаг четности сброшен (PF=0).
- JPO – переход, если нечетно. Полный аналог JNP.
- JP – переход, если четно, то есть флаг четности установлен (PF=1).
- JPЕ – переход, если четно. Полный аналог JP.
- JNS – переход, если результат не является отрицательным. Выполняется, если флаг знака SF=0.
- JS – переход, если результат является отрицательным. Выполняется, если флаг знака SF=1.
- JNZ – переход, если не ноль. Полный аналог JNE.
- JNG – переход, если не больше. Полный аналог JLE.
- JNGE – переход, если не больше и не равно. Полный аналог JL.
По мнению автора, ассемблер перенасыщен командами условного перехода, и это даже не учитывая абсолютно идентичные по действию пары команд. Поэтому рекомендуется использовать лшь те из них, которые представляются тому или иному пользователю наиболее эффетивными и удобными. Естественно, не следует заучивать их все наизусть. Достаточно иметь общее представление о принципах работы данного типа команд.
Следующая рассматриваемая нами команда и ее разновидности является основным и наиболее эффективным инструментом ассемблера для реализации циклов. Это переход по счетчику LOOP. Формат команды:
LOOP метка
Суть заключается в том, что данная команда уменьшает значение регистра счетчика на единицу, то есть CX=CX-1. После этого уменьшения мы передаем управление по метке, если только значение счетчика не стало равным нулю (CX=0). В тексте программы это может выглядеть следующим образом:
…
MOV CX, 5
m1:
ADD AX, 3
LOOP m1
…
Здесь мы создаем цикл из пяти шагов-повторений, записывая перед его началом в регистр счетчик CX число 5. Каждый раз, когда команда LOOP будет переносить нас на метку m1, выполняется сложение содержимого регистра AX и числа 3. Как только проходит заданное число шагов, цикл считается завершенным, и мы выходим из него, следуя дальше по программе.
Следующие вариации команды LOOP представляют собой:
- LOOPE – цикл, пока равно, то есть если первый операнд ранее выполненной вычисляющей команды был равен второму. Выполняется, если CX≠0, флаг знака ZF=1
- LOOPNE – цикл, пока не равно, то есть если первый операнд ранее выполненной вычисляющей команды не был равен второму. Выполняется, если CX≠0, флаг знака ZF=0
- LOOPZ – цикл, пока ноль. Полный аналог LOOPE.
- LOOPNZ – цикл, пока не ноль. Полный аналог LOOPNE
Теперь разберемся с парой команд CALL и RET. Первая из них осуществляет вызов подпрограммы, сохраняя текущий адрес в стеке и передавая управление по адресу, указанному в операнде. Операндом может быть метка, регистр или переменная, содержащие адрес перехода.
CALL имя подпрограммы
Следует отметить, что вызов подпрограммы может быть ближним (NEAR), что происходит по умолчанию, и дальним (FAR), когда вызываемая подпрограмма находится вне указанного сегмента. Если в качестве адреса перехода указано только смещение, считается, что адрес расположен в том же сегменте, что и команда CALL. Выполняется ближний вызов процедуры. Процессор помещает значение программного регистра указателя IP, соответствующее следующей за CALL команде, в стек и загружает в IP новое значение, осуществляя тем самым передачу управления.
В случае, если осуществляется дальний вызов (FAR), то процессор сначала заносит в стек текущее значение сегментного регистра кода CS, затем текущее значение IP, а потом уже передает управление вызываемой подпрограмме.
Вторая команда из пары, то есть RET, осуществляет возврат из подпрограммы, передавая управление команде, которая следует сразу же за CALL. Теоретически у команды возврата из подпрограммы есть две разновидности – RETN и RETF. Они соответствую, как понятно из названия, ближнему и дальнему вызову.
RETN считывает из стека и загружает его в IP, выполняя тем самым действия, обратные ближнему вызову процедуры командой, CALL. RETF соответственно загружает из стека IP и CS, возвращаясь из дальней процедуры. Если в программе указана команда RET, ассемблер заменит ее на RETN или RETF в зависимости от того, как была описана процедура, которую эта команда завершает.
Закончив с командами передачи управления, рассмотрим команды управления флагами. Отметим, что все они не нуждаются в операндах и не меняют ничего, кроме значения конкретного флага, с которым ассоциированы.
С флагом переноса CF работают следующие команды:
- STC – устанавливает флаг переноса равным 1.
- СLC – сбрасывает флаг переноса в 0.
- CMC – инвертирует флаг переноса. То есть если он был равен 0, то станет равным 1 и наоборот.
С флагом направления DF работают команды:
- STD – устанавливает флаг направления равным 1. Это влечет за собой уменьшение индексных регистров SI и DI и чаще всего применяется в командах обработки строк.
- CLD – сбрасывает флаг направления в 0. Это влечет за собой увеличение индексных регистров SI и DI.
С флагом прерываний IF работают команды:
- STI – устанавливает флаг прерывания равным 1, разрешая процессору работать с прерываниями от внешних устройств.
- CLI – сбрасывает флаг прерывания в 0. Это влечет за собой то, что процессор перестает воспринимать любые прерывания от внешних устройств.
Помимо перечисленных, есть и другие команды. Работающие с флагами, такие как:
- LAHF – загружает флаги состояния в AH. Иными словами, в АН копируется младший байт регистра-аккумулятора, включающий в себя флаги основного и дополнительного переноса (CF, AF), четности (PF), нуля (ZF) и знака (SF).
- SAHF – загружает флаги SF, ZF, AF, PF и CF из AH.
- PUSHF – копирует содержимое регистра флагов в стек.
- POPF – считывает значение из вершины стека и помещает его в регистр флагов.
Разобрав работу команд, управляющих состоянием флагов, не будем игнорировать и более редкие управляющие команды.
NOP – команда, которая не делает ничего, только занимает место и процессорное время. Используется в целях подгонки времени и для выравнивания памяти.
LOCK – блокирует шину данных. Пока эта команда действует, шина заблокирована. Используется в тех случаях, когда требуется запретить другому (другим) процессорам обращаться к памяти.
ESC – используется в случаях, когда необходимо передать управление внешнему процессору.
HLT – останавливает процессор. Он будет находиться в ожидании до получения специального сигнала сброса или немаскированного прерывания.
