SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

インラインアセンブラで学ぶアセンブリ言語

インラインアセンブラで学ぶアセンブリ言語 第2回

アセンブリ言語で算術演算/論理演算を行う


論理演算

 ビット単位の論理演算を行うには、算術演算と同様に論理演算を行う専用の命令を使います。論理演算を行う命令の名前は明快です。論理積を求める命令はAND、論理和を求める命令はOR、排他的論理和を求める命令はXORとなります。

and dest, src
or dest, src
xor dest, src

 これらの命令は、srcにソース・オペランドを、destにディスティネーション・オペランドを設定し、ソース・オペランドとディスティネーション・オペランドとの論理演算を行い、その結果をディスティネーション・オペランドにストアします。ディスティネーション・オペランドにはレジスタまたはメモリ領域を指定し、ソース・オペランドには即値、レジスタ、またはメモリ領域を指定することができます。

sample07
#include <stdio.h>

int main() {
    int and_r, or_r, xor_r;

    __asm {
        mov eax, 0xFF00FF00;
        and eax, 0x0F0FF00F;
        mov and_r, eax;

        mov eax, 0xFF00FF00;
        or eax, 0x0F0FF00F;
        mov or_r, eax;

        mov eax, 0xFF00FF00;
        xor eax, 0x0F0FF00F;
        mov xor_r, eax;
    }

    printf("and=%X, or=%X, xor=%X\n", and_r, or_r, xor_r);
    return 0;
}
実行結果
and=F00F000, or=FF0FFF0F, xor=F00F0F0F

 sample07は、ディスティネーション・オペランドの値を0xFF00FF00で、ソース・オペランドの値を0x0F0F00Fで、AND、OR、XORの命令をそれぞれ実行しています。実行結果を見れば、適切に論理積、論理和、排他的論理和が求められていることを確認できます。このサンプルではソース・オペランドをすべて即値で指定していますが、これらの命令のソース・オペランドにはメモリ領域やレジスタを指定することも可能です。

シフトと回転

 レジスタやメモリに保存されている値をビットの列として解釈し、ビットの列を左右に移動させるシフト演算は行うにはシフト命令を使います。シフト命令には、ビット列を左に移動させる左シフト命令と、ビット列を右にシフトさせる右シフト命令があります。

 コンピュータの世界では、2進数値の最上位ビットを符号として利用し、最上位ビットが1であれば負数であることを表します。そのため、右シフトするときに最上位ビットの符号を保存したままで、最上位ビット以外をシフトの対象とする算術シフトと、最上位ビットの符号に関係なく、すべてのビットをシフトさせる論理シフトに分かれています。

  • 左シフト命令
    • 論理シフト命令 : SHL
    • 算術シフト命令 : SAL
  • 右シフト命令
    • 論理シフト命令 : SHR
    • 算術シフト命令 : SAR

 こうした構造から、右論理シフトを実行したい場合はSHRを、右算術シフトを実行したい場合はSARを使います。左シフト命令には算術シフトと論理シフトの区別は存在しないのですが、命令としては個別に用意されています。しかし、実行結果はSHLSAR共に同じです。

shl dest, count
sal dest, count
shr dest, count
sar dest, count

 これらの命令の第1オペランドdestはディスティネーション・オペランド、第2オペランドcountはカウント・オペランドと呼ばれています。カウント・オペランドに指定されている値だけ、右または左にディスティネーション・オペランドの値をシフトさせ、その結果をディスティネーション・オペランドにストアします。ディスティネーション・オペランドに即値を指定することはできませんが、カウント・オペランドには即値を指定することができます。

sample08
#include <stdio.h>

int main() {
    int a = 100, b = 40, c = -10, d = -1;

    __asm {
        shl a, 2;
        shr b, 3;

        sar c, 1;
        shr d, 1;
    }

    printf("a=%d, b=%d, c=%d, d=%d\n", a, b, c, d);
    return 0;
}
実行結果
a=400, b=5, c=-5, d=2147483647

 sample08は、変数abcdを宣言し、それぞれを適当な値で初期化しています。これらの変数をアセンブリ言語から右および左シフトし、その結果を出力しています。最初にSHL命令を用いてa変数を左に2回シフトしています。ビット列の性質から、左に1回シフトするということは2で乗算することに等しいため、結果としてaの値は400となります。次にb変数をSHR命令で右に3回シフトしています。右に1回シフトすると言うということは2で除算することに等しいため、bの値は5となります。

 c変数とd変数の値はそれぞれ負数になっているため、符号用の最上位ビットが1となっています。このような符号付きの値に対しては算術シフトと論理シフトで結果が異なります。算術シフトを行った場合は最上位ビットが維持されたまま右にシフトされるため符号が残されますが、論理シフトを行った場合は最上位ビットを含めてビット列全体をシフトし、最上位ビットが0で埋められるため符号が反転します。

 シフト命令の場合、シフトによってビット列から漏れた値は破棄されてしまいます。例えば、右に1回シフトするとシフトの場合は最下位のビットがビット列から漏れ出して破棄され、最上位ビットが0で埋められます。これに対して、シフトによって漏れたビットを反対側のビットに埋める計算があります。これを回転と呼びます。

 左シフトの回転はROL命令を使い、右シフトの回転はROR命令を使います。

rol dest, count
ror dest, count

 destには回転対象となるディスティネーション・オペランド、countには左右に何回シフトするのかを指定するカウント・オペランドです。オペランドの指定方法は基本的にシフト演算と変わりません。これらの命令ではシフトによって漏れ出したビットを反対側のビットに設定します。つまり、ビット列が回転します。

sample09
#include <stdio.h>

int main() {
    int a = 0xFF00FF00, b = 0x0F0F0F0F;

    __asm {
        rol a, 4;
        ror b, 4;
    }

    printf("a=%X, b=%X\n", a, b);
    return 0;
}
実行結果
a=F00FF00F, b=F0F0F0F0

 sample09は、int型の変数abを用意し、これらの変数を左右に回転させています。a変数は初期値で0xFF00FF00という値を持っていますが、これを左に4回シフトすると、最上位の4ビットが最下位に回転によって移動するため0xF00FF00Fという結果が得られています。同様に、0x0F0F0F0Fという値を持つb変数を右に4回シフトすると、最下位4ビットが回転によって移動するため0xF0F0F0F0という結果が得られています。

 この他に、回転命令には漏れ出した1ビットをキャリーフラグと呼ばれる特殊なレジスタに保存し、キャリーフラグを含めて回転を行う命令があります。キャリー通過左回転はRCL、キャリー通過右回転はRCR命令を使います。RCLRCRも、キャリーフラグ1ビットが回転するビット列に加わるという点を除いて、基本的に通常の回転と同じです。

おわりに

 本稿では、インラインアセンブラ内で基本的な計算を行う方法を解説してきました。これらの計算はC言語を用いれば簡単に行うことができるため、特別な事情がない限りプログラム内で使われることはないと考えられます。しかし、プログラムの事情からレジスタを直接操作しなければならない場合や、ゲームなどで描画用のイメージをより効率的に計算させる手段としてインラインアセンブラが使われるというシナリオは現実的に考えられるものであります。

 今後、より高度なアセンブリ言語の命令を学習するためにも、こうした基本的な計算命令は必要な知識となるでしょう。また、こうした基礎的な計算プログラムを通してアセンブリ言語の面白さを体感していただければ幸いです。複雑なプログラムをアセンブリ言語で記述した場合は可読性も低くなり、初心者にとっては学ぶ意欲が失われてしまうかと思います。プロフェッショナルでも、他人が書いたアセンブリ言語のプログラムを読みたいと思うものではありません。

 数行の計算プログラムでは実用的なプログラムを作ることはできませんが、こうした単純な命令からアセンブリ言語を少しずつ学んでいただければ、高度で複雑といわれているアセンブリ言語も難なく身に付けることができます。

参考資料

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
インラインアセンブラで学ぶアセンブリ言語連載記事一覧

もっと読む

この記事の著者

赤坂 玲音(アカサカ レオン)

平成13年度「全国高校生・専門学校生プログラミングコンテスト 高校生プログラミングの部」にて最優秀賞を受賞。2005 年度~ Microsoft Most Variable Professional Visual Developer - Visual C++。プログラミング入門サイト WisdomSoft の管理人。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/442 2006/08/08 20:48

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー