Intel x86 プロセッサは、複雑な命令セット コンピューター (CISC) アーキテクチャを使用します。つまり、大量の汎用レジスタではなく、少数の特殊な用途のレジスタが存在します。 また、複雑な特殊な目的の命令が優先されることを意味します。
x86 プロセッサは、少なくとも 8 ビット Intel 8080 プロセッサまで、その遺産をトレースします。 x86命令セットの多くの特殊性は、そのプロセッサ(およびそのZilog Z-80バリアント)との下位互換性によるものです。
Microsoft Win32 は、x86 プロセッサを 32 ビット フラット モードで使用します。 このドキュメントでは、フラット モードのみに焦点を当てます。
レジスター
x86 アーキテクチャは、次の特権のない整数レジスタで構成されています。
eax |
アキュムレータ |
ebx |
ベース レジスタ |
ecx |
カウンターレジスタ |
edx |
データ レジスタ - I/O ポート アクセスおよび算術関数に使用できます |
esi |
ソース インデックス レジスタ |
edi |
宛先インデックス レジスタ |
ebp |
ベース ポインター レジスタ |
esp を |
スタック ポインター |
すべての整数レジスタは 32 ビットです。 ただし、その多くは 16 ビットまたは 8 ビットのサブレジスタを持っています。
軸 |
eax の下位 16 ビット |
bx |
ebx の下位 16 ビット |
cx |
ecx の下位 16ビット |
dx |
edx の下位 16 ビット |
si |
低 16 ビットの esi |
ディ |
下位 16 ビットの edi |
bp |
ebp の下位 16 ビット |
sp |
esp の下位 16 ビット |
アル |
eax の下位 8 ビット |
ah |
高 8 ビットの ax |
bl |
ebx の下位 8 ビット |
bh |
上位 8 ビットの bx |
cl |
ecx の下位 8 ビット |
ch |
cx の上位 8 ビット |
dl |
edx の下位 8 ビット |
dh |
dx の上位 8 ビット |
サブ登録で操作すると、サブ登録のみが影響を受け、サブ登録外の部分は影響を受けありません。 たとえば、 ax レジスタに格納すると、 eax レジスタの上位 16 ビットは変更されません。
?(式の評価) コマンドを使用する場合、レジスタは「@」記号 (@) を先頭に付ける必要があります。 たとえば、? ax ではなく ? @axを使用する必要があります。 これにより、デバッガーがシンボルではなくレジスタとして ax を認識できるようになります。
ただし、 r (Registers ) コマンドでは (@) は必要ありません。 たとえば、 r ax=5 は常に正しく解釈されます。
プロセッサの現在の状態には、他の 2 つのレジスタが重要です。
eip |
命令ポインター |
フラグ |
flags |
命令ポインターは、実行中の命令のアドレスです。
フラグ レジスタは、単一ビット フラグのコレクションです。 命令の結果を記述するために、多くの命令によってフラグが変更されます。 これらのフラグは、条件付きジャンプ命令によってテストできます。 詳細については 、x86 フラグ を参照してください。
呼び出し規約
x86 アーキテクチャには、いくつかの異なる呼び出し規則があります。 幸いなことに、それらはすべて同じレジスタの保持と関数の戻りルールに従います。
関数は、eax、ecx、および edx を除き、すべてのレジスタを保持する必要があります。これは関数呼び出し全体で変更でき、esp は呼び出し規則に従って更新する必要があります。
eax レジスタは、結果が 32 ビット以下の場合に関数の戻り値を受け取ります。 結果が 64 ビットの場合、結果は edx:eax ペアに格納されます。
x86 アーキテクチャで使用される呼び出し規則の一覧を次に示します。
Win32 (__stdcall)
関数パラメーターはスタックを通じて渡され、右から左にプッシュされ、呼び出された関数がスタックをクリーンアップします。
ネイティブ C++ メソッド呼び出し (thiscall とも呼ばれます)
関数パラメーターはスタックで渡され、右から左にプッシュされ、"this" ポインターが ecx レジスタに渡され、呼び出し先がスタックをクリーンアップします。
COM (C++ メソッド呼び出しの__stdcall )
関数パラメーターはスタックで渡され、右から左にプッシュされた後、"this" ポインターがスタックにプッシュされ、関数が呼び出されます。 呼び出し先がスタックをクリーンアップします。
__fastcall
最初の 2 つの DWORD または小さい引数は 、ecx レジスタと edx レジスタで渡されます。 残りのパラメーターはスタックで渡され、右から左にプッシュされます。 呼び出し先がスタックをクリーンアップします。
__cdecl
関数パラメーターはスタックで渡され、右から左にプッシュされ、呼び出し元はスタックをクリーンアップします。 __cdecl呼び出し規則は、可変長パラメーターを持つすべての関数に使用されます。
レジスタとフラグのデバッガー表示
デバッガー レジスタの表示例を次に示します。
eax=00000000 ebx=008b6f00 ecx=01010101 edx=ffffffff esi=00000000 edi=00465000
eip=77f9d022 esp=05cffc48 ebp=05cffc54 iopl=0 nv up ei ng nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000286
ユーザー モード デバッグでは、 iopl とデバッガーの最後の行全体を無視できます。
x86 フラグ
前の例では、2 行目の末尾にある 2 文字のコードは フラグです。 これらはシングルビット レジスタであり、さまざまな用途があります。
次の表に、x86 フラグの一覧を示します。
| フラグ コード | フラグ名 | 価値 | フラグの状態 | 説明 |
|---|---|---|---|---|
| の | オーバーフロー フラグ | 0 1 | nvov | オーバーフローなし - オーバーフロー |
| df | 方向フラグ | 0 1 | updn | 上方向 - 下方向 |
| もし | 割り込みフラグ | 0 1 | diei | 割り込みが無効 - 割り込みが有効 |
| sf | 署名フラグ | 0 1 | plng | 正 (またはゼロ) - 負 |
| zf | ゼロ フラグ | 0 1 | nzzr | ゼロ以外 - ゼロ |
| af | 補助キャリングフラグ | 0 1 | naac | 補助キャリングなし - 補助キャリング |
| pf | パリティ フラグ | 0 1 | pepo | パリティ奇数 - パリティ偶数 |
| cf | フラグを運ぶ | 0 1 | nccy | 持ち運びなし - 持ち運び |
| tf | トラップ フラグ | tf が 1 の場合、プロセッサは 1 つの命令の実行後にSTATUS_SINGLE_STEP例外を発生させます。 このフラグは、シングルステップ トレースを実装するためにデバッガーによって使用されます。 他のアプリケーションでは使用しないでください。 | ||
| iopl | I/O 特権レベル | I/O 特権レベル 0 ~ 3 の値を持つ 2 ビット整数です。 これは、ハードウェアへのアクセスを制御するためにオペレーティング システムによって使用されます。 アプリケーションでは使用しないでください。 |
デバッガー コマンド ウィンドウで何らかのコマンドの結果としてレジスタが表示される場合、表示されるのはフラグ状態です。 ただし、 r (Registers) コマンドを使用してフラグを変更する場合は、 フラグ コードで参照する必要があります。
WinDbg の [レジスタ] ウィンドウでは、フラグ コードを使用してフラグを表示または変更します。 フラグの状態はサポートされていません。
次に例を示します。 上記のレジスタ表示では、フラグの状態 ng が表示されます。 これは、署名フラグが現在 1 に設定されていることを意味します。 これを変更するには、次のコマンドを使用します。
r sf=0
これにより、符号フラグが 0 に設定されます。 別のレジスタ表示を行った場合、 ng ステータス コードは表示されません。 代わりに、 pl 状態コードが表示されます。
署名フラグ、ゼロ フラグ、およびキャリング フラグは、最も一般的に使用されるフラグです。
条件
条件は、1 つ以上のフラグの状態を表します。 x86 に対するすべての条件付き操作は、条件の観点から表されます。
アセンブラーは、1 文字または 2 文字の省略形を使用して条件を表します。 条件は、複数の省略形で表すことができます。 たとえば、AE ("above or equal") は NB ("not below") と同じ条件です。 次の表に、一般的な条件とその意味を示します。
| 条件名 | フラグ | 意味 |
|---|---|---|
ゼット |
ZF=1 |
最後の操作の結果は 0 でした。 |
ニュージーランド |
ZF=0 |
最後の操作の結果が 0 ではありません。 |
C |
CF=1 |
最後の操作には、持ち込みまたは借用が必要です。 (符号なし整数の場合、オーバーフローを示します)。 |
ノースカロライナ州 |
CF=0 |
最後の操作では、持ち運びや借用は必要ありませんでした。 (符号なし整数の場合、オーバーフローを示します)。 |
S |
SF=1 |
最後の操作の結果には、最上位ビットが設定されています。 |
NSの |
SF=0 |
最後の操作の結果は、上位ビットがクリアされています。 |
O |
OF=1 |
符号付き整数演算として扱われると、最後の操作によってオーバーフローまたはアンダーフローが発生しました。 |
いいえ |
OF=0 |
符号付き整数演算として扱われた場合、最後の操作ではオーバーフローやアンダーフローは発生しませんでした。 |
条件を使用して、2 つの値を比較することもできます。 cmp 命令は、その 2 つのオペランドを比較し、一方のオペランドを他方から減算したかのようにフラグを設定します。 次の条件を使用して 、cmpvalue1、 value2 の結果を確認できます。
| 条件名 | フラグ | CMP 操作後の意味。 |
|---|---|---|
E |
ZF=1 |
value1 == value2。 |
NE |
ZF=0 |
value1 != value2。 |
| GE NL | SF=OF |
value1>= value2。 値は符号付き整数として扱われます。 |
| LE NG | ZF=1 または SF!=OF |
value1<= value2。 値は符号付き整数として扱われます。 |
| G NLE | ZF=0 および SF=OF |
value1>value2。 値は符号付き整数として扱われます。 |
| L NGE | SF!=OF |
value1<value2。 値は符号付き整数として扱われます。 |
| AE NB | CF=0 |
value1>= value2。 値は符号なし整数として扱われます。 |
| BE NA | CF=1 またはZF=1 |
value1<= value2。 値は符号なし整数として扱われます。 |
| A NBE | CF=0 およびZF=0 |
value1>value2。 値は符号なし整数として扱われます。 |
| B NAE | CF=1 |
value1<value2。 値は符号なし整数として扱われます。 |
通常、条件は cmp 命令または テスト 命令の結果に作用するために使用されます。 たとえば、
cmp eax, 5
jz equal
は、式 ( eax - 5) を計算し、結果に応じてフラグを設定することで、eax レジスタを数値 5 と比較します。 減算の結果が 0 の場合、 zr フラグが設定され、 pz 条件が true になるため、ジャンプが実行されます。
データ型
byte: 8 ビット
word: 16 ビット
dword: 32 ビット
qword: 64 ビット (倍精度浮動小数点数を含む)
tword: 80 ビット (拡張倍精度浮動小数点を含む)
oword: 128 ビット
記法
次の表は、アセンブリ言語命令の記述に使用される表記を示しています。
| 記法 | 意味 |
|---|---|
r、 r1、 r2... |
レジスター |
m |
メモリ アドレス (詳細については、後続のアドレス指定モードに関するセクションを参照してください)。 |
#n |
イミディエイト定数 |
r/m |
レジスタまたはメモリ |
r/#n |
レジスタ定数または即時定数 |
r/m/#n |
レジスタ、メモリ、または即時定数 |
cc |
前の「条件」セクションにリストされている条件コード。 |
T |
"B"、"W"、または "D" (バイト、ワード、またはダブルワード) |
accT |
サイズ T アキュムレータ: al if T = "B"、 ax if T = "W"、または eax if T = "D" |
アドレス指定モード
いくつかの異なるアドレス指定モードがありますが、それらはすべて T ptr [expr] という形式になります。 T は何らかのデータ型 (前のデータ型セクションを参照) で、 expr は定数とレジスタを含む式です。
ほとんどのモードの表記は、それほど難しくなく推測できます。 たとえば、 BYTE PTR [esi+edx*8+3] は 、" esi レジスタの値を取得し、 edx レジスタの値の 8 倍に加算し、3 つ追加してから、結果のアドレスのバイトにアクセスする" を意味します。
パイプライン処理
Pentium はデュアルイシューです。つまり、1 クロック ティックで最大 2 つのアクションを実行できます。 ただし、一度に 2 つのアクション ( ペアリングと呼ばれます) を実行できる場合のルールは非常に複雑です。
x86 は CISC プロセッサであるため、ジャンプ遅延スロットについて心配する必要はありません。
同期メモリ アクセス
命令を読み込み、変更、および格納すると、次のように命令を変更する ロック プレフィックスを受け取ることができます。
命令を発行する前に、CPU は保留中のすべてのメモリ操作をフラッシュして一貫性を確保します。 すべてのデータ プリフェッチは破棄されます。
命令の発行中、CPU はバスへの排他的アクセス権を持つことになります。 これにより、読み込み/変更/ストア操作のアトミック性が確保されます。
xchg 命令は、値をメモリと交換するたびに、前の規則に自動的に従います。
その他のすべての命令は、既定では非ロックです。
ジャンプ予測
無条件ジャンプが実行されると予測されます。
条件付きジャンプは、最後に実行された時刻に取得されたかどうかに応じて、取得されるかどうかが予測されます。 ジャンプ履歴を記録するためのキャッシュのサイズは限られています。
CPU に、前回の実行時に条件付きジャンプが取得されたかどうかの記録がない場合は、後方の条件付きジャンプが取得済みとして予測され、前の条件付きジャンプは取得されていないと予測されます。
アラインメント
x86 プロセッサは、パフォーマンスにペナルティが発生するものの、整列されていないメモリアクセスを自動的に修正します。 例外は発生しません。
アドレスがオブジェクト サイズの整数倍数である場合、メモリ アクセスはアラインされていると見なされます。 たとえば、すべての BYTE アクセスがアラインされ (すべてが 1 の整数倍数)、偶数アドレスへの WORD アクセスがアラインされ、DWORD アドレスがアラインされるためには 4 の倍数である必要があります。
ロック プレフィックスは、アライメントされていないメモリ アクセスには使用しないでください。