
前回、SIN、COS、TANについて「コマケー話」を書いたつもりが「誤ってました」その理由はほぼ半世紀前の書物に頼ったことにあります。今回、実機の挙動が異なるのでこれまた半世紀前のインテル公式マニュアルみたらば違ってました。詳しくは本文にて。まあ、訂正してお詫びして実際に動作したコードを貼付いたします。ぐだぐだ。
※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら
※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。
※オリジナルの8086+8087の組み合わせでは8086と8087の同期をとるためにWAIT命令が必用になる場合があります。後継機種ではWAIT命令が不要(演習に使用しているエミュレータQEMUでも不要)なのでWAIT命令は使用しません。メンドイし。
FCOSとFSIN
前回、8087にはTANを計算する命令のみあり、SINやCOSを直接計算する命令が無いと書きました。SINやCOSを直接計算できる命令が備わったのは80387以降です。80387のFSIN、FCOS命令の挙動は、ほぼほぼ半世紀前の以下の文書(日本語版)を参照させていただいておりました。
80386プログラミング、 クロフォード&ゲルシンガー著、岩谷訳 工学社 1988
上記の書物を以下の別シリーズにて取り上げさせていただいております。登場したての386の宣伝?のために書かれた古い本ですが、80386の設計の中心人物であった、敬愛する偉大なエンジニア様2人の共著であります。お惚け老人はそこに書いてあることを(ちょっと引っかかりながらも)長年信じ込んでおりました。
L.W.R.(57)80386プログラミング、クロフォード、ゲルシンガー著 岩谷訳 1988
心の底でちょっと引っかかったのは、FSIN命令とFCOS命令が返してくる結果です。該当命令から戻った時点のレジスタスタック上、ST(1) にFSINなりFCOSの結果が入り、スタックトップであるST(0)には何やら不思議な1.0 が格納されることになっております。
8087から存在するFPTAN命令においては、FPTANの結果がST(1)に入り、ST(0)には何やら不思議な1.0という形になってます。ある意味「8087」に合わせた形ではあるのです。お惚け老人が勝手に妄想するに、8087はFPTANを求める計算シーケンスで「プログラマに見える」レジスタを1個潰してしまう必要があり、このような形でその内部使用を糊塗したんではないかと。知らんけど。
しかし、 この1.0、無駄じゃないですかね。多分、80387が公式登場するまでに、プログラマから見えるレジスタを1個潰すという暴挙にでることなく、計算できるように内部回路が改良されたんではないかと。結果、現物のFSIN、FCOS命令の実装は、スタックトップの入力(ラジアン)を読んで、スタックトップにFSINあるいはFCOSの結果を返すという「素直」な方向になったのでしょう。今になって、以下の約50年前の「インテルの公式マニュアル」をひっくり返すと
80387 プログラマーズ・リファレンス・マニュアル インテル 1987
FSINやFCOSが謎の1.0を返すという記述はありませんでした。
想像するに偉大な設計者のお二人が解説書を御執筆された時点ではまだ「8087方式」のレジスタの使い方で実装される予定が、後になって改良されたのではないかと勝手に推測しております。まあね、インテル公式のマニュアルやデータシートが常にあっているとは限らんのだけれども。386も「途中で引っ込んだ命令」あるくらいだし。
なお、FPTAN命令は既に8087に実装されて世に出てしまっているので、その後も「不思議な1.0」は残ったままです。互換性命。
今回実験のプログラム
さて今回実験のプログラムは、中学生?でも計算できるありがちな三角関数値を各1点だけ、計算するというものです。
-
- 入力は π/6 ラジアン(30°ね。)浮動小数的にはπ/6 = 0.523498…
- SIN(π/6)= 0.5、ピッタンコ。
- COS(π/6)= (√3)/2 = 0.866025…
- TAN(π/6)=0.577350…
上記のような値が見えれば、まあ、SIN、COS、TANの計算できとるということ。例によって強力なx86用アセンブラNASM用のソースです(MSのMASMともインテルASM86とも微妙に異なるけど、まあ分かるっしょ。)
%use fp segment code ..start: mov ax, data mov ds, ax mov ax, stack mov ss, ax mov sp, stacktop test: fldpi mov bx, src1 fld dword [bx] fdiv fld st0 fsin fld st1 fcos fld st2 fptan fin: mov ax, 0x4c00 int 0x21 resb 2048 segment data align=16 resb 1024 * 63 src1: dd 6.0 segment stack class=STACK align=16 resb 2048 stacktop: dw 1024 dup (0) stackend:
アセンブルして実行
MS-DOS互換で機能強化されているフリーなFreeDOS上、以下のステップで上記のアセンブラソース fsin.asm から実行可能なオブジェクトファイルを生成して実行することができます(nasmとwatcom Cがインストール済であること。)
なお、FreeDOS付属の「debugx」デバッガは、8087のレジスタ内容を浮動小数で見せてくれます。
nasm -f obj -l fsin.lst fsin.asm wlink name fsin.exe format dos file fsin.obj debugx fsin.exe
FDIVPのところ(π/6を計算してスタックトップに置くところ)まで進めます。こんな感じ。
ちょっと見ずらいですが、上の青枠のところで π/6 = 0.523498…が計算されております。
つづいて折角求めた π/6 をが消えてしまわないように「複製」してます。
緑の値 π/6 がコピーされて黄色枠の新たなスタックトップに現れました。
予定通り、0.5ピッタンコの結果が出てよかった。
黄色枠のFCOSの結果は、 (√3)/2であるようです。よかった。
FPTANは8087時代の命令なので、ST1にTANの結果が入り、ST0(スタックトップ)には謎の1が入ってます。まあ、これはこれで納得。