リバースエンジニアリングへの道
出田 守です。
最近、情報セキュリティに興味を持ち、『リバースエンジニアリング-Pythonによるバイナリ解析技法』という本(以降、「教科書」と呼びます)を読みました。
「こんな世界があるのか!かっこいい!」と感動し、私も触れてみたいということでド素人からリバースエンジニアリングができるまでを書いていきたいと思います。
ちなみに、教科書ではPython言語が使用されているので私もPython言語を使用しています。
ここを見ていただいた諸先輩方からの意見をお待ちしております。
軌跡
環境
OS: Windows10 64bit Home (日本語)
CPU: Intel® Core™ i3-6006U CPU @ 2.00GHz × 1
メモリ: 2048MB
Python: 3.6.5
私の環境は、普段Ubuntu16.04を使っていますが、ここではWindows10 64bitを仮想マシン上で立ち上げております。
ちなみに教科書では、Windowsの32bitで紹介されています。
Intel Deveroper's Manual [1] Chapter7 - Chapter8
現在CPU周りの知識をつけるために『Intel Software Developer's Manual(IDMと呼びます)』をさっくりまとめています。
前回はIDMのChapter6までをまとめました。
今回はChapter7 - Chapter8までをまとめます。
CHAPTER 7 PROGRAMMING WITH GENERAL-PURPOSE INSTRUCTIONS
汎用(General-purpose:GP)命令はIA-32プロセッサの基本的な命令セットのサブセットです。これらの命令は(8086,8088などの)最初のIA-32プロセッサのアーキテクチャに導入されていました。それ以降のIA-32プロセッサにも命令が追加されました。
さらにIntel64では64bitモードで64bitデータを扱うために殆どの汎用命令の機能を拡張しました。汎用命令のいくつかは64bitモードではサポートされていません(64bitモード以外ではサポートされています)。
汎用命令は整数、ポインタ、BCDデータ型の基本的なデータ転送、メモリアドレッシング、算術と論理、プログラムフロー制御、入出力、文字列を実行します。
7.1 PROGRAMMING ENVIRONMENT FOR GP INSTRUCTIONS
汎用命令のプログラミング環境は、レジスタとアドレススペースで構成されています。環境には以下が含まれています。
- 汎用レジスタ
- セグメントレジスタ
- EFLAGSレジスタ
- EIPレジスタ
汎用命令は以下のデータ型を操作します。
- Byte, Word, Dword
- 符号付きと符号なしByte、Word, Dword整数
- NearとFarポインタ
- Bit領域
- BCD
7.2 PROGRAMMING ENVIRONMENT FOR GP INSTRUCTIONS IN 64-BIT MODE
64bitモードの汎用命令のプログラミング環境は7.1節のものと似ています。
(以下省略)
7.3 SUMMARY OF GP INSTRUCTIONS
汎用命令は以下のサブグループに分かれます。
- データ転送
- バイナリ算術
- 10進数算術
- 論理
- シフトと回転
- bitとByte
- 制御転送
- 文字列
- I/O
- EnterとLeave
- フラグ制御
- セグメントレジスタ
- その他
(以下省略)
7.3.1 Data Transfer Instructions
データ転送命令はByte、Dword, Qwordをメモリとレジスタ間、レジスタ間を移動します。
7.3.1.1 General Data Movement Instructions
移動命令のMOV命令とCMOVcc命令はメモリとレジスタ間、レジスタ間でデータを移動します。
MOV命令は基本データの読み込みと保存を実行します。MOV命令はあるメモリ位置から他のメモリ位置へまたは、あるセグメントレジスタから、他のセグメントレジスタへの移動は出来ません。メモリ間の移動はMOVS命令で実行されます。
CMOVcc命令はEFLAGレジスタのステータスフラグの状態をチェックし、もしフラグが指定されていれば移動を実行する命令のグループです。これらの命令はメモリと汎用レジスタ間または、レジスタ間で16bitまたは32bitの移動が出来ます。テストのフラグ状態は、命令に関連する状態コード(cc)で指定されます。条件が満たされない場合、移動は実行されず、CMOVcc命令に続く次の命令を実行し継続されます。
7.3.1.2 Exchange Instructions
交換命令は一つ以上のオペランドを交換し、場合によっては、LOCKシグナルのアサートまたはEFLAGSレジスタのフラグの変更などの追加の操作を実行します。
XCHG(exchange:交換)命令は2つのオペランドの内容を交換します。この命令は3つのMOV命令の代わりに使用され、他のオペランドの内容を読み込みをしている間にもうひとつのオペランドの内容を保管しておく一時領域を必要としません。XCHG命令でメモリオペランドが使用されるとき、プロセッサのLOCKシグナルは自動的にアサートされます。したがって、この命令は、プロセス同期のためのセマフォまたは同様のデータ構造を実装するのに有用です。
BSWAP(Byte swap)命令は32bitレジスタオペランドのByteを逆順にします。bit位置0-7は24-31へ、8-15は16から23へ交換されます。この命令を2回連続で実行すれば元に戻ります。BSWAP命令は算術演算で有用です。(XCHG命令はWord内のByteを交換することが出来ます。)
XADD(exchange and add:交換と加算)命令は2つのオペランドを交換し、2つのオペランドの合計を宛先オペランドへ保存します。EFLAGSのステータスフラグは加算の結果を示します。この命令はDOループを許可するマルチプロセッサのマルチプロセッサシステムでLOCKプレフィクスと組み合わせることが出来ます。
CMPXCHG(compare and exchange:比較と交換)命令とCMPXCHG8B(compare and exchange 8 bytes:比較と8byte交換)命令はマルチプロセッサを使用するシステムで操作を同期するために使用されます。CMPXCHG命令は3つのオペランド(レジスタのソースオペランド、もうひとつのEAXレジスタのソースオペランド、宛先オペランド)が必要です。もし宛先オペランドに値が含まれ、EAXレジスタと等しい場合、宛先オペランドはEAXレジスタではないもうひとつのソースオペランドの値で置き替えます。また、宛先オペランドの元の値はEAXレジスタに読み込まれます。EFLAGSレジスタのステータスフラグは、EAXレジスタの値から宛先オペランドを減算することによって得られた結果を反映します。
CMPXCHG命令はセマフォのテストと変更によく使われます。この命令はセマフォが開放されているかチェックします。もし開放されているなら、割り当て済みのマークを付けます。それ以外は現在の所有者のIDを取得します。これはすべて1つの無停止操作で実行されます。シングルプロセッサシステムでは、CMPXCHG命令は、セマフォをテストおよび変更するために複数の命令を実行する前に、保護レベル0(割り込みを無効にする)に切り替える必要性を排除します。
マルチプロセッサシステムの場合、CMPXCHGをLOCKプレフィックスと組み合わせて、原子的に比較および交換操作を実行することができます。
CMPXCHG8B命令には、EDX:EAXの64bit値、ECX:EBXの64bit値、およびメモリ内の宛先オペランドの3つのオペランドが必要です。この命令は、EDX:EAXレジスタの64bit値を宛先オペランドと比較します。等しい場合は、ECX:EBXレジスタの64ビット値が宛先オペランドに格納されます。EDX:EAXレジスタと宛先オペランドが等しくない場合、宛先オペランドはEDX:EAXレジスタに読み込まれます。CMPXCHG8B命令をLOCKプレフィックスと組み合わせて、原子的に操作を実行することができます。
7.3.1.3 Exchange Instructions in 64-Bit Mode
CMPXCHG16B命令は64bitモードのみで使用できます。CMPXCHG8Bが提供する機能の拡張で、128ビットのデータで動作します。
7.3.1.4 Stack Manipulation Instructions
PUSH命令, POP命令, PUSHA(全てのレジスタをpush)命令, POPA(全てのレジスタをpop)命令はスタックからまたはスタックへデータを移動します。PUSH命令はスタックの先頭へソースレジスタをコピーし、(ESPレジスタに含まれる)スタックポインタを減少させます。メモリオペランド、即値オペランド、(セグメントレジスタを含む)レジスタオペランドで動作します。PUSH命令はプロシージャを呼ぶ前にパラメータをスタックへ積むのによく使用されます。一時変数をスタックに確保することが出来ます。
PUSHA命令はスタックへ8つの汎用レジスタの内容を保存します。この命令は、汎用レジスタの内容を保存するのに必要な命令の数を減らすことによって、プロシージャコールを簡素化します。レジスタはEAX, ECX, EDX, EBX, EAXがプッシュされる前のESP, EBP, ESI, EDIの順にスタックにpushされます。
POP命令は宛先オペランドで指定された場所へ(ESPレジスタで示された)現在のスタックの先頭にWordかDwordでコピーします。そのとき、新しいスタックの先頭としてESPレジスタを加算します。宛先オペランドには汎用レジスタ、セグメントレジスタ、メモリ位置を指定することがあります。
POPA命令はPUSHA命令と逆の操作をします。スタックの先頭から8つのWordかDwordでESPレジスタを除く汎用レジスタへpopします。もしオペランドサイズが32の場合、スタックのDwordはEDI, ESI, EBP, Dword無視(?), EBX, EDX, ECX, EAXの順にレジスタへ転送されます。ESPレジスタは、スタックをpopする動作によって復元されます。もしオペランドサイズが16の場合、スタックのWordはDI, SI, BP, Word無視(?), BX, DX, CX, AXの順にレジスタへ転送されます。
7.3.1.5 Stack Manipulation Instructions in 64-Bit Mode
64bitモードではスタックポインタサイズが64bitで命令プレフィクスによる上書きが出来ません。暗黙のスタック参照では、アドレスサイズの上書きは無視されます。 64bitモードでは、スタック上の32bit値のpushおよびpopは不可能です。66Hのオペランドサイズのプレフィクスを使用すると、16bitのpushとpopがサポートされます。PUSHA、PUSHAD、POPA、およびPOPADはサポートされていません。
7.3.1.6 Type Conversion Instructions
型変換命令は、ByteをWord、WordをDword、DwordをQwordに変換します。これらの命令は、符号拡張を実行するため、整数をより大きな整数形式に変換する場合に特に便利です。
シンプルな変換(simple conversion)と移動と変換(move and convert)の2種類のタイプ変換命令が用意されています。
- Simple conversion
CBW(ByteからWordへの変換)、CWDE(WordからDword拡張への変換)、CWD(DwordからDwordへの変換)およびCDQ(DwordからQwordへの変換)命令は、ソースオペランドのサイズを2倍に拡張する符号拡張を実行します。
CBW命令は、ALレジスタのByteの符号(bit7)をAXレジスタの上位Byteの各bit位置にコピーします。CWDE命令は、AXレジスタ内のWordの符号(bit15)をEAXレジスタの上位Wordのすべてのbit位置にコピーします。
CWD命令は、AXレジスタ内のWordの符号(bit15)をDXレジスタ内のすべてのbit位置にコピーします。
CDQ命令は、EAXレジスタ内のDwordの符号(bit31)をEDXレジスタ内のすべてのbit位置にコピーします。CWD命令は、Wrod分割前のWordからDwordの被除数を生成するために使用することができ、CDQ命令は、Dword除算の前にDwordからQword被除数を生成するために使用することができます。 - move with sign or zero extensyon
MOVSX(符号拡張付き移動)およびMOVZX(拡張子ゼロで移動)命令は、ソース・オペランドをレジスタに移動し、符号拡張を実行します。
MOVSX命令は、ソースオペランドを符号拡張することによって、8bit値を16bit値または8bitまたは16bit値から32bit値に拡張します。MOVZX命令は、ソースオペランドをゼロ拡張することによって、8bit値を16bit値に、または8bitまたは16bit値を32bit値に拡張する。
7.3.1.7 Type Conversion Instructions in 64-Bit Mode
MOVSXD命令は64bitモードで操作します。32bitから64bitへ符号拡張を行います。この命令は64bitモード以外ではエンコードされません。
7.3.2 Binary Arithmetic Instructions
バイナリ算術命令は、符号付きまたは符号なしの2進整数として符号化された8bit, 16bit, 32bitの数値データで動作します。バイナリ算術命令は、BCDで動作するアルゴリズムでも使用できます。
7.3.2.1 Addition and Subtraction Instructions
ADD(整数加算)、ADC(キャリー付き整数加算)、SUB(整数減算)、SBB(borrow付き整数減算)命令は、符号付きまたは符号なし整数オペランドに対して加算および減算演算を実行します。
ADD命令は、2つの整数オペランドの合計を計算します。
ADC命令は、2つの整数オペランドの合計を計算し、CFフラグが設定されている場合は1を加算します。この命令は、段階的に数値を加算するときにキャリーを伝播するために使用されます。
SUB命令は、2つの整数オペランドの差を計算します。
SBB命令は、2つの整数オペランドの差を計算し、CFフラグが設定されている場合は1を減算します。 この命令は、段階的に数値を減算するときにborrowを伝播するために使用されます。
7.3.2.2 Increment and Decrement Instructions
INC(インクリメント)命令とDEC(デクリメント)命令は符号なしオペランドから1を加算または減算します。それぞれの命令は主に、カウンタを実装するために使われます。
7.3.2.3 Increment and Decrement Instructions in 64-Bit Mode
INCおよびDEC命令は、64bitモードでサポートされています。 ただし、オペコードがREXプレフィクスとして扱われるため、INCおよびDECの一部の形式(MOD R/M Byteのレジスタ拡張フィールドを使用してエンコードされるレジスタオペランド)は64bitモードでエンコードできません。
7.3.2.4 Comparison and Sign Change Instructions
CMP(比較)命令は、2つの整数オペランド間の差を計算し、結果に応じてOF、SF、ZF、AF、PF、CFフラグを更新します。ソースオペランドは変更されず、結果も保存されません。CMP命令は、Jcc(ジャンプ)またはSETcc(Byteセット状態)命令と併せて一般に使用され、後者の命令はCMP命令の結果に基づいて動作を実行する。
NEG命令は、符号付き整数オペランドをゼロから減算します。NEG命令の効果は、2の補数オペランドの符号をその大きさを維持しながら変更することです。
7.3.2.5 Multiplication and Division Instructions
プロセッサは、MUL(符号なし乗算)およびIMUL(符号付き乗算)の2つの乗算命令と、DIV(符号なし除算)およびIDIV(符号付き除算)の2つの除算命令を提供します。
MUL命令は、2つの符号なし整数オペランドを乗算します。結果は、ソースオペランドの2倍のサイズに計算されます(たとえば、Wordオペランドが乗算される場合、結果はDwordになります)。
IMUL命令は、2つの符号付き整数オペランドを乗算します。結果はソースオペランドの2倍のサイズに計算されます。ただし、結果がソースオペランドのサイズに切り捨てられることもあります。
DIV命令は、1つの符号なしオペランドを別の符号なしオペランドで除算し、商と剰余を返します。
IDIV命令は、DIV命令と同じですが、IDIVが符号付き除算を実行する点が異なります。
7.3.3 Decimal Arithmetic Instructions
小数点演算は、バイナリ算術演算命令ADD、SUB、MUL、DIVを10進算術命令と組み合わせて実行できます。小数点以下の算術命令は、以下の操作を実行するために提供されます。
- 前のバイナリ算術演算の結果を調整して有効なBCD結果を生成
- オペレーションが有効なBCD結果を生成するように、後のバイナリ算術演算のオペランドを調整
これらの命令は、パックされたBCD値とアンパックされたBCD値の両方で動作します。 この議論の目的のために、10進算術命令は、以下を提供する命令の下位サブグループに分割される。
- パックされたBCD調整
- 開梱されたBCD調整
7.3.3.1 Packed BCD Adjustment Instructions
DAA(加算後の小数調整)およびDAS(減算後の小数調整)命令は、パックされたBCD整数に対して実行された演算結果を調整します。2つのパックされたBCD値を追加するには、ADD命令の後にDAA命令を追加するという2つの命令が必要です。ADD命令は、2つの値を加算し(バイナリ加算)、その結果をALレジスタに格納します。その後、DAA命令は、有効な2桁のパックされたBCD値を取得するためにALレジスタの値を調整し、加算の結果として小数点桁上げが発生した場合にCFフラグを設定します。
同様に、パックされたBCD値を別のものから減算するには、SUB命令に続いてDAS命令が必要です。
SUB命令は、別のBCD値から1つのBCD値を減算(バイナリ減算)し、その結果をALレジスタに格納します。次に、DAS命令は、ALレジスタの値を調整して有効な2桁のパックされたBCD値を取得し、減算の結果として小数点の桁下げが発生した場合にCFフラグを設定します。
7.3.3.2 Unpacked BCD Adjustment Instructions
AAA(加算後のASCII調整)、AAS(減算後のASCII調整)、AAM(乗算後のASCII調整)、AAD(除算前のASCII調整)命令は、アンパックされたBCD値に対して実行された算術演算の結果を調整します。これらの命令はすべて、調整される値がALレジスタに格納されていることを前提としています。
AAA命令は、2つのアンパックされたBCD値の加算後にALレジスタの内容を調整します。ALレジスタのバイナリ値を10進数に変換し、結果をALレジスタにアンパックされたBCD形式で格納します(10進数はレジスタの下位4ビットに格納され、上位4ビットはクリアされます)。 加算の結果、小数点の桁上がりが発生した場合、CFフラグがセットされ、AHレジスタの内容が1だけインクリメントされます。
AAS命令は、2つのアンパックされたBCD値の減算に続いてALレジスタの内容を調整します。ここでもまた、バイナリ値は、アンパックされたBCD値に変換されます。BCD減算を完了するためにborrowが必要だった場合は、CFフラグがセットされ、AHレジスタの内容が1だけデクリメントされます。
AAM命令は、2つのアンパックされたBCD値の乗算に続いてALレジスタの内容を調整します。
ALレジスタのバイナリ値を10進数値に変換し、その結果の最下位桁をALレジスタ(アンパックされたBCD形式)に格納し、最上位桁がある場合はAHレジスタに格納します(アンパックされたBCD形式)。
AAD命令は2桁のBCD値を調整し、その値がDIV命令で除算されたときに有効なアンパックされたBCD結果が得られるようにします。この命令は、レジスタAH(最上位桁)およびAL(最下位桁)のBCD値をバイナリ値に変換し、結果をレジスタALに格納します。 ALの値をアンパックされたBCD値で除算すると、商と剰余はアンパックされたBCD形式で自動的にエンコードされます。
7.3.4 Decimal Arithmetic Instructions in 64-Bit Mode
小数点以下の算術命令は、64ビットモードではサポートされておらず、無効またはエンコード不可能です。
7.3.5 Logical Instructions
論理命令AND、OR、XOR(排他的論理和)、NOTは、それらが命名された標準論理演算を実行する。AND、OR、XOR命令には2つのオペランドが必要です。NOT命令は単一オペランドで動作します。
7.3.6 Shift and Rotate Instructions
シフトおよび回転命令は、オペランド内のビットを再配置します。
7.3.6.1 Shift Instructions
SAL(シフト算術左)命令、SHL(シフト論理左)命令、SAR(シフト算術右)命令、SHR(シフト論理右)命令は、Byte、Word、Dwordのビットの算術または論理シフトを実行します。
SAL命令とSHL命令は同じ操作を実行します。ソースオペランドを1から31bitの位置にシフトします。空のビット位置がクリアされます。 CFフラグは、最後のbitがオペランドからシフトされて読み込まれます。
SHR命令は、ソースオペランドを1-31bitの位置にシフトします。SHL/SAL命令と同様に、空のbit位置がクリアされ、オペランドからシフトされた最後のビットがCFフラグに読み込まれます。
SAR命令は、ソースオペランドを1-31bitの位置にシフトします。この命令は、オペランドが正の場合は空のbit位置をクリアするか、オペランドが負の場合は空のbitをセットすることにより、ソースオペランドの符号を保持するという点でSHR命令とは異なります。また、CFフラグは、最後のbitがオペランドからシフトされて読み込まれます。
SAR命令とSHR命令を使用して、2の累乗で除算を実行することもできます。
7.3.6.2 Double-Shift Instructions
SHLD(ダブル左シフト)命令とSHRD(ダブル右シフト)命令は、指定された数のbitをあるオペランドから別のオペランドにシフトします。それらは、アライメントされていないbit列に対する操作を容易にするために提供されています。また、さまざまな文字列移動操作の実装にも使用できます。
SHLD命令は、宛先オペランドのbitを左にシフトし、宛先オペランド内の空のbit位置をソースオペランドからビットシフトして埋めます。宛先オペランドとソースオペランドは同じ長さ(WordまたはDword)でなければなりません。シフトカウントの範囲は0-31bitです。このシフト演算の結果は宛先オペランドに格納され、ソースオペランドは変更されません。CFフラグには、宛先オペランドからシフトされた最後のbitが読み込まれます。
SHRD命令は、SHLD命令と同じように動作しますが、宛先オペランドのbitが右にシフトされます。空のbit位置は、ソースオペランドからシフトされたbitで埋められます。
7.3.6.3 Rotate Instructions
ROL(左回転)、ROR(右回転)、RCL(左へ回転)、RCR(右へ回転)命令は、宛先オペランドのbitを一方の端から他方の端へと回転させます。シフトとは異なり、回転中はbitが失われません。回転数の範囲は0-31です。
ROL命令は、オペランドのビットを左に(より重要なビット位置に向かって)回転させます。 ROR命令はオペランドを右に(下位ビット位置に向かって)回転させます。
RCL命令は、オペランドのbitをCFフラグを介して左に回転させます。この命令は、CFフラグをオペランドの上端の1bit拡張として扱います。オペランドの最上位bit位置から出る各bitはCFフラグに移動します。同時に、CFフラグのbitがオペランドの最下位bit位置に入ります。
RCR命令は、オペランドのbitをCFフラグを通して右に回転させます。
すべての回転命令では、命令がオペランドの拡張としてCFフラグを使用しない場合でも、CFフラグは常にオペランドから回転された最後のビットの値を含みます。 このフラグの値は、条件付きジャンプ命令(JCまたはJNC)によってテストできます。
7.3.7 Bit and Byte Instructions
bit命令とByte命令はbitまたはByte文字列を操作します。
7.3.7.1 Bit Test and Modify Instructions
bitテストと変更命令はオペランドの単一bitを操作します。bitの位置は、オペランドの最下位bitからのオフセットとして指定されます。プロセッサがテストされ、修正されるべきbitを識別すると、まずbitの現在の値でCFフラグを読み込みます。次に、命令の変更操作によって決定されるように、選択されたbitに新しい値を割り当てます。
7.3.7.2 Bit Scan Instructions
BSF(bitを前にスキャン)命令とBSR(bitを後ろにスキャン)命令はセットbitのソースオペランド内のbit文字列をスキャンし、宛先レジスタにある最初のセットビットのbitインデックスを格納します。bitインデックスは、ビット列の最下位ビット(ビット0)から最初のセットビットまでのオフセットです。BSF命令は、ソースオペランドをlowからhighに(ソースオペランドのbit0から最上位bitに向かって)スキャンします。 BSR命令は、highからlowに(最上位ビットから最下位ビットに向かって)にスキャンします。
7.3.7.3 Byte Set on Condition Instructions
EFLAGSレジスタ内の選択されたステータスフラグ(CF、OF、SF、ZF、PF)の状態に応じて、SETcc(Byteのset条件)命令は宛先オペランドバイトを0または1に設定します。SETニーモニックに追加されたプレフィクス(cc)は、テスト対象の条件を決定します。
たとえば、SETO命令はオーバーフローをテストします。OFフラグがセットされている場合、宛先バイトは1にセットされます。OFがクリアされている場合は、宛先バイトは0にクリアされます。付録B「EFLAGS Condition Codes」には、この命令でテストできる条件がリストされています。
7.3.7.4 Test Instruction
TEST命令は、2つのオペランドの論理ANDを実行し、その結果に応じてSF、ZF、PFフラグを設定します。その後、条件付きジャンプまたはループ命令またはSETcc命令によってフラグをテストできます。TEST命令は、どちらのオペランドも変更しない点で、AND命令と異なります。
7.3.8 Control Transfer Instructions
プロセッサはプログラム実行の流れを指示するために条件制御転送命令と無条件転送命令を提供します。条件転送命令はEFLAGSレジスタのステータスフラグの状態を指定された状態でのみ実行されます。無条件転送命令はいつも実行されます。
7.3.8.1 Unconditional Transfer Instructions
JMP命令, CALL命令, RET命令, INT命令, IRET命令は命令ストリームの他の位置(宛先アドレス)へプログラム制御を転送します。
宛先は同じコードセグメント(near transfer)または異なるコードセグメント(far transfer)どちらでも指定可能です。
-
Jump instruction
JMP命令は宛先命令へプログラム制御を無条件転送します。転送は片道です。つまり、戻りアドレスは保存されません。宛先オペランドは宛先命令のアドレス(命令ポインタ)を指定します。アドレスは相対アドレスまたは絶対アドレスを指定できます。
相対アドレスはEIPレジスタのアドレスに対するディスプレースメント(オフセット)です。宛先アドレス(near pointer)はEIPレジスタのアドレスにディスプレースメントを加算することによって形成されます。ディスプレースメントは符号付き整数で指定され、命令ストリーム内で前後にジャンプすることが出来ます。
絶対アドレスはセグメントのアドレス0からのオフセットです。次のいずれかの方法で指定できます。- 汎用レジスタ内のアドレス
このアドレスはEIPレジスタにコピーされ、near pointerとして扱われます。プログラム実行は、現在のコードセグメント内の新しいアドレスで継続されます。 - プロセッサの標準アドレッシングモードを使用して指定されたアドレス
アドレスはnear pointerまたはfar pointerを使用できます。もしfar pointerのアドレスなら、アドレスは(CSレジスタ内でコピーされる)セグメントセレクタと(EIPレジスタにコピーされる)オフセットに変換されます。
- 汎用レジスタ内のアドレス
保護モードではJMP命令はコールゲート、タスクゲート、タスク状態セグメントへのジャンプも出来ます。
- Call and retrun instructions
CALL(call procedure)命令とRET(return from procedure)命令はあるプロシージャ(またはサブルーチン)から別のプロシージャ(またはサブルーチン)へジャンプし、呼び出しプロシージャへのジャンプバック(戻り)を行います。
CALL命令は現在の(または呼び出し)プロシージャから別のプロシージャ(または呼びだされたプロシージャ)へプログラム制御を転送します。呼び出し元のプロシージャに後で戻るために、CALL命令は呼び出されたプロシージャにジャンプする前にEIPレジスタの現在の内容をスタックに保存します。(プログラム制御を転送する前の)EIPレジスタには、CALL命令に続く命令のアドレスが含まれます。このアドレスがスタックにpushされると、それはリターン命令ポインタまたはリターンアドレスと呼ばれます。
呼び出されるプロシージャのアドレス(ジャンプ先のプロシージャ内の最初の命令アドレス)は、JMP命令と同様にCALL命令で指定されます。アドレスは、相対アドレスまたは絶対アドレスとして指定できます。絶対アドレスが指定されている場合は、near pointerまたはfar pointerのいずれかになります。
RET命令は、現在実行中のプロシージャ(呼び出されたプロシージャ)から呼び出されたプロシージャ(呼び出し側プロシージャ)にプログラム制御を戻します。制御の転送は、リターン命令ポインタをスタックからEIPレジスタにコピーすることによって達成されます。プログラムの実行は、EIPレジスタが指す命令を続けます。
RET命令にはオプションのオペランドがあり、その値は戻り操作の一部としてESPレジスタの内容に加算されます。このオペランドは、スタックポインタをインクリメントして、呼び出し元のプロシージャによってスタックにプッシュされたパラメータをスタックから削除します。
- 割り込み命令からの復帰
プロセッサが割り込みを処理するとき、プロセッサは割り込み処理プロシージャへの暗黙の呼び出しを実行します。IRET(return from interrupt)命令は、割込みハンドラから中断されたプロシージャ(すなわち、割込みが発生したときに実行していたプロシージャ)へのプログラム制御を戻す。
IRET命令は、EFLAGSレジスタをスタックから復元することを除いて、RET命令と同様の動作を実行します。EFLAGSレジスタの内容は、プロセッサが割り込みを処理するときにリターン命令ポインタとともにスタックに自動的に格納されます。
7.3.8.2 Conditional Transfer Instructions
条件転送命令は、指定された条件が満たされた場合に命令ストリーム内の別の命令にプログラム制御を転送するジャンプまたはループを実行します。制御転送の条件は、EFLAGSレジスタ内のステータスフラグ(CF, ZF, OF, SF)の様々な状態を定義する一連の条件コードで指定されます。
- 条件ジャンプ命令
Jccジャンプ命令は、命令に関連付けられた条件コード(cc)で指定された条件が満たされている場合、プログラム制御を宛先命令に転送します。条件が満たされない場合、Jcc命令の次の命令で実行が継続されます。JMP命令と同様に、転送は片道です。つまりリターンアドレスは保存されません。
宛先オペランドは現在のコードセグメント内の命令を指し示す相対アドレス(EIPレジスタ内のアドレスに対する符号付きオフセット)を指定します。Jcc命令はfar transferをサポートしていません。ただしfar transferはJcc命令とJMP命令を組み合わせて実行できます。
Jcc命令のニーモニックを形成するために、条件コード「J」が付加されます。命令は符号なし条件付きジャンプと署名付き条件付きジャンプという2つのグループに分かれています。これらのグループは、それぞれ符号なし整数および符号付き整数に対して実行された演算の結果に対応します。(JA/JNBEなどの)ペアとしてリストされた命令は、同じ命令の代替名です。アセンブラは、プログラムリストを読みやすくするための代替名を提供します。
JCXZ命令とJECXZ命令は1つ以上のステータスフラグの代わりに、CXレジスタとECXレジスタをそれぞれテストします。
- ループ命令
LOOP, LOOPE(等価のループ)命令、LOOPZ(ゼロのループ)命令、LOOPNE(等価ではないループ)命令、LOOPNZ(ゼロではないループ)命令は、ECXレジスタの値をループを実行する回数のカウンタとして使用します。全てのループ命令は、実行されるたびにECXレジスタのカウンタをデクリメントしゼロに達するとループを終了します。また、LOOPE命令, LOOPZ命令, LOOPNE命令, LOOPNZ命令は、カウンタがゼロになる前にループを終了する条件としてZFフラグを受け入れます。
LOOP命令は、ECXレジスタ(またはアドレスサイズが16の場合はCXレジスタ)の内容をデクリメントしてから、レジスタのループ終了条件をテストします。ECXレジスタのカウンタが0でなければ、宛先オペランドで指定された命令アドレスにプログラム制御が移ります。宛先オペランドは相対アドレス(つまり、EIPレジスタの内容に関連するオフセット)であり、一般にループ内で実行されるコードブロック内の最初の命令を指します。
ECXレジスタのカウンタがゼロになると、ループを終了するLOOP命令の直後の命令にプログラム制御が移ります。LOOP命令が最初に実行された時にECXレジスタのカウンタがゼロである場合レジスタはFFFFFFFFHにプリデクリメントされ、ループが$32^2$回実行されます。
(以下省略)
7.3.8.3 Control Transfer Instructions in 64-Bit Mode
64bitモードでは全てのnear branches(CALL, RET, JCC, JCXZ, JMP, LOOP)のオペランドサイズは64bitにされます。リストされた命令は、REXオペランドサイズプレフィクスを必要とせずに64ビットRIPを更新します。
次の操作のnear branchesは64bitに強制されます(オペランドサイズプレフィクスに関係なく)。
- 命令ポインタのサイズの切り捨て
- CALLまたはRETによるスタックpopまたはpushのサイズ
- CALLまたはRETによるスタックポインタのインクリメントまたはデクリメントのサイズ
- 間接分岐オペランドサイズ
相対分岐のディスプレースメントフィールドは依然として32bitに制限されており、近くの分岐のアドレスサイズは強制されないことに注意してください。
アドレスサイズは、JCXZおよびLOOPに使用されるレジスタサイズ(CX/ECX/RCX)を決定します。また、メモリ間接分岐のアドレス計算にも影響します。アドレスのサイズはデフォルトで64bitですが、32bitにオーバーライドすることができます(プレフィクスを使用)。
7.3.8.4 Software Interrupt Instructions
INTn(ソフトウェア割り込み)命令, INTO(オーバーフローによる割り込み)命令, BOUND(範囲外値の検知)命令は指定された割り込みまたは例外を明示的に発生させることができ、割り込みまたは例外のハンドラルーチンを呼び出します。
INTn命令は、命令内の割り込みまたは例外のベクタをエンコードすることによって、プロセッサの割り込みまたは例外を発生させることが出来ます。この命令は、ソフトウェアによって生成された割り込みをサポートしたり、割り込みハンドラと例外ハンドラの動作をテストするために使用できます。
IRET命令は、割り込みハンドラから割り込み処理にプログラム制御を戻します。IRET命令は、RET命令と同様の操作を実行します。
CALL命令、RET命令は、あるプロシージャから別のプロシージャへのジャンプおよびその後の呼び出しプロシージャへの戻り操作を行います。 EFLAGSレジスタの内容は、プロセッサが割り込みを処理するときにリターン命令ポインタと共にスタックに自動的に格納されます。
OFフラグがセットされている場合、INTO命令はオーバーフロー例外を発生させます。 フラグがクリアされている場合、例外は発生せずに実行が継続されます。 この命令によって、ソフトウェアはオーバーフロー例外ハンドラに明示的にアクセスして、オーバーフロー状態をチェックすることができます。
BOUND命令は符号付きの値を上限と下限と比較し、値が下限より小さいか上限より大きい場合は "BOUND range exceeded"例外を発生させます。この命令は、配列インデックスをチェックして配列に定義された範囲内にあるかどうかを確認するなどの操作に役立ちます。
7.3.8.5 Software Interrupt Instructions in 64-bit Mode and Compatibility Mode
64bitモードではBOUNDはサポートされていません
(省略)
7.3.9 String Operations
GP命令には大きいデータ構造にアクセスするための文字列命令セットが含まれます。
7.3.9.1 String Instructions
MOVS命令、CMPS命令、SCAS命令、LODS命令、STOS命令は英数字文字列を許容し、メモリ内で移動し、調べることが出来ます。これらの命令はByte, Word, Dwordの文字列の個々に対して操作します。操作される文字列要素はESI(ソース文字列)レジスタとEDI(宛先文字列)レジスタで認識されます。2つのレジスタには文字列を指す(セグメントのオフセット)絶対アドレスが含まれます。
通常、ESIレジスタはCS, SS, ES, FS, GSセグメントレジスタで構成されます。EDIレジスタはESセグメントレジスタで識別されるセグメントをアドレス指定します。セグメントのオーバーライドはEDIレジスタには許可されません。文字列命令に2つの異なるセグメントレジスタを使用することにより、異なるセグメントに位置するストリングに対して演算を実行することができます。 また、ESIレジスタをESセグメントレジスタに関連付けることによって、ソース文字列と宛先文字列の両方を同じセグメントに配置することができます。(この後者の条件は、DSおよびESセグメントレジスタを同じセグメントセレクタに読み込み、ESIレジスタをDSレジスタにデフォルト設定させることによっても達成できます)。
MOVS命令はEDIレジスタによってアドレス指定された位置へESIレジスタによってアドレス指定された文字列を移動させます。アセンブラはこの命令を3つの省略形(MOVSB, MOVSW, MOVSD)で認識し、これらは移動される文字列のサイズを指定します。
CMPS命令はソース文字列から宛先文字列を取り除き、結果をEFLAGSレジスタのステータスフラグ(CF, ZF, OF, SF, PF, AF)を更新します。どちらの文字列もメモリに書き戻されません。 アセンブラは、3つの省略形(CMPSB、CMPSW、CMPSD)を認識します。
SCAS命令は(オペランド長によって)EAXレジスタ, AXレジスタ, ALレジスタの内容から宛先文字列を取り除き、結果をステータスフラグに更新します。文字列とレジスタの内容は変更されません。オペランド長によってSCAS命令はSCASB, SCASW, SCASDを指定します。
LODS命令はESIレジスタによって識別されたソース文字列を(Dword文字列であれば)EAXレジスタ、(Word文字列であれば)AXレジスタ、(Byte文字列であれば)ALレジスタに読み込みます。この命令の省略形としてLODSB, LODSW, LODSDが指定できます。この命令は、ターゲットレジスタに読み込まれた文字列の各要素を処理するためのループ内で使用されます。
STOS命令はEAXレジスタ, AXレジスタ, ALレジスタからソース文字列をEDIレジスタで識別された位置のメモリへ格納されます。この命令の省略形にはSTOSB, STOSW, STOSDがあります。この命令は通常ループで使用されます。このとき、文字列は一般的にLODS命令を使用してレジスタに読み込まれ、他の命令によって操作され、STOS命令でメモリに再度格納されます。
7.3.9.2 Repeated String Operations
それぞれの文字列命令は文字列の一つのイテレーションを実行します。Dwordより長い文字列を操作するために、文字列命令は繰り返し命令またはループを作成するためのrepeat prefix(REP)を組み合わせることが出来ます。
文字列命令を使用したとき、ESIレジスタとEDIレジスタは文字列次の(Byte, Word, Dwordの)要素のそれぞれのイテレーション後に自動的にインクリメントまたはデクリメントされます。したがって文字列操作は上位アドレスから下位アドレス方向へまたは下位アドレスから上位アドレス方向へ始めることが出来ます。EFLAGSレジスタのDFフラグはレジスタがインクリメントされた(DF=0)またはデクリメントされた(DF=1)かどうか制御します。STD命令とCLD命令はこのフラグをセットまたはクリアします。
次に示すrepeat prefixは文字列命令を繰り返すためのECXレジスタのカウンタと組み合わせて使用することが出来ます。
- REP
ECXレジスタがゼロでない間繰り返します。 - REPE/REPZ
ECXレジスタがゼロでないかつ、ZFフラグがセットされている間繰り返します。 - REPNE/REPNZ
ECXレジスタがゼロでないかつ、ZFフラグがクリアされている間繰り返されます。
文字列命令でrepeat prefixを指定したとき、操作はプレフィクスによって指定された条件の一つが満たされるまで操作は実行されます。REPE/REPZ、REPNE/REPNZはCMPS命令とSCAS命令でのみ使用されます。また、REP STOS命令は、大きなメモリブロックを初期化する最も早い方法です。
7.3.9.3 Fast-String Operation
パフォーマンスを向上するために、最近のプロセッサはMOVS, MOVSB, STOS, STOSBで開始された文字列格納操作中のプロセッサの操作に対する変更をサポートします。高速文字列操作と呼ばれるこの最適化された操作は、それらの命令のうちの1つの実行が特定の初期条件を満たす場合に使用されます。高速文字列操作を使用する命令は、ネイティブデータサイズ(Byte、Word、Dword、Qword)の複数の要素を含む可能性があるグループ内の文字列を効果的に処理します。高速文字列操作では、プロセッサはこれらのグループ間の境界でのみ割り込みとデータブレークポイントを認識します。高速ストリング操作は、ソースアドレスと宛先アドレスの両方がWBまたはWCメモリータイプのいずれかを使用する場合にのみ使用されます。高速文字列操作の初期条件は実装固有であり、ネイティブストリングサイズによって異なる場合があります。高速文字列操作の使用に影響する可能性のあるパラメーターの例は、以下のとおりです。
- EDIおよびESIアライメントレジスタに示されているアライメント
- 文字列操作のアドレス順
- 初期動作カウンタ(ECX)の値
- ソースアドレスと宛先アドレスの差
(以下省略)
7.3.9.4 String Operations in 64-Bit Mode
64bitモードのMOVS, CMPS, SCAS, LODS, STOSの動作は非64bitモードのものと似ています。違いのみを以下に示します。
- ソースオペランドは操作のアドレスサイズによってRSIまたはDS:ESIが指定されます。
- 宛先オペランドは操作のアドレスサイズによってRDIまたはDS:EDIが指定されます。
- 64bitデータの操作はREX.Wプレフィクスを使うことによりサポートされます。
64bitモードで文字列操作にREPプレフィクスを使用したとき、繰り返しカウンタは(アドレスサイズによって)RCXまたはECXを指定されます。デフォルトのアドレスサイズは64bitです。
7.3.10 I/O Instructions
メモリとのI/O命令の場合、64bitモードの違いは次のとおりです。
- ソースオペランドはアドレスサイズによってRSIまたはDS:ESIが指定されます。
- 宛先オペランドはアドレスサイズによってRDIまたはDS:EDIが指定されます。
- 64bitデータによる操作はエンコードできず、REXプレフィクスは暗黙のうちに無視されます。
7.3.12 Enter and Leave Instructions
ENTER命令とLEAVE命令はC、Pascalのようなブロック構造言語によるプロシージャコールのマシン語のサポートを提供します。
7.3.13 Flag Control (EFLAG) Instructions
フラグ制御(EFLAGS)命令はEFLAGSレジスタの選択された状態を読み込みまたは変更します。
7.3.13.1 Carry and Direction Flag Instructions
STC命令, CLC命令, CMC命令はEFLAGSレジスタのCFフラグを変更します。これらの命令は一般的にフラグを使用する命令が実行される前に、CFフラグを既知の状態に初期化するために使用されます。これらはまたRCLあるいはRCR命令と一緒に使われます。
STD命令とCLD命令はEFLAGSレジスタのDFフラグを変更します。DFフラグは、文字列処理命令を実行するときにインデックスレジスタESIおよびEDIがステップされる方向を決定します。DFフラグがクリアされている場合、インデックスレジスタは文字列命令の各イテレーションの後にインクリメントされます。DFフラグがセットされていれば、レジスタはデクリメントされます。
7.3.13.2 EFLAGS Transfer Instructions
EFLAGS転送命令はEFLAGSレジスタのフラググループをレジスタやメモリにコピーまたはレジスタやメモリから読み込みます。
LAHF命令とSAHF命令はEFLAGSレジスタの5つのステータスフラグ(SF, ZF, AF, PF, CF)を操作します。LAHF命令はAHレジスタのbit 7, 6, 4, 2, 0へステータスフラグをコピーします。レジスタの残されたbit(bit 5, 3, 1)の内容は影響を受けず、EFLAGSレジスタの残りの内容は変更されません。SAHF命令はAHレジスタからSFフラグ, ZFフラグ, AFフラグ, PFフラグ, CFフラグにbit 7, 6, 4, 2, 0をコピーします。
PUSHF命令、PUSHFD命令, POPF命令, POPFD命令はEFLAGSレジスタのフラグをスタックからまたはスタックへコピーします。PUSHF命令はEFLAGSレジスタの下位Wordをスタックへpushします。PUSHFD命令はEFLAGSレジスタの全てを(RFフラグとVMフラグをクリアとして読み込み)スタックにpushします。
POPF命令はスタックからEFLAGSレジスタにWordをpopします。EFLAGSレジスタのbit 11, 10, 8, 7, 6, 4, 2, 0だけに影響します。もし現在のコードセグメントのCPL(current privilege level)が0の場合、IOPL bit(bit 13-12)は影響を受けます。もしIOPL(I/O privilege level)が数値的にCPL以上ならIFフラグ(bit 9)は影響を受けます。
POPFD命令はEFLAGSレジスタへDwordでpopします。この命令はAC bit(bit 18)とID bit(bit 21)の状態とPOPF命令の影響を受けるbitを変更することが出来ます。POPF命令に対して与えられたIOPLbitおよびIFフラグを変更するための制限は、POPFD命令にも当てはまります。
7.3.13.3 Interrupt Flag Instructions
STI命令とCLI命令は割り込みでEFLAGSレジスタのIFフラグによる変更をします。IFフラグは(プロセッサのINTR pinで受信された)ハードウェア割り込みの処理を制御します。もしIFフラグがセットされている場合、プロセッサはハードウェア割り込みを処理します。もしクリアされている場合、ハードウェア割り込みはマスクされます。
これらの命令を実行する能力は、プロセッサの動作モードおよびこれらの命令を実行しようとしているプログラムまたはタスクの現在の特権レベル(CPL)に依存します。
7.3.14 Flag Control (RFLAG) Instructions in 64-Bit Mode
64bitモードでは、LAHF命令とSAHF命令はCPUID.80000001H:ECX.LAHF-SAHF[bit 0] = 1のときにサポートされます。
(以下省略)
7.3.15 Segment Register Instructions
プロセッサはプロセッサのセグメントレジスタを直接アドレス指定するための様々な命令を提供します。これらの命令はOSあるいは実行環境がセグメントを使用するまたはメモリモデルでreal-addressモードのときにのみ使用されます。
7.3.15.1 Segment-Register Load and Store Instructions
MOV命令、PUSH命令、POP命令はセグメントレジスタ(DS, ES, FS, GS, SS)へ16bitセグメントセレクタを転送またはセグメントレジスタから16bitセグメントセレクタを転送します。転送は常にセグメントレジスタと汎用レジスタまたはメモリとの間で行われます。セグメントレジスタ間の転送はサポートされていません。
POPおよびMOV命令はCSレジスタに値を配置できません。遠隔制御転送バージョンのJMP、CALL、およびRET命令のみがCSレジスタに直接影響します。
7.3.15.2 Far Control Transfer Instructions
JMP命令とCALL命令は、プログラム制御を現在のCSレジスタが指しているセグメント以外のセグメントに転送するための宛先としてfar pointerを受け入れます。
CALL命令でfar callを指定した場合、現在のEIPレジスタとCSレジスタの値はスタックへpushされます。
RET命令はfar returnで実行できます。このとき、プログラム制御は、呼び出されたプロシージャを含むコードセグメントから、呼び出したプロシージャを含むコードセグメントに戻される。 RET命令は、呼び出し側のプロシージャのCSおよびEIPレジスタの値をスタックから復元します。
7.3.15.3 Software Interrupt Instructions
INT命令, INTO命令, IRET命令は、現在のコードセグメント以外のコードセグメントにある割り込みハンドラおよび例外ハンドラプロシージャから呼び出して戻すこともできます。しかし、これらの命令では、コードセグメントの切り替えは、アプリケーションプログラムから透過的に処理されます。
7.3.15.4 Load Far Pointer Instructions
LDS命令, LES命令, LFS命令, LGS命令, LSS命令はメモリからセグメントレジスタまたは汎用レジスタへfar pointerを読み込みます。far pointerのセグメントセレクタの一部は選択されたセグメントレジスタへ読み込まれ、オフセットは選択された汎用レジスタへ読み込まれます。
7.3.16 Miscellaneous Instructions
(省略)
7.3.16.1 Address Computation Instruction
LEA命令はソースオペランドの(セグメント内のオフセット)メモリあるいは汎用レジスタの場所の実行アドレスを計算します。この命令は、プロセッサのアドレッシングモードのいずれかを解釈することができ、必要なインデックス付けまたはスケーリングを実行することができます。 これは、文字列命令の実行前にESIまたはEDIレジスタを初期化する場合、またはXLAT命令の前にEBXレジスタを初期化する場合に特に便利です。
7.3.16.2 Table Lookup Instructions
XLAT命令とXLATB命令はメモリの変換テーブルから読まれたByteでALレジスタの内容を入れ替えます。変換テーブルの符号なしインデックスとしてALレジスタの初期値は解釈されます。このインデックスはテーブルエントリのアドレスを計算するために(テーブルのベースアドレスを含む)EBXレジスタの内容へ追加されます。
7.3.16.3 Processor Identification Instruction
CPUID命令は命令を実行せるためのプロセッサについての情報を返します。
7.3.16.4 No-Operation and Undefined Instructions
NOP命令は次の命令を指すためにEIPレジスタをインクリメントするが、それ以外は何も影響しない。
UD命令は無効オペコード例外を生成します。インテルはこの命令のオペコードを逆順にします。この命令は無効オペコード例外ハンドラをソフトウェアでテストするために提供されます。
7.3.17 Random Number Generator Instructions
(省略)
7.3.17.1 RDRAND
RDRAND命令はランダムな数値を返します。全てのRDRAND命令をサポートしているインテルプロセッサは、CPUID.01H:ECX.RDRAND [bit 30] = 1を報告することにより、RDRAND命令の可用性を示します。
RDRAND命令は暗号的にセキュアなDRBGを提供します。DRBGはNIST SP 800-90A standardで設計されています。DRBGは、オンチップ非決定論的エントロピー源から頻繁に再シードされ、RDRANDによって返されるデータが統計的に均一で、非周期的で非決定論的であることを保証する。
(以下省略)
7.3.17.2 RDSEED
RDSEED命令はランダムな数値を返します。全てのRDRAND命令をサポートしているインテルプロセッサは、CPUID.(EAX=07H, ECX=0H):EBX.RDSEED[bit 18] = 1を報告することにより、RDSEED命令の可用性を示します。
Chapter7まとめ
- 各命令の概要を説明
- CPUID命令で調べられる情報によってサポートされているか分かる命令がある
- 64bitモードではサポートされていない命令がある
CHAPTER 8 PROGRAMMING WITH THE X87 FPU
FPU(x87浮動小数点ユニット)は画像処理、科学、エンジニアリング、ビジネスアプリケーションで使うためのハイパフォーマンス浮動小数点処理能力を提供します。バイナリ浮動小数点算術のためのIEEE Standard 754で設計された浮動小数点、整数、パックされたBCD整数データ型と浮動小数点処理アルゴリズム、例外ハンドラアーキテクチャをサポートします。
(以下省略)
8.1 X87 FPU EXECUTION ENVIRONMENT
x87 FPUはIA-32内の別の実行環境です。この実行環境は8つのデータレジスタ(x87 FPUデータレジスタ)と次に示す特定用途レジスタが含まれます。
- ステータスレジスタ
- 制御レジスタ
- タグワードレジスタ
- 最新命令ポインタレジスタ
- 最新データ(オペランド)ポインタレジスタ
- オペコードレジスタ
8.1.1 x87 FPU in 64-Bit Mode and Compatibility Mode
互換モードと64bitモードではx87 FPU命令は保護モードと同様に機能します。
8.1.2 x87 FPU Data Registers
x87 FPUデータレジスタは8つの80bitレジスタが含まれます。値は倍精度拡張浮動小数点でそれらのレジスタに格納されます。浮動小数点または整数、パックされたBCDの値はメモリから読み込みx87 FPUデータレジスタに格納され、値は自動的に倍精度拡張浮動小数点形式に変換されます。計算結果が後でx87 FPUレジスタのいずれかからメモリに戻されると、結果は倍精度浮動小数点形式で残されるか、より短い浮動小数点形式、整数形式、パックされたBCD整数に変換されます。
x87 FPU命令はスタックレジスタとして8つのデータレジスタを扱います。データレジスタのすべてのアドレッシングは、スタックの最上位にあるレジスタを基準にしています。 現在のスタックの先頭のレジスタ番号は、x87 FPUステータスワードのトップ(スタックトップ)フィールドに格納されます。読み込み操作は先頭を1減らして新しいスタックトップレジスタに値を読み込み、格納操作は現在のトップレジスタの値をメモリに格納し、次にトップを1だけインクリメントします。(x87 FPUの場合、読み込み操作はpush操作に相当し、格納操作はpop操作に相当します)。スタックをpushしてpopしない読み込み操作と格納操作も使用できます。
トップが0のときに読み込み操作を実行すると、ラップアラウンドが発生し、トップの新しい値が7に設定されます。
浮動小数点スタックオーバーフロー例外は、ラップアラウンドによって未保存の値が上書きされる可能性があることを示します。
多くの浮動小数点命令には、プログラマが暗黙的にスタックのトップで動作すること、またはトップに対して特定のレジスタに対して明示的に動作することを可能にするいくつかのアドレッシングモードがあります。アセンブラは、現在のスタックトップを表すためにST(0)または単にSTを使用し、スタックのTOPからi番目のレジスタを指定するST(i)を使用して、これらのレジスタアドレッシングモードをサポートします))。たとえば、トップに011B(レジスタ3がスタックの先頭)がある場合、次の命令はスタック内の2つのレジスタ(レジスタ3と5)の内容を追加します。
FADD ST, ST(2);
(以下省略)
8.1.2.1 Parameter Passing With the x87 FPU Register Stack
汎用レジスタと同様に、x87 FPUデータレジスタの内容はプロシージャコールの影響を受けません。つまり、値はプロシージャの境界を越えて維持されます。したがって、呼び出し側のプロシージャは、プロシージャ間でパラメータを渡すために、x87 FPUデータレジスタ(およびプロシージャスタック)を使用できます。呼び出されたプロシージャは、現在のスタックレジスタポインタ(TOP)とST(0)およびST(i)命名法を使用してレジスタスタックを通過したパラメータを参照できます。呼び出されたプロシージャまたはプログラムに実行を戻すときに、呼び出されたプロシージャがレジスタST(0)に戻り値または結果を残すことも一般的な方法です。
MMXおよびx87 FPU命令をプロシージャまたはコードシーケンスで混合する場合、プログラマはx87 FPUデータレジスタに渡されるパラメータの整合性を維持する責任があります。x87 FPUデータレジスタのパラメータが別のプロシージャに渡される前にMMX命令が実行されると、パラメータが失われる可能性があります。
8.1.3 x87 FPU Status Register
16bit x87 FPU ステータスレジスタは現在のx87 FPUの状態を示します。x87ステータスレジスタのフラグにはFPU busyフラグ、スタック先頭(トップ)ポインタ、条件コードフラグ、例外サマリ状態フラグ、スタック違反フラグ、例外フラグが含まれます。x87 FPUはこのレジスタにフラグを設定して、演算結果を表示します。
x87 FPUステータスレジスタ(x87 FPUステータスワードと呼ばれる)の内容は、FSTSW/FNSTSW、FSTENV/FNSTENV、FSAVE/FNSAVE、FXSAVE命令を使用してメモリに格納できます。また、FSTSW/FNSTSW命令を使用して整数ユニットのAXレジスタに格納することもできます。
8.1.3.1 Top of Stack (TOP) Pointer
現在、x87 FPUレジスタスタックの先頭にあるx87 FPUデータレジスタへのポインタは、x87 FPUステータスワードのbit 11-13に格納されています。TOPと呼ばれるこのポインタは、0-7のバイナリ値です。
8.1.3.2 Condition Code Flags
4つの条件コードフラグ(C0-C3)は、浮動小数点比較および算術演算の結果を示します。
これらの条件コードbitは、主に条件付き分岐および例外処理で使用される情報の格納に使用されます。
C1条件コードフラグはさまざまな機能に使用されます。スタックオーバーフローまたはアンダーフロー例外(#IS)を示すx87 FPUステータスワードのIEフラグとSFフラグの両方がセットされている場合、C1フラグはオーバーフロー(C1 = 1)とアンダーフロー(C1 = 0)を区別します。 不正確な(丸められた)結果を示すステータスワードのPEフラグがセットされている場合、C1フラグは、命令による最後の丸めが上向きの場合に1に設定されます。FXAM命令は、調べる値の符号にC1を設定します。
C2条件コードフラグは、FPREMおよびFPREM1命令によって、不完全なリダクション(または部分的な剰余)を示すために使用されます。成功したリダクションが完了すると、C0、C3、C1の条件コードフラグが商の最下位3ビット(それぞれQ2、Q1、Q0)に設定されます。
FPTAN、FSIN、FCOS、およびFSINCOS命令は、C2フラグを1に設定してソース・オペランドが$±2^{63}$の許容範囲を超えていることを示し、ソースオペランドが許容範囲内であればC2フラグをクリアします。
8.1.3.3 x87 FPU Floating-Point Exception Flags
x87 FPUステータスワードの6つのx87 FPU浮動小数点例外フラグ(bit 0-5)は、bitが最後にクリアされてから1つ以上の浮動小数点例外が検出されたことを示します。
各例外フラグは、x87 FPU制御ワード内の例外マスクbitによってマスクすることができます。例外サマリステータスフラグ(ES、bit 7)は、マスクされていない例外フラグのいずれかがセットされたときにセットされます。
例外フラグは「スティッキー」bitです(一度設定すると、明示的にクリアされるまで設定されたままです)。これらは、FCLEX/FNCLEX(クリア例外)命令を実行するか、FINIT/FNINIT命令またはFSAVE/FNSAVE命令でx87 FPUを再初期化するか、FRSTORまたはFLDENV命令でフラグを上書きすることでクリアできます。
B bit(bit 15)は、8087互換性のためにのみ含まれています。これは、ESフラグの内容を反映します。
8.1.3.4 Stack Fault Flag
スタック障害フラグ(x87 FPUステータスワードのbit 6)は、スタックオーバーフローまたはスタックアンダーフローがx87 FPUデータレジスタスタックのデータで発生したことを示します。x87 FPUは、スタックオーバーフローまたはアンダーフロー状態を検出したときにSFフラグを明示的に設定しますが、無効な算術オペランド条件を検出したときに明示的にフラグをクリアしません。
このフラグが設定されている場合、条件コードフラグC1は、オーバーフロー(C1 = 1)およびアンダーフロー(C1 = 0)の障害の性質を示します。SFフラグは「スティッキー」フラグです。
8.1.4 Branching and Conditional Moves on Condition Codes
x87 FPU(P6ファミリプロセッサから)は、2つの浮動小数点値の比較に基づいて条件付き移動を分岐および実行する2つのメカニズムをサポートしています。 これらのメカニズムは、ここでは「古いメカニズム」および「新しいメカニズム」と呼ばれます。
古いメカニズムは、P6ファミリプロセッサとP6ファミリプロセッサより前のx87 FPUで利用できます。このメカニズムは、浮動小数点比較命令(FCOM、FCOMP、FCOMPP、FTST、FUCOMPP、FICOM、FICOMP)を使用して2つの浮動小数点値を比較し、結果に応じて条件コードフラグ(C0〜C3)を設定します。次に、2段階のプロセスを使用して、条件コードフラグの内容がEFLAGSレジスタのステータスフラグにコピーされます。
- FSTSW AX命令は、x87 FPUステータスワードをAXレジスタに移動
- SAHF命令は、条件コードフラグを含むAXレジスタの上位8bitをEFLAGSレジスタの下位8bitにコピー
(以下省略)
8.1.5 x87 FPU Control Word
16bit x87 FPU制御ワードは、使用されるx87 FPUおよび丸め方法の精度を制御します。また、x87 FPU浮動小数点例外マスクbitも含まれています。制御ワードはx87 FPU制御レジスタにキャッシュされます。このレジスタの内容は、FLDCW命令で読み込まれ、FSTCW/FNSTCW命令を使用してメモリに格納されます。
x87 FPUがFINIT/FNINITまたはFSAVE/FNSAVE命令で初期化されると、x87 FPU制御ワードは037FHに設定され、すべての浮動小数点例外がマスクされ、丸めが最も近い値に設定され、x87 FPU精度が64bitに設定されます。
8.1.5.1 x87 FPU Floating-Point Exception Mask Bits
例外フラグマスクbit(x87 FPU制御ワードのbit 0-5)は、x87 FPUステータスワードの6つの浮動小数点例外フラグをマスクします。これらのマスクbitの1つが設定されると、対応するx87 FPU浮動小数点例外が生成されなくなります。
8.1.5.2 Precision Control Field
x87 FPUによって行われる浮動小数点計算の精度(64,53,24bit)は、精度制御(PC)フィールド(x87 FPU制御ワードの8-9bit)によって決まります。デフォルトの精度は倍精度であり、x87 FPUデータレジスタの倍精度浮動小数点フォーマットで使用可能な完全64bitの仮数を使用します。この設定は、アプリケーションがx87 FPUデータレジスタで利用可能な最大精度をフルに活用できるため、ほとんどのアプリケーションに最適です。
倍精度と単精度の設定は、仮数のサイズをそれぞれ53bitと24bitに縮小します。これらの設定は、IEEE Standard 754をサポートし、特定の既存のプログラミング言語の仕様との互換性を提供するために提供されています。これらの設定を使用すると、倍精度浮動小数点フォーマットの64bit仮数長の利点が無効になります。精度の低下が指定された場合、仮数の値を四捨五入すると、右の未使用ビットが0にクリアされます。
精度制御bitは、次の浮動小数点命令の結果にのみ影響します。FADD、FADDP、FIADD、FSUB、FSUBP、FISUB、FSUBR、FSUBRP、FISUBR、FMUL、FMULP、FIMUL、FDIV、FDIVP、FIDIV、FDIVR、FDIVRP、FIDIVR、FSQRT。
8.1.5.3 Rounding Control Field
(以下省略)
8.1.6 Infinity Control Flag
(省略)
8.1.7 x87 FPU Tag Word
16bitタグワードは、x87 FPUデータレジスタスタック内の8つのレジスタ(レジスタごとに1つの2bitタグ)の内容を示します。 タグコードは、レジスタに有効な数値、ゼロ、特殊浮動小数点数(NaN、無限大、デノーマル、サポートされていない形式)が含まれているかどうか、または空であるかどうかを示します。x87 FPUタグワードは、x87 FPUタグワードレジスタのx87 FPUにキャッシュされます。x87 FPUがFINIT/FNINIT命令またはFSAVE/FNSAVE命令で初期化されると、x87 FPUタグワードはFFFFHに設定され、すべてのx87 FPUデータ・レジスタを空とマークします。
x87 FPUタグワードの各タグは、物理レジスタ(0-7の番号)に対応しています。x87 FPUステータスワードに格納されている現在のトップポインタを使用して、タグをST(0)に関連するレジスタに関連付けることができます。
(以下省略)
8.1.8 x87 FPU Instruction and Data (Operand) Pointers
x87 FPUには、最後に実行された非制御命令の命令およびデータ(オペランド)へのポインタが格納されます。これらはx87 FPU命令ポインタとx87 FPUデータ(オペランド)ポインタです。ソフトウェアはこれらのポインタを保存して例外ハンドラの状態情報を提供することができます。
x87 FPUデータポインタの値は常にメモリオペランドへのポインタであることに注意してください。実行された最後の非制御命令にメモリオペランドがない場合、データポインタの値は未定義(予約済み)です。CPUIDが(EAX = 07H、ECX = 0H):EBX[bit 6] = 1の場合、マスクされていないx87例外が発生するx87非制御命令の場合にのみデータポインタが更新されます。
FCLEX/FNCLEX、FLDCW、FSTCW/FNSTCW、FSTSW/FNSTSW、FSTENV/FNSTENV、FLDENV、およびWAIT/FWAITのいずれかの命令が実行されると、x87 FPU命令およびデータポインタの内容は変更されません。
8087を除くすべてのx87 FPUおよびNumeric Processor Extensions(NPX)では、x87 FPU命令ポインタは、その命令の前の接頭辞を指します。 8087の場合、x87 FPU命令ポインタは実際のオペコードのみを指します。
(以下省略)
8.1.9 Last Instruction Opcode
x87 FPUは、マスクされていないx87例外を発生させた最後のx87非制御命令のオペコードを11bit x87 FPU命令コードレジスタ(FOP)に格納します。(この情報は例外ハンドラの状態情報を提供します。)第1オペコードバイトと第2オペコードバイト(すべてのプレフィクスの後)のみがx87 FPUオペコードレジスタに格納されます。
第1オペコード・バイトの上位5bitはすべての浮動小数点オペコード(11011B)について同じであるので、このByteの下位3bitのみがオペコードレジスタに格納されます。
8.1.9.1 Fopcode Compatibility Sub-mode
(省略)
8.1.10 Saving the x87 FPU’s State with FSTENV/FNSTENV and FSAVE/FNSAVE
FSTENV/FNSTENVおよびFSAVE/FNSAVE命令は、例外ハンドラおよび他のシステムおよびアプリケーションソフトウェアが使用するために、x87 FPU状態情報をメモリに格納します。FSTENV/FNSTENV命令は、ステータス、コントロール、タグ、x87 FPU命令ポインタ、x87 FPUデータポインタ、およびオペコードレジスタの内容を保存します。FSAVE/FNSAVE命令は、その情報とx87 FPUデータレジスタの内容を格納します。FSAVE/FNSAVE命令は、x87 FPUの元の状態を保存した後に、x87 FPUをデフォルト値(FINIT/FNINIT命令と同じように)に初期化することにも注意してください。
FLDENVおよびFRSTOR命令は、メモリからx87 FPUにx87 FPU状態情報を読み込むことを許可します。このとき、FLDENV命令は、ステータス、コントロール、タグ、x87 FPU命令ポインタ、x87 FPUデータポインタ、およびオペコードレジスタのみをロードし、FRSTOR命令はx87 FPUスタックレジスタを含むすべてのx87 FPUレジスタを読み込みます。
8.1.11 Saving the x87 FPU’s State with FXSAVE
FXSAVEおよびFXRSTOR命令は、それぞれXMMレジスタおよびMXCSRレジスタの状態とともにx87 FPU状態を保存および復元します。FXSAVE命令を使用してx87 FPU状態を保存すると、(1)FXSAVEがFSAVEより速く実行されます。(2)FXSAVEはx87 FPU、MMX、およびXMM状態全体を1回の操作で保存します。
8.2 X87 FPU DATA TYPES
x87 FPUは、単精度浮動小数点、倍精度浮動小数点、倍精度拡張浮動小数点、符号付きワード整数、符号付きDword整数、符号付きQword整数の7つのデータ型、パックされたBCD 10進整数を認識して動作します。
80bitの倍精度浮動小数点形式を除いて、これらのデータ型はすべてメモリ内にのみ存在します。これらがx87 FPUデータレジスタに読み込まれると、倍精度拡張浮動小数点形式に変換され、その形式で処理されます。
IEEE標準754で要求されるように、各浮動小数点型でも非正規化値がサポートされています。単精度または倍精度浮動小数点形式の非正規化数をソースオペランドとして使用し、非正規化例外をマスクすると、x87 FPUは、倍精度拡張形式に変換されるときに自動的に数値を正規化します。
メモリに格納されると、x87 FPUデータ型の値の最下位Byteが、その値に指定された最初のアドレスに格納されます。その後、値からの連続するByteは、メモリ内の連続して高いアドレスに格納されます。浮動小数点命令は、オペランドの最初のアドレスのみを使用してメモリオペランドを読み込んで格納します。
原則として、値は倍精度形式でメモリに格納する必要があります。この形式は、プログラマの注意を最小限に抑えて正しい結果を返すための十分な範囲と精度を提供します。単精度形式は、アルゴリズムのデバッグに便利です。丸めの問題は、この形式でより迅速に現れます。倍精度拡張形式は通常、x87 FPUのレジスタと定数に中間結果を保持するために予約されています。余分な長さは、中間結果の丸めやオーバーフロー/アンダーフローの影響から最終結果を保護するように設計されています。ただし、アプリケーションがx87 FPUの最大範囲と精度(データの格納、計算、および結果用)を必要とする場合、値を倍精度拡張形式でメモリに格納することができます。
8.2.1 Indefinites
(省略)
8.2.2 Unsupported Double Extended-Precision Floating-Point Encodings and Pseudo-Denormals
(省略)
8.3 X87 FPU INSTRUCTION SET
x87 FPUがサポートする浮動小数点命令は、次の6つの機能カテゴリに分類できます。
- データ転送命令
- 基本的な算術命令
- 比較手順
- 高等な指示
- 定数読み込み命令
- x87 FPU制御命令
8.3.1 Escape(ESC) Instructions
x87 FPU命令セットのすべての命令は、エスケープ(ESC)命令として知られる命令のクラスに分類されます。これらの命令はすべて共通のオペコード形式を持ち、オペコードの最初のバイトはD8HからDFHまでの数字の1つです。
8.3.2 x87 FPU Instruction Operands
ほとんどの浮動小数点命令には、x87 FPUデータレジスタスタックまたはメモリに1つまたは2つのオペランドが必要です。(浮動小数点命令のどれも即値オペランドを受け入れません)。
オペランドがデータレジスタ内にある場合、物理レジスタ番号ではなくST(0)レジスタ(レジスタスタックの最上位にあるレジスタ)を基準にして参照されます。多くの場合、ST(0)レジスタは暗黙のオペランドです。
8.3.3 Data Transfer Instructions
データ転送命令は、次の操作を実行します。
- 浮動小数点、整数、またはパックされたBCDオペランドをメモリからST(0)レジスタに読み込み
- ST(0)レジスタの値を浮動小数点、整数、またはパックされたBCD形式でメモリに格納
- x87 FPUレジスタスタックのレジスタ間で値を移動
FLD命令は、浮動小数点オペランドをメモリからx87 FPUデータレジスタスタックの先頭にpushします。オペランドが単精度または倍精度浮動小数点形式である場合は、自動的に倍精度拡張浮動小数点形式に変換されます。この命令を使用して、選択したx87 FPUデータレジスタの値をレジスタスタックの最上位にpushすることもできます。
FILD命令は、メモリ内の整数オペランドを倍精度拡張浮動小数点形式に変換し、その値をレジスタスタックの先頭にpushします。FBLD命令は、メモリ内のパックされたBCDオペランドに対して同じ読み込み操作を実行します。
FST命令とFIST命令は、レジスタST(0)の値を宛先の形式(それぞれ浮動小数点または整数)でメモリに格納します。この場合も、フォーマット変換は自動的に実行されます。
FSTP、FISTP、FBSTP命令は、ST(0)レジスタの値を宛先の形式(浮動小数点、またはBCDをパックした後)、レジスタスタックに対してpop動作を実行します。pop命令を実行すると、ST(0)レジスタは空に、x87 FPU制御のスタックポインタ(TOP)は1だけインクリメントされます。FSTP命令を使用して、ST(0)別のx87 FPUレジスタ[ST(i)]に登録してください。FXCH(交換レジスタ内容)命令は、スタック[ST(i)]内の選択されたレジスタの値をST(0)の値と交換します。
FCMOVcc命令は、条件コード(cc)で指定された条件が満たされている場合に、スタック[ST(i)]内の選択されたレジスタの値をレジスタST(0)に移動します。テストされる条件は、EFLAGSレジスタのステータスフラグによって表されます。条件コードニーモニックは、FCMOVcc命令のニーモニックを形成するために、文字「FCMOV」に付加されます。
CMOVcc命令と同様に、FCMOVcc命令は、小さなIF構成を最適化するのに役立ちます。 また、IF演算の分岐オーバーヘッドを排除し、プロセッサによる分岐誤予測の可能性を排除します。
ソフトウェアは、CPUID命令を使用してプロセッサの機能情報をチェックすることによって、FCMOVcc命令がサポートされているかどうかを確認できます。
8.3.4 Load Constant Instructions
以下の命令は、一般的に使用される定数をx87 FPUレジスタスタックの最上部[ST(0)]にpushします。
- FLDZ Load $+0.0$
- FLD1 Load $+1.0$
- FLDPI Load $\pi$
- FLDL2T Load $\log_2 10$
- FLDL2E Load $\log_2 e$
- FLDLG2 Load $log_10 2$
- FLDLN2 Load $log_e 2$
定数値は完全な倍精度拡張浮動小数点精度(64bit)を持ち、精度は約19桁です。これらは、倍精度拡張浮動小数点よりも精度の高いフォーマットで内部的に格納されます。定数を読み込むとき、x87 FPUは、x87 FPU制御ワードのRC(丸め制御)フィールドに従って、より正確な内部定数を丸めます。 この丸めの結果不正確結果例外(#P)は生成されず、値が切り上げられた場合にはC1フラグがx87 FPUステータスワードに設定されません。
8.3.5 Basic Arithmetic Instructions
次の浮動小数点命令は、浮動小数点数の基本的な算術演算を実行します。
- FADD/FADDP 浮動小数点の加算
- FIADD 浮動小数点に整数を加算
- FSUB/FSUBP 浮動小数点の減算
- FISUB 浮動小数点から整数を減算
- FSUBR/FSUBRP 浮動小数点の逆減算
- FISUBR 整数から浮動小数点を逆減算
- FMUL/FMULP 浮動小数点の乗算
- FIMUL 整数に浮動小数点を乗算
- FDIV/FDIVP 浮動小数点の除算
- FIDIV 整数で浮動小数点を除算
- FDIVR/FDIVRP 浮動小数点を逆除算
- FIDIVR 整数を浮動小数点で逆除算
- FABS 絶対値
- FCHS 符号変更
- FSQRT 平方根
- FPREM 剰余
- FPREM1 IEEE剰余
- FRNDINT 積分値に丸める
- FXTRACT 指数と仮数を抽出
加算、減算、乗算、除算命令は、以下のタイプのオペランドで動作します。
- 2つのx87 FPUデータレジスタ
- x87データレジスタとメモリの浮動小数点または整数値
メモリ内のオペランドは、単精度浮動小数点、倍精度浮動小数点、Word整数、Dword整数形式にすることができます。これらは、倍精度拡張浮動小数点形式に自動的に変換されます。
FSUBRおよびFDIVR命令は、効率的なコーディングを可能にします。 たとえば、指定されたx87 FPUデータレジスタST(i)およびST(0)レジスタの値を操作するためのFSUBおよびFSUBR命令では、次のオプションを使用できます。
FSUB:
ST(0) ← ST(0) − ST(i)
ST(i) ← ST(i) − ST(0)
FSUBR:
ST(0) ← ST(i) − ST(0)
これらの命令は、減算または除算を実行するために、ST(0)レジスタと別のx87 FPUレジスタとの間で値を交換する必要性を排除します。
加算、減算、乗算、および除算命令のpopバージョンでは、算術演算後にx87 FPUレジスタスタックをpopするオプションが提供されます。これらの命令は、ST(i)レジスタおよびST(0)レジスタの値に作用し、その結果をST(i)レジスタに格納し、ST(0)レジスタをpopします。
FPREM命令は、インテル8087およびインテル287算術コプロセッサによって使用される方法で、2つのオペランドの除算から剰余を計算します。FPREM1命令は、IEEE標準754で指定されている方法で剰余を計算します。
FSQRT命令は、ソースオペランドの平方根を計算します。
FRNDINT命令は、x87 FPU制御ワードのRCフィールドで指定された丸めモードの方向にソース値に最も近い整数値である浮動小数点値を返します。
FABS、FCHS、およびFXTRACT命令は、便利な算術演算を実行します。
FABS命令は、ソースオペランドの絶対値を生成します。
FCHS命令は、ソースオペランドの符号を変更します。
FXTRACT命令は、ソースオペランドをその指数と分数に分離し、各値をレジスタに浮動小数点形式で格納します。
8.3.6 Comparison and Classification Instructions
次の命令は、浮動小数点値を比較または分類します。
- FCOM/FCOMP/FCOMPP 浮動小数点を比較し、x87 FPU条件コードフラグを設定
- FUCOM/FUCOMP/FUCOMPP 順不同浮動小数点を比較し、x87 FPU条件コードフラグを設定
- FICOM/FICOMP 整数を比較し、x87 FPU条件コードフラグを設定
- FCOMI/FCOMIP 順不同浮動小数点を比較し、EFLAGSステータスフラグを設定
- FTST テスト(浮動小数点と0.0を比較)
- FXAM 調査
浮動小数点値の比較は、整数の比較とは異なります。浮動小数点値には、より小さい、等しい、より大きい、および順序付けられていない4つの相互排他的な関係があります。
順序付けられていない関係は、比較される2つの値の少なくとも1つがNaNまたはサポートされていない形式である場合にtrueになります。定義上、NaNは数値ではないので、他の浮動小数点値との関係よりも小さい、等しい、または大きいことはできないため、この追加の関係が必要です。
FCOM、FCOMP、およびFCOMPP命令は、レジスタST(0)の値を浮動小数点ソースオペランドと比較し、結果に応じてx87 FPUステータスワードの条件コードフラグ(C0、C2、C3)を設定します。
順序付けられていない条件が検出された場合(値の一方または両方がNaNまたは未定義形式の場合)、浮動小数点無効操作例外が生成されます。
命令のpopバージョンは、比較操作が完了した後、1回または2回x87 FPUレジスタスタックをpopします。
FUCOM、FUCOMP、FUCOMPP命令は、FCOM命令、FCOMP命令、FCOMPP命令と同じように動作します。
唯一の違いは、FUCOM、FUCOMP、FUCOMPP命令では、オペランドの1つまたは両方がQNaNであるために順序付けられていない条件が検出された場合、浮動小数点無効操作例外は生成されません。
FICOMおよびFICOMP命令も、ソースオペランドがメモリ内の整数値であることを除いて、FCOMおよびFCOMP命令と同じように動作します。比較を行う前に、整数値は倍精度拡張浮動小数点値に自動的に変換されます。FICOMP命令は、比較演算の後にx87 FPUレジスタスタックをpopします。
FTST命令は、レジスタST(0)の値が常に値0.0と比較される点を除いて、FCOM命令と同じ操作を実行します。
FCOMIおよびFCOMIP命令は、P6ファミリプロセッサのIA-32アーキテクチャに導入されました。これらは、x87 FPUの代わりに比較結果を示すためにEFLAGSレジスタにステータスフラグ(ZF、PF、CF)を設定する点を除いて、FCOMおよびFCOMP命令と同じ比較を実行します条件コードフラグ。FCOMIおよびFCOMIP命令を使用すると、条件分岐命令(JCC)を比較結果から直接実行できます。
ソフトウェアは、CPUID命令を使用してプロセッサの機能情報をチェックすることによって、FCOMIおよびFCOMIP命令がサポートされているかどうかを確認できます。
FUCOMIおよびFUCOMIP命令は、順序付けられていない条件がオペランドの1つまたは両方がQNaNである場合、浮動小数点の無効な操作例外を生成しない点を除いて、FCOMIおよびFCOMIP命令と同じように動作します。FCOMIPおよびFUCOMIP命令は、比較操作の後にx87 FPUレジスタスタックをpopします。
FXAM命令は、ST(0)レジスタ内の浮動小数点値の分類(すなわち、値がゼロ、非正規数、通常の有限数、∞、NaN、またはサポートされていない形式)を判別するか、 レジスタは空です。分類を示すx87 FPU条件コードフラグを設定します。また、値の符号を示すC1フラグを設定します。
8.3.6.1 Branching on the x87 FPU Condition Codes
プロセッサは、x87 FPUステータスワード内の条件コードフラグ(C0、C2、C3)の設定で分岐する制御フロー命令を提供しません。これらのフラグの状態を分岐するには、まずx87 FPUステータスワードを整数単位のAXレジスタに移動する必要があります。FSTSWAX(ストアステータスワード)命令をこの目的に使用することができます。これらのフラグがAXレジスタにあるとき、TEST命令を使用して以下のように条件分岐を制御することができます。
- 順序付けられていない結果がないか確認。TEST命令を使用して、AXレジスタの内容と定数0400Hを比較します。この操作は、条件コードフラグが順序付けられていない結果を示す場合、EFLAGSレジスタ内のZFフラグをクリアします。それ以外の場合は、ZFフラグが設定されます。その後、JNZ命令を使用して、必要に応じて制御を順序なしオペランドを処理するプロシージャに転送できます。
- 順序付けられた比較結果を確認。TEST命令の表8-8に示す定数を使用して、結果がより小さいか等しいかどうかをテストし、対応する条件分岐命令を使用してプログラム制御を適切なプロシージャまたはコード・セクションに転送します。
(以下省略)
8.3.7 Trigonometric Instructions
(省略)
8.3.8 Approximation of Pi
(省略)
8.3.9 Logarithmic, Exponential, and Scale
(省略)
8.3.10 Transcendental Instruction Accuracy
(省略)
8.3.11 x87 FPU Control Instructions
以下の命令は、x87 FPUの状態および動作モードを制御します。また、x87 FPUのステータスを調べることもできます。
- FINIT/FNINIT x87 FPU初期化
- FLDCW x87 FPU制御ワードの読み込み
- FSTCW/FNSTCW x87 FPU制御ワードの格納
- FSTSW/FNSTSW x87 FPUステータスワードの格納
- FCLEX/FNCLEX x87 FPU例外フラグのクリア
- FLDENV x87 FPU環境の読み込み
- FSTENV/FNSTENV x87 FPU環境の格納
- FRSTOR x87 FPU状態をリストア
- FSAVE/FNSAVE x87 FPU状態を保存
- FINCSTP x87 FPUレジスタスタックポインタインクリメント
- FDECSTP x87 FPUレジスタスタックポインタデクリメント
- FFREE x87 FPUレジスタの開放
- FNOP NOP
- WAIT/FWAIT 保留中のマスクされていないx87 FPU例外をチェックして処理
FINIT/FNINIT命令は、x87 FPUとその内部レジスタをデフォルト値に初期化します。
FLDCW命令はx87 FPU制御ワードレジスタにメモリからの値を読み込みます。
FSTCW/FNSTCWおよびFSTSW/FNSTSW命令は、x87 FPU制御およびステータスワードをそれぞれメモリ(または汎用レジスタのFSTSW/FNSTSW命令)に格納します。
FSTENV/FNSTENVおよびFSAVE/FNSAVE命令は、x87 FPU環境および状態をそれぞれメモリに保存します。x87 FPU環境には、すべてのx87 FPUの制御レジスタとステータスレジスタが含まれています。x87 FPU状態には、x87 FPU環境と、x87 FPUレジスタスタック内のデータレジスタが含まれます。(FSAVE/FNSAVE命令は、x87 FPUの元の状態を保存した後で、FINIT/FNINIT命令のようにx87 FPUをデフォルト値に初期化します)。
FLDENVおよびFRSTOR命令は、メモリからx87 FPUにそれぞれx87 FPU環境および状態を読み込みます。これらの命令は、タスクやコンテキストを切り替えるときによく使用されます。
WAIT/FWAIT命令は同期命令です。(これらは実際には同じオペコードのニーモニックです。)これらの命令は、未処理のマスクされていないx87 FPU例外のx87 FPUステータスワードをチェックします。保留中のマスクされていないx87 FPU例外が見つかった場合、プロセッサは命令ストリーム内の命令(整数、浮動小数点、またはシステム命令)の実行を再開する前に処理されます。 WAIT/FWAIT命令は、x87 FPUとプロセッサの整数ユニット間の命令実行の同期を可能にするために提供されています。
8.3.12 Waiting vs. Non-waiting Instructions
(省略)
8.3.13 Unsupported x87 FPU Instructions
(省略)
8.4 X87 FPU FLOATING-POINT EXCEPTION HANDLING
(省略)
8.4.1 Arithmetic vs. Non-arithmetic Instructions
(省略)
8.5 X87 FPU FLOATING-POINT EXCEPTION CONDITIONS
以下のセクションでは、x87 FPUによって浮動小数点例外が生成され、これらの条件が検出されたときにx87 FPUのマスクされた応答が生成されるさまざまな条件について説明します。
8.5.1 Invalid Operation Exception
浮動小数点無効操作例外は、操作の2つのサブクラスに応答して発生します。
- スタックオーバーフローまたはアンダーフロー(#IS)
- 無効な算術演算子(#IA)
この例外(IE)のフラグはx87 FPUステータスワードのbit 0で、マスクbit(IM)はx87 FPU制御ワードのbit 0です。x87 FPUステータスワードのスタックフォルトフラグ(SF)は、例外の原因となった操作のタイプを示します。SFフラグが1にセットされると、スタック操作がスタックオーバーフローまたはアンダーフローを生じた。フラグが0にクリアされると、算術命令は無効なオペランドを検出しました。x87 FPUは、スタックオーバーフローまたはアンダーフロー状態を検出したときに明示的にSFフラグを設定しますが、無効な算術オペランド条件を検出したときに明示的にフラグをクリアしないことに注意してください。その結果、SFフラグの状態は、スタックオーバーフローまたはアンダーフロー状態が最後に発生したときにクリアされなかった場合、無効な算術演算例外の後に1になる可能性があります。
8.5.1.1 Stack Overflow or Underflow Exception(#IS)
x87 FPUタグワードは、x87 FPUレジスタスタック内のレジスタの内容を追跡します。次に、この情報を使用して、2つの異なるタイプのスタック障害を検出します。
- スタックオーバーフロー
命令がメモリから空でないx87 FPUレジスタを読み込もうとします。空でないレジスタは、ゼロ(タグ値01)、有効値(タグ値00)、または特殊値(タグ値10)を含むレジスタとして定義されます。 - スタックアンダーフロー
空のx87 FPUレジスタをソースオペランドとして参照し、空のレジスタの内容をメモリに書き込むことを含めます。空のレジスタのタグ値は11です。
スタックオーバーフローという用語は、プログラムがメモリからx87 FPUレジスタスタックに8つの値をロード(push)し、スタックにpushされた次の値が既に値を含むレジスタにstack wrap aroundさせる状況から発生します。スタックアンダーフローという用語は、逆の状況に由来します。このとき、プログラムはx87 FPUレジスタスタックからメモリに8つの値を格納(pop)し、スタックからポップされた次の値は空のレジスタにstack wrap aroundを引き起こします。
x87 FPUがスタックオーバーフローまたはアンダーフローを検出すると、x87 FPUステータスワードのIEフラグ(bit 0)およびSFフラグ(bit 6)を1に設定します。次に、条件コードフラグC1(bit 9)をスタックオーバーフローが発生した場合はx87 FPUステータスワードが1になり、スタックアンダーフローが発生した場合は0になります。
無効操作例外がマスクされている場合、x87 FPUは、実行されている命令に応じて、浮動小数点、整数、またはパックされた10進整数の不定値を宛先オペランドに戻します。この値は、命令で指定された宛先レジスタまたはメモリ位置を上書きします。
無効操作例外がマスクされていない場合、ソフトウェア例外ハンドラが呼び出され、スタックトップポインタとソースオペランドは変更されません。
8.5.1.2 Invalid Arithmetic Operand Exception(#IA)
x87 FPUは、プログラムでコーディングできるさまざまな無効な算術演算を検出できます。
x87 FPUが無効な算術演算を検出すると、x87 FPUステータスワードのIEフラグ(bit 0)が1にセットされます。
無効操作例外がマスクされている場合、x87 FPUは不定値またはQNaNを宛先オペランドに返し、浮動小数点条件コードを設定します。無効操作例外がマスクされていない場合、ソフトウェア例外ハンドラが呼び出され、スタックトップポインタ(トップ)およびソースオペランドは変更されません。
通常、ソースオペランドの1つまたは両方がQNaN(いずれもSNaNでもサポートされていない形式でもない)の場合、無効なオペランド例外は生成されません。この規則の例外は、FCOMおよびFCOMI命令などの比較命令と浮動小数点から整数への変換命令(FIST/FISTPおよびFBSTP)のほとんどです。これらの命令では、QNaNソース・オペランドが無効オペランド例外を生成します。
8.5.2 Denormal Operand Exception(#D)
x87 FPUは、以下の条件のもとで、非正規オペランド例外を通知します。
- 算術命令が非正規のオペランドに対して操作しようとした場合
- 非正規な単精度浮動小数点または倍精度浮動小数点値をx87 FPUレジスタに読みこもうとした場合(読み込まれる非正規値が倍精度拡張浮動小数点値の場合、非正規オペランド例外は報告されません)
この例外に対するフラグ(DE)はx87 FPUステータスワードのbit 1で、マスクbit(DM)はx87 FPU制御ワードのbit 1です。
非正規オペランド例外が発生し、例外がマスクされると、x87 FPUはDEフラグをセットし、次に命令を続行します。単精度または倍精度浮動小数点形式の非正規化オペランドは、倍精度拡張浮動小数点形式に変換すると自動的に正規化されます。 後続の演算では、内部倍精度拡張浮動小数点形式の精度を向上させることができます。
非正規オペランド例外が発生し、例外がマスクされていない場合、DEフラグがセットされ、ソフトウェア例外ハンドラが呼び出されます。スタックトップポインタ(トップ)およびソースオペランドは変更されません。
8.5.3 Divide-By-Zero Exception(#Z)
x87 FPUは、命令が有限非ゼロオペランドを0で除算しようとするたびに浮動小数点除算例外を報告します。この例外のフラグ(ZE)は、x87 FPUステータスワードのbit 2であり、マスクbit(ZM)はx87 FPU制御ワードのbit 2です。FDIV、FDIVP、FDIVR、FDIVRP、FIDIV、FIDIVR命令および内部で除算を実行するその他の命令(FYL2XおよびFXTRACT)は、ゼロ除算の例外を報告できます。
ゼロ除算例外がマスクされていない場合、ZEフラグがセットされ、ソフトウェア例外ハンドラが呼び出され、スタックトップポインタ(トップ)およびソースオペランドは変更されません。
8.5.4 Numeric Overflow Exception(#O)
x87 FPUは、算術命令の丸められた結果が宛先オペランドの浮動小数点形式に収まる最大許容有限値を超えるたびに、浮動小数点数値オーバーフロー例外(#O)を報告します。
x87 FPUを使用する場合、結果がx87 FPUデータレジスタに格納される算術演算で数値オーバーフローが発生する可能性があります。また、データレジスタの範囲内の値が単精度または倍精度浮動小数点形式でメモリに格納される浮動小数点演算(FSTおよびFSTP命令を使用)で発生することもあります。数値オーバーフロー例外は、整数またはBCD整数形式で値を格納するときには発生しません。
代わりに、無効な算術演算例外が通知されます。
数値オーバーフロー例外のフラグ(OE)はx87 FPUステータスワードのbit 3で、マスクbit(OM)はx87 FPU制御ワードのbit 3です。
数値オーバーフロー例外が発生し、例外がマスクされると、x87 FPUはOEフラグを設定し、表4-10に示す値の1つを返します。返される値は、x87 FPUの現在の丸めモードによって異なります。
数値オーバーフローが発生し、数値オーバーフロー例外がマスクされていないときにx87 FPUが実行するアクションは、命令が結果をメモリまたはレジスタスタックに格納するかどうかによって異なります。
(以下省略)
8.5.5 Numeric Underflow Exception(#U)
x87 FPUは、算術命令の結果がゼロではなく、小さなものであるときはいつでも、潜在的な浮動小数点数値アンダーフロー条件を検出します。つまり、無限の指数を持つ丸められた結果の大きさは非ゼロであり、宛先オペランドの浮動小数点形式に収まる可能な最小の正規化された有限値よりも小さいです。
(以下省略)
8.5.6 Inexact-Result (Precision) Exception(#P)
操作の結果が宛先フォーマットで正確に表現できない場合、不正確結果例外(精度例外とも呼ばれる)が発生します。高等命令(FSIN、FCOS、FSINCOS、FPTAN、FPATAN、F2XM1、FYL2X 、FYL2XP1)は本質的に不正確な結果を生じます。
不正確結果例外フラグ(PE)はx87 FPUステータスワードのbit 5で、マスクbit(PM)はx87 FPU制御ワードのbit 5です。
不正確結果の条件が発生し、数値オーバーフローまたはアンダーフロー条件が発生していない場合、不正確結果例外がマスクされている場合、追加のアクションを1つ追加します。x87 FPUステータスワードのC1(切り上げ)bitは、不正確な結果が切り上げられた(C1が設定されている)か、切り上げられていない(C1がクリアされている)かを示すように設定されています。 「切り上げられない」ケースでは、不正確な結果の最下位ビットが切り捨てられ、結果が宛先フォーマットに収まるようになります。
不正確な結果が発生し、数値オーバーフローまたはアンダーフローが発生していないときに不正確例外がマスクされない場合、x87 FPUは前の段落で説明した例外を処理し、さらにソフトウェア例外ハンドラを呼び出します。
(以下省略)
8.6 X87 FPU EXCEPTION SYNCHRONIZATION
整数ユニットとx87 FPUは別々の実行ユニットであるため、プロセッサは浮動小数点、整数、およびシステム命令を同時に実行することができます。同時実行の利点を得るために特別なプログラミング技術は必要ありません。 (浮動小数点命令は、整数命令とシステム命令とともに命令ストリームに配置されます)。しかし、並列実行は浮動小数点例外ハンドラに問題を引き起こす可能性があります。
この問題は、x87 FPUがマスキングされていない浮動小数点例外の存在を通知する方法に関連しています。
(マスクされた浮動小数点例外の場合、x87 FPUは常にマスクされた結果を宛先オペランドに返すため、特別な例外同期は不要です)。
浮動小数点例外がマスク解除され、例外条件が発生すると、x87 FPUは浮動小数点命令のさらなる実行を停止し、例外イベントを通知します。命令ストリーム内の浮動小数点命令またはWAIT/FWAIT命令の次の発生時に、プロセッサはx87 FPUステータスワード内のESフラグをチェックして、保留中の浮動小数点例外をチェックします。浮動小数点例外が保留されている場合、x87 FPUは浮動小数点ソフトウェア例外ハンドラに暗黙の呼び出し(トラップ)を行います。例外ハンドラは、選択された浮動小数点例外またはすべての浮動小数点例外のリカバリ手順を実行できます。
同期の問題は、例外が通知されてから実際に処理されるまでの間に発生します。 並行実行のため、この時間中に整数命令またはシステム命令を実行することができます。
したがって、違反した浮動小数点命令のソースオペランドまたは宛先オペランドがメモリに上書きされ、例外ハンドラが例外を解析または回復できなくなる可能性があります。
この問題を解決するために、例外同期命令(浮動小数点命令またはWAIT/FWAIT命令のいずれか)を、浮動小数点例外の状態情報が存在する可能性のある浮動小数点命令の直後に配置することができます。データをメモリに格納する浮動小数点命令は、同期の主要な候補です。
(以下省略)
8.7 HANDLING X87 FPU EXCEPTIONS IN SOFTWARE
Pentium以降のIA-32プロセッサーのx87 FPUは、浮動小数点例外のためのソフトウェア例外ハンドラーを呼び出すために、NativeモードとMS-DOS互換モードの2つの異なる操作モードを提供します。動作モードはCR0.NE [bit5]によって選択されます。
8.7.1 Native Mode
浮動小数点例外を処理するためのネイティブモードは、CR0.NE [bit 5]を1に設定することによって選択されます。このモードでは、x87 FPUが浮動小数点命令の実行中に例外条件を検出し、例外がマスクされていない(マスクbitがクリアされている場合)、x87 FPUはx87 FPUステータスワードに例外およびESフラグのフラグを設定します。その後、プロセッサの命令ストリーム内の次の命令のいずれかを実行する直前に、浮動小数点エラー例外(#MF、例外ベクトル16)を介してソフトウェア例外ハンドラを呼び出します。
- 非待機命令(FNINIT、FNCLEX、FNSTSW、FNSTCW、FNSTENV、およびFNSAVE)のいずれかでない限り、次の浮動小数点命令
- 次のWAIT/FWAIT命令
- 次のMMX命令
命令ストリーム中の次の浮動小数点命令が非待機命令である場合、x87 FPUはソフトウェア例外ハンドラを呼び出さずに命令を実行します。
8.7.2 MS-DOS* Compatibility Sub-mode
CR0.NE [bit 5]が0の場合、浮動小数点例外を処理するためのMS-DOS互換モードが選択されます。このモードでは、浮動小数点例外のソフトウェア例外ハンドラは、プロセッサのFERR#、INTR、IGNNE#ピンを使用して外部から呼び出されます。浮動小数点エラーを報告し、例外ハンドラを呼び出すこの方法は、MS-DOSまたはWindows * 95オペレーティングシステムを実行しているPCシステムで使用される浮動小数点例外処理メカニズムをサポートするために提供されています。
最新のオペレーティングシステムでは、FERR#とIGNNE#を使用して浮動小数点例外を処理することは推奨されていません。
(以下省略)
8.7.3 Handling x87 FPU Exceptions in Software
(省略)
Chapter8まとめ
- x87 FPU概要を説明
- 8つのデータレジスタ(x87 FPUデータレジスタ)と特定用途レジスタがある
- x87 FPUと整数を実行するユニットは別々