
前回、8087数値演算コープロセッサの実行制御などハード的な部分を復習しました。何年、いや何十年ぶりだ?いよいよ浮動小数点演算命令の演習に突入?かと思われましたが、まだ復習必要っす。8087のレジスタです。こいつがまたメンドクセー奴。浮動小数点数のルールの数々、お惚け老人は覚えきれませぬが、知らないと命令動かせませぬ。
※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら
※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。
今回も動作確認すべきコードが無いんですけど。。。
8087はスタックマシン
近代的なCPUどもの浮動小数点レジスタは単純な順番で呼ばれることがフツーじゃないかと思われます。そして機械語命令の中に該当レジスタ番号がエンコードされておる、と。しかし8087が登場した1970年代、「科学技術計算」というと「スタックマシン」という連想ゲームが成立していたような気がします。大型機では、かのバローズ(バートニアン一派が集合していたらしい。。。)社のマシンがそう。また、小さいところではHP(ヒューレット・パッカード、当時は計測器のトップメーカ、後、計測器部門はアジレントとして切り出されてしまう)の関数電卓がそう(RPN、逆ポーランド記法。)
まあ、そういう時代の「流れ」に乗ったのじゃないかと想像します。8087、浮動小数点コープロセッサはスタックマシンです。スタックトップを暗黙のアキュムレータ的に使い、それとの間で演算を行い、スタックが伸びたり縮んだりしながら計算を行うようになってます。登場時はともかく、少し後になってコンパイラ技術が発達してくると、何でスタックマシン?ということになってきます。実際、86系でも浮動小数点SIMD命令が導入されるようになると、それらはフツーに順番で呼ばれるSIMDレジスタを使うようになります。8087互換の浮動小数点命令は肩身が狭くなっていくのですが、今だに出る幕はある、と。単純な四則演算などはともかく、メンドクセー超越関数などを一撃で仕留めることができる87系命令です。
さて、8087はスタックとして使える8本のレジスタを持っています。内部的に使われる80ビット幅のTEMPORARY REAL型(符号1ビット、指数15ビット、仮数64ビット+暗黙の1ビット)を格納できる80ビット幅です。このレジスタに、単精度浮動小数32ビット、倍精度浮動小数64ビット、整数型3種類、パックド10進型1種類も格納されることになります。
物理的な0番から7番のレジスタとは別に、通常、ST(スタックトップ)、ST(1)のように書かれるスタック上の「相対位置」でプログラミングが成されます。メモリから持ってきたデータを1個スタックにプッシュとか、スタックトップとスタック+1の内容を加算してポップといった塩梅に操作していくわけっす。
さてそんな80ビットのレジスタのスタックの位置は、Status wordという16ビット幅のレジスタの13ビット目から11ビット目におかれているSTフィールドの値で記録されてます。PUSHするとSTは減、POPするとST増。
一筋縄でいかないデータ
整数レジスタであれば、そこに書きこまれているバイナリ値をどう解釈するかは、適用する命令次第っす。しかし、浮動小数点数においてはメンドクセーものがあります。それぞれのレジスタに収まっている値の「素性」を「キープトラック」するために、各レジスタ2ビットのTAGというものが定義されており、2x8で合計16ビットのTag wordというレジスタとしてもアクセス可能です。
そこに書きこまれているのは以下の値(バイナリ)
-
- 00、バリッド
- 01、ゼロ
- 10、NaN、無限大、デノーマル数
- 11、空
みんな大好きNaNは Not-A-Number です。これが登場すると良くない兆候っす、多分。また、無限大といってもいろいろアリーの、これまた奥が深いっす。そしてデノーマル数です(アブノーマルではないよ。)そのうちやるかもしれませんが、8087は、ノーマルな場合は仮数部のMSBに「暗黙の1」をもってきて1ビット精度を稼いでいるのですが、指数部がいっぱいいっぱいになった後も仮数部を操作して頑張ってくれたりします。その時の状態がデノーマルです。知らんけど。
8087登場当時は、結構過激だった気もしますが、IEEEの浮動小数点規格が8087をもとに決まったので、モダーンなプロセッサどもは踏襲しておるルールっすね。
例外(イクセプション)
整数型だとせいぜいゼロ割り算くらいしかありませんが、浮動小数点数を演算する場合、例外事項がいろいろあります。Statusワードの下位には合計6ビットの例外フラグが存在します。一方 Controlワードの下位6ビットにはStatusワードの例外フラグのそれぞれに対応する「マスク・ビット」が存在します。浮動小数点数の場合、例外が起きたけれどもそこはマスクして先に進めるなんてことがあるためです。ラグビーやサッカーのアドバンテージみたいなやつね。いいのかそのたとえで?
そして例外を捕捉した場合に例外を発生した命令のオペコード、命令のアドレス、それと引数のアドレスを保持してくれるレジスタが存在します。8087の場合、メインの8086が20ビットの物理アドレスなので命令、データのアドレス・ポインタは20ビット幅です。また、命令のオペコードは前回みたとおり最初の5ビットは全て同じなので、保存されるのは残りの11ビット分(ディスプレースメントなどは無視)のみです。
他にも「丸め」とかメンドイ奴らがいろいろあるけど、今回はここまで。