ぐだぐだ低レベルプログラミング(229)x86(16bit)、FPUつかった整数演算

Joseph Halfmoon

前回は整数もFPUレジスタにロードした後は浮動小数と同じ指数と仮数の組み合わせ、だから「整数演算命令は不在」などと軽はずみなことを書いてしまいました。すみません。訂正させていただきます。整数演算のFPU命令あります。その理由は8087(x86)がCISCだから。演算のオペランドにメモリをとれるのだよね。忘れてました。

※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら

※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。

    •  Windows 11 PC (i5-1235U)
    •  Ubuntu 24.04 LTS on WSL2
    •  QEMU 8.2.2
    •  FreeDOS 1.3
そうだ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

まずはプログラムの逆アセンブルリスト。unasmFIADD

FILD命令だけでなく、FIADD命令にもオペランドとしてDWORD PTRが指すメモリが指定されとります。

早速、肝心の足し算部分へ突入。FIADD

赤枠のFILD命令でDS:[BX]から、ロードしている 0x000186A1は十進の100001です。緑枠のFIADDのオペランドのDS:[SI]が指している0x2710は10000っす。その二つを加算した結果が下の緑枠のST0=110001のところに格納されてます。

CISCだな~。 何を今更寝ぼけたことを。

ぐだぐだ低レベルプログラミング(228)x86(16bit)、整数のFPUレジスタ上の表現 へ戻る

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です