2.SSE2命令の実装

2-1.浮動小数点演算をアセンブラで書く(64ビット版)

 1-4.今後のサンプルについて(64ビット版)で説明したように、この章のサンプルでは、プロジェクトの設定等は、変化がない限り説明しません。
 ソリューションエクスプローラのソースファイルにあるSource.asmがアセンブラのコードです。
 以下に内容を紹介します。
.code

;float funcSample
funcSample  proc
    subss   xmm0,   xmm1
    ret 
funcSample  endp
        end
 C/C++側のSampleMASM.cppは以下です。
#include <iostream>

extern "C" float funcSample(float, float);

int main()
{
    std::cout << funcSample(70.5f, 50.0f) << "\n";
}
 実行すると以下の出力があります。
20.5

...\ProjectMASM\Sample201\x64\Debug\SampleMASM.exe (プロセス 17512) は、コード 0 を伴って終了しました。
このウィンドウを閉じるには、任意のキーを押してください . . .
 つまりfuncSample()関数は引き算をする関数です。

コード説明

 この項から少しずつアセンブラの文法について紹介します。
 まず、8086系のCPUにはセグメントという概念があります。領域くらいの意味に考えてもらえればいいと思います。
 CPUはマシン語しか読めません、マシン語はただの数字の羅列です。プログラムが実行されているときは、メモリ上にそのプログラムの数字群がロードされています。
 しかしそれらの数字群には、役割があります。プログラムを実行する役割(コードセグメント)データとして使う役割(データセグメント)スタックとして使う役割(スタックセグメント)です。(ほかにありますが、まずこの3つを覚えましょう)。
 それらの役割をする数字群を領域で区切ります。それがセグメントです。
 セグメントはアセンブラ上では以下のように記述します。
名前    segument [オプション]
;内容を記述
名前    ends
 オプションにはどういう役割をするセグメントかを記述します。
 しかし、このドキュメントではWindowsアプリとして動くプログラムを記述します。
 とすると、C/C++言語(デスクトップアプリ)で作成したプログラムと互換性がなければなりません。
 具体的には、アセンブラでは.objという拡張子のオブジェクトファイル(中はマシン語でできている)を作成して、それをC/C++のコンパイラが作成した.objファイルとリンクすることで実行ファイルを作成します。
 ですのでセグメントの定義もそれに合わせる必要があります。  規約では
_TEXT   segment
;...内容
_TEXT   ends
 のように記述します。これでコードセグメントを作成することができます。
 ですので、アセンブラコードでは
_TEXT   segment
funcSample  proc
    subss   xmm0,   xmm1
    ret 
funcSample  endp
_TEXT   ends
        end
 と書いても問題なくビルドできます。もとのソースにあった.codeコードセグメント定義を省略して書く手法です。

 続くfuncSample procプロシージャの宣言です。
名前    proc
名前    endp
 でプロシージャを作成できます。プロシージャC/C++の関数と同等と考えて差し支えありません。
 関数引数と戻り値があります。この規約が問題でして、x64環境のWindowsアプリは、かなり複雑になっています。
 気になる人はx64 での呼び出し規則(MSDN)がありますので調べてみましょう。
 この規約によると浮動小数点は、まず、XMM0からXMM3に渡されるとあります。XMM0等はレジスタといわれる計算用の専用メモリです。というか、CPUというのはレジスタ、メモリ、IOポートの3つしか扱えません。メモリ上にあるデータは、いったんレジスタにコピーしないと計算できないのです。ですが、レジスタはそんなにたくさんあるわけではありません。x64でも全部で32個しかありません。これでも32ビット環境よりはずいぶん増えたのです。
 このドキュメントではレジスタの説明は行いません。出てきたときに若干コメントします。
 プロシージャ内を見てみましょう。
    subss   xmm0,   xmm1
    ret 
 というのが記述されています。アセンブラの命令は、基本的に
ニーモニック(オペコード)  オペランド1, オペランド2, ...
 のように記述します。多くの場合、オペランドは3個以内です。
 subssというニーモニックは浮動小数点の引き算です。
subss op1,op2
;op1からop2を引いて結果をop1に入れる
 という計算です。
 C/C++の関数呼び出しで渡される引数は
第1引数: xmm0
第2引数: xmm1
 なので
    subss   xmm0,   xmm1
 の結果は、xmm0に収められます。
 その後
    ret 
 が記述されています。これはプロシージャから戻るという意味です。
 戻り値は基本的にxmm0に格納されます。
 C/C++側では戻り値xmm0を関数の戻り値と解釈し、coutで出力します。