基本的なアセンブリ命令(Intel記法)

基本的なアセンブリ命令について勉強したのでアウトプットします。

前提

今回はIntel記法で書いていきます。
他にもAT&T記法があるのですが、より直感的に理解しやすい方を優先的に覚えることにしました。
一応差分を

Intel記法
  mov eax, 1
AT&T記法
  mov $1, %eax

基本命令

代入命令

mov dst, src

srcの値をdstにコピーします。
アセンブリではなくプログラミング言語で書くとdst = srcといったところでしょうか。

アドレス計算

lea dst, src
例:
lea eax, [esp+0x10]

srcのアドレス計算を行った後にdstに値を格納するという命令です。
例で言えばespに0x10を加算した値をeaxへ格納します。

交換

xchg op1, op2

op1とop2の値を交換します。

加減算

add dest, src
sub dest, src

addが加算。subが減算です。
dest = dest + src dest = dest - src になります。
例:add eax, 1 -> eax = eax + 1

乗算

mul src

これは加減算と違い引数が一つしかありません。
かける数はsrcに依存したAレジスタ(al, ax, eax)となります。というのも以下の表をみてください。

サイズ 上位bit 下位bit
8bit AH AL
16bit DX AX
32bit EDX EAX
64bit RDX RAX

例えばmul ebxの場合、ebxは32bitレジスタです。
なのでebxにeaxをかけます。
計算の結果上位32bitをEDXへ、下位32bitをEAXへ格納します。
これがmulの計算になります。
srcのレジスタのアドレス幅によってかける値が変わるので覚えるのが大変そうです。

除算

div src

これもsrcのアドレス幅に依存するという意味では同じです。
例をあげます。

div rbx

この場合rbxは64bitなのでrbx / raxの計算が行われます。
その後、商をrax, 剰余をrdxへ格納されます。

分岐命令

フラグレジスタ

分岐命令はフラグレジスタと呼ばれるもので判断します。
そのため先に書いておくことで分岐の仕組みが少し理解しやすくなるかもしれません。
代表的なもの(?) を取り上げます。

フラグレジスタ bit 名前 内容
ZF 6 ゼロフラグ 演算結果がゼロなら1が立つ
SF 7 サインフラグ 演算結果の正負(正なら0, 負なら1)
OF 11 オーバーフローフラグ 桁溢れがあれば1

フラグレジスタは他のレジスタと違い各bitに意味があります。
それが上記の表のbitの部分です。
後で紹介するcmpなどの演算結果を用いて各bitに値を設定していくことになります。
分岐処理はそれらの各bitを見て判断していきます。
では実際に各命令をみていきます。

比較命令

cmp op1, op2

cmpは比較を行う命令です。
少し詳しく言うとこの命令ではop1 - op2という減算が行われます。
しかし、その結果は保持されません。結果は保持されませんが、フラグレジスタの変更は行われます。
これがsub命令とは違う点です。あくまで目的はフラグレジスタの変更となっています。
演算の結果が0であればZFに1が設定されます。
つまりop1とop2の値が同じであればゼロであることを判断するZFにbitが立つということです。
また、op1の方が大きい場合場合(op1 - op2 > 0)、負数を表すSFには0が入ります。
その逆でop2の方が大きい場合(op1 - op2 < 0)、SFには1が入ります。
c言語風に書くと以下のような形です。

if ((op1 - op2) > 0) {
  SF = 0
}
else if ((op1 - op2) < 0) {
  SF = 1
} else {
  ZF = 1
}

あくまでイメージなので書き方が汚いのは気にしないでください...

cmpは減算処理を行ってフラグを設定しますが、実は同じようにsubでも設定することができます。
その場合演算結果は保持されますが、フラグレジスタも設定されます。

無条件分岐命令

jmp addr

これは一番シンプルな分岐命令で引数に指定したアドレスへ処理を移します。
これは条件なく分岐するため、無条件分岐命令とも呼ばれます。

条件分岐命令

条件分岐命令ではフラグレジスタを元に分岐を行っていきます。
表形式で基本的な命令を書きます。

命令 意味 内容
JE Jump if Equal ZF=1のとき分岐
JZ Jump if Zero ZF=1のとき分岐
JNE Jump if Not Equal ZF=0の時分岐
JNZ Jump if Not Zero ZF=0の時分岐
JG Jump if Grater ZF=0 && SF=OF
JL Jump if Less SF != OF

JE, JZ

ZF = 1のときとはどういうときか。
それはcmp op1, op2という命令があったときop1とop2の値が同じ時です。
op1 - op2 = 0のときZFにはビットが立つという話は比較命令のときにもしましたので、振り返ってもらえれば良いと思います。
JNE, JNZはその逆でZF=0のときに分岐するのでop1, op2の値が違った場合に分岐します。

JG

ZF=0 かつ SF=OFとはどういう場合か。
簡単にいえば演算結果が負の数ではない、かつオーバーフローを起こしていないということになります。
op1 > op2 のときと言ってよいと思います。

JL

これはJGの逆で演算結果が負数で、かつオーバーフローを起こしていないときということになります。

条件分岐を簡易言語で書く

if (op1 == op2)
  (JE|JZ)処理
if (op1 > op2)
  (JG|JNE|JNZ)処理
if (op1 < op2)
  (JL|JNE|JZE)処理

他言語風に書くとこんな感じでしょうか。

まとめ

今回の記事ではpush, pop, call, retなどの重要な命令が書いてありません。
それには理由があって、次に各記事で関数呼び出し処理と照らし合わせながらまとめていこうと考えているからです。
アドバイス・補足等ありましたらTwitterまでお願いします。