0から作るソフトウェア開発

日々勉強中。。。

0から作るOS開発

環境準備

環境設定

ブートローダ

カーネルローダ

GRUB

カーネル

ドライバーその他

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データレジスター構成


x87 FPU命令ではR0からR7の8個の汎用レジスターをレジスタースタックとして扱います

データレジスターのアドレスで指定は、スタックのトップにあるレジスタに対して相対アドレスを指定します

スタックのトップであるレジスターの番号はステータスレジスター内のTOP(スタックのトップ)ビットに

格納されています。ロード操作でTOPが1デクリメントされます。ストア操作でTOPのレジスターから

値がメモリーに読み込まれた後、TOPが1インクリメントされます。(x87 FPUではロード操作=PUSH、

ストア操作=POPとなります)。

x87 FPUデータレジスタースタック


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次元のドット積が次のように計算されます

  1. 最初の命令(FLD value1)がスタックレジスターポインタをデクリメントし、
    値5.6をメモリからST(0)にロードします。この操作は図の(a)となります

  2. 2番目の命令が、ST(0)の値をメモリからロードした値2.4で乗算し、その結果を
    ST(0)に格納します。この操作は図の(b)となります

  3. 3番目の命令がTOPをデクリメントし、値3.8をST(0)にロードします

  4. 4番目の命令がST(0)の値をメモリからロードした値10.3で乗算し、その結果を
    ST(0)に格納します。この操作は図の(c)となります

  5. 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によるドット積の計算例


x87 FPUのスタックの値を交換するにはFXCH命令を使用します

x87 FPUステータスレジスター

ステータスレジスターにはTOP、条件コードなどがあります。

ステータスレジスターの内容はFSTSW/FNSTSW、FSTENV/FNSTENV、FSAVE/FNSAVE命令で

メモリーに格納することができます。また、FSTSW/FNSTSW命令でAXレジスターに格納

することもできます。

x87 FPUステータスレジスター

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命令は、読み込み元オペランドが許容範囲である

± 263を超えたことを示す場合に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レジスターのステータスフラグに

コピーします

  1. FSTSW AX命令で、x87 FPUステータスレジスターの内容をAXレジスターに移動します

  2. SAHF命令で、AXレジスターの上位8ビット(条件コードが含まれている)をEFLAGSレジスタ—の
    下位8ビットにコピーします

EFLAGSレジスターに値をロードした後は、ロードされたEFLAGSレジスターのフラグ条件に基づいて

条件付きジャンプ、条件付きMOVEが実行できます

条件コードのEFLAGSレジスターへの移動

条件コードの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命令を

使用してメモリに格納することができます。

x87 FPU制御レジスター


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データレジスタが

空としてタグ付けされていることを示しています

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命令時にマスクされていない例外が有ったときのみ有効となります

x87 FPUオペコードレジスター


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レジスターが

ロードされます。

プロテクティっドモードにおけるメモリ内のx87 FPUステートイメージ(32ビットフォーマット)


リアルモードにおけるメモリ内のx87 FPUステートイメージ(32ビットフォーマット)


プロテクティっドモードにおけるメモリ内のx87 FPUステートイメージ(16ビットフォーマット)


リアルモードにおけるメモリ内のx87 FPUステートイメージ(16ビットフォーマット)


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は次のデータ型を操作することができます。

x87 FPU浮動小数点例外条件

無効操作例外

無効操作例外は次の操作が原因で発生します

x87 FPUステータスレジスターのSF(スタックフォルト)フラグにより、この例外の原因を判断します

SF=1の場合:スタックオーバーフロー、アンダーフローが生じました。

SF=0の場合:算術命令に無効なオペランドがありました。

スタックオーバーフロー例外、スタックアンダーフロー例外(#IS)

x87 FPUタグレジスターはレジスタースタックのレジスター内容を記録し続けます。

この情報から2つのスタックフォルトを検出します

スタックのオーバーフロー、アンダーフローが発生するとステータスレジスターの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)

デノーマルオペランドは次の条件で発生します

この例外のフラグはステータスレジスターの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の処理が異なります

数値アンダーフロー例外(#U)

算術命令の丸められた結果が極小(格納先オペランドの浮動小数点フォーマットに収まる最小の

フォーマット型より小さい)場合、浮動小数点値アンダーフロー例外が発生する



数値オーバーフローの場合と同様に、データレジスターに格納される算術演算で発生する可能性が

あります。また、データレジスターの範囲内の値がより小さな単精度または倍精度の浮動小数点

フォーマットでメモリーに格納されるような(FST命令とFSTP命令)浮動小数点ストア操作でも

発生することがありますが、数値を整数フォーマットやBCD整数フォーマットで格納する際は

発生することはありません。極小値は、常に有効な丸めモードに従って0または1の整数値に

丸められます。数値アンダーフロー例外のフラグはステータスレジスターのUEです。

例外が発生したとき、マスクされている場合は 数値オーバーフロー例外 の操作と同じとなります

例外がマスクされていない場合は次のような場合があります

不正確結果(精度)例外(#P)

不正確結果例外(精度例外とも呼ばれます)は、演算の結果が格納先フォーマットで正確に表現

できない場合に発生します。但し、FSIN、FCOS、FSINCOS、FPTAN、FPATAN、F2XM1、FYL2X、FYL2XP1命令

などの超越関数命令は、その性質上、不正確な結果を生じますので注意します。

不正確結果例外のフラグはステータスレジスターのPEです。不正確結果例外が発生したときに

不正確結果例外がマスクされており、数値オーバーフロー/アンダーフローの条件がいずれも発生していない

場合は、x87 FPUはこの例外を処理を行った後に、条件コードC1はC1=1の場合は不正確結果が切り上げられた、

C1=0の場合は切り上げられなかったことを示します。切り上げられなかった場合(C1=0)では、丸められた

結果が格納先に収まるように不正確結果の最下位ビットが切り捨てられます



不正確結果が発生したときに不正確結果例外がマスクされておらず、数値オーバーフロー/アンダーフローの

いずれの条件も発生していない場合は、x87 FPUは、前述の処理を行い、例外ハンドラが呼び出されます



数値オーバーフロー/アンダーフローと同時に不正確例外が発生した場合は次のようになります

マスクされていない数値オーバーフロー/アンダーフロー例外が発生し、格納先オペランドがメモリーアドレスで

ある場合は(浮動小数点のストアの場合に限られます)、不正結果条件は発生せず、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)で例外ハンドラを呼び出します。その直後にプロセッサは現在実行中の

プログラムにある次の命令のいずれかを実行します

プログラム上の次の浮動小数点命令が非同期型命令である場合は、x87 FPUは例外ハンドラを

呼び出さずに次の命令を実行します

MS-DOS互換モード

制御レジスターCR0のNEフラグが0に設定されている場合は、MS-DOS互換モードとなります

このモードでは、浮動小数点例外が、プロセッサのFERR#、INTR、IGNNE#の各ピンを使用して

外部から呼び出されます。浮動小数点エラーとハンドラの呼び出しにこの方法が用いられるのは

MS-DOSやWindows95が動作しているPCの浮動小数点処理をサポートするためとなります。



MS-DOS互換モードでは、一般的に次のように浮動小数点例外ハンドラが呼び出されます。

  1. マスクされていない浮動小数点例外を検出すると、x87 FPUは例外に対するフラグをセットし
    ステータスレジスターのESフラグをセットします

  2. IGNNE#ピンの入力が無い状態では、x87 FPUはFERR#ピンをすぐにアクティブにするか、
    次に待機している浮動小数点命令またはMMX命令の実行直前までアクティブになるのを
    待ちます。FERR#ピンをすぐにアクティブにするか遅らせるかは、プロセッサ、命令、例外の
    タイプによって異なります

  3. 直前の浮動小数点命令が、マスクされていないx87 FPU例外のフラグを既にセットしている
    状態では、プロセッサは次のWAIT命令の実行直前にフリーズし、浮動小数点命令または
    MMX命令を待ちます。FERR#ピンが直前の浮動小数点命令の時点でアクティブになっていたか
    現時点でアクティブになっているにかかわらず、プロセッサがフリーズすることによって次の
    浮動小数点命令(またはMMX命令)が実行される前にx87 FPU例外ハンドラを確実に
    呼出すことができます。

  4. FERR#ピンがカスケード接続されたPICのIRQ13に外部ハードウェアが接続されています
    PICはFERR#ピンがアクティブになると割り込み0x75を発生させます

  5. PICがプロセッサ上のINTRピンをアクティブにし、割り込み0x75を通知します

  6. BIOSが割り込み2(NMI)ハンドラを呼出すことで割り込み0x75を処理します

  7. 割り込み2ハンドラが、割り込みがNMI割り込みの結果または浮動小数点例外の結果の
    いずれかであるかを判断します

  8. 浮動小数点例外が検出された場合は、割り込み2ハンドラが浮動小数点例外を呼び出します

IGNNE#ピンがアクティブになっている場合、プロセッサは浮動小数点エラー条件を無視します

このピンは浮動小数点例外ハンドラが以前に通知された浮動小数点例外を処理している間に

別の浮動小数点例外が発生するのを防ぐためにあります

プログラム中のx87 FPU例外処理

フォルトが生じた浮動小数点命令の後に、非浮動小数点命令が1つ以上有る場合は、フォルトを

発生した命令を実行し直しても無駄な場合があります。(x87 FPU例外の同期 を参照してください)。

ハンドラがフォルトを発生した命令からプログラムの実行を再開する必要がある場合、IRET命令

を使用することはできません。これは、フォルトを発生した浮動小数点命令の後に続く浮動小数点命令

または、WAIT/FWAIT命令までは例外が発生しないため、スタック上のリターン命令ポインタがフォルトを

発生した命令を指していない可能性があるからです。フォルトを発生した命令からプログラムの実行を

再開するには、例外ハンドラは保存されているx87 FPU状態情報からその命令ポインタを取得し、

それをスタック上にあるリターン命令ポインタにロードして、IRET命令を実行する必要があります

inserted by FC2 system