はじめに
本稿では、Microsoft Visual C++のインラインアセンブラを用いて、アセンブリ言語の基本的な命令セットを学習します。本来のアセンブリ言語は、単純にアセンブリ言語の文法を覚えるだけでは使うことができず、CPUが内部で何を行っているのかや、メモリアドレスに関連した高度な知識が必要になります。しかし、インラインアセンブラであれば、多くのプログラマが使い慣れているC言語の内部にアセンブリ言語を埋め込むことができるので、スムーズにアセンブリ言語の学習ができます。インラインアセンブラであればメモリアドレスの計算などが不要で、C言語で宣言された変数などの識別子をそのまま利用することができるのです。アセンブリ言語初心者が、アセンブリ言語の学習を開始するのに非常に適しているでしょう。
インラインアセンブラの基本については、前稿『インラインアセンブラによるアセンブリプログラミング体験』を参照してください。
算術命令
C言語のような高水準言語では加算や減算などの算術操作は10+5
というように、数学の記法に近い式として記述することができました。しかし、アセンブリ言語では、式を表記することはできず、基本的な計算も含めて、あらゆる処理が命令によって記述されます。なぜならば、アセンブリ言語はコンピュータが直接理解することができる機械語と1対1の関係を持っているためです。アセンブリ言語の命令は、オペコードと呼ばれる機械語の命令値に対応しています。
最も基本的な計算である加算はADD
命令を使います。ADD
命令は次のように記述します。
add dest , src
ADD
命令は、ソース・オペランドに指定された値とディスティネーション・オペランドに指定されたレジスタまたはメモリの値を加算し、その結果をディスティネーション・オペランドにストアします。こうした性質から、ソース・オペランドには即値を含め、メモリとレジスタを指定することができますが、ディスティネーション・オペランドには即値を指定することはできません。
#include <stdio.h> int main() { int result; __asm { mov eax, 10; add eax, 100; mov result, eax; add result, 40; } printf("result=%d\n", result); return 0; }
result=150
sample01の__asm
ブロックでは、最初に加算演算で利用するEAX
レジスタの値を初期化する目的で10を設定し、10を保存するEAX
レジスタを対象にADD
命令で100を加算しています。このADD
命令を実行すると、CPUは即値100とEAX
の現在の値を加算し、その結果をディスティネーション・オペランドであるEAX
に保存します。よって、EAX
の値はADD
を実行した時点で上書きされるので注意してください。
その後、EAX
レジスタの値をresult
変数にストアし、さらにresult
変数の値に40加えます。ADD
命令のディスティネーション・オペランドにはメモリを指定することもできるので、問題はありません。
sample01の実行結果では、最終的なresult
変数の値を表示させています。result
の値は、上記の通り150となります。
減算の場合も、基本的な操作は加算命令と同じです。減算を行う場合はSUB
命令を使います。
sub dest , src
SUB
命令の考え方はADD
命令と同じです。この命令では、ディスティネーション・オペランドの値からソース・オペランドの値を引き、その結果をディスティネーション・オペランドにストアします。ソース・オペランドに即値を指定することはできますが、ディスティネーション・オペランドには指定できません。
#include <stdio.h> int main() { int result; __asm { mov eax, 100; sub eax, 30; mov result, eax; sub result, 20; } printf("result=%d\n", result); return 0; }
result=50
sample02は、最初にEAX
レジスタに100をロードし、この値からSUB
命令を用いて30を引きます。この時点でEAX
レジスタの値は70になっています。次に、EAX
レジスタの値をresult変数にストアし、さらにSUB
命令でresult変数の値から20を引いています。よって、このプログラムの結果result
の値は50となります。