Как я могу умножить два 64-разрядных числа с помощью ассемблера x86?

Как бы я пошел...

  • умножение двух 64-битных чисел

  • умножение двух 16-значных шестнадцатеричных чисел

...использование языка ассемблера.

мне разрешено использовать только регистры %eax, %ebx, %ecx, %edx и стек.

EDIT: О, я использую синтаксис ATT на x86
EDIT2: не разрешено декомпилировать в сборку...

12 ответов


используйте то, что, вероятно, должно быть вашим учебником курса, "Искусство ассемблера"Рэндалла Хайда.

посмотреть 4.2.4 - Повышенной Точности Умножения

хотя обычно достаточно умножить 8x8, 16x16 или 32x32, бывают случаи, когда вы можете захотеть умножить большие значения вместе. Для расширенного прецизионного умножения вы будете использовать инструкции x86 single operand MUL и IMUL ..

пожалуй, самая важно помнить при выполнении расширенного прецизионного умножения необходимо также выполнить сложение с несколькими точностями одновременно. Сложение всех частичных продуктов требует нескольких дополнений, которые дадут результат. В следующем списке показан правильный способ умножения двух 64-разрядных значений на 32-разрядном процессоре ..

(см. ссылку для полного списка сборки и иллюстраций.)


Так ты на x86 нужно 4 Малл инструкции. Разделить на две 32бит 64бит количество слов и умножить минимум слов самой низкой и 2-й низкой словом результат, тогда как пар низкого и высокого слова с разных номеров (они идут на 2-й и 3-й низкой словом результат) и, наконец, оба высокие слова в 2 высоких слов о результате. Добавьте их все вместе, не забывая иметь дело с carry. Вы не указали макет памяти входов и выходов, поэтому это невозможно написать пример кода.


Если это было 64x86,

function(x, y, *lower, *higher)
movq %rx,%rax     #Store x into %rax
mulq %y           #multiplies %y to %rax
#mulq stores high and low values into rax and rdx.
movq %rax,(%r8)   #Move low into &lower
movq %rdx,(%r9)   #Move high answer into &higher

этот код предполагает, что вы хотите x86 (не x64 код), что вы, вероятно, хотите только 64-битный продукт, и что вас не волнует переполнение или подписанные номера. (Подписанная версия аналогична).

MUL64_MEMORY:
     mov edi, val1high
     mov esi, val1low
     mov ecx, val2high
     mov ebx, val2low
MUL64_EDIESI_ECXEBX:
     mov eax, edi
     mul ebx
     xch eax, ebx  ; partial product top 32 bits
     mul esi
     xch esi, eax ; partial product lower 32 bits
     add ebx, edx
     mul ecx
     add ebx, eax  ; final upper 32 bits
; answer here in EBX:ESI

Это не соответствует точным ограничениям регистра OP, но результат полностью вписывается в регистры, предлагаемые x86. (Этот код непроверен, но я думаю, что это правильно).

[Примечание: я передал (мой) этот ответ из другого вопроса, который был закрыт, потому что нет из остальных "ответов" здесь прямо ответили на вопрос.


вы можете указать что сборка, которую вы используете. Общие методы перекрестно применимы (обычно), но мнемоника почти всегда отличается между платформами. :-)


Это зависит от того, какой язык вы используете. Из того, что я помню из обучения сборке MIPS, есть переход от высшего командования и переход от команды Lo, или mflo и mfhi. mfhi хранит верхние 64bits пока mflo хранит более низкие 64bits полного числа.


ах сборка, было некоторое время, так как я использовал его. поэтому я предполагаю, что реальная проблема заключается в том, что микроконтроллер (то, что я использовал для написания кода в сборке), над которым вы работаете, не имеет 64-битных регистров? если это так, вы собираетесь разбить числа, с которыми вы работаете, на части и выполнить многократное умножение с частями.

Это звучит так, как будто это домашнее задание по тому, как вы его сформулировали, поэтому я не буду много писать далее :П


просто сделайте нормальное длинное умножение, как если бы вы умножали пару 2-значных чисел, за исключением того, что каждая "цифра" на самом деле является 32-разрядным целым числом. Если вы умножаете два числа по адресам X и Y и сохраняете результат в Z, то то, что вы хотите сделать (в псевдокоде):

Z[0..3] = X[0..3] * Y[0..3]
Z[4..7] = X[0..3] * Y[4..7] + X[4..7] * Y[0..3]

обратите внимание, что мы отбрасываем верхние 64 бита результата (так как 64-разрядное число, умноженное на 64-разрядное число, является 128-разрядным числом). Также обратите внимание, что это предполагает little-endian. Кроме того, будьте осторожны о знаковом и беззнаковом умножении.


найдите компилятор C, который поддерживает 64bit (GCC делает IIRC), скомпилируйте программу, которая делает именно это, а затем получите разборку. GCC может выплюнуть его самостоятельно, и вы можете получить его из объектного файла с помощью правильных инструментов.

OTOH их является 32bX32b = 64b op на x86

a:b * c:d = e:f
// goes to
e:f = b*d;
x:y = a*d;  e += x;
x:y = b*c;  e += x;

все остальное переливается

(непроверено)

редактировать только без знака


Я держу пари, что вы студент, так что посмотрите, если вы можете сделать эту работу: сделать это слово за словом, и использовать битовые сдвиги. Придумать наиболее эффективное решение. Остерегайтесь знака.


Если вы хотите, 128 режим попробовать это...

__uint128_t AES::XMULTX(__uint128_t TA,__uint128_t TB)
{
    union
    {
        __uint128_t WHOLE;
        struct
        {
            unsigned long long int LWORDS[2];
        } SPLIT;
    } KEY;
    register unsigned long long int __XRBX,__XRCX,__XRSI,__XRDI;
    __uint128_t RESULT;

    KEY.WHOLE=TA;
    __XRSI=KEY.SPLIT.LWORDS[0];
    __XRDI=KEY.SPLIT.LWORDS[1];
    KEY.WHOLE=TB;
    __XRBX=KEY.SPLIT.LWORDS[0];
    __XRCX=KEY.SPLIT.LWORDS[1];
    __asm__ __volatile__(
                 "movq          %0,             %%rsi           \n\t"       
                 "movq          %1,             %%rdi           \n\t"
                 "movq          %2,             %%rbx           \n\t"
                 "movq          %3,             %%rcx           \n\t"
                 "movq          %%rdi,          %%rax           \n\t"
                 "mulq          %%rbx                           \n\t"
                 "xchgq         %%rbx,          %%rax           \n\t"
                 "mulq          %%rsi                           \n\t"
                 "xchgq         %%rax,          %%rsi           \n\t"
                 "addq          %%rdx,          %%rbx           \n\t"
                 "mulq          %%rcx                           \n\t"
                 "addq          %%rax,          %%rbx           \n\t"
                 "movq          %%rsi,          %0              \n\t"
                 "movq          %%rbx,          %1              \n\t"
                 : "=m" (__XRSI), "=m" (__XRBX)
                 : "m" (__XRSI),  "m" (__XRDI), "m" (__XRBX), "m" (__XRCX)
                 : "rax","rbx","rcx","rdx","rsi","rdi"
                 );
    KEY.SPLIT.LWORDS[0]=__XRSI;
    KEY.SPLIT.LWORDS[1]=__XRBX;
    RESULT=KEY.WHOLE;
    return RESULT;
}

Если вы хотите 128-битное умножение, то это должно работать в формате AT&T.

__uint128_t FASTMUL128(const __uint128_t TA,const __uint128_t TB)
{
    union
    {
        __uint128_t WHOLE;
        struct
        {
            unsigned long long int LWORDS[2];
        } SPLIT;
    } KEY;
    register unsigned long long int __RAX,__RDX,__RSI,__RDI;
    __uint128_t RESULT;

KEY.WHOLE=TA;
__RAX=KEY.SPLIT.LWORDS[0];
__RDX=KEY.SPLIT.LWORDS[1];
KEY.WHOLE=TB;
__RSI=KEY.SPLIT.LWORDS[0];
__RDI=KEY.SPLIT.LWORDS[1];
__asm__ __volatile__(
    "movq           %0,                             %%rax                   \n\t"
    "movq           %1,                             %%rdx                   \n\t"
    "movq           %2,                             %%rsi                   \n\t"
    "movq           %3,                             %%rdi                   \n\t"
    "movq           %%rsi,                          %%rbx                   \n\t"
    "movq           %%rdi,                          %%rcx                   \n\t"
    "movq           %%rax,                          %%rsi                   \n\t"
    "movq           %%rdx,                          %%rdi                   \n\t"
    "xorq           %%rax,                          %%rax                   \n\t"
    "xorq           %%rdx,                          %%rdx                   \n\t"
    "movq           %%rdi,                          %%rax                   \n\t"
    "mulq           %%rbx                                                   \n\t"
    "xchgq          %%rbx,                          %%rax                   \n\t"
    "mulq           %%rsi                                                   \n\t"
    "xchgq          %%rax,                          %%rsi                   \n\t"
    "addq           %%rdx,                          %%rbx                   \n\t"
    "mulq           %%rcx                                                   \n\t"
    "addq           %%rax,                          %%rbx                   \n\t"
    "movq           %%rsi,                          %%rax                   \n\t"
    "movq           %%rbx,                          %%rdx                   \n\t"
    "movq           %%rax,                          %0                      \n\t"
    "movq           %%rdx,                          %1                      \n\t"
    "movq           %%rsi,                          %2                      \n\t"
    "movq           %%rdi,                          %3                      \n\t"
    : "=m"(__RAX),"=m"(__RDX),"=m"(__RSI),"=m"(__RDI)
    :  "m"(__RAX), "m"(__RDX), "m"(__RSI), "m"(__RDI)
    : "rax","rbx","ecx","rdx","rsi","rdi"
);
KEY.SPLIT.LWORDS[0]=__RAX;
KEY.SPLIT.LWORDS[1]=__RDX;
RESULT=KEY.WHOLE;
return RESULT;
}