Условные конструкции позволяют направить действие программы по одному из путей в зависимости от определенного условия. В языке ассемблера нет подобных конструкций в отличие от языков высокого уровня. Однако, используя сравнение значения флагов, метки и переходы, мы можем сами определить подобные конструкции.
Во многих распространеных языках высокого уровня есть конструкция if..else, которая в зависимости от условия выполняет те или иные действия. С помощью бейсикоподобного синтаксиса обобщенно эту конструкцию можно представить следующим образом:
IF условие THEN выполняемые действия, если условие верно ELSE выполняемые действия, если условие НЕ верно END IF
Или конструкция if..else в большинстве сиподобных языков
if(условие) { выполняемые действия, если условие верно } else { выполняемые действия, если условие НЕ верно }
Например, сначала возьмем простейшую форму конструкции if, которая сравнивает два числа и, если они равны, устанавливает некоторую переменную. Например, на сиподобном языке это выглядело бы так:
if(a == b) { Xn = Xn + 1; }
Интуитивно мы могли бы определить подобную конструкцию на языке ассемблера следующим образом. Вначале указываются операторы, которые оценивают выражение - инструкция CMP
.
Если условие верно, выполняют переход, к метке, где выполняются собственно действия конструкции if. Если условие неверно, выполняем переход к метке, которая выступает в качестве
завершения конструкции if. Пример:
.global _start _start: mov x0, #1 // устанавливаемый в конструкции if регистр mov x1, #2 // a = 4 mov x2, #4 // b = 4 begin_if: cmp x1, x2 // сравниваем два числа на неравенство b.eq equal // если x1 == x2, переход к метке equal b end_if // если x1 != x2 , то выходим из конструкции if equal: // собственно действия конструкции if add x0, x0, #1 // если x1 == x2, то x0 = x0 + 1 end_if: mov x8, #93 // номер функции Linux для выхода из программы - 93 svc 0 // вызываем функцию и выходим из программы
Здесь условная конструкция if располагается между метками begin_if
и end_if
. Допустим, мы хотим увеличить на 1 значение регистра Х0, если регистры Х1 и Х2 равны.
Сначала сравниваем регистры Х1 и Х2 инструкцией CMP.
begin_if: cmp x1, x2
Далее проверяем условие EQ (что регистры Х1 и Х2 равны):
b.eq equal
Если регистры равны, переходим к метке equal
, на которую проецируются собственно действия конструкции if - увеличение значения в регистре Х0
equal: // собственно действия конструкции if add x0, x0, #1
Однако если условие не верно (числа Х1 и Х2 не равны), то инструкцией b выходим из конструкции if - переходим к метке end_if
b end_if
На первый взгляд в этой программе все замечательно, условия проверяются, действия выполняются, программа работает корректно. Но она не оптимальна. В примере выше мы используем две инструкции перехода. И одна из них избыточна. Поэтому в общем случае подобные условные конструкции делают иначе, а именно наоборот - проверяют противоположное условие. И если оно верно, то выполняется переход к конце конструкции if. Если же условия НЕ верно, выполняются действия конструкции ifЖ
.global _start _start: mov x0, #1 // устанавливаемый в конструкции if регистр mov x1, #4 // a = 4 mov x2, #4 // b = 4 begin_if: cmp x1, x2 // сравниваем два числа на неравенство b.ne end_if // если x1 != x2, то выходим из конструкции if add x0, x0, #1 // если x1 == x2, то x0 = x0 + 1 end_if: mov x8, #93 // номер функции Linux для выхода из программы - 93 svc 0 // вызываем функцию и выходим из программы
Таким образом, по сравнению с первым вариантом программа стала проще, мы избавились от одной метки и одной инструкции перехода.
Конструкция if во многих языках также позволяет задать альтернативные условия (обычно для этого применяется оператор else
). В этом случае в ассемблере надо использовать
дополнительную метку, на которую будут проецироваться альтернативные действия
begin_if cmp X, Y // проверяем условие b else // переход к метке else, если условие не верно действия, если условие верно b end_if // выход из конструкции if else: действия, если условие не верно end_if:
Например, если условие верно, прибавим 1 к X0, а если не верно, вычтем 1:
.global _start _start: mov x0, #2 // устанавливаемый в конструкции if регистр mov x1, #4 mov x2, #4 begin_if: cmp x1, x2 // сравниваем x1 и x2 b.ne else_if // если условие не верно (x1 != x2) - к метке else_if add x0, x0, 1 // если x1 == x2 b end_if // в конец конструкции if else_if: // альтернативные действия, если условие не верно sub x0, x0, 1 // если x1 != x2 end_if: mov x8, #93 // номер функции Linux для выхода из программы - 93 svc 0 // вызываем функцию и выходим из программы
Здесь, если условие не верно (a и b не равны), то переходим к метке else_if, после которой определено альтернативное действие.