Арифметика: add, sub, mul, div

Урок разбирает арифметические команды и их особенности — особенно неочевидное деление.

Целочисленная арифметика в процессоре работает в фиксированной разрядности, поэтому за результатом следят флаги переполнения.

Сложение и вычитание

Самые простые команды. add и sub меняют приёмник и выставляют флаги:

add rax, rbx    ; rax = rax + rbx
sub rax, 10     ; rax = rax - 10
inc rax         ; rax = rax + 1 (короче, чем add rax,1)
dec rax         ; rax = rax - 1

Умножение и его коварство

Команда mul rbx умножает rax на rbx, но результат может не влезть в 64 бита, поэтому он кладётся в пару регистров: старшая половина в rdx, младшая в rax. Для знакового умножения есть imul, у которого удобнее формы: imul rax, rbx, 5.

Деление: главная ловушка

Деление div rbx устроено непривычно. Делимое берётся из пары rdx:rax, и результат раскладывается так:

РегистрДо divПосле div rbx
rdx:raxделимое (128 бит)
raxчастное
rdxостаток

Перед делением старшую часть rdx обязательно очищают (для беззнакового — xor rdx, rdx), иначе делимое будет огромным и div «упадёт».

Как работает под капотом

Смоделируем целочисленное деление с частным и остатком на Python — ровно то, что делает div:

dividend = 47
divisor = 5
quotient = dividend // divisor   # частное -> rax
remainder = dividend % divisor   # остаток -> rdx
print("47 / 5 =", quotient, "остаток", remainder)

Вывод:

47 / 5 = 9 остаток 2

Процессор за одну команду div получает и частное, и остаток — именно поэтому в C операции / и % часто компилируются в одно деление.

Частые ошибки

  • Забыть очистить rdx перед div. Самая частая причина «деления на ноль»/аварии, хотя делитель не ноль.
  • Перепутать mul и imul. mul беззнаковое, imul знаковое; для отрицательных нужен imul.
  • Не помнить, что mul портит rdx. Если там были данные, они затрутся старшей половиной произведения.

Итог

  • add/sub/inc/dec — простая арифметика с обновлением флагов.
  • mul/imul кладут результат в пару rdx:rax.
  • div делит rdx:rax, даёт частное в rax и остаток в rdx.
  • Перед div обязательно очищают rdx (xor rdx, rdx).
Проверьте себя
1. Куда div rbx помещает остаток от деления?
AВ rax
BВ rbx
CВ rdx
DВ rcx
2. Что нужно сделать перед беззнаковым делением div?
AОчистить rdx (xor rdx, rdx)
BОчистить rax
CУстановить флаг CF
DСкопировать делитель в rdx
3. Чем mul отличается от imul?
Amul работает только с rax
Bmul беззнаковое, imul знаковое
Cimul медленнее в 100 раз
DОни идентичны