MASM32によるアセンブラ入門:パート3お読みになる前に本稿の内容は、読者の環境に MASM32がインストールされていることを前提にしています。まだインストールしていない場合は、http://www.masm32.com/から入手してください。 はじめにいよいよこのチュートリアルの最終回です。(シリーズ第2回はこちら)この第3回では、一般的な算術関数と、アセンブラを実装するうえで大きな助けになる MASM32のマクロをいくつか紹介したいと思います。 増分と減分 増分と減分の処理は TestProc proc mov eax, 5 dec eax dec eax mov dl, 10 inc dl inc dl ret TestProc endp TestProc proc mov ecx, 10 xor eax, eax ; efficient way of saying eax=0 LoopStart: inc eax dec ecx jnz LoopStart ; eax now equals 10 ret TestProc endp 加算と減算 加算と減算は add/sub (destination), (source) レジスタとレジスタを加算するほかに、定数とレジスタを加算したり、メモリの内容とレジスタを加算したりすることができます。 AddValues proc dwValue1:DWORD, dwValue2:DWORD mov eax, dwValue1 add eax, dwValue2 ret AddValues endp このメソッドでは、渡された2つの値を加算し、結果を返します。 乗算と除算 乗算と除算は 次の図は、この2つの命令でアキュムレータレジスタとデータレジスタが使用されるときに、これらのレジスタがどのように組み合わされるかを示しています。 ![]() したがって、予想どおりの結果を得るためには、 TestProc proc mov eax, 10 xor edx, edx ; set edx to zero mul 10 div 10 ret TestProc endp 論理演算 通常の論理演算は logical operation (destination), (source) LogicalFunction proc xor eax, eax ; the efficient way of saying eax=0 mov ax, 100 mov bx, 5 and ax, 1 or ax, bx ret LogicalFunction endp ビットシフト演算 ShiftFunction proc mov eax, 1 shl eax, 2 ; shift eax’s bits left 2 times : i.e. eax *= 4 shr eax, 2 ; shift eax’s bits right 2 times : i.e. eax /= 4 ret ShiftFunction endp テスト命令とループ特定の条件をテストするために使用できる命令は数多くあります。これらの命令は算術演算と同じ演算を行いますが、レジスタ内の値を変更せず、フラグだけを設定します。 たとえば CmpFunction proc mov eax, 100 cmp eax, 100 ; jump if equals je Equals ; not equal mov eax, 2 jmp EndIf Equals: mov eax, 1 EndIf: ret CmpFunction endp LoopFunction proc xor eax, eax mov ecx, 10 LoopStart: inc eax loop LoopStart ret LoopFunction endp MASM32のマクロMASM32には、アセンブラ開発者の役に立つマクロが多数用意されています。以降ではそのごく一部を紹介したいと思います。 1つ目は.
IfProc proc
mov eax, 100
mov ecx, 200
.if eax == ecx
; do something
.else
; do something else
.endif
ret
IfProc endp
2つ目は
LoopProc proc
xor eax, eax
mov ecx, 100
.repeat
inc eax
.untilcxz
ret
LoopProc endp
ループ内でループを実行するときは、レジスタ
LoopInLoopProc proc
xor eax, eax
mov ecx, 100
.repeat
push ecx
mov ecx, 100
.repeat
inc eax
.untilcxz
pop ecx
.untilcxz
ret
LoopInLoopProc endp
アセンブラ内から関数を呼び出す アセンブラコード内から関数を呼び出すには Function1 proc dwValue:DWORD add eax, 100 ret Function1 endp MainFunction proc mov eax, 100 invoke Function1, eax ; eax now = 200, i.e. eax += 100 ret MainFunction endp 関数名と最初のパラメータの間にカンマを指定することに注意してください。 ローカルメモリMASM32では、関数にローカルなメモリを割り当て、このメモリにラベルを付けることができます。これはローカル変数と同じように考えられますが、対応するマシン語コードを分析してみると、メモリアクセスの変形にすぎないということがわかります。 ローカルメモリは関数の冒頭で定義します。マシン語コードを分析してみると、実際には、関数内の最初の命令の前に静的なメモリブロックを割り当てていることがわかります。このメモリのサイズは、MASM32の基本型( ExampleLocalMemory proc LOCAL dwValue:DWORD ; allocates 4 bytes and labels it ’dwValue’ LOCAL wValue:WORD ; allocates 2 bytes and labels it ’wValue’ LOCAL bValue:BYTE ; allocates 1 byte and labels it ’bValue’ xor eax, eax mov dwValue, eax mov wValue, ax mov bValue, al ret ExampleLocalMemory endp 最適化 効率的なコードを書くためには、すべての命令に同じ時間がかかるわけではないということを理解する必要があります。たとえば 効率的なコードを書くためにもう1つ注意しなければならないのは、ループ内に含まれる命令の数です。命令数が少なければ少ないほど、コードの実行速度は速くなります。 メモリへのアクセスはレジスタへのアクセスよりも遅いので、コードを記述するときは、関数のローカルメモリよりもレジスタを使用するように努めてください。 また、 最も重要なのはアルゴリズムそのものです。高速なアルゴリズムとは、単純なアルゴリズムです。アルゴリズムが単純であれば、必要な命令の数も少なくなるからです。特定のタスクに使用しているアルゴリズムを見直すことが大切です。ある程度の正確性または柔軟性を犠牲にすることで大幅な速度向上が見込める場合は、速度の方を優先すべきです。 アセンブラコードの最適化については、この他にも検討すべき点が数多くあります。コードの微調整に関しては、MASM32のヘルプファイルが大いに役立つでしょう。 まとめ今回のシリーズはほんの導入であり、これですべてがわかるというものではありません。もっと詳しく知りたい方は、MASM32に付属しているチュートリアルとヘルプファイルを参照してください。 このシリーズを通じて、アセンブラのコーディングは難しいものではないと理解していただければ嬉しく思います。ぜひアセンブラを使用して、自作のアプリケーションの高速化や、リアルタイム処理の実現に挑戦してみてください。 この全3回のチュートリアルが皆さんのお役に立てば幸いです。 著者紹介darwen(darwen)
Windows 3.11のVisual C++/MFC 1.5の時代からWindowsプログラミングに取り組み始める。現在は、多言語展開を図っているとあるソフトウェア会社のベテラン開発者としてあらゆるプロジェクトに従事。暇さえあればプログラミングをしているのではないかという噂あり。
|