ぐだぐだ低レベルプログラミング(216)x86(16bit)、浮動小数点数の加減乗除

Joseph Halfmoon

前回は8087の浮動小数加算命令を練習、これで加減乗除はバッチリかと思えば、「演算順序」の問題もあり。某国の小学校のようにスカラーの乗算に順序を求めることは流石にないです。減算、除算のときだけです。オペランドの表記法のルールと相まって慣れないとハマります。それもこれもスタック・アーキテクチャのせい?知らんけど。

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

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

    •  Windows 11 PC (i5-1235U)
    •  Ubuntu 24.04 LTS on WSL2
    •  QEMU 8.2.2
    •  FreeDOS 1.3

※オリジナルの8086+8087の組み合わせでは8086と8087の同期をとるためにWAIT命令が必用になる場合があります。後継機種ではWAIT命令が不要(演習に使用しているエミュレータQEMUでも不要)なのでWAIT命令は使用しません。メンドイし。

浮動小数の加減乗除命令

合計12のニーモニックが浮動小数の加減乗除に割り当てられております。こんな感じね。

Operation FADD FSUB FMUL FDIV
NRM FADD FSUB FMUL FDIV
NRM,w/POP FADDP FSUBP FMULP FDIVP
REV FSUBR FDIVR
REV,w/POP FSUBRP FDIVRP

前回、ニーモニックの末尾に「P」がつくのはPOPの意味で、スタックから要素を一つポップして、スタックが縮小するのだと説明しました。

今回新たに登場するのが「R」です。リバースのRぞなもし。通常の引き算、割り算の場合、順序あり。引き算であればどっちからドッチを引くのかで結果が異なりますな。オペランド2つを明示してあれば、ディスティネーション(兼第1ソース)引く(第2)ソースでありますが、この順序をひっくり返すためのRです。

分かった、というのは多分早とちりっす。ここに以下のアセンブラ表記上のお約束が存在します。

明示オペランドの無いFSUBはスタックがポップされる

なんとオペランド無で記述された「FSUB」は、「FSUBP ST(1), ST」と同じ意味となるのでした。実はこのルールはFSUBだけでなく、他の加減乗除命令どもにあまねく適用されます。オペランドを明示しないと必ずレジスタスタックは一つ短くなるのでした。その上のデスティネーションST(1)ね。そしてこのルールのため、FSUBやFDIVの場合の演算順序が反転しているようにも見えます。コマケー表記の話なんだが、知らないとビックリだよ。以下で実例登場するのでよ~く順序をお確かめくだされや。

今回実験のプログラム

加減乗除とタイトルに掲げつつ、上記の「演算順序とスタックポップ」問題を確認するため、減算を4通りで練習しているだけのコードです。強力なx86用アセンブラNASM用のソースです。インテル式とはオペランドの表記法など微妙に異なってます。そのソースが以下に。

segment code

..start:
    mov ax, data
    mov ds, ax
    mov ax, stack
    mov ss, ax
    mov sp, stacktop
test:
    fld1
    mov bx, src1
    fld dword [bx]
    fsub st0, st1
    fsubp
    fld1
    fld dword [bx]
    fsubr st0, st1
    fsubrp
fin:
    mov ax, 0x4c00
    int 0x21
    resb    2048

segment data    align=16
    resb    1024 * 63
src1: dd 7.5

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

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

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

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

まずは、オブジェクトコードの逆アセンブルからuasmfsub

まずは「R」なしのFSUBから。緑枠が引かれる数、黄色枠が引く数で一番下の赤枠が引いた結果です。FSUBba

次は「R」なしのFSUBPです。緑枠が引かれる数、黄色枠が引く数で一番下の赤枠が引いた結果です。例の「ルール」によって演算の順序が逆転しているように見えないこともない。おっと。FSUBPba

今度は「R」付のFSUBPです。ただしオペランドを記述。上記の緑枠が引かれる数、黄色枠が引く数で一番下の赤枠が引いた結果です。こちらはフツーにリバース演算。FSUBRba

最後は「R」と「P」付のFSUBRPです。オペランド無。またまたルール発動。緑枠が引かれる数、黄色枠が引く数で一番下の赤枠が引いた結果です。こちらはリバースのリバースで普通な順番に見えないこともない。FSUBRPba

引き算の順序に翻弄されるとは、トホホ。

ぐだぐだ低レベルプログラミング(215)x86(16bit)、浮動小数点数の加算 へ戻る

コメントを残す

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