
前回で8087の加減乗除は極めてしまったかと思いましたが早とちりデス。計算したら、計算結果を判断するという過程が必要。当然、8087には8087の比較命令というものが存在するのですが、8086側が司っているプログラム・フローに反映するのはどしたら良いの?ここで古代の遺物と蔑まれていた命令が活躍。ホントか?
※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら
※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。
※オリジナルの8086+8087の組み合わせでは8086と8087の同期をとるためにWAIT命令が必用になる場合があります。後継機種ではWAIT命令が不要(演習に使用しているエミュレータQEMUでも不要)なのでWAIT命令は使用しません。メンドイし。
浮動小数の比較命令
8087の浮動小数比較命令は以下の3つあります。いずれもスタックトップと他を比較して、その結果を「8087の」ステータスワードに反映するもの。
-
- FCOM
- FCOMP
- FCOMPP
3種類もあるのは、無印=レジスタスタックに触れない、P1個=レジスタスタックから1個ポップ、P2個=レジスタスタックから2個ポップ、と3通りのレジスタ・スタック操作が出来るからであります。
結果は過去回にて図を掲げたステータスワードの「黄色の」4ビットC3からC0に反映されます。図の一部を切り出したものが以下に。
しかしこれは8087内部のフラグどもです。これに反映したとて、何もしなければ8086には全然伝わりませぬ。そして条件判断をするには8086から「見える」ところになければなりません。どうするの?
8087ステータスレジスタの8086フラグへの反映
さて、8087側の状態を8086に報告するために、以下の命令が用意されております。
-
- FSTSW AX
- FNSTSW AX
8087のレジスタのストアは基本、メモリ相手なのですが、上記の命令に関しては珍しくレジスタ間でのやりとりになってます。スペシャルな奴だね。そしてデスティネーションは8086らしいAXレジスタのキメウチ。
さて上記の2命令とも8087内の「ステータスワード」16ビットを8086のAXにコピーしてくれるのですが、ニーモニックが2つあるココロは以下です。
-
- FSTSW、マスクされていない数値例外をチェック
- FNSTSW、数値例外をチェックしない
8087の数値例外(これがまたいろいろあるのよ)はまだ練習していないので今回はパス。触らぬ神に祟りなし、今回はFNSTSW命令の方を使用させていただいております。
さて、8087のステータスワードがAXレジスタに転送できたからと言って落ち着いてもいられません。条件分岐するためにはAXの内容をFlags内のフラグどもに反映させねばならんのです。そこで活躍することになるのが以下の命令デス。
SAHF
AXレジスタの上半分(AHレジスタ)をFlagsの下位へ転送してくれる命令です。SAHFについて、お惚け老人は、過去回で御先祖の8ビット機との互換性をとるための遺物的な命令みたいにディスってました。すみません。私が悪うございました。ちゃんと対8087で活躍の局面がありましたな。
SAHFによる8087ステータスの転送を観察すると以下の3ビットのみ意味があることに気づきます。ビットはFlags内の位置です。
-
- bit6=ZFに C3ビットが反映される
- bit2=PFに C2ビットが反映される
- bit0=CFに C0ビットが反映される
一応、C1ビットも転送はされるのですが、あまり役に立つ局面は無さそうに見えます、宛先がAFだし、ホントか?
また、PF(パリティ・フラグ)に転送されるC2が立っている条件は8種ある全てがNaN、無限大、デノーマル数など「フツーじゃない」奴らです。PFが立っているときは普通の比較ができんかったということであります。
比較結果の判断利用としては以下のとおり。
-
- JA(ジャンプ・アバブ)でZF==0とCF==0確認できたら「大なり」
- JB(ジャンプ・ビロー)でCF==1を確認できたら「小なり」
- JE(ジャンプ・イコール)でZ==1を確認できたら「イコール」
今回実験のプログラム
1引くπ(3.14…)を8087で計算して、その結果で分岐(当然「小なり」となる予定)するだけのプログラムです。強力なx86用アセンブラNASM用のソースです。インテル式とはオペランドの表記法など微妙に異なってます。そのソースが以下に。
segment code ..start: mov ax, data mov ds, ax mov ax, stack mov ss, ax mov sp, stacktop test: fldpi fld1 fcom st1 fnstsw ax sahf jp ERR_NEVER ;NEVER je ST_EQ ;NEVER ja ST_GT ;NEVER jb ST_LT jmp fin ;NEVER ST_GT: nop ERR_NEVER: nop ST_EQ: nop ST_LT: nop fin: mov ax, 0x4c00 int 0x21 re:wqsb 2048 segment data align=16 resb 1024 * 63 segment stack class=STACK align=16 resb 2048 stacktop: dw 1024 dup (0) stackend:
アセンブルして実行
MS-DOS互換で機能強化されているフリーなFreeDOS上、以下のステップで上記のアセンブラソース fcom.asm から実行可能なオブジェクトファイルを生成して実行することができます(nasmとwatcom Cがインストール済であること。)
なお、FreeDOS付属の「debugx」デバッガは、8087のレジスタ内容を浮動小数で見せてくれます。
nasm -f obj -l fcom.lst fcom.asm wlink name fcom.exe format dos file fcom.obj debugx fcom.exe
上記の赤枠部分で判定して反映したフラグにたいして、緑枠のJB(ジャンプ・ビロウ)が反応すればめでたしっす。
まずは肝心のFCOM命令の実行のビフォーアフター。緑枠がビフォーで、赤枠がアフターです。FCOM自体はスタック操作は供わないのでレジスタスタックはそのまま、唯一異なるのはSW(ステータス・ワード)のビット8のところの「1」です。
この「1」を反映させるってことだね。
続いて、FNSTSW AXで8087のSWを、8086のAXレジスタに転送するところ(赤枠。)そしてAXレジスタの上位8ビットをFlagsに反映させるSAHF命令の動作(緑枠。)
実行前に、ZR、NA、PE、NCだったフラグどもが、実行後にNZ、AC、PO、CYとなっております。これだね。
さて上記のフラグ設定により。必然的にJBまで落ちてきて、そこでジャンプと。
良かった、予定どおりのところでジャンプしたぞなもし。