Для манипуляции отдельными битами числа ассемблер GAS предоставляет ряд логических операторов-инструкций:
and source, dest or source, dest xor source, dest neg dest not dest
Первые три инструкции имеют два операнда.
Инструкции and
, or
и xor
также влияют на регистр флагов. Они всегда сбрасывают флаг переноса CF и переполнения OF. Флаг нуля ZF устанавливается,
если результат равен нулю. Флаг знака SF всегда получает значение старшего бита результата.
В зависимости от используемых типов данных эти инструкции принимают соответствующие суффиксы: q (для 64-разрядных чисел), l (для 32-разрядных чисел), w (для 16-разрядных чисел), b (для 8-разрядных чисел).
Инструкция AND выполняет поразрядное логическое умножение. Операция логического умножения возвращает 1, если соответствующие разряды обоих операндов равны 1. Например,
.globl _start .text _start: movq $12, %rdi # помещаем в регистр RDI число 12 - 1100 andq $6, %rdi # rdi = rdi AND 6 = 1100 AND 0110 = 0100 = 4 movq $60, %rax syscall
Здесь в 64-битный регистр RDI помещается число 12, которое в бинарной форме равно 1100
2. Затем инструкция AND
поразрядно умножает его на число 6, которое в бинарной форме равно 0110
2.
1100 * 0110 = 0100
То есть в итоге в регистре RDI будет число 0100 или 4 в десятичной системе.
В ассемблере также есть инструкция test - она также умножает поразрядно биты обоих операндов, однако первый операнд не изменяется. Ее цель установить состояние флагов, в частности, флаг нуля:
.globl _start .text _start: movq $12, %rbx test $12, %rbx # проверяем значение регистра rbx je zero movq $1, %rdi jmp exit zero: movq $0, %rdi exit: movq $60, %rax syscall
Здесь мы проверяем значение регистра RBX - если оно равно 0, то будет установлен флаг нуля. При этом значение регистра RBX не меняется.
Инструкция OR выполняет поразрядное логическое сложение. Операция логического слоожения возвращает 1, если хотя бы один из соответствующих разрядов обоих операндов равен 1. Например,
.globl _start .text _start: movq $12, %rdi # помещаем в регистр rdi число 12 - 1100 orq $6, %rdi # rdi = rdi OR 6 = 1100 OR 0110 = 1110 = 14 movq $60, %rax syscall
Здесь в 64-битный регистр RDI помещается число 12, которое в бинарной форме равно 1100
2. Затем инструкция OR
поразрядно складывает на число 6:
1100 + 0110 = 1110
То есть в итоге в регистре W0 будет число 11102 или 1410 в десятичной системе.
Инструкция XOR выполняет поразрядную операцию исключающего ИЛИ, при которой возвращается 1, если соответствующие разряды обоих операндов не равны друг другу. если соответствующие разряды обоих операндов равны, то возвращается 0. Например,
.globl _start .text _start: movq $12, %rdi # помещаем в регистр rdi число 12 - 1100 xorq $6, %rdi # rdi = rdi XOR 6 = 1100 XOR 0110 = 1010 = 10 movq $60, %rax syscall
В результате в регистре RDI будет число 10102 или 1010 в десятичной системе.
1100 ^ 0110 = 1010
Стоит отметить, что операция xor
нередко применяется для обнуления содержимого регистра, например
xorq %rdi, %rdi # RDI = 0
Инструкция NOT выполняет поразрядное отрицание и принимает один параметр:
not dest
Суть операции: если разряд операнда dest равен 1, то он меняется на 0. И наоборот - если разряд в dest равен 0, то он меняется на 1. Например,
.globl _start .text _start: xorq %rdi, %rdi # rdi = 0 movq $12, %rdi # помещаем в регистр rdi число 12 - 00000000 00000000 00000000 00001100 notq %rdi # rdi =NOT(rdi)=NOT(12)= 11111111 11111111 11111111 11110011 movq $60, %rax syscall
Здесь в регистр RDI помещается число 12, которое в бинарной форме равно 1100
2. Однако поскольку регистр у нас 64-битный,
то все передние разряды будут представлять нули:
00000000 00000000 00000000 00001100
Поразрядное отрицание или инверсия изменяет значения битов на противоположное (изменяются все 64 бита в регистре). В итоге мы получим число
11111111 11111111 11111111 11110011
Что это за число, зависит от контекста. Так в дополнительном коде старший бит рассматривается как знаковый бит - если он равен 1, то число отрицательное. Соответственно инвертированное выше число будет интерпретироваться как -13. Если число рассматривается как число без знака, которое всегда положительное, то результат: 18446744073709551603
Кроме обычной инверсии в ассемблере есть арифметическое отрицацие, которое выполняет инструкция NEG. Она также принимает один операнд:
neg dest
фактически значение операнда будет умножаться на -1. Таким образом, мы сможем получить из положительного числа отрицательное, а из отрицательного - положительное. Например:
.globl _start .text _start: movq $-12, %rdi negq %rdi # rdi = -1 * rdi = -1 * -12 = 12 movq $60, %rax syscall