Украина, г. Киев |
Команды ассемблера
Кто-то скажет: а ещё есть цикл 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; }