x86 アセンブリ言語を使用して 2 つの 64 ビット数値を乗算するにはどうすればよいですか?
質問
どうやって行けばいいのでしょうか...
2 つの 64 ビット数値の乗算
2 つの 16 桁の 16 進数の乗算
...アセンブリ言語を使用します。
使用できるのはレジスタ %eax、%ebx、%ecx、%edx、およびスタックのみです。
編集:ああ、私は x86 で ATT 構文を使用しています
編集2:アセンブリに逆コンパイルすることは許可されていません...
解決
おそらくコースの教科書になるはずの、Randall Hyde の「The Art of Assembly Language」を使用してください。
通常は 8x8、16x16、または 32x32 の乗算で十分ですが、より大きな値を一緒に乗算したい場合もあります。拡張精度乗算には、x86 単一オペランド MUL 命令と IMUL 命令を使用します。
おそらく覚えておくべき最も重要なこと 拡張精度の乗算を実行する場合は、同時に多精度の加算も実行する必要があります。. 。すべての部分積を加算するには、結果を生成するためにいくつかの加算が必要です。次のリストは、32 ビット プロセッサで 2 つの 64 ビット値を乗算する適切な方法を示しています。
(完全なアセンブリリストと図については、リンクを参照してください。)
他のヒント
これが 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 を使用しているため、4 つの mull 命令が必要です。64 ビット量を 2 つの 32 ビット ワードに分割し、下位ワードを結果の最下位ワードと 2 番目に下位のワードに乗算し、その後、異なる数値からの下位ワードと上位ワードの両方のペアを乗算します (結果の 2 番目と 3 番目に下位のワードになります)。最後に、両方の上位ワードが結果の上位 2 ワードに変換されます。キャリーへの対処を忘れずにそれらをすべて合計します。入力と出力のメモリ レイアウトが指定されていないため、サンプル コードを記述することはできません。
このコードは、(x64 コードではなく) x86 が必要であり、おそらく 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 アセンブリの学習で覚えていることによると、Move From High コマンドと Move From Lo コマンド、つまり mflo と mfhi があります。mfhi は上位 64 ビットを格納し、mflo は合計数の下位 64 ビットを格納します。
ああ、アセンブリ、久しぶりに使いました。ここでの本当の問題は、あなたが取り組んでいるマイクロコントローラー(とにかくアセンブリでコードを書いていたもの)に64ビットレジスタがないことだと思いますか?その場合は、作業中の数値を分割し、それらの部分に対して複数の乗算を実行することになります。
あなたの言い方からすると宿題のように聞こえるので、これ以上詳しくは説明しません:P
それぞれの「桁」が実際には 32 ビットの整数である点を除き、2 桁の数値のペアを掛けているかのように、通常の long 乗算を行うだけです。アドレス X と Y で 2 つの数値を乗算し、その結果を 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 ビットの数値になるため)。また、これはリトルエンディアンを前提としていることに注意してください。また、符号付き乗算と符号なし乗算にも注意してください。
64 ビットをサポートする C コンパイラを見つけて (GCC は IIRC を実行します)、それを行うプログラムをコンパイルしてから、逆アセンブリを取得します。GCC はそれ自体でそれを吐き出すことができ、適切なツールを使用してオブジェクト ファイルからそれを取り出すことができます。
OTOH は x86 で 32bX32b = 64b 演算です
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;
}