3.ループをやってみる
作成日:2004年11月29日
更新日:2005年04月07日
cmp命令の書式が間違っていたのを修正
nameタグを追加



プログラムを作るときに,ループ命令なしでプログラムを作る人はまずいないでしょう.極稀に小規模なプログラムの場合はループなしで作りますが.

で,まあ今回はそんなループについてです.サンプルプログラムとして,1からnまでの総和演算を行うプログラムを作りました.


#include <stdio.h>

int sum_int (int n) {
    int c;
    __asm {
        mov    eax, 0      // eaxレジスタをゼロクリア
        mov    ecx, n      // ecxレジスタにnの値をコピー
LOOP1:
        add    eax, ecx    // eaxレジスタとecxレジスタの値を加算してeaxレジスタに保存
        loop   LOOP1       // ecxレジスタがゼロになるまでループ
        mov    c, eax      // ループ終了時にeaxレジスタの値をcにコピー

    }
    return c;
}

int main (void) {
    printf ("sum(10) = %d\n", sum_int (10));
    return 0;
}

実行結果は次のようになります.


sum(10) = 55


loop命令は,ecxレジスタをカウンタとして使用し,ecxレジスタの値だけループを実行します.

・・・と,書いてみたものの,正確な表現ではありませんでした.正確には,loop命令を実行するたびに,ecxレジスタの値をデクリメント,すなわち1だけ減算して,ecxレジスタの値が0でなかったら第1オペランドのレジスタへジャンプします.

サンプルプログラムでは,eaxレジスタにecxレジスタの値をループ毎に保存します.よって,sum_int (10)ならば10+9+8+・・・・+1といった感じに加算を繰り返していきます.通常の総和演算が値の小さい順に加算するのとは逆になっていますね.ちょっとした修正で小さい順に加算出来ます.暇な人プログラムが得意な人は試してみてください.


さらに,loop命令ではZFフラグの状態によってループ判定を行う命令が用意されています.このZFフラグは,算術命令やcmp命令の結果により変化します.

詳しく説明する前に,具体例を示してみましょう.次のサンプルはaからbまでの総和演算を行うプログラムです.


#include <stdio.h>

int sum_int2 (int a, int b) {
    int c;
    __asm {
        mov    eax, 0      // eaxレジスタをゼロクリア
        mov    ecx, b      // ecxレジスタにbの値をコピー
LOOP1:
        add    eax, ecx    // eaxレジスタとecxレジスタの値を加算してeaxレジスタに保存
        cmp    ecx, a      // ecxレジスタとaの値を比較
        loopnz LOOP1       // ecxレジスタがゼロでなく,かつZFが0ならループ
        mov    c, eax      // ループ終了時にeaxレジスタの値をcにコピー

    }
    return c;
}

int main (void) {
	printf ("sum(3, 10) = %d\n", sum_int2 (3, 10));
    return 0;
}

実行結果は次のようになります.


sum(3, 10) = 52


結果が合っているかどうかは電卓片手に計算してください.

さて,このcmp命令ですが,名前の通り第1オペランドと第2オペランドの値を比較します.比較した結果は,EFLAGSレジスタ内の各ステータスフラグ(CF,OF,SF,ZF,PF)に保存されます.このステータスフラグは,例えば比較結果が等しい場合,すなわち第1オペランドと第2オペランドの減算結果が0の場合ZFフラグ(ゼロフラグ)が1(True)となり,それ以外はZFフラグが0(False)となります.

で,サンプルプログラムですが,まずecxレジスタに初期値を入力するのは先程と同じです.違いですが,add命令の後にcmp命令を実行し,ecxレジスタとaの値を比較しています.比較した結果,ecxレジスタとaの値が一致(ecxレジスタの値-aの値=0)した場合はZFフラグが1(True)となり,loopnz命令のループ終了条件に当てはまるので,その段階でループ終了です.

ZFフラグを使用した条件付ループはloopnz命令の他にloopz命令がありますが,どちらを使うか迷う場合が多々あります.loopz命令は,ZFフラグが1(True)の場合にループ続行,loopnz命令はZFフラグが0(False)の場合にループ続行です.注意してください.

なお,loopz命令とloope命令,loopnz命令とloopne命令はそれぞれまったく同じ命令です.ZFフラグがTrueとなる条件が二つの値を減算した結果ゼロとなる,すなわち二つの値が同じである,ということですから.


今回新たに使用した命令は次のとおりです.

LOOP/LOOPcc - ECXカウンターによるループ
ECXまたはCXレジスタをカウンタとして使用して,ループ操作を実行する.LOOP命令が実行されるたびに,カウント・レジスタが1デクリメントされ,次に0かどうかチェックされる.カウントが0である場合はループは終了し,LOOP命令の次の命令からプログラムの実行が継続される.カウントが0でない場合は,第1オペランドへのnearジャンプが行われる.
命令説明
LOOP rel8カウントをデクリメントし,カウント≠ 0 の場合shortジャンプ
LOOPE rel8カウントをデクリメントし,カウント≠ 0 かつZF=1 の場合shortジャンプ
LOOPZ rel8カウントをデクリメントし,カウント≠ 0 かつZF=1 の場合shortジャンプ
LOOPNE rel8カウントをデクリメントし,カウント≠ 0 かつZF=0 の場合shortジャンプ
LOOPNZ rel8カウントをデクリメントし,カウント≠ 0 かつZF=0 の場合shortジャンプ

CMP - 2つのオペランドを比較
第1オペランドと第2オペランドを比較し,結果に従ってEFLAGSレジスタ内のステータスフラグをセットする.比較は,第1オペランドから第2オペランドを引き,次にSUB命令の場合と同様にステータスフラグをセットして行われる.オペランドとして即値を使用した場合は,そのオペランドは第1オペランドの長さに符号拡張される.
命令説明
CMP AL, imm8ALとimm8を比較
CMP AX, imm16AXとimm16を比較
CMP EAX, imm32EAXとimm32を比較
CMP r/m8, imm8r/m8とimm8を比較
CMP r/m16, imm16r/m16とimm16を比較
CMP r/m32,imm32r/m32とimm32を比較
CMP r/m16,imm8r/m16とimm8を比較
CMP r/m32,imm8r/m32とimm8を比較
CMP r/m8,r8r/m8とr8を比較
CMP r/m16,r16r/m16とr16を比較
CMP r/m32,r32r/m32とr32を比較
CMP r8,r/m8r8とr/m8を比較
CMP r16,r/m16r16とr/m16を比較
CMP r32,r/m32r32とr/m32を比較


また,ステータスフラグは次のような仕様になっています.

名前 意味
Carry Flag
(キャリーフラグ:CF)
算術演算において,結果の最上位ビットでキャリーまたはボローが生じた場合にセット.
生じなかった場合にはクリア.
符号なし整数演算でのオーバーフロー状態を示す.
Overflow Flag
(オーバーフローフラグ:OF)
整数の演算結果が大きすぎる正の数であるか小さすぎる負の数で,
デスティネーションオペランドに収まらない場合(符号ビットは除く)にセット.
そうでない場合にクリア.
符号付き整数(2の補数)演算でのオーバーフロー状態を示す.
Sign Flag
(サインフラグ:SF)
符号付き整数の符号ビットである結果の最上位ビットと同じ値にセット.
0は正の値,1は負の値.
Zero Flag
(ゼロフラグ:ZF)
結果がゼロの場合にセット.ゼロでない場合にクリア.
Adjust Flag
(調整フラグ:AF)
2進化10進(BCD)算術演算において,結果のビット3にキャリーまたはボローが生じた場合にセット.
生じなかった場合にはクリア.
Parity Flag
(パリティフラグ:PF)
結果の最下位バイトに値1のビットが偶数個含まれている場合にセット.
奇数個の場合にはクリア.


次回は条件付ジャンプ命令を使った条件分岐に行きましょう.



もどる

楽天モバイル[UNLIMITが今なら1円] ECナビでポインと Yahoo 楽天 LINEがデータ消費ゼロで月額500円〜!


無料ホームページ 無料のクレジットカード 海外格安航空券 解約手数料0円【あしたでんき】 海外旅行保険が無料! 海外ホテル