web-dev-qa-db-ja.com

ADC(キャリー付き追加)をC ++にアセンブリ

アセンブリ命令ADCがあります。これは「キャリーで追加」を意味することがわかりました。しかし、thatが何を意味するのかわかりません。または、この命令をC++で記述する方法。そして、私はそれがADDと同じではないことを知っています。したがって、単純な合計を作成することは正しくありません。

情報:
Windowsでコンパイルされました。 32ビットのWindowsインストールを使用しています。私のプロセッサはIntelのCore2Duoです。

16

ADCはADDと同じですが、プロセッサのキャリーフラグが設定されている場合は1が追加されます。

25
Simone

ここ (壊れた)または ここ から

ただし、Intelプロセッサにはadcと呼ばれる特別な命令があります。このコマンドは、addコマンドと同様に動作します。唯一の余分なことは、値キャリーフラグも追加することです。したがって、これは大きな整数を追加するのに非常に便利な場合があります。 16ビットレジスタを備えた32ビット整数を追加するとします。どうすればそれができますか?さて、最初の整数がレジスタペアDX:AXに保持され、2番目の整数がBX:CXに保持されているとしましょう。こうやって:

add  ax, cx
adc  dx, bx

ああ、最初に、下位16ビットがadd ax、cxによって追加されます。次に、addの代わりにadcを使用して上位16ビットが追加されます。これは、オーバーフローがある場合、キャリービットが上位16ビットに自動的に追加されるためです。したがって、面倒なチェックはありません。このメソッドは64ビットなどに拡張できます...注:32ビット整数の加算が上位16ビットでもオーバーフローすると、結果が正しくなくなり、キャリーフラグが設定されます。 50億から50億を追加します。

これ以降はすべて、実装で定義された動作のゾーンにかなり分類されることを忘れないでください。

これは、VS 2010(32ビット、WinXp)で機能する小さなサンプルです。

警告:$ 7.4/1-「asm宣言は条件付きでサポートされています。その意味は実装で定義されています。[注:通常、実装を介してアセンブラに情報を渡すために使用されます。—endnote]」

int main(){
   bool carry = false;
   int x = 0xffffffff + 0xffffffff;
   __asm {
      jc setcarry
setcarry:
      mov carry, 1
   }
}
9
Chubsdad

ADCの動作は、CとC++の両方でシミュレートできます。次の例では、2つの数値を追加します(1つの符号なしに収まらないほど大きいため、符号なしの配列として格納されます)。

unsigned first[10];
unsigned second[10];
unsigned result[11];

....   /* first and second get defined */

unsigned carry = 0;
for (i = 0; i < 10; i++) {
    result[i] = first[i] + second[i] + carry;
    carry = (first[i] > result[i]);
}
result[10] = carry;

お役に立てれば。

6
Sparky

C++言語にはキャリーフラグの概念がないため、 ADC命令 の周りに組み込み関数ラッパーを作成するのは不格好です。ただし、Intelはとにかくそれを行いました: unsigned char _addcarry_u32 (unsigned char c_in, unsigned a, unsigned b, unsigned * out); 。最後に確認したところ、gccはこれでうまくいきませんでした(キャリー結果をCFに残すのではなく、整数レジスタに保存します)が、Intel独自のコンパイラの方がうまくいくことを願っています。

アセンブリのドキュメントについては、 x86 タグwikiも参照してください。


コンパイラは、単一のレジスタよりも幅の広い整数を追加するときにADCを使用します。追加int64_t 32ビットコード、または__int128_t64ビットコード。

#include <stdint.h>
#ifdef __x86_64__
__int128_t add128(__int128_t a, __int128_t b) { return a+b; }
#endif
    # clang 3.8 -O3  for x86-64, SystemV ABI.
    # __int128_t args passed in 2 regs each, and returned in rdx:rax
    add     rdi, rdx
    adc     rsi, rcx
    mov     rax, rdi
    mov     rdx, rsi
    ret

Godboltコンパイラエクスプローラ からのasm出力。 clangの-fverbose-asmはそれほど厄介ではありませんが、gcc 5.3 /6.1は2つのmov命令を浪費するため、読みにくくなります。

4
Peter Cordes

これにはバグがあります。この入力を試してください:

unsigned first[10] =  {0x00000001};
unsigned second[10] = {0xffffffff, 0xffffffff};

結果は{0、0、1、...}になりますが、結果は{0、0、0、...}になります。

この行を変更する:

carry = (first[i] > result[i]);

これに:

if (carry)
    carry = (first[i] >= result[i]);
else
    carry = (first[i] > result[i]);

それを修正します。

4
Oshkosher
unsigned long result;
unsigned int first;
unsigned int second;

result = first + second;
result += (result & 0x10000) ? 1 : 0;
result &= 0xFFFF
1
Serg Stetsuk