
前回までに8086/8088命令はほぼ舐め終わりデス。残るはWAIT命令とESC命令群です。ただし、WAIT命令を使うには「数値演算コープロセッサ」8087に登場していただかねばなりません。そしてESC命令には8087の命令が詰まってますが、8086/8088も遊んでいるわけではありませぬ。
※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら
※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。
今回も動作確認すべきコードが無いんですけど。。。
「数値演算コープロセッサ」8087
IEEEの浮動小数点数の規格がありますが、アレは8087の仕様が元になってます。IBMでもDECでもない8087です。他に比べたらショボい16ビットと思われていたインテル8086ですが、こと浮動小数点数の演算に関しては積極的。8087を登載するだけで重い浮動小数点数の計算が何十倍にも高速化されるのは衝撃だったかも。
さてそんな8087ですが、後継の80287(286と組み合わせる)、80387(386と組み合わせる)とはハード的に大きく異なってました。後継機が「単なるIO接続の浮動小数点計算器」になってしまったのに対して、8087は「コープロセッサ」と胸を張って名乗っているとおり、単なる「浮動小数点計算器」ではなく
-
- 命令コードは自前で取得
- メモリとのやりとりは自前で制御
という過激なチップでした。8086と8087の接続図を描いてみたので御覧じろ。
8086(MAXモード)と8087のピン配がよく似ていることがお分かりでしょう。ほとんど重なる感じ。実際、アドレス・データ・バスを含め主要な信号は両方の「プロセッサ」に並列に接続されてます。8087の動作をかいつまむと以下のようです。
-
- 8087は8086がオペコードをフェッチするのを横でみている(データバス共有している上に、8086のプリフェッチ・キューの様子をQS0、QS1信号で教わっているので、8086が今どの命令をやろうとしているのか、手にとるように分かる)
- そこで自分宛の命令(オペコードの先頭にESCコードを持つもの)を発見するとこれは自分のだ、ということで実行に入る。
- その際、メモリのリード、ライトが必要な命令については、8086側ではダミーのリード命令として解釈し、ターゲット・メモリの先頭アドレスを生成して送り出す。
- 8087側では上記のダミーアドレスをキャプチャしている。先頭アドレスにつづくアドレス(浮動小数点数などはバイト長が長い)は自前で生成し、バスサイクル自体を制御する。その際、8086に対してバスの使用権をいただくために、RQ/GT0#信号を使う。また、リードかライトなどの制御は自らバスステータス信号(S0-S2)を制御してバス制御回路に知らせる。
- また、8087側で時間がかかる操作を行っていて、8086側がそれと待ち合わせする必要がある場合、8086側でWAIT命令を実行しておくと、8087がBUSY信号を出している間待つことができる。8086側ではTEST端子でBUSY信号をチェックする。
コープロセッサしていたのね。
WAIT命令
このようにWAITは、8086(8088)+8087では必要であったのですが、その後CPU側が浮動小数点側の実行制御に関わるようになって不要となっていきます。「やるだけ無駄」な盲腸(虫垂)命令となってしまいます。
オペコードマップと浮動小数点命令のエンコード
以下は8086(8088)のファースト・バイト・オペコード・マップの一部です。
白ヌキのところのWAITと、ESCと書いてある0xD8~0xDFが該当の命令となります。
先頭部分の赤色の5ビットがESC命令であることを示すお印です。命令は2バイトから4バイトで、2バイト目の上位2ビットがmodフィールド、下位3ビットがR/Mフィールドであることは8086の通常命令と変わりませぬ。
mod=11(一番上のNon-Memory形式)の場合、8087内のレジスタ間演算のような命令をここにエンコードすることができ、それに使えるビット数は上記のように9ビットあります(使っているのは一部。)
mod=00,01,11はそれぞれ R/M でアドレシングモード(8086のレジスタ使った)を指定し、それにディスプレースメントをつけられます。なおダイレクトアドレシング(レジスタ使わない)の場合R/Mを上記のように110とすることになってます。メモリ形式の場合、使えるビット数は6ビットです。
次回より、8087命令の練習に突入予定です。大丈夫か?