Для деления применяется команда div, к которой добавляется суффикс типа данных
divq: для деления 64-разрядных чисел
divl: для деления 32-разрядных чисел
divw: для деления 16-разрядных чисел
divb: для деления 8-разрядных чисел
Инструкция div делит два беззнаковых числа. Если операнд 8-разрядный, то div делит регистр AX на операнд, помещая частное в AL, а остаток (по модулю) в AH.
Если операнд 16-разрядный, то инструкция div делит 32-разрядное число в DX:AX на операнд, помещая частное в AX, а остаток в DX.
Если операнд 32-разрядный, div делит 64-битное число в EDX:EAX на операнд, помещая частное в EAX, а остаток в EDX.
И если операнд 64-разрядный, div делит 128-битное число в RDX:RAX на операнд, помещая частное в RAX, а остаток в RDX.
Инструкция idiv имеет аналогичное действие, только в качестве операндов принимает числа со знаком.
Пример деления:
.globl _start .text _start: movq $22, %rax movq $0, %rdx # расширяем RDX нулем для корректности деления movq $5, %rcx divq %rcx # RAX = RAX / RCX # RAX =4 (результат), RDX = 2 (остаток) movq %rax, %rdi # RDI = RAX = 4 movq %rdx, %rdi # RDI = RDX = 2 movq $60, %rax # RAX = 60 syscall
Здесь делим 64-разрядное значение (регистр RAX) на другое 64-разрядное значение (регистр RCX). После деления в RAX помещается результат (в данном случае число 4), а в RDX - остаток (здесь число 2).
При этом в x86-64 нельзя просто разделить два числа одинаковой разрядности, например, одно 8-разрядное на другое 8-разрядное. Если знаменатель представляет собой 8-битное значение, числитель должен быть 16-битным значением. Если же нужно разделить одно 8-битное значение без знака на другое, то необходимо дополнить числитель нулями до 16 бит, загрузив числитель в регистр AL, а затем переместив 0 в регистр AH. Отсутствие расширения AL до нуля перед выполнением div может привести к тому, что x86-64 выдаст некорректный результат.
Если нужно разделить два беззнаковых 16-разрядных числа, то надо расширить регистр AX (который содержит числитель) до регистра DX. Для этого достаточно загрузить 0 в регистр DX.
Если нужно разделить одно беззнаковое 32-битное значение на другое, перед делением надо расширить регистр EAX нулями до EDX (загрузив 0 в EDX).
И чтобы разделить одно 64-битное число на другое, перед делением нужно расширить RAX нулями до RDX (поместив 0 в RDX).
Перед делением целочисленных значений со знаком одинаковой разрядности с помощью idiv необходимо расширить знаком AL в AX (поместив знаковый бит числа в AH),
AX в DX (знаковый бит в DX), EAX в EDX (знаковый бит в EDX) или RAX в RDX (знаковый бит в RDX). Для этого можно использовать следующие инструкции cbw, cwd, cdq
или
cqo
cbw: преобразует байт в AL в слово в AX через расширение знаком
cwd: преобразует 16-разрядное число в AX в 32-разрядное в DX:AX через расширение знаком
cdq: преобразует 32-разрядное число в EAX в 64-разрядное в EDX:EAX с помощью расширения знаком
cqo: преобразует 64-разрядное число в RAX в 128-разрядное в RDX:RAX через расширение знаком
cwde: преобразует 16-разрядное число в AX в 32-разрядное в EAX с помощью расширения знаком
cdqe: преобразует 32-разрядное число в EAX в 64-разрядное в RAX с помощью расширения знаком
Эти инструкции не принимают операндов. Отсутствие расширения знаком может привести к неверным результатам. Например, разделим два 64-разрядных числа со знаком:
.globl _start .text _start: movq $-5, %rcx movq $-22, %rax cqo # расширяем регистр RDX знаковым битом из RAX idivq %rcx # RAX = RAX / RCX # RAX =4 (результат), RDX = -2 (остаток) movq %rax, %rdi # RDI = RAX = 4 movq $60, %rax # RAX = 60 syscall