
前回は整数もFPUレジスタにロードした後は浮動小数と同じ指数と仮数の組み合わせ、だから「整数演算命令は不在」などと軽はずみなことを書いてしまいました。すみません。訂正させていただきます。整数演算のFPU命令あります。その理由は8087(x86)がCISCだから。演算のオペランドにメモリをとれるのだよね。忘れてました。
※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら
※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。
そうだ8087FPUはCISCだった
ついつい、演算はレジスタ間、メモリへ行くのはロードストア命令というRISCスタイルが染みついてしまってました。ボケたね。忘却力。
x86系CPUの命令ストリームの中に間借りしている?x87系FPUは純然たるCISCスタイルです。確かに、FPUのレジスタへ値をロードしてしまえば、整数といえども前回見た通りで、指数+仮数というフォーマットに統一され、浮動小数向けの演算命令で操作するのに何の問題もありませぬ。しかしCISCです。演算の一方にレジスタ・スタックをとるものの、他方にメモリをとることも可能なのでした。その場合、メモリ上のオペランドが整数であるのか浮動小数であるのか分からないとメモリから読み出すことができませぬ。よってFPUにも整数演算命令という命令が存在します。列挙すれば以下の通り。
-
- FIADD
- FISUB
- FISUBR
- FIMUL
- FIDIV
- FIDIVR
ニーモニックの先頭のFIの「I」という一文字がこの命令のオペランドを整数として解釈しろ、という御威光を表してます。オペランドはCPUも扱える2の補数表現の符号付整数ということになります。ワード(繰り返しになりますが、x86のワードは16ビットです)、ダブルワード、クワッドワードなどのポインタ修飾によりデータ幅を指定です。なお、FPUにはバイト幅の処理は定義されてないデス。
今回実験のプログラム
今回はFILD命令でメモリ上に置かれた整数(2の補数)をスタックトップにロードし、それにFIADD命令でメモリ上に置かれた整数(2の補数)を足し込んでみます。
-
- ロードする整数値は 100001 (DWORD PTRで指す「ショート(32ビット整数)」
- 足し込む整数値は 1000 (DWORD PTRで指す「ショート(32ビット整数)」
計算原理からいって、FPUの場合はショートだとかロングだとかは意識しないでも(混ぜても)計算可能。ただし途中の計算精度をコントロールする必要がある場合のみ「丸め」制御ビットを操作してね、という感じじゃないかと思います。
今回は代表選手ということでFIADD一個だけ練習しましたが、後は以下同文ということでよしなに(手抜きだな自分。)
なお以下は「強力な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: mov bx, src1 mov si, src2 fild dword [bx] fiadd dword [si] fin: mov ax, 0x4c00 int 0x21 resb 2048 segment data align=16 resb 1024 * 63 src1: dd 100001 src2: dd 10000 segment stack class=STACK align=16 resb 2048 stacktop: dw 1024 dup (0) stackend:
アセンブルして実行
MS-DOS互換で機能強化されているフリーなFreeDOS上、以下のステップで上記のアセンブラソース fiadd.asm から実行可能なオブジェクトファイルを生成して実行することができます(nasmとwatcom Cがインストール済であること。)
なお、FreeDOS付属の「debugx」デバッガは、8087のレジスタ内容を浮動小数で見せてくれます
nasm -f obj -l fiadd.lst fiadd.asm wlink name fiadd.exe format dos file fiadd.obj debugx fiadd.exe
FILD命令だけでなく、FIADD命令にもオペランドとしてDWORD PTRが指すメモリが指定されとります。
赤枠のFILD命令でDS:[BX]から、ロードしている 0x000186A1は十進の100001です。緑枠のFIADDのオペランドのDS:[SI]が指している0x2710は10000っす。その二つを加算した結果が下の緑枠のST0=110001のところに格納されてます。
CISCだな~。 何を今更寝ぼけたことを。