Linuxでx86アセンブラ（浮動小数点編）

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

# FPUデータレジスタ
FPUデータレジスタは浮動少数の値を格納します．一つのレジスタは80bit長でST0からST7までの8本あります．
FPUデータレジスタはeaxなどの他のレジスタと異なり、下の図のようなスタック構造になっています．スタックにデータを入れるとST0から順に積まれていきます．スタックの一番上のレジスタのアドレスはFPU専用
のTOS(top of stack)レジスタに格納されます．
![FPU_Stack.png](https://qiita-image-store.s3.amazonaws.com/0/77676/1b9f0193-0424-8a8b-4b36-56938b6a5599.png)


# FPU制御レジスタ

![g6167.png](https://qiita-image-store.s3.amazonaws.com/0/77676/cf7a2763-4b9f-7c45-5470-1e6b917a354a.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](https://qiita-image-store.s3.amazonaws.com/0/77676/d85f961e-5068-2c65-bf1d-17935460f62b.png)

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


# タグレジスタ

![TagResistor.png](https://qiita-image-store.s3.amazonaws.com/0/77676/b0274e70-0047-0ab0-3e60-d46f6ba73a84.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つの命令ではスタックの一番上の値を移動させますが、消去（ポップ）はしません．
消去したい場合は```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``` と同様にオペランド２つある```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``` で表示してください．他のレジスタも同じ方法で表示できます．



# 参考文献
* 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/

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


* 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



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