12
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

Linuxでx86アセンブラ(浮動小数点編)

この記事について

今回は浮動少数の演算です.浮動少数と普通の整数の違いは常識ですね.メモリ上では正負を表す1bitと指数部、仮数部に別れて保存されます.
浮動少数の演算はCPUではなくFPUという専用回路で演算されます.この専用回路はスタック構造となりプッシュ・ポップを繰り返して演算します.整数の演算と比べて少し煩雑ですが、基本は同じです.
今までの知識で十分理解できるはずです.それでは、見て行きましょう.

FPUデータレジスタ

FPUデータレジスタは浮動少数の値を格納します.一つのレジスタは80bit長でST0からST7までの8本あります.
FPUデータレジスタはeaxなどの他のレジスタと異なり、下の図のようなスタック構造になっています.スタックにデータを入れるとST0から順に積まれていきます.スタックの一番上のレジスタのアドレスはFPU専用
のTOS(top of stack)レジスタに格納されます.
FPU_Stack.png

FPU制御レジスタ

g6167.png

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ステータスレジスタ

StatusRegister.png

FPUステータスレジスタはFPUの状態を保持します.C0からC3の条件コードはCPUのフラグと同様に浮動少数の計算結果を反映します.C0はCF、C2はPF、C3はZFと同じ役割を持ちます.C1についてはオーバーフロー・アンダーフローに基づいてフラグをセットします.CFが1の時オーバフロー、0の時アンダーフローです.
FPUレジスタの値をAXレジスタにコピーするにはfstsw を使います.また、フラグレジスタにコピーするにはsahf をつかいます.

タグレジスタ

TagResistor.png

データレジスタの値の状態を格納します.

タグレジスタの値 状態
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つの命令ではスタックの一番上の値を移動させますが、消去(ポップ)はしません.
消去したい場合はfstpfistp を使います.

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と同じです.

デモコード

float.asm
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 で表示してください.他のレジスタも同じ方法で表示できます.

参考文献

↓↓:bowtie:過去の投稿もよろしくね:bowtie:↓↓

↓↓:bowtie:コメントをいただけたら励みになります:bowtie:↓↓

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
12
Help us understand the problem. What are the problem?