| Россия |
Команды ассемблера
Кто-то скажет: а ещё есть цикл for()! Но цикл
for(init; cond; incr)
{
body;
}
эквивалентен такой конструкции:
init;
while(cond)
{
body;
incr;
}
Таким образом, нам достаточно и уже рассмотренных двух видов циклов.
Логическая арифметика
Кроме выполнения обычных арифметических вычислений, можно проводить и логические, то есть битовые.
and источник, приёмник or источник, приёмник xor источник, приёмник not операнд test операнд_1, операнд_2
Команды and, or и xor ведут себя так же, как и операторы языка Си &, |, ^. Эти команды устанавливают флаги согласно результату.
Команда not инвертирует каждый бит операнда (изменяет на противоположный), так же как и оператор языка Си
.
Команда test выполняет побитовое И над операндами, как и команда and, но, в отличие от неё, операнды не изменяет, а только устанавливает флаги. Её также называют командой логического сравнения, потому что с её помощью удобно проверять, установлены ли определённые биты. Например, так:
testb $0b00001000, %al /* установлен ли 3-й (с нуля) бит? */
je not_set
/* нужные биты установлены */
not_set:
/* биты не установлены */
Обратите внимание на запись константы в двоичной системе счисления: используется префикс 0b.
Команду test можно применять для сравнения значения регистра с нулём:
testl %eax, %eax
je is_zero
/* %eax != 0 */
is_zero:
/* %eax == 0 */
Intel Optimization Manual рекомендует использовать test вместо cmp для сравнения регистра с нулём3Intel® 64 and IA-32 Architectures Optimization Reference Manual, 3.5.1.7 Compares.
Ещё следует упомянуть об одном трюке с xor. Как вы знаете, a XOR a = 0. Пользуясь этой особенностью, xor часто применяют для обнуления регистров:
xorl %eax, %eax /* теперь %eax == 0 */
Почему применяют xor вместо mov? Команда xor короче, а значит, занимает меньше места в процессорном кэше, меньше времени тратится на декодирование, и программа выполняется быстрее. Но эта команда устанавливает флаги. Поэтому, если вам нужно сохранить состояние флагов, применяйте mov4Intel® 64 and IA-32 Architectures Optimization Reference Manual, 3.5.1.6 Clearing Registers and Dependency Breaking Idioms.
Иногда для обнуления регистра применяют команду sub. Помните, она тоже устанавливает флаги.
subl %eax, %eax /* теперь %eax == 0 */
К логическим командам также можно отнести команды сдвигов:
/* Shift Arithmetic Left/SHift logical Left */ sal/shl количество_сдвигов, назначение /* SHift logical Right */ shr количество_сдвигов, назначение /* Shift Arithmetic Right */ sar количество_сдвигов, назначение
количество_сдвигов может быть задано непосредственным значением или находиться в регистре %cl. Учитываются только младшие 5 бит регистра %cl, так что количество сдвигов может варьироваться в пределах от 0 до 31.
Принцип работы команды shl:
До сдвига:
+---+ +----------------------------------+
| ? | | 10001000100010001000100010001011 |
+---+ +----------------------------------+
Флаг CF Операнд
Сдвиг влево на 1 бит:
+---+ +----------------------------------+
| 1 | <-- | 00010001000100010001000100010110 | <-- 0
+---+ +----------------------------------+
Флаг CF Операнд
Сдвиг влево на 3 бита:
+----+ +---+ +----------------------------------+
| 10 | | 0 | <-- | 01000100010001000100010001011000 | <-- 000
+----+ +---+ +----------------------------------+
Улетели Флаг CF Операнд
в никуда
Принцип работы команды shr:
До сдвига:
+----------------------------------+ +---+
| 10001000100010001000100010001011 | | ? |
+----------------------------------+ +---+
Операнд Флаг CF
Логический сдвиг вправо на 1 бит:
+----------------------------------+ +---+
0 -- > | 01000100010001000100010001000101 | -- > | 1 |
+----------------------------------+ +---+
Операнд Флаг CF
Логический сдвиг вправо на 3 бита:
+----------------------------------+ +---+ +----+
000 -- > | 00010001000100010001000100010001 | -- > | 0 | | 11 |
+----------------------------------+ +---+ +----+
Операнд Флаг CF Улетели
в никуда
Эти две команды называются командами логического сдвига, потому что они работают с операндом как с массивом бит. Каждый "выдвигаемый " бит попадает в флаг cf, причём с другой стороны операнда "вдвигается " бит 0. Таким образом, в флаге cf оказывается самый последний "выдвинутый " бит. Такое поведение вполне допустимо для работы с беззнаковыми числами, но числа со знаком будут обработаны неверно из-за того, что знаковый бит может быть потерян.
Для работы с числами со знаком существуют команды арифметического сдвига. Команды shl и sal выполняют полностью идентичные действия, так как при сдвиге влево знаковый бит не теряется (расширение знакового бита влево становится новым знаковым битом). Для сдвига вправо применяется команда sar. Она "вдвигает " слева знаковый бит исходного значения, таким образом сохраняя знак числа:
До сдвига:
+----------------------------------+ +---+
| 10001000100010001000100010001011 | | ? |
+----------------------------------+ +---+
Операнд Флаг CF
старший бит равен 1 == >
== > значение отрицательное == >
== > "вдвинуть " бит 1 ---+
|
+-------------------------------+
|
V Арифметический сдвиг вправо на 1 бит:
+----------------------------------+ +---+
1 -- > | 11000100010001000100010001000101 | -- > | 1 |
+----------------------------------+ +---+
Операнд Флаг CF
Арифметический сдвиг вправо на 3 бита:
+----------------------------------+ +---+ +----+
111 -- > | 11110001000100010001000100010001 | -- > | 0 | | 11 |
+----------------------------------+ +---+ +----+
Операнд Флаг CF Улетели
в никуда
Многие программисты Си знают об умножении и делении на степени двойки (2, 4, 8…) при помощи сдвигов. Этот трюк отлично работает и в ассемблере, используйте его для оптимизации.
Кроме сдвигов обычных, существуют циклические сдвиги:
/* ROtate Right */ ror количество_сдвигов, назначение /* ROtate Left */ rol количество_сдвигов, назначение
Объясню на примере циклического сдвига влево на три бита: три старших ( "левых ") бита "выдвигаются " из регистра влево и "вдвигаются " в него справа. При этом в флаг cfзаписывается самый последний "выдвинутый " бит.
Принцип работы команды rol:
До сдвига:
+---+ +----------------------------------+
| ? | | 10001000100010001000100010001011 |
+---+ +----------------------------------+
Флаг CF Операнд
Циклический сдвиг влево на 1 бит:
+---+ 1 1 +----------------------------------+
| 1 | <--+--- | 00010001000100010001000100010111 | ---+
+---+ | +----------------------------------+ |
Флаг CF V Операнд ^
| |
+------------------- >--- >--- >----------------+
1
Циклический сдвиг влево на 3 бита:
+---+ 0 100 +----------------------------------+
| 0 | <--+--- | 01000100010001000100010001011100 | ---+
+---+ | +----------------------------------+ |
Флаг CF V Операнд ^
| |
+------------------- >--- >--- >----------------+
100
Принцип работы команды ror:
До сдвига:
+----------------------------------+ +---+
| 10001000100010001000100010001011 | | ? |
+----------------------------------+ +---+
Операнд Флаг CF
Циклический сдвиг вправо на 1 бит:
+----------------------------------+ 1 1 +---+
+--- | 11000100010001000100010001000101 | ---+-- > | 1 |
| +----------------------------------+ | +---+
^ Операнд V Флаг CF
| |
+------------------- <--- <--- <----------------+
1
Циклический сдвиг вправо на 3 бита:
+----------------------------------+ 011 0 +---+
+--- | 01110001000100010001000100010001 | ---+-- > | 0 |
| +----------------------------------+ | +---+
^ Операнд V Флаг CF
| |
+------------------- <--- <--- <----------------+
011
Существует ещё один вид сдвигов - циклический сдвиг через флаг cf. Эти команды рассматривают флаг cf как продолжение операнда.
/* Rotate through Carry Right */ rcr количество_сдвигов, назначение /* Rotate through Carry Left */ rcl количество_сдвигов, назначение
Принцип работы команды rcl:
До сдвига:
+---+ +----------------------------------+
| X | | 10001000100010001000100010001011 |
+---+ +----------------------------------+
Флаг CF Операнд
Циклический сдвиг влево через CF на 1 бит:
X +---+ +----------------------------------+
+- <- | 1 | <--- | 0001000100010001000100010001011X | ---+
| +---+ +----------------------------------+ |
V Флаг CF Операнд ^
| |
+------------------------------ >--- >--- >----------------+
Циклический сдвиг влево через CF на 3 бита:
X10 +---+ +----------------------------------+
+- <- | 0 | <--- | 01000100010001000100010001011X10 | ---+
| +---+ +----------------------------------+ |
V Флаг CF Операнд ^
| |
+------------------------------ >--- >--- >----------------+
Принцип работы команды rcr:
До сдвига:
+----------------------------------+ +---+
| 10001000100010001000100010001011 | | X |
+----------------------------------+ +---+
Операнд Флаг CF
Циклический сдвиг вправо через CF на 1 бит:
+----------------------------------+ +---+ X
+--- | X1000100010001000100010001000101 | --- > | 1 | - >-+
| +----------------------------------+ +---+ |
^ Операнд Флаг CF V
| |
+------------------- <--- <--- <---------------------------+
Циклический сдвиг вправо через CF на 3 бита:
+----------------------------------+ +---+ 11X
+--- | 11X10001000100010001000100010001 | --- > | 0 | - >-+
| +----------------------------------+ +---+ |
^ Операнд Флаг CF V
| |
+------------------- <--- <--- <---------------------------+
Эти сложные циклические сдвиги вам редко понадобятся в реальной работе, но уже сейчас нужно знать, что такие инструкции существуют, чтобы не изобретать велосипед потом. Ведь в языке Си циклический сдвиг производится приблизительно так:
int main()
{
int a = 0x11223344;
int shift_count = 8;
a = (a < < shift_count) | (a > > (32 - shift_count));
printf( "%x\n ", a);
return 0;
}