|
Tips x87 FPUプログラミング
|
x87 FPU
このサイトではx87 FPUについて説明します。x87 FPUは浮動小数点演算を行うユニットです。
x86で数値演算として整数演算命令しかサポートされていなかったので、浮動小数点演算を行うには
専用のライブラリが必要でした。x87をx86システムに組み込むことで、プログラムからは浮動小数点演算を
1つのCPUで実行しているかのように扱うことができます。x87はx86のバスと接続されていて、実行する命令を
常時監視し、x86の無効命令(ESC)となる浮動小数点演算命令を検出するとx86がアドレスモードを変更することで
x87用のオペコードとオペランドを読み込み処理します。
x87 FPUは16ビットのコプロセッサで、もともとx86とは独立したICでしたが、80486DXおよびPentium以降に
1つのプロセッサとして統合されました。
x87 FPUの構成
x87 FPUはIA-32アーキテクチャ内にある独立したIC(実行環境)です。x87 FPUは次の8個のデータレジスター
(これらをx87 FPUデータレジスターと言います)と汎用レジスターがあります
-
ステータスレジスター
-
制御レジスター
-
タグワードレジスター
-
ラスト命令ポインタレジスター
-
ラストデータポインタレジスター
-
オペコードレジスター
x87 FPUデータレジスター
x87 FPUデータレジスターは独立していて、SSE、SSE2、SSE3の影響は受けません
ただし、MMXレジスターはx87 FPUデータレジスターを別名で定義しているため、共有となります。
このため、MMX命令とx87 FPU命令を実行する場合に注意する必要があります。
x87 FPU命令ではR0からR7の8個の汎用レジスターをレジスタースタックとして扱います
データレジスターのアドレスで指定は、スタックのトップにあるレジスタに対して相対アドレスを指定します
スタックのトップであるレジスターの番号はステータスレジスター内のTOP(スタックのトップ)ビットに
格納されています。ロード操作でTOPが1デクリメントされます。ストア操作でTOPのレジスターから
値がメモリーに読み込まれた後、TOPが1インクリメントされます。(x87 FPUではロード操作=PUSH、
ストア操作=POPとなります)。
TOPが0の場合にロード操作を実行すると、TOPに新しい値として7がセットされます。このとき
保存していない値が上書きされる可能性がある場合は、浮動小数点スタックオーバーフロー例外
より判断します。
浮動小数点命令ではST(0)またはSTという表記が使用されている場合には現在のスタックの
トップを表し、ST(i)という表記ではスタック内のTOPからi番目(0 ≤ i ≤ 7)のレジスタ
を指定します。例えば、図のTOPに011bが格納されている場合(この例ではスタックのトップが
レジスター3)、次の命令は、スタック内の2つのレジスター(レジスター3と5)の内容を加算します
FADD ST, ST(2);
次の図に一連の計算を実行する場合に、x87 FPUレジスターのスタックがどのように使用されるかの
例となります。この例では、2次元のドット積が次のように計算されます
-
最初の命令(FLD value1)がスタックレジスターポインタをデクリメントし、
値5.6をメモリからST(0)にロードします。この操作は図の(a)となります
-
2番目の命令が、ST(0)の値をメモリからロードした値2.4で乗算し、その結果を
ST(0)に格納します。この操作は図の(b)となります
-
3番目の命令がTOPをデクリメントし、値3.8をST(0)にロードします
-
4番目の命令がST(0)の値をメモリからロードした値10.3で乗算し、その結果を
ST(0)に格納します。この操作は図の(c)となります
-
5番目の命令がST(0)の値とST(1)の値を加算し、その結果をST(0)に格納します。
この操作は図の(d)となります
計算
ドット積 = ( 5.6 × 2.4 ) + ( 3.8 × 10.3 )
コード:
FLD value1 ; (a) value1 = 5.6
FMUL value2 ; (b) value2 = 2.4
FLD value3 ; value3 = 3.8
FMUL value4 ; (c) value4 = 10.3
FADD ST(1) ; (d)
x87 FPUのスタックの値を交換するにはFXCH命令を使用します
x87 FPUステータスレジスター
ステータスレジスターにはTOP、条件コードなどがあります。
ステータスレジスターの内容はFSTSW/FNSTSW、FSTENV/FNSTENV、FSAVE/FNSAVE命令で
メモリーに格納することができます。また、FSTSW/FNSTSW命令でAXレジスターに格納
することもできます。
x87 FPUステータスレジスター
TOP
現在のx87 FPUレジスタースタックのトップを指すポインタです。
条件コードフラグ
ステータスレジスターのC0からC3は条件コードが格納されます。
条件コードは条件付き分岐・例外処理で使用される情報が格納されます
C1条件コード
C1はステータスレジスターのIEフラグとSFフラグがともにセットされている
(スタックのオーバーフロー例外またはアンダーフロー例外(#IS)を示す)場合は、
C1=1の時はオーバーフロー例外、C1=0のときはアンダーフローと判断することができます
また、PEフラグがセットされている(計算結果が丸められて不正確であることを示す)
場合は、命令による最後の丸めが切り上げであった場合にC1=1となります。また、FXAM命令
を使用した場合には、現在チェックしている値の符号が設定されます
C2条件コード
C2はFPREM命令とFPREM1命令が剰余計算の未完了(部分剰余)かどうかを示します
剰余計算が正常に完了している場合は、C0、C3、C1の各条件コードにそれぞれ
商の最下位ビット(Q2、Q1、Q0)がセットされます。
FPTAN、FSIN、FCOS、FSINCOS命令は、読み込み元オペランドが許容範囲である
± 2
63を超えたことを示す場合にC2を1にセットし、読み込み元
オペランドが許容範囲の場合は、C2をクリアします。
条件コードのセットと解釈
|
命令
|
C0
|
C3
|
C2
|
C1
|
FCOM, FCOMP, FCOMPP, FICOM, FICOMP, FTST,
FUCOM, FUCOMP, FUCOMPP
|
比較の結果
|
オペランドが比較できない
|
0または#IS
|
FCOMI, FCOMIP, FUCOMI, FUCOMIP
|
未定義(これらの命令はEFLAGSのフラグをセットします)
|
#IS
|
FXAM
|
オペランドクラス
|
符号
|
FPREM, FPREM1
|
Q2
|
Q1
|
0:余剰計算完了
1:余剰計算未完了
|
F2XM1, FADD, FADDP, FBSTP, FCMOVcc, FIADD,
FDIV, FDIVP, FDIVR, FDIVRP, FIDIV, FIDIVR,
FIMUL, FIST, FISTP, FISUB, FISUBR,FMUL,
FMULP, FPATAN, FRNDINT, FSCALE, FST, FSTP,
FSUB, FSUBP, FSUBR, FSUBRP, FSQRT, FYL2X, FYL2XP1
|
未定義
|
切り上げまたは#IS
|
FCOS, FSIN, FSINCOS, FPTAN
|
未定義
|
0:読み込み元オペランドが範囲内
1:読み込み元オペランドが範囲外
|
切り上げまたは#IS(C2=1の場合は未定義)
|
FABS, FBLD, FCHS, FDECSTP, FILD, FINCSTP, FLD, Load Constants, FSTP (ext. real), FXCH, FXTRACT
|
未定義
|
0または#IS
|
FLDENV, FRSTOR
|
メモリからロードされた各ビット
|
FFREE, FLDCW, FCLEX/FNCLEX, FNOP, FSTCW/FNSTCW, FSTENV/FNSTENV, FSTSW/FNSTSW
|
未定義
|
FINIT/FNINIT, FSAVE/FNSAVE
|
0
|
0
|
0
|
0
|
スタックフォルト
スタックフォルトビット(SFフラグ)は、x87 FPUデータレジスタースタック内のデータに、
スタックオーバーフロー、スタックアンダーフローが発生したことを示します。
SFフラグは無効演算オペランド条件を検出した場合には、明示的にクリアされません
SF=1の場合は、条件コードC1からフォルトを判断します。(C1=1でオーバーフロー、C1=0で
アンダーフロー)。SFフラグは1回セットされると、FINIT/FNINIT、FCLEX/FNCLEX、FSAVE/FNSAVE命令
で明示的にクリアしない限り、プロセッサが勝手にフラグをクリアすることはありません。
条件コードに基づく分岐と条件付きMOVE
x87 FPUでは浮動小数点の比較結果に基づいて、分岐や条件付きMOVEができます。
特に、P6ファミリプロセッサ以降のx87 FPUでは、新旧2つのメカニズムがあります。
このメカニズムを「旧メカニズム」「新メカニズム」と呼びます
「旧メカニズム」
「旧メカニズム」は、インテルPentium Proプロセッサより前のx87 FPUとP6ファミリプロセッサで
利用することができます。このメカニズムは、浮動小数点比較命令(FCOM、FCOMP、FCOMPP、FTST、
FUCOMPP、FICOM、FICOMP)を実行し、その比較結果に従って条件コードC0-C3がセットされます
次に条件コードC0-C3の内容を、次の2ステップで処理し、EFLAGSレジスターのステータスフラグに
コピーします
-
FSTSW AX命令で、x87 FPUステータスレジスターの内容をAXレジスターに移動します
-
SAHF命令で、AXレジスターの上位8ビット(条件コードが含まれている)をEFLAGSレジスタ—の
下位8ビットにコピーします
EFLAGSレジスターに値をロードした後は、ロードされたEFLAGSレジスターのフラグ条件に基づいて
条件付きジャンプ、条件付きMOVEが実行できます
条件コードのEFLAGSレジスターへの移動
「新メカニズム」
「新メカニズム」は、P6ファミリプロセッサでしか使用できません。新しい浮動小数点比較命令と
EFLAGS設定命令(FCOMI、FCOMIP、FUCOMI、FUCOMIP)を使用して浮動小数点値比較後にEFLAGSレジスター
のZF、PF、CFフラグを設定します。これにより、「旧メカニズム」で必要だった3つの命令を1つの命令に
置き換えることができるようになりました。
また、P6ファミリプロセッサで導入されたFCMOVcc命令を使用しても、EFLAGSレジスターのZF、PF、CFの
値に基づいて浮動小数点値(x87 FPUデータレジスターの値)を条件付きMOVEができます。
x87 FPU制御レジスター
x87 FPU制御レジスターは16ビットのレジスターです。このレジスターを制御することで、x87 FPUの
精度と丸め方法を制御することができます。このレジスターはx87 FPU浮動小数点例外マスクビットも
格納します。x87 FPU制御レジスタの内容は、FLDCW命令を使用してロードし、FSTCW/FNSTCW命令を
使用してメモリに格納することができます。
FINIT/FNINITかFSAVE/FNSAVEFPUのの命令でx87 FPUを初期化すると、x87 FPU制御レジスターは0x037Fに
設定されます。この場合、全ての浮動小数点例外がマスクされ、丸めモードは直近値への丸めに設定され
x87 FPUの精度は64ビットに設定されます
x87 FPU浮動小数点例外マスク
例外マスクビット(x87 FPU制御レジスターのビット0-5)は、x87 FPUステータスレジスターの浮動小数点
例外フラグをマスクします。これらのマスクビットがセットされると、対応するx87 FPU浮動小数点例外は
発生しません
精度制御フィールド
精度制御(PC)フィールド(x87 FPU制御レジスターのビット8-9)は、x87 FPUが行う浮動小数点計算の精度
(64ビット、53ビット、24ビット)を決定します。デフォルトの精度は拡張倍精度で、x87 FPUデータレジスター
の拡張倍精度浮動小数点フォーマットで使用可能な64ビットの仮数部が使用されます。この設定にすれば
アプリケーションはx87 FPUデータレジスターで使用可能な最大精度をフル活用できます。
精度制御フィールド(PC)
|
精度
|
PCフィールド
|
単精度(24ビット)
|
00b
|
予約
|
01b
|
倍精度(53ビット)
|
10b
|
拡張倍精度(64ビット)
|
11b
|
倍精度、単精度では仮数部のサイズはそれぞれ53ビット、24ビットとなります。これらの設定は
IEEE規格をサポートするとともに既存のプログラム言語の仕様と互換性を保つためにあります。
精度制御ビットは、、FADD、FADDP、FIADD、FSUB、FSUBP、FISUB、FSUBR、FSUBRP、FISUBR、FMUL、
FMULP、FIMUL、FDIV、FDIVP、FIDIV、FDIVR、FDIVRP、FIDIVR、FSQRT命令の結果に対してのみ有効です
丸め制御フィールド
x87 FPU制御レジスタの丸め制御(RC)フィールド(ビット10-11)は、x87 FPU浮動小数点命令の
結果を丸める方法を制御します。
丸めモードと丸め制御(RC)フィールドのエンコーディング
|
丸めモード
|
RCフィールドの設定
|
説明
|
直近値への丸め(偶数)
|
00b
|
丸められた結果は、無限精度の結果に最も近い値となります。2つの値が同じ近さの場合は、
結果は偶数値(最下位のビットが0)になります。この設定値がデフォルト値となります。
|
切捨て(-∞の方向)
|
01b
|
丸められた結果は、無限精度の結果に最も近い値となります。(但し、無限精度の結果より
大きくない値)
|
切り上げ(+∞の方向)
|
10b
|
丸められた結果は、無限精度の結果に最も近い値となります。(但し、無限精度の結果より
小さくない値)
|
ゼロ方向への丸め(真の切捨て)
|
10b
|
丸められた結果は、無限精度の結果に最も近い値となります。(但し、無限精度の結果より
絶対値が大きくない値)
|
無限大制御フラグ
無限大制御フラグ(x87 FPU制御レジスターのビット12)は、インテル287数値演算個プロセッサとの
互換性を維持するために用意されたフラグです。従って、新しいx87 FPUプロセッサ、IA-32プロセッサ
にとっては意味の無いビットとなります。
x87 FPUタグレジスター
x87 FPUタグレジスターはx87 FPUデータレジスタースタックの8つのレジスタそれぞれの内容を
示します。(2ビットのタグが各レジスタに対応しています)。タグは、対応するレジスターに
有効な数値、ゼロ、特殊な浮動小数点値(NaN、無限大、異常値、未サポートフォーマット)
のどれが格納されているか、または空であるかどうかを示します。FINIT/FNINIT、FSAVE/FNSAVE命令で
x87 FPUを初期化するとタグワードは0xFFFFに初期化されます。これはx87 FPUデータレジスタが
空としてタグ付けされていることを示しています
各タグは汎用レジスターの番号0-7に対応しています。タグは、x87 FPUステータスレジスターのに
格納されているTOPを使用してST(0)に相対的なアドレスでレジスタに関連づけることができます。
x87 FPUは、タグの値を使用してスタックのオーバーフロー条件とアンダーフロー条件を検出します
アプリケーション、例外ハンドラはタグ情報を使用することで、レジスターのデータをデコード
しなくてもレジスターに格納されている内容をチェックすることができます。タグレジスターの
内容を読み取るには、FSTENV/FNSTENV、FSAVE/FNSAVE命令を使用して、メモリーに格納することが
できます。タグレジスターのタグは直接ロードしたり、変更したりすることはできません。
FLDENV命令とFRSTOR命令では、タグレジスターのイメージをx87 FPUにロードしますが、x87 FPU
がそれらのタグの値を使用するのは、データレジスタが空(11b)か、空でない(00b、01b、10b)かを
確認するためだけとなります。タグレはジスターのイメージがデータレジスターが空であることを
示している場合は、そのデータレジスターに対するタグレジスターのタグは空(11b)とタグ付けされて
います。タグレジスターのイメージがデータレジスターが空でないことを示している場合は、
x87 FPUはデータレジスターの値を読み取り、その値に従ってレジスターのタグを設定します
x87 FPU命令とデータ(オペランド)ポインタ
x87 FPUは、最後に実行された非制御型命令に対し、命令とデータ(オペランド)に対するポインタ
を2つの48ビットレジスター(x87 FPU命令ポインタレジスターとx87 FPUオペランド(データ)
ポインタレジスター)に格納します。こうするのは、例外ハンドラに状態を知らせるためです。
但し、x87 FPU命令ポインタレジスターとデータポインタレジスターの内容は制御命令
(FINIT/FNINIT、FCLEX/FNCLEX、FLDCW、FSTCW/FNSTCW、FSTSW/FNSTSW、FSTENV/FNSTENV、FLDENV、
FSAVE/FNSAVE、FRSTOR、WAIT/FWAIT)を実行しても変更されません。
x87 FPU命令ポインタレジスターとデータポインタレジスターに格納されるポインターは
オフセット(ビット0-31に格納されます)とセグメントセレクタ(ビット32-47に格納されます)
で構成されます
x87 FPU命令ポインタレジスターとデータポインタレジスターはFSTENV/FNSTENV、FLDENV、
FINIT/FNINIT、FSAVE/FNSAVE、FRSTOR、FXSAVE、FXRSTOR命令を使ってアクセスします。
レジスターをクリアーするにはFINIT/FNINIT命令、FSAVE/FNSAVE命令を使用します
最後の命令オペコード
x87 FPUは最後に実行された非制御命令のオペコードを11ビットのx87 FPUオペコードレジスターに
に格納します。x87 FPUオペコードレジスターにはオペコードの1番目と2番目のバイトだけが格納
されます。オペコードの1バイト目の上位5ビットはすべての浮動小数点オペコード(11011b)と
同じですので、オペコードレジスターには下位3ビットだけが格納されます
fopcode互換モード
インテルPentium4とXenonプロセッサから最後の命令オペコード(fopcodeと表記されます)の
格納方法をプログラムによって制御できるようになりました。IA32_MISC_ENABLE MSRのビット2は
fopcode互換モードを有効(セット)または無効(クリア)します。
fopcode互換モードが有効になっている場合、FOPは以前のIA-32アーキテクチャと同じ定義となります
(常にFSAVE/FSTENV/FXSAVEの前に実行された最後の非透過的なFP命令のFOPとして定義されます)
fopcoce互換モードが無効になっている場合(デフォルトです)、FOPはFSAVE/FSTENV/FXSAVEの前に
実行された最後の非透過的なFP命令時にマスクされていない例外が有ったときのみ有効となります
FSTENV/FNSTENV命令、FSAVE/FNSAVE命令によるx87 FPUの状態保存
FSTENV/FNSTENV命令、FSAVE/FNSAVE命令は、例外ハンドラやシステムソフトウェア、アプリケーション
上で使用できるように、x87 FPUの状態をメモリーに格納します。FSTENV/FNSTENV命令は、ステータス、
制御レジスター、タグレジスター、x87 FPU命令ポインタレジスター、オペランドポインタレジスター
オペコードレジスターの各レジスター内容を保存します。
FSAVE/FNSAVE命令は、上記状態保存に加え、x87 FPUデータレジスターの内容も格納します。
FSAVE/FNSAVE命令は、(FINIT/FNINIT命令と同じように)x87 FPUの元の状態を保存してから、
x87 FPUをデフォルト値に初期化する点に注意します。
これらの情報がどのよにメモリに格納されるかは、リアルモード/プロテクティッドモード、
有効なオペランドサイズ属性(32ビット/16ビット)によって決まります。
x87 FPUステート情報は、FLDENV命令、FRSTOR命令を使ってメモリーからx87 FPUにロードする
ことができます。FLDENV命令は、ステータス、制御レジスター、タグレジスター、x87 FPU命令ポインタ
レジスター、x87 FPUオペランドポインタレジスター、オペコードレジスターの各レジスターだけが
ロードされます。FRSTOR命令は、x87 FPUスタックレジスターを含む全てのx87 FPUレジスターが
ロードされます。
FXSAVE命令によるx87 FPU状態の保存
FXSAVE命令は、x87 FPU状態とXMMレジスタ、MXCSRレジスターの状態を保存します
FXRSTOR命令は、この状態を元に戻します。FXSAVE命令を使用してx87 FPU状態を保存すると
(1)FXSAVEはFSAVEより高速に実行できる(2)FXSAVEは1回の操作でx87 FPU、MMX、XMMの
全体を保存することができるというメリットがあります。
x87 FPUデータ型
x87 FPUは次のデータ型を操作することができます。
-
単精度浮動小数点
-
倍精度浮動小数点
-
拡張倍精度浮動小数点
-
符号付きワード整数
-
符号付きダブルワード整数
-
符号付きクワッドワード整数
-
パックドBCD10進整数
x87 FPU浮動小数点例外条件
無効操作例外
無効操作例外は次の操作が原因で発生します
-
スタックオーバーフローまたはスタックアンダーフロー(#IS)
-
無効算術オペランド(#IA)
x87 FPUステータスレジスターのSF(スタックフォルト)フラグにより、この例外の原因を判断します
SF=1の場合:スタックオーバーフロー、アンダーフローが生じました。
SF=0の場合:算術命令に無効なオペランドがありました。
スタックオーバーフロー例外、スタックアンダーフロー例外(#IS)
x87 FPUタグレジスターはレジスタースタックのレジスター内容を記録し続けます。
この情報から2つのスタックフォルトを検出します
-
スタックオーバーフロー
命令が空のx87 FPUレジスターにメモリーの値をロードしようとしました。空でない
レジスターは0(タグの値は01b)、有効(タグの値は00b)、特殊な値(10b)の
レジスターとなります
-
スタックアンダーフロー
命令が空のx87 FPUレジスターを読み込み元オペランドとして参照しました。
(空のレジスターの内容をメモリーに書き込もうとする操作など)。
空のレジスターのタグの値は11bです
スタックのオーバーフロー、アンダーフローが発生するとステータスレジスターのIEフラグと
SFフラグを1にセットします。次にオーバーフローが発生した場合はステータスレジスターの
C1を1にセットし、アンダーフローが発生した場合にはC1を0にクリアします。
無効操作例外がマスクされている場合は、次に、実行中の命令によって、倍精度浮動小数点値、
整数値、パック形式10進整数の不定値を格納先オペランドに格納します。
無効操作例外がマスクされていない場合は、例外ハンドラが呼び出されますが、TOPと読み込み元
オペランドはそのままで変わることはありません。
無効算術オペランド例外(#IA)
x87 FPUは各種の無効算術演算を検出することができます。無効算術オペランドを検出すると
ステータスレジスターのIEフラグを1にセットします。無効操作例外がマスクされている場合は、
下記表に従って、不定値またはQNaNを格納先オペランドに格納するか、浮動小数点条件コードを
設定します。マスクされていない場合は例外ハンドラが呼び出されます。TOPと読み込み元
オペランドはそのままで変わることはありません
無効算術演算とマスク応答
|
条件
|
マスク応答
|
サポートされていない形式のオペランドに対する算術演算
|
QNaN浮動小数点値格納先オペランドに格納します
|
SNaNに対する算術演算
|
QNaNを格納先オペランドに格納します
|
順序比較、テスト操作:一方または両方のオペランドがNaN
|
ステータスレジスターの条件コード(C0、C2、C3)またはEFLAGSのCF、PF、ZFフラグを
111b(比較不可能)にセットします
|
加算:両オペランドが反対符号の無限大
減算:両オペランドが同じ符号の無限大
|
QNaN浮動小数点不定値を格納先オペランドに格納します
|
乗算:∞×0、0×∞
|
QNaN浮動小数点不定値を格納先オペランドに格納します
|
除算:∞×∞、0×0
|
QNaN浮動小数点不定値を格納先オペランドに格納します
|
剰余命令FPREM、FPREM1:除数が0または被除数が∞
|
QNaN浮動小数点不定値を格納先オペランドに格納し、条件コードC2を0にクリアします
|
三角関数FCOS、FPTAN、FSIN,FSINCOS:読み込み元オペランドが∞
|
QNaN浮動小数点不定値を格納先オペランドに格納し、条件コードC2を0にクリアします
|
FSQRT:オペランドが負(FSQRT(-0)=-0を除く)
FYL2X:オペランドが負(FYL2X(-0)=-∞を除く)
FYL2XP1:オペランドが-1より小さい
|
QNaN浮動小数点不定値を格納先オペランドに格納します
|
FBSTP:変換された値が18桁の10進数で表現できない。または読み込み値がSNaN、QNaN、
サポートされていないフォーマット。
|
パックドBCD整数不定値を格納先オペランドに格納します
|
FIST/FISTP:変換された値が格納先オペランドの表現可能な整数範囲を超えている。
または読み込み値がSNaN、QNaN、±∞、サポートされていないフォーマット。
|
パックドBCD整数不定値を格納先オペランドに格納します
|
FXCH:一方または両方のレジスターが空としてタグ付けされています。
|
空のレジスターにQNaN浮動小数点値をロードし、交換を実行します。
|
通常は、読み込み元オペランドのいずれかまたは両方がQNaNである(いずれもSNaN、サポートされていない
フォーマットではない)場合は、無効オペランド例外は発生しません。但し、これは比較命令や浮動小数点から
整数への変換命令には適用されません。これら命令では、読み込み値としてQNaNがあると、無効オペランド例外が
発生します。
デノーマルオペランド例外(#D)
デノーマルオペランドは次の条件で発生します
-
算術命令がデノーマルオペランドに対して演算を行おうとした場合
-
デノーマルの単精度か倍精度浮動小数点値をx87 FPUレジスターにロードしようとした場合
(デノーマル値が拡張倍精度浮動小数点値である場合は発生しません)
この例外のフラグはステータスレジスターのDEフラグとなります。
デノーマルオペランド例外がマスクされている場合、x87 FPUはDEフラグをセットしてから命令の実行を
再開します。単精度/倍精度浮動小数点フォーマットのデノーマルオペランドは、拡張倍精度浮動小数点
フォーマットに変換されるときに自動的に標準化されます。
デノーマルオペランド例外がマスクされていない場合、DEフラグがセットされ、例外ハンドラが呼び出されます
TOPと読み込みオペランドはそのまま変わりません
ゼロ除算例外(#Z)
命令が非ゼロの有限値オペランドを0で割ろうとすると、ゼロ除算例外が発生します
マスクされていない場合、ZEフラグがセットされ例外ハンドラが呼び出されます
TOPと読み込みオペランドはそのまま変わりません
ゼロ除算条件とマスク応答
|
条件
|
マスク応答
|
0の除数による除算または逆除算
|
2つのオペランドの符号のXORを符号とする∞を格納先オペランドに格納します
|
FYL2X命令
|
非ゼロオペランドの反対の符号を持つ∞を格納先オペランドに格納します
|
FXTRACT命令
|
ST(1)が-∞にセットされ、ST(0)が読み込み元オペランドと同じ符号を持つ0がセットされます
|
数値オーバーフロー例外(#O)
算術命令で丸められた結果が格納先オペランドの浮動小数点フォーマットの範囲内に収まらず、
許容可能な最大有限値を超えた場合は、常に浮動小数点値オーバーフロー例外(#0)が発生します
FPUデータレジスターに結果が格納される算術命令で発生する場合があります。また、データレジスター
に格納された範囲内の値が単精度/倍精度浮動小数点フォーマットでメモリに格納される浮動小数点
ストア命令(FST/FSTP命令)でも発生する可能性があります。値を整数フォーマット/BCD整数フォーマットで
格納する祭にオーバーフローが発生するときは数値オーバーフロー例外とはなりません。この場合は
無効算術オペランド例外が発生します
数値オーバーフロー例外が発生し、この例外がマスクされている場合は、OEフラグがセットされます
数値オーバーフロー例外がマスクされていない場合は、命令のけかkがメモリーとレジスタースタック
のいずれかに格納されるかにうよってx87 FPUの処理が異なります
-
格納先がメモリーアドレスの場合
OEフラグがセットされ、例外ハンドラが呼び出されます。TOPと読み込み元オペランドはそのまま
変わりません。スタック内のデータは拡張倍精度フォーマットであるため、例外ハンドラは、
オペランドを調整した後、ストア命令を実行するか、IEEE規格に従ってスタック上の仮数部を
格納先の精度に合わせて丸めるかを選択できます。プログラムを続行する場合、例外ハンドラは
メモリー内の格納先位置に値を格納する必要があります
-
格納先がレジスタースタックの場合
結果の仮数部はx87 FPU制御レジスターの精度ビットと丸め制御ビットの設定に従って丸められ、
結果の指数部は224567で割る事によって調整されます。(精度フィールドの影響を受け
ない命令の場合は、仮数部は拡張倍精度に丸められます)。結果は格納先オペランドに格納されます
仮数が切り上げ方向に丸められる場合は、条件コードC1がセットされ、結果が0方向に丸められる
場合はクリアされます。結果が格納された後、OEフラグがセットされ、例外ハンドラが呼び出されます
スケーリングバイアス値の24,576は3×213に等しくなります。指数が24,576でバイアス
されると通常、数値は可能な限り拡張倍精度浮動小数点の指数範囲の中央に近い値に変換されます
従って、必要に応じてスケーリング操作に使用すれば、例外を発生させる危険性を減らすことが
できます。FSCALE命令を使用しているときに、結果が大き過ぎてバイアス調整型の指数を使用しても
表現できない場合にあh、大きなオーバーフローが発生することがあります。結果をバイアスした後
に再びオーバーフローが発生した場合は、適切な符号を持つ∞が格納先オペランドに格納されます
数値アンダーフロー例外(#U)
算術命令の丸められた結果が極小(格納先オペランドの浮動小数点フォーマットに収まる最小の
フォーマット型より小さい)場合、浮動小数点値アンダーフロー例外が発生する
数値オーバーフローの場合と同様に、データレジスターに格納される算術演算で発生する可能性が
あります。また、データレジスターの範囲内の値がより小さな単精度または倍精度の浮動小数点
フォーマットでメモリーに格納されるような(FST命令とFSTP命令)浮動小数点ストア操作でも
発生することがありますが、数値を整数フォーマットやBCD整数フォーマットで格納する際は
発生することはありません。極小値は、常に有効な丸めモードに従って0または1の整数値に
丸められます。数値アンダーフロー例外のフラグはステータスレジスターのUEです。
例外が発生したとき、マスクされている場合は
数値オーバーフロー例外
の操作と同じとなります
例外がマスクされていない場合は次のような場合があります
-
格納先がメモリーアドレスの場合(ストア命令のみ)
UEフラグがセットされ、例外ハンドラが呼び出されます。TOPと読み込み元オペランド、格納先
オペランドは変わりません。スタック内のデータは拡張倍精度フォーマットであるため、
例外ハンドラではデータを適切なフォーマットに調整してストア命令を行うか、IEEE規格の
要件に従ってスタック上の仮数部を格納先の精度に合わせて丸めるかを選びます。
プログラムを続行する場合は、例外ハンドラは、値をメモリー内に格納しておく必要があります
-
格納先がレジスタースタックの場合
結果の仮数部は制御レジスターの精度・丸め制御ビットに従って丸められ、結果の指数部は
224576を掛けることによって調整され(精度フィールドの影響を受けない
命令の場合は、仮数部は拡張倍精度に丸められます)、格納先オペランドに格納されます
仮数が切り上げ方向に丸められた場合は、条件コードC1がセットされ、けかkが0方向に
丸められた場合はクリアされます。結果が格納された後、UEフラグがセットされ、例外ハンドラが
呼び出されます。スケーリングバイアス値24576はオーバーフロー例外に使用される値と
同じであり、働きも同じです。これにより、結果を可能な限り拡張倍精度浮動小数点の
指数範囲の中央に近い値に変換します。FSCALE命令を使用しているときに、結果が小さ
過ぎてバイアス調整型の指数を使用しても表現できない場合には、大きなアンダーフローが
発生することがある。結果をバイアスした後に再びアンダーフローが発生した場合は、
適切な符号を持つ0が格納先オペランドに格納されます
不正確結果(精度)例外(#P)
不正確結果例外(精度例外とも呼ばれます)は、演算の結果が格納先フォーマットで正確に表現
できない場合に発生します。但し、FSIN、FCOS、FSINCOS、FPTAN、FPATAN、F2XM1、FYL2X、FYL2XP1命令
などの超越関数命令は、その性質上、不正確な結果を生じますので注意します。
不正確結果例外のフラグはステータスレジスターのPEです。不正確結果例外が発生したときに
不正確結果例外がマスクされており、数値オーバーフロー/アンダーフローの条件がいずれも発生していない
場合は、x87 FPUはこの例外を処理を行った後に、条件コードC1はC1=1の場合は不正確結果が切り上げられた、
C1=0の場合は切り上げられなかったことを示します。切り上げられなかった場合(C1=0)では、丸められた
結果が格納先に収まるように不正確結果の最下位ビットが切り捨てられます
不正確結果が発生したときに不正確結果例外がマスクされておらず、数値オーバーフロー/アンダーフローの
いずれの条件も発生していない場合は、x87 FPUは、前述の処理を行い、例外ハンドラが呼び出されます
数値オーバーフロー/アンダーフローと同時に不正確例外が発生した場合は次のようになります
-
不正確結果が、マスクされたオーバーフロー/アンダーフローと一緒に発生した場合は、
OE、UEとPEがセットされ、オーバーフロー/アンダーフロー例外の場合と同じ方法で結果が
格納されます。
-
不正確けかkが、マスクされていないバーフロー/アンダーフローと一緒に発生し、格納先
オペランドがレジスターである場合は、OE、UE、PEフラグがセットされ、オーバーフロー/
アンダーフロー例外と同じ方法で結果が格納され、例外ハンドラが呼び出されます
マスクされていない数値オーバーフロー/アンダーフロー例外が発生し、格納先オペランドがメモリーアドレスで
ある場合は(浮動小数点のストアの場合に限られます)、不正結果条件は発生せず、C1フラグがクリアされます
x87 FPU例外の同期
整数ユニットとx87 FPUは独立したユニットであるため、プロセッサは浮動小数点命令、整数命令、システム命令を
同時に並列して実行ができます。並列実行のために特に特殊なプログラミングをする必要はありません。ただし、
並列実行では浮動小数点例外ハンドラ処理が必要となる問題はあります。浮動小数点例外がマスクされていない
状態で例外が発生した場合に、x87 FPUはそれ以降の浮動小数点命令を停止し、例外を通知します。
プロセッサは、次の命令に浮動小数点命令またはWAIT/FWAIT命令があるとx87 FPUのステータスレジスターの
ESフラグを調べ、保留されている浮動小数点例外を確認します。浮動小数点例外が保留されている場合は、
x87 FPUは浮動小数点例外をコール(トラップ)します。例外が通知されてから、ハンドラで処理されるまでの間に
同期問題が発生します。並列実行されているため、ハンドラが呼び出される間に意図しないタイミングで
システム命令が実行される可能性があります。従って、フォルトを生じた浮動小数点命令の読込元オペランドまたは
格納先オペランドがメモリーを上書きされ、例外ハンドラで例外を解析したり、復帰できなくなる可能性があります
この問題を解決するために、情報が破壊されてしまう可能性がある浮動小数点命令の直後に、例外と同期するための
命令(浮動小数点命令、またはWAIT/FWAIT命令)を置きます。データをメモリーに格納するような浮動小数点命令
では、まず同期する必要があります。例えば次の3行のコードには上記問題が発生する可能性があります。
FILD COUNT ; FPU命令
INC COUNT ; 整数命令
FSQRT ; 次のFPU命令
この命令では、INC命令は、浮動小数点命令FILDの読込元オペランドを変更します。FILD命令の実行中に
例外が発生した場合は、浮動小数点例外ハンドラが呼び出される前にINC命令がCOUNT(メモリー上にあります)
に格納された値を上書きする可能性があります。COUNT変数が変更されてしまうと、浮動小数点例外ハンドラは
エラーから復帰できなくなります。
命令の順序を次のように変更し、FSQRT命令をFILD命令の後に置けば、浮動小数点例外処理と同期でき、
浮動小数点例外ハンドラが呼び出される前にCOUNT変数が上書きされることは無くなります
FILD COUNT ; FPU命令
FSQRT ; 次のFPU命令のFILD命令で発生する例外と同期します
INC COUNT ; 整数命令
FSQRT命令の結果はx87 FPUデータレジスターに格納され、次の浮動小数点命令またはWAIT/FWAIT命令が
実行されるまでは上書きされずに保持されるため、FSQRT命令で同期する必要はありません。FSQRT命令に
よって発生した例外を、関数呼び出しの前に確実に実行するにはFSQRT命令の直ぐ後にWAIT命令を置きます
一部の浮動小数点命令(非同期型命令)は、保留中のマスクされていない例外の有無を確認しないので
注意する必要があります。これらの命令には、FNINIT、FNSTENV、FNSAVE、FNSTSW、FNSTCW、FNCLEX
などの命令が含まれます。FNINIT、FNSTENV、FNSAVE、FNCLEXが実行されると、保留中の例外全てが
無くなります(x87 FPUステータスレジスターがクリアされるか、全ての例外がマスクされます)。これに対し
FNSTSW命令とFNSTCW命令では、保留中の例外の有無は確認しないが、x87 FPUステータスレジスターや
制御レジスターも変更されません。従って、その後に「同期型」浮動小数点命令を置けば、すべての
保留中の例外を処理することができます。
ソフトウェアによるx87 FPU例外処理
インテルPentiumプロセッサおよびIA-32以降のプロセッサのx87 FPUには、浮動小数点例外ハンドラを
呼出すために、ネイティブモードとMS-DOS互換モードがあります。これらのモードはCR0制御レジスターの
NEフラグで選択します。
ネイティブモード
CR0制御レジスターのNEフラグを1にセットするとネイティブモードとなります。このモードでは、浮動小数点命令
実行中で、その例外に対するマスクビットがクリアされている場合にx87 FPUが例外を検出すると、x87 FPU
はまずその例外に対するフラグとx87 FPUステータスレジスターのESフラグをセットします。次に、浮動小数点
エラー例外(#MS、ベクタ番号16)で例外ハンドラを呼び出します。その直後にプロセッサは現在実行中の
プログラムにある次の命令のいずれかを実行します
-
プログラム上の次の浮動小数点命令。但し、それが非同期型命令(FNINIT、FNCLEX、FNSTSW、
FNSTCW、FNSTENV、FNSAVE)のいずれかである場合を除きます。
-
プログラム上の次のWAIT/FWAIT命令
-
プログラム上の次のMMX命令
プログラム上の次の浮動小数点命令が非同期型命令である場合は、x87 FPUは例外ハンドラを
呼び出さずに次の命令を実行します
MS-DOS互換モード
制御レジスターCR0のNEフラグが0に設定されている場合は、MS-DOS互換モードとなります
このモードでは、浮動小数点例外が、プロセッサのFERR#、INTR、IGNNE#の各ピンを使用して
外部から呼び出されます。浮動小数点エラーとハンドラの呼び出しにこの方法が用いられるのは
MS-DOSやWindows95が動作しているPCの浮動小数点処理をサポートするためとなります。
MS-DOS互換モードでは、一般的に次のように浮動小数点例外ハンドラが呼び出されます。
-
マスクされていない浮動小数点例外を検出すると、x87 FPUは例外に対するフラグをセットし
ステータスレジスターのESフラグをセットします
-
IGNNE#ピンの入力が無い状態では、x87 FPUはFERR#ピンをすぐにアクティブにするか、
次に待機している浮動小数点命令またはMMX命令の実行直前までアクティブになるのを
待ちます。FERR#ピンをすぐにアクティブにするか遅らせるかは、プロセッサ、命令、例外の
タイプによって異なります
-
直前の浮動小数点命令が、マスクされていないx87 FPU例外のフラグを既にセットしている
状態では、プロセッサは次のWAIT命令の実行直前にフリーズし、浮動小数点命令または
MMX命令を待ちます。FERR#ピンが直前の浮動小数点命令の時点でアクティブになっていたか
現時点でアクティブになっているにかかわらず、プロセッサがフリーズすることによって次の
浮動小数点命令(またはMMX命令)が実行される前にx87 FPU例外ハンドラを確実に
呼出すことができます。
-
FERR#ピンがカスケード接続されたPICのIRQ13に外部ハードウェアが接続されています
PICはFERR#ピンがアクティブになると割り込み0x75を発生させます
-
PICがプロセッサ上のINTRピンをアクティブにし、割り込み0x75を通知します
-
BIOSが割り込み2(NMI)ハンドラを呼出すことで割り込み0x75を処理します
-
割り込み2ハンドラが、割り込みがNMI割り込みの結果または浮動小数点例外の結果の
いずれかであるかを判断します
-
浮動小数点例外が検出された場合は、割り込み2ハンドラが浮動小数点例外を呼び出します
IGNNE#ピンがアクティブになっている場合、プロセッサは浮動小数点エラー条件を無視します
このピンは浮動小数点例外ハンドラが以前に通知された浮動小数点例外を処理している間に
別の浮動小数点例外が発生するのを防ぐためにあります
プログラム中のx87 FPU例外処理
フォルトが生じた浮動小数点命令の後に、非浮動小数点命令が1つ以上有る場合は、フォルトを
発生した命令を実行し直しても無駄な場合があります。(
x87 FPU例外の同期
を参照してください)。
ハンドラがフォルトを発生した命令からプログラムの実行を再開する必要がある場合、IRET命令
を使用することはできません。これは、フォルトを発生した浮動小数点命令の後に続く浮動小数点命令
または、WAIT/FWAIT命令までは例外が発生しないため、スタック上のリターン命令ポインタがフォルトを
発生した命令を指していない可能性があるからです。フォルトを発生した命令からプログラムの実行を
再開するには、例外ハンドラは保存されているx87 FPU状態情報からその命令ポインタを取得し、
それをスタック上にあるリターン命令ポインタにロードして、IRET命令を実行する必要があります