プログラミングノート - x86

※この記事はリンク切れしたページhttp://milkpot.sakura.ne.jp/note/x86.html の無断転載です。リンクの復活もしくはページ作成者からの指摘があった場合このページを削除する予定です。

<h1>プログラミングノート - x86</h1>

<p>
x86 のアセンブラを勉強するページ。

</p><h2>レジスタ</h2>
<table>
<tbody><tr><td>eax, ecx, edx, ebx, esp, ebp, esi, edi</td><td>... 汎用レジスタ (32 ビット)
</td></tr><tr><td>rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8～15</td><td>... 汎用レジスタ (64 ビット)
</td></tr><tr><td>eflags</td><td>... フラグレジスタ (32 ビット)
</td></tr><tr><td>rflags</td><td>... フラグレジスタ (64 ビット)
</td></tr><tr><td>eip</td><td>... プログラムカウンタ (32 ビット)
</td></tr><tr><td>rip</td><td>... プログラムカウンタ (64 ビット)
</td></tr><tr><td>st(0)～(7)</td><td>... FPU レジスタ
</td></tr><tr><td>FPU ステータスレジスタ</td><td>... FPU ステータスレジスタ
</td></tr><tr><td>FPU コントロールレジスタ</td><td>... FPU コントロールレジスタ
</td></tr><tr><td>xmm0～7</td><td>... XMM レジスタ
</td></tr><tr><td>mxcsr</td><td>... MXCSR コントロール＆ステータスレジスタ
</td></tr></tbody></table>

<h3>汎用レジスタの内訳</h3>
<table border="1">
<tbody><tr><td></td><td>32 ビット Windows</td><td>32 ビット Linux</td><td>64 ビット Windows</td><td>64 ビット Linux
</td></tr><tr><td>{e|r}ax</td><td colspan="4">戻り値を入れるレジスタ
</td></tr><tr><td>{e|r}cx</td><td colspan="2">自由に使っていいレジスタ</td><td colspan="2">引数を入れるレジスタ
</td></tr><tr><td>{e|r}dx</td><td colspan="2">自由に使っていいレジスタ</td><td colspan="2">引数を入れるレジスタ
</td></tr><tr><td>{e|r}bx</td><td>自由に使っていいレジスタ</td><td colspan="3">保存しなければならないレジスタ
</td></tr><tr><td>{e|r}sp</td><td colspan="4">スタックポインタ
</td></tr><tr><td>{e|r}bp</td><td colspan="4">元の値を保存しなければならないレジスタ
</td></tr><tr><td>{e|r}si</td><td colspan="3">元の値を保存しなければならないレジスタ</td><td>引数を入れるレジスタ
</td></tr><tr><td>{e|r}di</td><td colspan="3">元の値を保存しなければならないレジスタ</td><td>引数を入れるレジスタ
</td></tr><tr><td>r8～9</td><td colspan="2">-</td><td colspan="2">引数を入れるレジスタ
</td></tr><tr><td>r10～11</td><td colspan="2">-</td><td colspan="2">自由に使っていいレジスタ
</td></tr><tr><td>r12～15</td><td colspan="2">-</td><td colspan="2">元の値を保存しなければならないレジスタ
</td></tr></tbody></table>
<p>
rsp は 16 バイト境界に合わせなければならない (64 ビットの場合)。
</p><p>
32 ビットでは引数はすべてスタックに積む。
ただしレジスタに入れる処理系依存の拡張も多い。
<a href="https://web.archive.org/web/20200621055851/http://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions">Wikipedia</a> が詳しい。
</p><p>
eax は rax の下位 32 ビット部分を指す。
また ax は eax の下位 16 ビット部分を指す。
さらに ax は上下 2 つの 8 ビットレジスタ ah, al に分割して使うことができる。
以下のようなレイアウトになっている。
</p><table border="1">
<tbody><tr><td>ビット番号</td><td>63～56</td><td>55～48</td><td>47～40</td><td>39～32</td><td>31～24</td><td>23～16</td><td>15～8</td><td>7～0
</td></tr><tr><td>64 ビット</td><td colspan="8" align="center">rax
</td></tr><tr><td>32 ビット</td><td colspan="4" align="center">-</td><td colspan="4" align="center">eax
</td></tr><tr><td>16 ビット</td><td colspan="4" align="center">-</td><td colspan="2" align="center">-</td><td colspan="2" align="center">ax
</td></tr><tr><td>8 ビット</td><td colspan="4" align="center">-</td><td colspan="2" align="center">-</td><td align="center">ah</td><td align="center">al
</td></tr></tbody></table>
<p>
32 ビットレジスタを使う場合、上位 32 ビットはクリアされる。
8/16 ビットレジスタを使う場合、他のビットは変化しない。
</p><p>
64ビットでは以下のレジスタがフルに使える。
</p><table>
<tbody><tr><td>rax,</td><td>rcx,</td><td>rdx,</td><td>rbx,</td><td>rsp,</td><td>rbp,</td><td>rsi,</td><td>rdi,</td><td>r8,</td><td>r9,</td><td>r10,</td><td>r11,</td><td>r12,</td><td>r13,</td><td>r14,</td><td>r15
</td></tr><tr><td>eax,</td><td>ecx,</td><td>edx,</td><td>ebx,</td><td>esp,</td><td>ebp,</td><td>esi,</td><td>edi,</td><td>r8d,</td><td>r9d,</td><td>r10d,</td><td>r11d,</td><td>r12d,</td><td>r13d,</td><td>r14d,</td><td>r15d
</td></tr><tr><td>ax,</td><td>cx,</td><td>dx,</td><td>bx,</td><td>sp,</td><td>bp,</td><td>si,</td><td>di,</td><td>r8w,</td><td>r9w,</td><td>r10w,</td><td>r11w,</td><td>r12w,</td><td>r13w,</td><td>r14w,</td><td>r15w
</td></tr><tr><td>ah,</td><td>ch,</td><td>dh,</td><td>bh
</td></tr><tr><td>al,</td><td>cl,</td><td>dl,</td><td>bl,</td><td>spl,</td><td>bpl,</td><td>sil,</td><td>dil,</td><td>r8l,</td><td>r9l,</td><td>r10l,</td><td>r11l,</td><td>r12l,</td><td>r13l,</td><td>r14l,</td><td>r15l
</td></tr></tbody></table>
<p>
sph とか r8h とかは存在しない。
</p><p>
32ビットでは以下のレジスタだけが使える。
</p><table>
<tbody><tr><td>eax,</td><td>ecx,</td><td>edx,</td><td>ebx,</td><td>esp,</td><td>ebp,</td><td>esi,</td><td>edi
</td></tr><tr><td>ax,</td><td>cx,</td><td>dx,</td><td>bx,</td><td>sp,</td><td>bp,</td><td>si,</td><td>di
</td></tr><tr><td>ah,</td><td>ch,</td><td>dh,</td><td>bh
</td></tr><tr><td>al,</td><td>cl,</td><td>dl,</td><td>bl
</td></tr></tbody></table>

<h3>フラグレジスタの内訳</h3>
<table>
<tbody><tr><td>ビット 11</td><td>(OF)</td><td>...</td><td>オーバーフロー発生
</td></tr><tr><td>ビット 10</td><td>(DF)</td><td>...</td><td>ストリング命令の方向を指定するフラグ
</td></tr><tr><td>ビット 7</td><td>(SF)</td><td>...</td><td>演算結果が負
</td></tr><tr><td>ビット 6</td><td>(ZF)</td><td>...</td><td>演算結果がゼロ
</td></tr><tr><td>ビット 2</td><td>(PF)</td><td>...</td><td>演算結果の奇数パリティ (1 が偶数個のときに立つ)
</td></tr><tr><td>ビット 0</td><td>(CF)</td><td>...</td><td>キャリー発生 / ボロー発生
</td></tr></tbody></table>
<p>
ビット数によって flags, eflags, rflags と呼び分けたりするが、上記の ax, eax, rax と同様にひとつの領域を共用している。
ちなみに ip, eip, rip も同様。

</p><h3>FPU レジスタの内訳</h3>
<table>
<tbody><tr><td>st(0)</td><td>...</td><td>戻り値を入れるレジスタ
</td></tr><tr><td>st(1)～(7)</td><td>...</td><td>自由に使っていいレジスタ
</td></tr></tbody></table>
<p>
各 80 ビット (拡張倍精度)。
</p><p>
引数は汎用のスタックに積む。

</p><h2>表記とか</h2>
<p>
本家インテルの記述方式と GNU as で使われている AT&amp;T; の記述方式はだいぶ違う。
特にソースとデスティネーションが逆になっているのが大きい。
ここではインテル方式を使うことにする。
</p><table border="1">
<tbody><tr><td>インテルスタイル</td><td>AT&amp;T; (GNU as) スタイル
</td></tr><tr><td> mov al, 123</td><td> movb $123, %al 
</td></tr><tr><td> mov eax, [esi+ebx*2+3] </td><td> movl 3(%esi,%ebx,2), %eax
</td></tr></tbody></table>

<h2>整数演算</h2>

<h3>ロード/ストア</h3>
<pre>mov eax, [4]
mov eax, [ebx]
mov eax, [ebx+4]
mov eax, [ecx*2+4]
mov eax, [ebx+ecx]
mov eax, [ebx+ecx+4]
mov eax, [ebx+ecx*2]
mov eax, [ebx+ecx*2+4]
mov eax, [rip+4]
</pre>
<p>
レジスタ (eax, ebx, ecx の部分) は汎用レジスタどれでも指定できる。
シフト (*2 の部分) は 2, 4, 8 のいずれか。
オフセット (+4 の部分) は符号付き 8 ビットまたは 32 ビット。
</p><p>
64 ビットでは rip 相対アドレッシングが可能。
ただし単純にオフセット (符号付き 32 ビット) を加算する形式のみ。
32 ビットでは eip 相対は不可。
call 命令のリターンアドレスを取り出すしか方法はない。
</p><p>
ストアの場合は単純に逆に書く。
</p><pre>mov [eax], ebx
</pre>
<p>
mov はレジスタ間の移動にも使う。
</p><pre>mov eax, ecx
</pre>
<p>
mov はソースとデスティネーションのサイズが同じでなければならない。
メモリ上の 8 ビット値を 8 ビットレジスタにロードすることはできるが、
8 ビット値を 32 ビットレジスタにロードなどするには movsx (符号拡張) または movzx (ゼロ拡張) を使う。
</p><pre>movzx  ax, al                   ; 16 ビット ← 8 ビット
movzx  ax, byte ptr [bx]        ; 16 ビット ← 8 ビット
movzx  eax, al                  ; 32 ビット ← 8 ビット
movzx  eax, byte ptr [ebx]      ; 32 ビット ← 8 ビット
movzx  eax, ax                  ; 32 ビット ← 16 ビット
movzx  eax, word ptr [ebx]      ; 32 ビット ← 16 ビット
movzx  rax, al                  ; 64 ビット ← 8 ビット
movzx  rax, byte ptr [rbx]      ; 64 ビット ← 8 ビット
movzx  rax, eax                 ; 64 ビット ← 32 ビット
movzx  rax, dword ptr [rbx]     ; 64 ビット ← 32 ビット
</pre>
<p>
64 ビット ← 16 ビット
はできない。
</p><p>
mov でいちいちロード/ストアしなくても、直接メモリ上の値を演算できる命令も多い。
</p><p>
mov はフラグレジスタを更新しない。

</p><h3>プッシュ＆ポップ</h3>
<p>
{e|r}sp を用いた専用の push 命令と pop 命令がある。
</p><pre>push  4
push  eax
push  [eax]
pop   eax
pop   [eax]
</pre>
<p>
スタックは full-descending。
即値は符号付き 8～16 ビットまたは符号なし 32 ビット。
</p><p>
push, pop はフラグレジスタを更新しない。

</p><h3>定数ロード</h3>
<p>
mov 命令で 64 ビットまでの任意の即値をロードできる。
</p><pre>mov eax, 12345678h
mov rax, 123456789abcdefh
</pre>

<h3>演算</h3>
<pre>add  eax, ecx   ; eax ← eax+ecx
adc  eax, ecx   ; eax ← eax+ecx+CF
sub  eax, ecx   ; eax ← eax-ecx
sbc  eax, ecx   ; eax ← eax-ecx-CF
and  eax, ecx   ; eax ← eax&amp;ecx
or   eax, ecx   ; eax ← eax|ecx
xor  eax, ecx   ; eax ← eax^ecx
</pre>
<pre>shl  eax, cl    ; eax ← eax&lt;&lt;cl
sar  eax, cl    ; eax ← eax&gt;&gt;cl
shr  eax, cl    ; eax ← eax&gt;&gt;&gt;cl
</pre>
<pre>neg  eax        ; eax ← -eax
not  eax        ; eax ← ~eax
inc  eax        ; eax ← eax+1
dec  eax        ; eax ← eax-1
</pre>
<p>
演算結果はフラグレジスタに反映される。
命令によってフラグへの影響が異なるので要マニュアル参照。
また、レジスタの代わりにメモリアクセスおよび即値を指定できる。
add～xor ではオペランドに以下の組み合わせが可能。
</p><pre>add  eax, 4
add  eax, ecx
add  eax, [ecx]
add  [eax], 4
add  [eax], ecx
</pre>
<p>
shl～shr では以下の組み合わせが可能 (シフト回数のレジスタは cl のみ)。
</p><pre>shl  eax, 1
shl  eax, cl
shl  [eax], 1
shl  [eax], cl
</pre>
<p>
neg～dec では以下の指定が可能。
</p><pre>neg  eax
neg  [eax]
</pre>
<p>
メモリアクセスの [eax] [ecx] 部分はロード/ストアの節で示したアドレッシングモードがどれでも使える。
</p><p>
命令とオペランドから自動的にサイズを判断できないときはサイズを明記する必要がある
(常に明記しても構わない)。
</p><pre>add  [rax], al               ; 1 バイト
add  [rax], ax               ; 2 バイト
add  [rax], eax              ; 4 バイト
add  [rax], rax              ; 8 バイト
add  [rax], 3                ; サイズ不明につきエラー
add  byte ptr [rax], 3       ; 1 バイト
add  word ptr [rax], 3       ; 2 バイト
add  dword ptr [rax], 3      ; 4 バイト
add  qword ptr [rax], 3      ; 8 バイト
</pre>
<p>
アドレッシングモードが豊富なおかげで、
アドレス計算をする (だけで実際にメモリアクセスはしない) lea 命令が通常の演算命令代わりに大変役立つ。
lea はフラグレジスタを更新しない。
</p><pre>lea  eax, [ebx+ecx+1]   ; eax ← ebx+ecx+1
lea  eax, [ebx+ebx*2]   ; eax ← ebx×3
</pre>

<h3>乗除算</h3>
<pre>mul  ecx        ; edx:eax ← eax×ecx (符号なし)
imul ecx        ; edx:eax ← eax×ecx (符号付き)
div  ecx        ; eax ← eax÷ecx, edx ← eax mod ecx (符号なし)
idiv ecx        ; eax ← eax÷ecx, edx ← eax mod ecx (符号付き)
</pre>
<p>
eax と edx を固定で使う 1 オペランド形式の乗除算。
ecx のところには任意のレジスタまたはメモリアドレスを書ける。
</p><pre>imul ecx, 2         ; ecx ← ecx×2
imul ecx, ebx       ; ecx ← ecx×ebx
imul ecx, [ebx]     ; ecx ← ecx×mem[ebx]
</pre>
<p>
乗算だけ 2～3 オペランド形式もある。
この形式では上位ビットは捨てられる。
</p><pre>imul ecx, ebx, 3    ; ecx ← ebx×3
imul ecx, [ebx], 3  ; ecx ← mem[ebx]×3
</pre>
<p>
3 オペランド形式では第 3 オペランド即値のみ。

</p><h3>比較</h3>
<pre>cmp  eax, ecx   ; eax-ecx
test eax, ecx   ; eax&amp;ecx;
</pre>
<p>
それぞれ sub, and の演算結果を保存しないバージョン。
フラグだけ更新する。
</p><p>
フラグは分岐命令で直接参照するほか、
pushf 系命令でスタックに積んだり、
lahf 命令で一部を ah レジスタに転送したり、
set 系命令で汎用レジスタに展開できる。
</p><pre>pushf         ; mem[sp-2]  ← flags,  sp  ← sp-2
pushfd        ; mem[esp-4] ← eflags, esp ← esp-4
pushfq        ; mem[rsp-8] ← rflags, rsp ← rsp-8
</pre>
<pre>lahf          ; ah ← flags の下位 8 ビット (SF～CF)
</pre>
<pre>setc al       ; al ← CF=1
setz al       ; al ← ZF=1
...
</pre>
<p>
set の語尾は下記の分岐命令 j の語尾と同じにつき省略。
条件に応じて 1 または 0 を 8 ビットレジスタまたはメモリアドレスにストアする。

</p><h3>分岐</h3>
<table>
<tbody><tr><td>jo</td><td>... OF=1
</td></tr><tr><td>jno</td><td>... OF=0
</td></tr><tr><td>jb, jc, jnae</td><td>... CF=1
</td></tr><tr><td>jae, jnc, jnb</td><td>... CF=0
</td></tr><tr><td>je, jz</td><td>... ZF=1
</td></tr><tr><td>jne, jnz</td><td>... ZF=0
</td></tr><tr><td>jbe, jna</td><td>... CF=1 and ZF=1
</td></tr><tr><td>ja, jnbe</td><td>... CF=0 and ZF=0
</td></tr><tr><td>js</td><td>... SF=1
</td></tr><tr><td>jns</td><td>... SF=0
</td></tr><tr><td>jp, jpe</td><td>... PF=1
</td></tr><tr><td>jnp, jpo</td><td>... PF=0
</td></tr><tr><td>jl, jnge</td><td>... SF≠OF
</td></tr><tr><td>jge, jnl</td><td>... SF=OF
</td></tr><tr><td>jle, jng</td><td>... ZF=1 or SF≠OF
</td></tr><tr><td>jg, jnle</td><td>... ZF=0 and SF=OF
</td></tr><tr><td>jcxz</td><td>... cx=0
</td></tr><tr><td>jecxz</td><td>... ecx=0
</td></tr></tbody></table>
<p>
すべて符号付き 8～32 ビット {e|r}ip 相対ジャンプ。
カンマで区切っているものはニモニックの別名。
</p><p>
無条件分岐の jmp はレジスタ間接ジャンプやメモリ間接ジャンプもできる。
</p><pre>jmp  $+4
jmp  eax
jmp  [eax]
</pre>
<p>
サブルーチンコール (他でいうジャンプ＆リンク) は専用の call 命令と ret 命令を用いる。
リンクレジスタは無く、リターンアドレスはスタックにプッシュされ、スタックからポップされる。
</p><pre>call $+4
call eax
call [eax]
</pre>
<pre>ret
</pre>

<h2>浮動小数点演算</h2>

<h3>ロード/ストア</h3>
<p>
FPU レジスタはスタック的動作をする。
つまり st(0) に値をプッシュすると、既存の st(0) は st(1) に、st(1) は st(2) に移動する。
st(0) の値をポップすると、逆に st(2) は st(1) に、st(1) は st(0) に移動する。
スタックオーバーフロー例外とかスタックアンダーフロー例外とかある。
</p><table border="1">
<tbody><tr><td></td><td>ロード (プッシュ)</td><td>ストア</td><td>ストア+ポップ
</td></tr><tr><td>2 バイト整数</td><td> fild word ptr [eax] </td><td> fist word ptr [eax] </td><td> fistp word ptr [eax] 
</td></tr><tr><td>4 バイト整数</td><td> fild dword ptr [eax] </td><td> fist dword ptr [eax] </td><td> fistp dword ptr [eax]
</td></tr><tr><td>8 バイト整数</td><td> fild qword ptr [eax] </td><td>- </td><td>fistp qword ptr [eax]
</td></tr><tr><td>単精度 (4 バイト)</td><td> fld  dword ptr [eax] </td><td> fst  dword ptr [eax] </td><td> fstp  dword ptr [eax] 
</td></tr><tr><td>倍精度 (8 バイト)</td><td> fld  qword ptr [eax]</td><td> fst  qword ptr [eax] </td><td> fstp  qword ptr [eax]
</td></tr><tr><td>拡張倍精度 (10 バイト)</td><td>fld  tbyte ptr [eax] </td><td> - </td><td> fstp  tbyte ptr [eax] 
</td></tr><tr><td>レジスタ</td><td> fld  st(2) </td><td> fst  st(2) </td><td> fstp  st(2)
</td></tr></tbody></table>
<p>
いずれもレジスタ内では拡張倍精度で保持される。

</p><h3>定数ロード</h3>
<p>
以下の定数だけが専用の命令でロードできる。
それ以外はすべてメモリからロードする必要がある。
</p><pre>fldz            ; 0
fld1            ; 1
fldpi           ; 円周率
fldl2t          ; log2(10)
fldl2e          ; log2(e)
fldlg2          ; log10(2)
fldln2          ; ln(2)
</pre>

<h3>演算</h3>
<pre>fadd    st(0), st(1)        ; st(0) ← st(0)+st(1)
fsub    st(0), st(1)        ; st(0) ← st(0)-st(1)
fsubr   st(0), st(1)        ; st(0) ← st(1)-st(0)
fmul    st(0), st(1)        ; st(0) ← st(0)×st(1)
fdiv    st(0), st(1)        ; st(0) ← st(0)÷st(1)
fdivr   st(0), st(1)        ; st(0) ← st(1)÷st(0)
</pre>
<p>
いずれも以下のようなバリエーションがある。
</p><pre>fiadd   word ptr [eax]      ; st(0) ← st(0)+mem[eax] (2 バイト整数)
fiadd   dword ptr [eax]     ; st(0) ← st(0)+mem[eax] (4 バイト整数)
fadd    dword ptr [eax]     ; st(0) ← st(0)+mem[eax] (単精度)
fadd    qword ptr [eax]     ; st(0) ← st(0)+mem[eax] (倍精度)
fadd    st(0), st(i)        ; st(0) ← st(0)+st(i)
fadd    st(i), st(0)        ; st(i) ← st(i)+st(0)
faddp   st(i), st(0)        ; st(i) ← st(i)+st(0), st(0)をポップ
</pre>
<p>
[eax] の部分は通常のアドレッシングモードをなんでも指定できる。
st(i) の部分は st(0)～(7) を指定できる。
</p><p>
語尾の p 付きは st(0) をポップする。
p なしはプッシュもポップもしない。
i 付きは整数 (符号付き)。
</p><pre>fabs                        ; st(0) ← abs(st(0))
fneg                        ; st(0) ← -st(0)
fsin                        ; st(0) ← sin(st(0))
fcos                        ; st(0) ← cos(st(0))
fsqrt                       ; st(0) ← sqrt(st(0))
frndint                     ; st(0) ← int(st(0))
fxch    st(i)               ; st(0) ← st(i), st(i) ← st(0)
</pre>
<p>
単項演算子はオペランドなしで st(0) 固定。
ポップもできない。
</p><p>
frndint は現在の丸めモードに従って小数点以下を切り捨てる。
fxch は st(0) と st(i) を入れ替える。
他にもいろいろあるけど省略。

</p><h3>比較</h3>
<pre>ficom   word ptr [eax]      ; 2 バイト整数
ficom   dword ptr [eax]     ; 4 バイト整数
fucom   dword ptr [eax]     ; 単精度
fucom   qword ptr [eax]     ; 倍精度
fucom   st(i)
</pre>
<p>
st(0) とオペランドを比較する。
語尾に p を付けると st(0) をポップする。
fucompp (オペランドなし) は st(0) と st(1) を比較して両方ポップする。
</p><p>
比較結果に基づいて下表の通りに FPU ステータスレジスタが設定される。
</p><table border="1">
<tbody><tr><td></td><td>C3</td><td>C2</td><td>C0
</td></tr><tr><td>ST(0) &gt; オペランド</td><td>0</td><td>0</td><td>0
</td></tr><tr><td>ST(0) &lt; オペランド</td><td>0</td><td>0</td><td>1
</td></tr><tr><td>ST(0) = オペランド</td><td>1</td><td>0</td><td>0
</td></tr><tr><td>順序付けできない</td><td>1</td><td>1</td><td>1
</td></tr></tbody></table>
<p>
これらのステータスは
</p><pre>fstsw  ax       ; ax ← fpu ステータスレジスタ
sahf            ; flags ← ah
</pre>
<p>
でフラグレジスタにコピーでき、条件分岐に使える。
それぞれ
</p><table>
<tbody><tr><td>C3</td><td>C2</td><td>C0
</td></tr><tr><td>↓</td><td>↓</td><td>↓
</td></tr><tr><td>ZF</td><td>PF</td><td>CF
</td></tr></tbody></table>
<p>
に対応している。
Pentium Pro 以降では fcomi 系命令 (ただしメモリアクセス不可) で比較結果を直接フラグレジスタにセットできる。

</p><h2>参考文献</h2>
<ul>
<li><a href="https://web.archive.org/web/20200621055851/https://web.archive.org/web/20150829075907/http://www.intel.co.jp/content/www/jp/ja/developer/download.html">Intel 日本語技術資料のダウンロード</a>
</li><li><a href="https://web.archive.org/web/20200621055851/http://en.wikipedia.org/wiki/X86_calling_conventions">x86 calling conventions - Wikipedia</a>
</li><li><a href="https://web.archive.org/web/20200621055851/http://msdn.microsoft.com/en-US/library/9z1stfyw.aspx">MSDN: x64 Software Conventions - Register Usage</a>
</li><li><a href="https://web.archive.org/web/20200621055851/http://www.sco.com/developers/devspecs/abi386-4.pdf">System V Application Binary Interface - Intel386 Architecture Processor Supplement</a>
</li><li><a href="https://web.archive.org/web/20200621055851/http://www.x86-64.org/documentation/abi.pdf">System V Application Binary Interface - AMD64 Architecture Processor Supplement</a>
</li><li><a href="https://web.archive.org/web/20200621055851/http://hp.vector.co.jp/authors/VA003988/how_to_o.htm">How to optimize for the Pentium family of the microprocessors</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://www.agner.org/optimize/">Software optimization resources</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190121-00/?p=100745">The Intel 80386, part 1: Introduction</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190122-00/?p=100755">The Intel 80386, part 2: Memory addressing modes</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190123-00/?p=100765">The Intel 80386, part 3: Flags and condition codes</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190124-00/?p=100775">The Intel 80386, part 4: Arithmetic</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190125-00/?p=100795">The Intel 80386, part 5: Logical operations</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190128-00/?p=100805">The Intel 80386, part 6: Data transfer instructions</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190129-00/?p=100815">The Intel 80386, part 7: Conditional instructions and control transfer</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190130-00/?p=100825">The Intel 80386, part 8: Block operations</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190131-00/?p=100835">The Intel 80386, part 9: Stack frame instructions</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190201-00/?p=100845">The Intel 80386, part 10: Atomic operations and memory alignment</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190204-00/?p=100855">The Intel 80386, part 11: The TEB</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190205-00/?p=100865">The Intel 80386, part 12: The stuff you don’t need to know</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190206-00/?p=100875">The Intel 80386, part 13: Calling conventions</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190207-00/?p=100885">The Intel 80386, part 14: Rescuing a stack trace after the debugger gave up when it reached an FPO function</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190208-00/?p=100895">The Intel 80386, part 15: Common compiler-generated code sequences</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190211-00/?p=100905">The Intel 80386, part 16: Code walkthrough</a>
</li><li><a href="https://web.archive.org/web/20200621055851/https://blogs.msdn.microsoft.com/oldnewthing/20190212-00/?p=100915">The Intel 80386, part 17: Future developments</a>
</li></ul>