X86アセンブラ/算術演算命令
算術演算命令
[編集]算術演算命令は、 2 つのオペランドを取る。デスティネーション (転送先) とソース (転送元) である。 デスティネーションは、レジスターか、メモリーでの位置でなくてはならない。 ソースは、メモリーでの位置、レジスター、定数のどれかでなくてはならない。 二つのうち少なくとも一つは、レジスターでなくてはならない。 操作は、ソースとデスティネーションの両方を、メモリーでの位置にすることはできないためである。
add src, dest | GAS文法 |
add dest, src | MASM文法 |
この命令は、src
をdest
に加算する。
計算結果は、dest
に格納される。
つまり、MASM や NASM の文法を使う場合には、結果は最初の引数に格納される。 GAS の文法を使う場合には、結果は 2 番目の引数に格納される。
sub src, dest | GAS文法 |
sub dest, src | MASM文法 |
この命令は ADD と同じようであるが、デスティネーションからソースを減算する。
mul arg
この命令は「arg」に A レジスターのバイト長に応じた値を乗算する。 下表を参照のこと。
オペランドのサイズ 1 バイト 2 バイト 4 バイト その他のオペランド AL AX EAX 結果の上位部分の格納先 AH DX EDX 結果の下位部分の格納先 AL AX EAX
2 番目の場合、古いプロセッサー向けに書かれたコードとの後方互換性のため、ターゲットは EAX ではない。
imul arg
MUL と同じであるが、符号付き数値のみを扱う。
div arg
この命令は、レジスターの内容を「arg」で除算する。 下表を参照のこと。
除数のサイズ 1 バイト 2 バイト 4 バイト 被除数 AX DX:AX EDX:EAX 剰余の格納先 AH DX EDX 商の格納先 AL AX EAX
%eax が設定されていれば、「cltd (MASM の文法では CDQ)」を使うことで、%edx を用意できる。
cltd idiv %ebx, %eax
商が商の格納先レジスターに合わなかった場合、数値オーバーフロー例外が発生する。 操作の後、全てのフラグは未定義の状態になる。
idiv arg
DIV と同じであるが、符号付き数値のみを扱う。
neg arg
引数の符号を算術的に反転させる。つまり 2 の補数を取る。
コード例
[編集]実際に、C言語のプログラムをGCCでアセンブリコードに変換してみて、どのような命令が使われているかを学びましょう。
- add.c
int add(int a, int b) { return a + b; }
-S
でアセンブリコードに変換すると、下記のようになります。
- アセンブリコードに変換後
.file "add.c"
.text
.p2align 4
.globl add
.type add, @function
add:
.LFB0:
.cfi_startproc
leal (%rdi,%rsi), %eax
ret
.cfi_endproc
.LFE0:
.size add, .-add
.ident "GCC: (FreeBSD Ports Collection) 12.0.0 20211010 (experimental)"
.section .note.GNU-stack,"",@progbits
- 加算を行っている部分
leal (%rdi,%rsi), %eax
- 関数の引数はレジスタで渡され、第一引数はDIレジスターに第二引数はSIレジスター渡されます。
- 関数の戻り地はEAXレジスターで返されます。
- LEAL命令は、Load Effective Address Long で、第一オペランドで指定された「アドレス」を第二オペランドに代入します。
- (%rdi,%rsi)はSIレジスターにDIレジスター分オフセットしたアドレスの値を参照しますので、EAXレジスターにはSIレジスターの値にDIレジスターの値を足した値が入ります。
- AMD64にはそのものズバリのADD命令があるのですが、LEA命令にはADD命令とは別の演算器(アドレス演算器)が使われ、2つの加算器は並行して動作が可能なのでADD命令に使う演算器を加算以外のより高度な演算に開けるためにLEA命令を生成しています。
以下、補足
[編集]キャリーあり算術演算命令
[編集]adc src, dest | GAS文法 |
adc dest, src | MASM文法 |
キャリーあり加算。
dest
にsrc
+ キャリーフラグ
を加算し、結果をdest
に格納する。
通常、加算命令でレジスターの 2 倍の長さの値を扱うのを助ける。
以下の例では、sourceは 64 ビットの数値であり、destinationに加算される。
mov eax, [source] ; read low 32 bits
mov edx, [source+4] ; read high 32 bits
add [destination], eax ; add low 32 bits
adc [destination+4], edx ; add high 32 bits, plus carry
sbb src, dest | GAS文法 |
sbb dest, src | MASM文法 |
ボローあり減算。
src
+ キャリーフラグ
をdest
から減算し、結果をdest
に格納する。
通常、減算命令でレジスターの 2 倍の長さの値を扱うのを助ける。
インクリメントとデクリメント
[編集]inc arg
引数のレジスターの値を 1 ずつインクリメントする。 ADD arg, 1よりずっと早く動作する。
dec arg
引数のレジスターの値を 1 ずつデクリメントする。
ポインターの算術演算
[編集]lea
命令は、算術演算にも使うことができ、特にポインターの算術演算に使われる。
アドレス計算命令を参照のこと。