この記事について
今回は浮動少数の演算です.浮動少数と普通の整数の違いは常識ですね.メモリ上では正負を表す1bitと指数部、仮数部に別れて保存されます.
浮動少数の演算はCPUではなくFPUという専用回路で演算されます.この専用回路はスタック構造となりプッシュ・ポップを繰り返して演算します.整数の演算と比べて少し煩雑ですが、基本は同じです.
今までの知識で十分理解できるはずです.それでは、見て行きましょう.
FPUデータレジスタ
FPUデータレジスタは浮動少数の値を格納します.一つのレジスタは80bit長でST0からST7までの8本あります.
FPUデータレジスタはeaxなどの他のレジスタと異なり、下の図のようなスタック構造になっています.スタックにデータを入れるとST0から順に積まれていきます.スタックの一番上のレジスタのアドレスはFPU専用
のTOS(top of stack)レジスタに格納されます.
FPU制御レジスタ
FPU制御レジスタは16bit長のレジスタで、浮動書数計算に関する設定を行います.
RCと呼ばれる2bitは丸め制御を行います.値によって
- 00 四捨五入
- 01 繰り下げ
- 10 繰り上げ
- 11 切り捨て
と、設定されます.
PCの2bitは精度を設定します.RCと同様に値によって
- 00 24 bits(単精度)
- 10 53 bits(倍精度)
- 11 64 bits(拡張倍精度)
このように設定されます.
制御レジスタの設定にはfldcw
を使います.
fldcw <メモリの16bit>
制御レジスタの値を取り出すにはfstcw
を使います.
fstcw <メモリ・レジスタ>
注意しなければいけないのはこの命令を実行したあと制御レジスタの値は未定義になるということです.
FPUステータスレジスタ
FPUステータスレジスタはFPUの状態を保持します.C0からC3の条件コードはCPUのフラグと同様に浮動少数の計算結果を反映します.C0はCF、C2はPF、C3はZFと同じ役割を持ちます.C1についてはオーバーフロー・アンダーフローに基づいてフラグをセットします.CFが1の時オーバフロー、0の時アンダーフローです.
FPUレジスタの値をAXレジスタにコピーするにはfstsw
を使います.また、フラグレジスタにコピーするにはsahf
をつかいます.
タグレジスタ
データレジスタの値の状態を格納します.
タグレジスタの値 | 状態 |
---|---|
00 | 有効 |
01 | ゼロ |
10 | 無効・無限・異常 |
11 | 空 |
浮動少数命令
データの移動
データの移動には2種類あります.一つはロードでスタックにデータを積みます.もう一つはストアでFPUのスタックからデータを取り出します.
fld
ニーモニックはソースの浮動少数の値をスタックに積みます.
fld <メモリ・レジスタ>
ソースは32bit、64bit、80bitの浮動少数である必要があります.32bit、64bitの浮動少数の場合は80bitの浮動少数に変換されてからFPUのスタックに積まれます.
整数の値を積みたい場合はfild
を使います.
fild <メモリ>
整数は16bit、または32bitである必要があります.
ストアにはfst
を使います.
fst <FPUレジスタ・メモリ>
整数値を取り出したい場合はfistp
を使います.
fistp <メモリ>
この2つの命令ではスタックの一番上の値を移動させますが、消去(ポップ)はしません.
消去したい場合はfstp
、fistp
を使います.
fstp <FPUレジスタ・メモリ>
fistp <メモリ>
足し算
浮動少数の足し算にはfadd
を使います.
fadd <メモリ>
fadd <FPUレジスタ>, <FPUレジスタ>
オペランドが一つの時は第二オペランドの値がスタックの一番上の値に足されます.
この命令ではポップは行いません.ポップしたい場合は
faddp <メモリ>
を使います.
引き算
引き算にはfsub
を用います.
fsub <メモリ>
fsub <FPUレジスタ>, <FPUレジスタ>
足し算と引き算は基本的には同じです.しかし、引き算は足し算と違ってオペランドの順序が計算結果に依存します.すなわち、a+bとb+aは同じなのに対して、a-bとb-aは異なるものです.
この問題に対処するためにfsubr
があります.
fsubr <メモリ>
この命令ではオペランドからスタックの一番上の値を引きます.
計算後ポップしたい場合はfsubp
を使います.
掛け算
掛け算の計算にはfmul
を使います.
fmul <メモリ>
メモリには32bitまたは64bitの浮動少数が入っているいる必要があります.
このメモリの値をスタックの一番上の値にかけて結果をスタックの一番上に積みます.
fadd
と同様にオペランド2つあるfmul
があります.
fmul <FPUレジスタ>, <FPUレジスタ>
計算後ポップするにはfmulp
を使います.
fmulp <FPUレジスタ>, <FPUレジスタ>
この命令はオペランドなしでも実行できます.
fmulp
その場合は、スタックの一番上の値とその次の値を掛けあわせ計算結果をスタックの二番目に格納したあとポップします.
整数の値をかけるには
fimul <メモリ>
を使います.この命令ではFPUスタックの一番上の値にメモリに置かれた整数をかけます.
割り算
割り算にはfdiv
を使います.
fdiv <メモリ>
この命令はスタックの一番上の浮動少数をメモリに置かれた値で割ります.
2つのオペランドを取るものもあります.
fdiv <FPUレジスタ>,<FPUレジスタ>
これもポップするものが用意されています.
fdivp <FPUレジスタ>, <FPUレジスタ>
これは、計算を行った後スタックの一番上の値をポップします.
オペランドをつけなくても使用可能で、その場合は、スタックの一番上の値をスタックの次の値で割ったあと結果を二番目のスタックに格納したあとポップします.
割り算も引き算と同様にA/BとB/Aの結果が異なります.そこで、fdivr
が用意されています.
fdivr <メモリ>
この命令はメモリにある値をスタックの一番上の値で割ったあと、結果をスタックの一番上に保存します.
比較
比較にはfcom
を使います.
fcom <メモリ・レジスタ>
この命令はスタックの一番上の値とオペランドの値を比較してFPUフラグをセットします.
引数のない場合はスタックの一番上の値とその次の値を比較します.
比較した後ぽっぷするには
fcompp
を使います.この命令は比較した後、スタックの一番上とその次の値をポップします.
整数の値を比較するにはficom
を使います.
ficom <メモリ>
比較と同時にポップするにはficomp
を使います.
0.0と比較するにはftst
を使います.
ftst
引数は取らずスタックの一番上の値と0.0を比較してフラグをセットします.
その他の浮動少数の比較・検査にはfxam
を使います.
その他
浮動少数の正負を反転させるにはfchs
を使います.オペランドは取らずスタックの一番上の値を反転させます.
絶対値を取る場合はfabs
を使います.使い方はfchs
と同じです.
デモコード
section .data
a: dq 0.25
b: dq 1.2
c: dq 2.0
section .bss
section .text
global main
main:
enter 0,0
nop
.add:
fld qword [a]
fadd st0, st0
fst qword [a]
.sub:
fld qword [b]
fld qword [c]
fsub st0, st2
.mul:
fmul qword [c]
.div:
fdiv qword [c]
.final:
ffree
mov eax, 0
leave
ret
いつものようにgdb上で実行してみてください.
FPUの情報を表示するには
info float
.fpuデータレジスタst0の値はdisplay $st0
で表示してください.他のレジスタも同じ方法で表示できます.
参考文献
- NASM development team site:
http://www.nasm.us/ - GDB: The GNU Project Debugger site:
https://www.gnu.org/software/gdb/ - Assembly Language Step-by-Step: Programming with Linux:
http://www.amazon.co.jp/Assembly-Language-Step---Step-Programming/dp/0470497025/ref=sr_1_1?ie=UTF8&qid=1446704607&sr=8-1&keywords=assembly+language+step+by+step - Stack Analysis with GDB:
http://resources.infosecinstitute.com/stack-analysis-with-gdb/
↓↓過去の投稿もよろしくね↓↓
-
Linuxでx86アセンブラ(BCD編)
http://qiita.com/MoriokaReimen/items/f66329680e98022c6bab -
Linuxでx86アセンブラ(条件分岐編)
http://qiita.com/MoriokaReimen/items/66b8c0e3b2ef1d2993bb -
Linuxでx86アセンブラ(マクロ編)
http://qiita.com/MoriokaReimen/items/45abbb6c2938abd505ff -
Linuxでx86アセンブラ(スタック編)
http://qiita.com/MoriokaReimen/items/31a8f8ce4a46ba0f3590 -
Linuxでx86アセンブラ(基本的なコードの構造編)
http://qiita.com/MoriokaReimen/items/b320e6cc82c8873a602f -
Linuxでx86アセンブラ(論理演算編)
http://qiita.com/MoriokaReimen/items/bf863585616ad0a0a969 -
Linuxでx86アセンブラ(四則演算編)
http://qiita.com/MoriokaReimen/items/4853587dcb9eb96fab62 -
Linuxでx86アセンブラ(Cとの連携編)
http://qiita.com/MoriokaReimen/items/590a4ddb3de15bfacb4b -
Linuxでx86アセンブラ(道具編)
http://qiita.com/MoriokaReimen/items/b316a68d76c1eafa18f8 -
tmuxの使い方
http://qiita.com/MoriokaReimen/items/d7d5b158cb412dd65a60
↓↓コメントをいただけたら励みになります↓↓