ЦИКЛЫС ИСПОЛЬЗОВАНИЕМ АССЕМБЛЕРНЫХ ВСТАВОК
- Цикл с постусловием (эквивалент do{}while)
- Цикл с предусловием (эквивалент while(){})
Команда loop
Синтаксис:
loop меткаПринцип работы:
§ уменьшить значение регистра %ecx на 1;
§ если %ecx = 0, передать управление следующей за loop команде;
§ если %ecx ≠ 0, передать управление на метку.
Напишем программу для вычисления суммы чисел от 1 до 10 (конечно же, воспользовавшись формулой суммы арифметической прогрессии, можно переписать этот код и без цикла — но ведь это только пример).
Команды сравнения и условные переходы. Безусловный переход
Команда loop неявно сравнивает регистр %ecx с нулём. Это довольно удобно для организации циклов, но часто циклы бывают намного сложнее, чем те, что можно записать при помощиloop. К тому же нужен эквивалент конструкции if(){}. Вот команды, позволяющие выполнять произвольные сравнения операндов:
cmp операнд_2, операнд_1Команда cmp выполняет вычитание операнд_1 – операнд_2 и устанавливает флаги. Результат вычитания нигде не запоминается.
Сравнили, установили флаги, — и что дальше? А у нас есть целое семейство jump-команд, которые передают управление другим командам. Эти команды называются командами условного перехода. Каждой из них поставлено в соответствие условие, которое она проверяет. Синтаксис:
|
Команды jcc не существует, вместо cc нужно подставить мнемоническое обозначение условия.
Мнемоника | Английское слово | Смысл | Тип операндов |
e | equal | равенство | любые |
n | not | инверсия условия | любые |
g | greater | больше | со знаком |
l | less | меньше | со знаком |
a | above | больше | без знака |
b | below | меньше | без знака |
Таким образом, je проверяет равенство операндов команды сравнения, jl проверяет условие операнд_1 < операнд_2 и так далее. У каждой команды есть противоположная: просто добавляем букву n:
§ je — jne: равно — не равно;
§ jg — jng: больше — не больше.
Ниже приведены перечень команд условного перехода, анализируемые ими флаги и соответствующие им логические условия перехода.
Команда | Состояние проверяемых флагов | Условие перехода |
JA | CF = 0 и ZF = 0 | если выше |
JAE | CF = 0 | если выше или равно |
JB | CF = 1 | если ниже |
JBE | CF = 1 или ZF = 1 | если ниже или равно |
JC | CF = 1 | если перенос |
JE | ZF = 1 | если равно |
JZ | ZF = 1 | если 0 |
JG | ZF = 0 и SF = OF | если больше |
JGE | SF = OF | если больше или равно |
JL | SF <> OF | если меньше |
JLE | ZF=1 или SF <> OF | если меньше или равно |
JNA | CF = 1 и ZF = 1 | если не выше |
JNAE | CF = 1 | если не выше или равно |
JNB | CF = 0 | если не ниже |
JNBE | CF=0 и ZF=0 | если не ниже или равно |
JNC | CF = 0 | если нет переноса |
JNE | ZF = 0 | если не равно |
JNG | ZF = 1 или SF <> OF | если не больше |
JNGE | SF <> OF | если не больше или равно |
JNL | SF = OF | если не меньше |
JNLE | ZF=0 и SF=OF | если не меньше или равно |
JNO | OF=0 | если нет переполнения |
JNP | PF = 0 | если количество единичных битов результата нечетно (нечетный паритет) |
JNS | SF = 0 | если знак плюс (знаковый (старший) бит результата равен 0) |
JNZ | ZF = 0 | если нет нуля |
JO | OF = 1 | если переполнение |
JP | PF = 1 | если количество единичных битов результата четно (четный паритет) |
JPE | PF = 1 | то же, что и JP, то есть четный паритет |
JPO | PF = 0 | то же, что и JNP |
JS | SF = 1 | если знак минус (знаковый (старший) бит результата равен 1) |
JZ | ZF = 1 | если ноль |
Логические условия "больше" и "меньше" относятся к сравнениям целочисленных значений со знаком, а "выше и "ниже" — к сравнениям целочисленных значений без знака. Если внимательно посмотреть, то у многих команд можно заметить одинаковые значения флагов для перехода. Это объясняется наличием нескольких ситуаций, которые могут вызвать одинаковое состояние флагов. В этом случае с целью удобства ассемблер допускает несколько различных мнемонических обозначений одной и той же машинной команды условного перехода. Эти команды ассемблера по действию абсолютно равнозначны, так как это одна и та же машинная команда. Изначально в микропроцессоре i8086 команды условного перехода могли осуществлять только короткие переходы в пределах -128...+127 байт, считая от следующей команды. Начиная с микропроцессора i386, эти команды уже могли выполнять любые переходы в пределах текущего сегмента команд. Это стало возможным за счет введения в систему команд микропроцессора дополнительных машинных команд. Для реализации межсегментных переходов необходимо комбинировать команды условного перехода и команду безусловного перехода jmp. При этом можно воспользоваться тем, что практически все команды условного перехода парные, то есть имеют команды, проверяющие обратные условия.
Применение jcxz/jecxz:
|
|
Команда | Состояние флагов в eflags/flags | Условие перехода |
JCXZ | не влияет | если регистр CX=0 |
JECXZ | не влияет | если регистр ECX=0 |
Команду jcxz/jecxz удобно использовать со всеми командами, использующими регистр ecx/cx для своей работы. Это команды организации цикла и цепочечные команды. Очень важно отметить то, что команда jcxz/jecxz, в отличие от других команд перехода, может выполнять только близкие переходы в пределах -128...+127 байт, считая от следующей команды. Поэтому для нее особенно актуальна проблема передачи управления далее чем в указанном диапазоне. Для этого можно привлечь команду безусловного перехода jmp. Например, команду jcxz/jecxz можно использовать для предварительной проверки счетчика цикла в регистре cx для обхода цикла, если его счетчик нулевой.
Кроме команд условного перехода, область применения которых ясна сразу, также существует команда безусловного перехода. Эта команда чем-то похожа на оператор goto языка Си. Синтаксис:
jmp адресЭта команда передаёт управление на адрес, не проверяя никаких условий. Заметьте, что адрес может быть задан в виде непосредственного значения (метки), регистра или обращения к памяти.
Примеры.
- Нахождение максимального значения в массиве
int max_a (int *a, int n)
{
int i,max=0;
for (i=0;i<n;i++)
{
asm volatile ("movl %[Max], %%eax\n"
"movl %[array], %%ebx\n"
"cmpl %%eax, %%ebx\n" //ebx<eax
"jbe less\n"
"movl %%ebx, %%eax\n"
"less:\n"
"movl %%eax,%[output]\n"
:[output]"=b"(max)
: [array]"d"(a[i]),[Max]"b"(max)
);
}
return max;
}
- Вычисление факториала
int factorial(int n)
{
int res;
asm volatile ("movl %[n], %%ebx\n"
"movl $1, %%eax\n"
"loop_start:\n"
"cmpl $0, %%ebx \n"
"jne not_zero\n"
"jmp loop_end\n"
"not_zero:\n"
"mull %%ebx\n"
"sub $1,%%ebx\n"
"jmp loop_start\n"
"loop_end:\n"
"movl %%eax,%[output]\n"
:[output]"=b"(res)
: [n]"d"(n)
);
return res;
}