Директивы ассемблера
.intel_syntax noprefix
.global main // экспортировать символ main
main:
mov eax, 42 // код в синтаксисе Intel
.att_syntax noprefix
movl $42, eax // noprefix — без символа %
// перед операндами-регистрами
.intel_syntax noprefix
.set answer, 4 * 10
mov eax, answer + 2
nop
.byte 0x90
.skip 5, 0x90
call finish
.word 1, 2, 3 // или .short 1, 2, 3
.int 4, 5, 6
.quad 7, 8, 9
Переходы и метки
.intel_syntax noprefix
.global main
main:
xor eax, eax
xor ebx, ebx
loop:
inc eax
inc ebx
jmp loop
Многофайловая сборка
Каждый ассемблерный файл (.S
) ассемблер превращает
в объектный файл с машинным кодом (.o
), а потом другая
программа — «компоновщик» или «линкер» —
объединяет объектные файлы в исполняемый.
Метки, которые должны быть доступны из других файлов,
необходимо объявлять .global
.
Флаги
Отдельные биты специального регистра rflags
называются флагами.
Большинство арифметических инструкций в результате вычисления результата инструкции устанавливают арифметические флаги.
Флаг ZF (zero) устанавливается, если в результате операции был получен нуль.
Флаг SF (sign) устанавливается, если в результате операции было получено отрицательное число.
Флаг CF (carry) устанавливается, если в результате выполнения операции произошел перенос из старшего бита результата. Например, для сложения CF устанавливается, если результат сложения двух беззнаковых чисел не может быть представлен беззнаковым числом соответствующей разрядности.
Флаг OF (overflow) устанавливается, если в результате выполняния операции произошло переполнение знакового результата. Например, при сложении OF устанавливается, если результат сложения двух знаковых чисел не может быть представлен знаковым числом соответствующей разрядности.
Обратите внимание, что и сложение add, и вычитание sub устанавливают одновременно и флаг CF, и флаг OF. Сложение и вычитание знаковых и беззнаковых чисел выполняется совершенно одинаково, и поэтому используется одна инструкция и для знаковой, и для беззнаковой операции.
stc // установить CF
clc // сбросить CF
setc al // установить al в 0 или 1 в зависимости от флага
// seto, setz, ...
Условные переходы
jz label /* переход, если равно (нуль), ZF == 1 */
jnz label /* переход, если не равно (не нуль), ZF == 0 */
jc label /* переход, если CF == 1 */
jnc label /* переход, если CF == 0 */
jo label /* переход, если OF == 1 */
jno label /* переход, если OF == 0 */
jg label /* переход, если больше для знаковых чисел */
jge label /* переход, если >= для знаковых чисел */
jl label /* переход, если < для знаковых чисел */
jle label /* переход, если <= для знаковых чисел */
ja label /* переход, если > для беззнаковых чисел */
jae label /* переход, если >= (беззнаковый) */
jb label /* переход, если < (беззнаковый) */
jbe label /* переход, если <= (беззнаковый) */
Посчитаем до 20:
.intel_syntax noprefix
main:
xor r12d, r12d
loop:
add r12d, 3
mov edi, r12d
call writei32
mov edi, r12d
sub edi, 20
jl loop
// тут чего-то не хватает
.global main
cmp dst, src // недеструктивная версия sub
test dst, src // недеструктивная версия and
Длинная арифметика
Сложим 128-разрядные числа в rdx:rax и rdi:rsi:
add rax, rsi // сложили младшие половины
jnc 1f
inc rdx // если был перенос, добавляем 1
1:
add rdx, rdi // сложили старшие половины
Чтобы не выписывать такую последовательность инструкций, есть инструкция adc
(add with carry),
которая прибавляет ко второму операнду не только первый операнд, но и значение
флага CF:
add rax, rsi
adc rdx, rdi
Аналог для вычитания — sbb
(subtract with borrow).
Условные инструкции
Инструкция cmovz
(conditional move if ZF) работает как mov
,
если ZF выставлен, или как nop
, если сброшен. Аналогично
с другими условиями.