x86 アーキテクチャ

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 ビットのサブレジスタを持っています。

ax

eax の下位 16 ビット

bx

ebx の下位 16 ビット

cx

ecx の下位 16ビット

dx

edx の下位 16 ビット

si

esi の下位 16 ビット

ディ

edi の下位 16 ビット

bp

ebp の下位 16 ビット

sp

esp の下位 16 ビット

al

eax の下位 8 ビット

ah

ax の上位 8 ビット

bl

ebx の下位 8 ビット

bh

bx の上位 8 ビット

cl

ecx の下位 8 ビット

ch

cx の上位 8 ビット

dl

edx の下位 8 ビット

dh

dx の上位 8 ビット

サブ登録で操作すると、サブ登録のみが影響を受け、サブ登録外の部分は影響を受けありません。 たとえば、 ax レジスタに格納すると、 eax レジスタの上位 16 ビットは変更されません。

?(式の評価) コマンドを使用する場合、レジスタは「@」記号 (@) を先頭に付ける必要があります。 たとえば、@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 フラグの一覧を示します。

フラグ コード フラグ名 価値 フラグの状態 説明
of オーバーフロー フラグ 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") と同じ条件です。 次の表に、一般的な条件とその意味を示します。

条件名 フラグ 意味

Z

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 プロセッサであるため、ジャンプ遅延スロットについて心配する必要はありません。

同期メモリ アクセス

命令を読み込み、変更、および格納すると、次のように命令を変更する ロック プレフィックスを受け取ることができます。

  1. 命令を発行する前に、CPU は保留中のすべてのメモリ操作をフラッシュして一貫性を確保します。 すべてのデータ プリフェッチは破棄されます。

  2. 命令の発行中、CPU はバスへの排他的アクセス権を持つことになります。 これにより、読み込み/変更/ストア操作のアトミック性が確保されます。

xchg 命令は、値をメモリと交換するたびに、前の規則に自動的に従います。

その他のすべての命令は、既定では非ロックです。

ジャンプ予測

無条件ジャンプが実行されると予測されます。

条件付きジャンプは、最後に実行された時刻に取得されたかどうかに応じて、取得されるかどうかが予測されます。 ジャンプ履歴を記録するためのキャッシュのサイズは限られています。

CPU に、前回の実行時に条件付きジャンプが取得されたかどうかの記録がない場合は、後方の条件付きジャンプが取得済みとして予測され、前の条件付きジャンプは取得されていないと予測されます。

アラインメント

x86 プロセッサは、パフォーマンスにペナルティが発生するものの、整列されていないメモリアクセスを自動的に修正します。 例外は発生しません。

アドレスがオブジェクト サイズの整数倍数である場合、メモリ アクセスはアラインされていると見なされます。 たとえば、すべての BYTE アクセスがアラインされ (すべてが 1 の整数倍数)、偶数アドレスへの WORD アクセスがアラインされ、DWORD アドレスがアラインされるためには 4 の倍数である必要があります。

ロック プレフィックスは、アライメントされていないメモリ アクセスには使用しないでください。

こちらも参照ください

x64 アーキテクチャ

X86-64 Wikipedia