ぐだぐだ低レベルプログラミング(232)x86(16bit)、FST、ストアあれこれ

Joseph Halfmoon

前回は8087のpacked な BCD 整数のロード、ストア命令がなんで18桁なのか年甲斐もなく悩んでみました。今回はフツーの浮動小数とフツーの整数(2の補数)のストア命令を練習してみます。そんなの簡単じゃん、といいつつ、そこはx86一族です。ここにも命令セットの非対称性あり。いつものコマケー話なんだが。

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

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

    •  Windows 11 PC (i5-1235U)
    •  Ubuntu 24.04 LTS on WSL2
    •  QEMU 8.2.2
    •  FreeDOS 1.3
8087レジスタからのストア命令

ここまで8087レジスタスタックからのストア命令には顔を背けてきました。しかし、前回BCD整数を扱うにあたってストア解禁? こうなったらレジスタ・スタックからフツーの浮動小数、フツーの整数(2の補数表現)のストアを行ってみたいと思います。

なんどか繰り返しているとおり、8087のレジスタは、テンポラリ実数(80ビット幅)型です。一方、通常の浮動小数は単精度(32ビット)、倍精度(64ビット)です(8087登場時には半精度なんてものが蔓延るとは誰も思っていなかった?ホントか?) 当然、なんらかの形でストア時にはストア先のメモリ幅の精度に合わせて「丸める」必要があります。

ついては過去回でもやってますが、このためにはCW(コントロール・ワード)のRC(ラウンディング・コントロール・ビット、ビット11と10)が参照されます。デフォルトは00で「より近い値に丸め」っす。必要に応じてこのビットを操作してくだされや。

浮動小数のストアはFSTおよびFSTP命令で行われます。Pなしはストア後のスタック操作無、P付だとストア後、スタックを1個ポップしてスタックは縮小します。ストア先の精度はメモリ・ポインタ指定により、以下の3形式で可能です。

    • 単精度(32ビット)
    • 倍精度(64ビット)
    • テンポラリ実数(80ビット)

ただし、予告したとおり「x86一族」です。命令セットが綺麗に対称などということはありません。単精度、倍精度はFSTでもFSTPでも扱えますが、テンポラリ実数型をストアするときはFSTPのみです。そういう捻くれたところに文句言いますまい。

一方、レジスタスタック上のテンポラリ実数型の数を「2の補数表現の整数」としてストアする命令も存在します。FISTとFISTPです。Pの意味は上記と同じ、実行後のスタックのポップをするか、否かです。

整数型では、ストア先の精度はメモリ・ポインタ指定により、以下の3形式で可能です。

    • ワード(16ビット)
    • ショート(32ビット、ポインタとしてはダブル・ワード型)
    • ロング(64ビット、ポインタとしてはクワッド・ワード型)

ここでも期待どおり?の非対称性あり。ロング型のストアはFISTPでないとなりません。慣れてしまえば疑問も持たんぞなもし。いいのか、そういうことで。

今回実験のプログラム

今回は以下のような処理を行ってみます。

    1. π(3.14…)をスタックトップにロード
    2. 浮動小数の3形式でメモリの別々のアドレスにストア
    3. 再びπ(3.14…)をスタックトップにロード(2のステップの最後でポップしてしまったので)
    4. バイナリ整数の3形式でメモリの別々のアドレスにストア
    5. ストアした6個の値をレジスタ・スタックにロード
    6. レジスタ・スタック上の値を観察

なお以下は「強力な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, dstS
    mov di, dstWI
    fldpi
    fst dword [dstS]
    fst qword [dstS+4]
    fstp tword [dstS+12]
    fldpi
    fist word [dstWI]
    fist dword [dstWI+2]
    fistp qword [dstWI+6]
    fld dword [dstS]
    fld qword [dstS+4]
    fld tword [dstS+12]
    fild word [dstWI]
    fild dword [dstWI+2]
    fild qword [dstWI+6]
    nop
fin:
    mov ax, 0x4c00
    int 0x21
    resb    2048

segment data    align=16
    resb    1024 * 63
dstS: dd 0.0
dstD: dq 0.0
dstT: dt 0.0
dstWI: dw 0
dstSI: dd 0
dstLI: dq 0

segment stack   class=STACK align=16
    resb    2048
stacktop:
    dw 1024 dup (0)
stackend:
アセンブルして実行

MS-DOS互換で機能強化されているフリーなFreeDOS上、以下のステップで上記のアセンブラソース fst.asm から実行可能なオブジェクトファイルを生成して実行することができます(nasmとwatcom Cがインストール済であること。)

なお、FreeDOS付属の「debugx」デバッガは、8087のレジスタ内容を浮動小数で見せてくれます

nasm -f obj -l fst.lst fst.asm
wlink name fst.exe format dos file fst.obj
debugx fst.exe

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

赤枠が浮動小数形式でのストア3連弾、緑枠がバイナリ整数形式でのストア3連弾です。例の非対称部分に黄色の矢印を書きこんでおきましたぞ。

以下の黄色枠は、FLDPI命令実行後のスタックトップの値です。下の方の桁の様子を覚えておいてくだされ(忘却力の老人はよう覚えきれません。。。)beforeFSTs.ECpng

上記で大事なのは、赤枠つけたCWの上から2桁目の値です。「3」とな。つまりマルメ制御は00ってこってす。ニアレスト丸めね。

さて、ストアしてロードした結果が以下に。afterFSTsEC

まず、赤枠の整数は、ターゲットのビット幅に関わらず「3」ね。あたりまえだね。一方、黄色枠(テンポラリ実数型)は元の値とピッタンコ。しかし、緑枠(倍精度整数)は見えている最後の桁が以前と違う。ピンク枠(単精度整数)は、3.141592くらいまでは一致しているけれどその下になると差異あり。

メモリに書きだすときは丸めを考えないとならないっす。まあ、浮動小数点数に精通された姉貴兄貴には余計なお世話か。

ぐだぐだ低レベルプログラミング(231)x86(16bit)、BCD整数のロード、セーブ へ戻る

コメントを残す

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