3. 3D Now! + Enhanced 3D Now!
FPUレジスタ1つを2つの単精度小数と見なしておのおの独立に演算する機構です. FPUの様なスタック形式では無いのでプログラムが組みやすいです. MMXと同じく殆んどの命令は第1オペランドにはレジスタのみ, 第2オペランドにはレジスタ / メモリを指定できます. AthlonやK6-III+(?)などのCPUでは5命令(Enhanced 3D Now!)が追加されています.
3.1 読み込み / 書き込み
3D Now!はMMXの拡張なので読み書きにはmovd / movqを使います. 上位32bitと下位32bitを交換する命令が無いのはかなり残念です.
mm0=[D1:D0]のD1とD0を交換するには例えば
punpckldq mm1,mm0 ;mm1=[D0:*]
punpckhdq mm0,mm1 ;mm0=[D0:D1]
のようにしないといけません.
Enhanced 3D Now!(以下E3DNと略)ではその命令が追加されました(初めからつけとけ〜).
pswapd
上位と下位とを交換します[
E3DN only].
pswapd mm0 |
レジスタ | 内容 |
mm0 | D1 | D0 |
↓ |
mm0 | D0 | D1 |
3.2 足し算 / 引き算 / 掛け算
pfadd, pfsub, pfmul
destの上位 / 下位にそれぞれsrcの上位 / 下位の値を作用させます.
FPUの場合と同様, 逆向きの引き算pfsubrがあります.
pfacc
上位と下位を足します.
pfacc mm0,mm1 |
レジスタ | 内容 |
mm0 | D1 | D0 |
mm1 | D1' | D0' |
↓ |
mm0 | D1'+D0' | D1+D0 |
pfnacc
下位から上位を引きます[E3DN only].
pfnacc mm0,mm1 |
レジスタ | 内容 |
mm0 | D1 | D0 |
mm1 | D1' | D0' |
↓ |
mm0 | D0'-D1' | D0-D1 |
pfpnacc
add/subをします[E3DN only].
pfpnacc mm0,mm1 |
レジスタ | 内容 |
mm0 | D1 | D0 |
mm1 | D1' | D0' |
↓ |
mm0 | D0'+D1' | D0-D1 |
3.3 比較
pfcmpge
destがsrc以上であればdestを1, そうでなければ0にします.
pfcmpgt
destがsrcより真に大きければdestを1, そうでなければ0にします.
pfcmpeq
destがsrcに等しければdestを1, そうでなければ0にします.
0以上の小数を整数と見なしてcmp命令で比較することは可能でした. が, 逆に整数の値に対してこれらの命令を使うことはかなり難しいです. 何故なら浮動小数の指数部が0だと値全体が0と見なされてしまうからです.
3.4 最大 / 最小
pfmax, pfmin
destとsrcの大きい方, あるいは小さいほうをdestに格納します.
この命令もうっかり整数に対して使いがちなので注意します.
3.5 実数⇔整数
pi2fd
srcの32bit符号付き整数を実数に変換してdestに格納します.
pf2id
srcの実数の小数部を切り捨てて32bit符号付き整数に変換してdestに格納します.
pi2fw[E3DN only]
srcの16bit符号付き整数を実数に変換してdestに格納します.
pf2iw[E3DN only]
srcの実数の小数部を切り捨てて16bit符号付き整数に変換してdestに格納します.
3.6 逆数
pfrcp
src=[*:D]に対してdest=[1/D:1/D]となります. 15bit精度しかありません.
32bit精度で逆数を求めたい場合は次の様にします.
mm0=[*:D]の逆数を求める.
pfrcp mm1,mm0
pfrcpit1 mm0,mm1
pfrcpit2 mm0,mm1 ;mm0=[*:1/D]
私はpfrcpit?命令の詳細を知りません. mm0, mm1の値を片っ端から代入して調べてみて大体分かってきたのですが...
3.7 平方根の逆数
pfrsprt
src=[*:D]に対してdest=[1/√D:1/√D]となります. やはり15bit精度しかありません.
32bit精度で平方根の逆数を求めたい場合は次のようにします.
mm0=[*:D]の平方根の逆数を求める.
pfrsqrt mm1,mm0
movq mm2,mm1
pfmul mm1,mm1
pfrsqit1 mm1,mm0
pfrcpit2 mm1,mm2 ;mm1=[*:1/√D]
平方根を求める場合はこの後 pfmul mm0,mm1をやればよいです. pfrsqit1の詳細も私は知りません. どなたか教えて頂けませんか?
3.8 平均
pavgusb
destとsrcを符号無し8bit整数*8とみなしてそれぞれの平均(0.5は切上げ)を取ります.
3.9 pmulhwもどき
pmulhrw
pmulhwと殆ど同じですが, W*W'=Dの計算でDの上位16bitを取り出す時に下位16bitが0x8000以上であれば1加えます.
う〜ん, こんな互換性の面から殆ど使われそうにない命令を加えるぐらいならやはり上位 / 下位の交換命令が欲しかった...
3.10 FPUとの切り替え
femms
emmsよりも高速にFPUとMMXの切り換えを行います.
3.11 キャッシュロード
prefetch
L1キャシッュに読み込みのみ行います. キャッシュされたメモリに書き込むことが分かっている場合はprefetchwを使うことで高速化できるそうですが, K6-2では対応していないそうです.
御意見は光成滋生<herumi@nifty.com>までお願いします