×[PR]この広告は3ヶ月以上更新がないため表示されています。
ホームページを更新後24時間以内に表示されなくなります。
 
■ cmp・jmp命令 ■
前回まではMMX技術について説明しました。
MMX技術を導入する事により複数のデータをいっぺんに処理す
る事が出来るのでした。
今回はプロセッサに「比較」を行なわせる為の命令を紹介しま
す。
インテルプロセッサに比較を行なわせる為には「cmp(Compare)」
という命令を使用します。
例えば、レジスタ「EAX」と「EBX」を比較しなさい、というの
をアセンブラ言語で表すと、「cmp eax, ebx」となります。
では、レジスタ「EAX」を「0」と比較しなさい、というのを
アセンブラ言語で表すとどうなるでしょうか?
答えは簡単ですね。
cmp eax, 0
です。
こんな感じでさまざまな比較を行なう事が出来ます。
ところで比較したは良いのですが、結果はどうなるのでしょう
か?
比較しっぱなしではどうにもならないので、比較結果を反映さ
せなければなりません。
その結果を反映させる為の場所として「EFLAGS」というレジス
タが使われます。
このレジスタはプロセッサの状態を表すさまざまなフラグが集
まっているレジスタです。
そして、このレジスタの第7ビットに「ゼロフラグ」という物
が存在しています。
「ゼロフラグ」というのは、1ビットの大きさしかありません。
比較した結果が「真」であればゼロフラグに「1」が格納され、
比較した結果が「偽」であればゼロフラグに「0」が格納され
ます。
例えば、「cmp eax, 0」という命令があるとして、「eax」レ
ジスタに格納されている値と「0」が同じ値(真)の時はゼロ
フラグに「1」が格納されます。
「eax」レジスタに格納されている値が「0」と異なる値(偽)
の時はゼロフラグには「0」が格納される事になります。
以下にEFLAGSレジスタのイメージを示します。
     EFLAGSレジスタ
┌──────────┬┬───┐
│          ││   │
└──────────┴┴───┘
            ↑
          7ビット目
          ゼロフラグ
それでは早速実践を行ってみましょう。
今回も以前作成したプログラム「inc.c」を流用してしまいま
す。
まずは「inc.c」をコピー&貼り付けして、新しく出来たファ
イルを「cmpjmp.c」というファイル名に変更しておきましょう。
今回はこの「cmpjmp.c」に変更を加えていきます。
まずは変更前のプログラムを見てみましょう。
─↓ここから──────────────────────
#include <stdio.h>
int main(void);
int sub(char*, char*);
int main()
{
  int i;
  char a[8]={ 0, 1, 2, 3, 4, 5, 6, 7};
  char b[8]={10,11,12,13,14,15,16,17};
  
  for(i=0;i<8;i++)
  {
    printf("a[%d]:%d b[%d]:%d\n", i, a[i], i, b[i]);
  }
  sub(&a[0], &b[0]);
  for(i=0;i<8;i++)
  {
    printf("a[%d]:%d b[%d]:%d\n", i, a[i], i, b[i]);
  }
  return 0;
}
int sub(char* a, char* b)
{
  printf("sub routine called\n");
  _asm{
    mov edi, a
    mov esi, b
    mov ecx, 8
LOOP1:
    mov al, [edi]  ;←データaをレジスタalに移動
    mov bl, [esi]  ;←データbをレジスタblに移動
    add al, bl    ;←データa+データbをalレジスタに格納
    mov [edi], al  ;←alレジスタをデータaに移動
    inc edi
    inc esi
    loop LOOP1
  }
}
─↑ここまで──────────────────────
このプログラムはデータbをデータaに加算するプログラムでし
た。
サブ関数ではデータbをデータaに1つずつ加算していく為に、
ループ命令を使用して8回繰り返し加算処理を行なっているの
でした。
(もう何度も見たプログラムなので飽きてきた方もいらっしゃ
るかもしれません。(^^;)
今回はループ命令を使用するのではなく、「cmp」命令を使用
して8回繰り返し処理を行なわせるように変更したいと思いま
す。
今回もメイン関数に変更はありません。サブ関数だけ変更しま
す。
変更後のサブ関数を以下に示します。
(変更部分にはコメントを付けておきます。)
─↓ここから──────────────────────
int sub(char* a, char* b)
{
  printf("sub routine called\n");
  _asm{
    mov edi, a
    mov esi, b
    mov ecx, 8
LOOP1:
    mov al, [edi]  ;←データaをレジスタalに移動
    mov bl, [esi]  ;←データbをレジスタblに移動
    add al, bl    ;←データa+データbをalレジスタに格納
    mov [edi], al  ;←alレジスタをデータaに移動
    inc edi
    inc esi
    dec ecx      ;●←変更した。ecx-1をecxに格納
    cmp ecx, 0    ;●←変更した。ecxが0か?
    jne LOOP1    ;●←変更した。ecx=0でなければLOOP1へジャンプ
  }
}
─↑ここまで──────────────────────
最後の3行が変わっただけです。
変更前は
loop LOOP1    ;←ecxの回数分ループ
というように「loop」命令を使用していましたが、変更後は
dec ecx      ;ecx-1をecxに格納
cmp ecx, 0    ;ecxが0か?
jne LOOP1    ;ecx=0でなければLOOP1へジャンプ
となっています。
ループ命令を忘れてしまった方の為に再度簡単に説明しますが、
ループ命令というのは「ecx」レジスタの回数分処理を繰り返
す為の命令です。
カウンタである「ecx」レジスタが0になるまで「ecx」レジス
タを1づつ減算しながらラベルに制御を移行してくれるのでし
た。
以下にループ処理のイメージを示します。
    ECX に8を格納      ┌───┐
      ├───────→ ECX│ 8 │
LOOP1:   │          └───┘
 ┌───→│
 │    │
 │    │
 │  何らかの処理
 ↑    │
 │    ↓
 │  loop LOOP1        ┌───┐
 │    │←─────── ECX│ 8 │
 │    ↓何回繰り返すか参照 └───┘
 │    │          ┌───┐
 │    ├───────→ ECX│ 7 │
 └────┘ECX を1減算    └───┘
以上が変更前のループ命令の説明です。
では変更後の命令の説明をします。
変更後の命令は3つあります。
dec ecx      ;ecx-1をecxに格納
cmp ecx, 0    ;ecxが0か?
jne LOOP1    ;ecx=0でなければLOOP1へジャンプ
最初の1つめは「dec ecx」という命令です。
これはインクリメント・デクリメントのプログラムを作成した
時に説明しましたが、「dec」命令というのは「1減算しなさ
い」という命令でした。
ただ単に1減算されるだけなので「sub ecx, 1」と書いても良
いのですが、インテルプロセッサの仕様により処理が速く行な
われる為、デクリメント命令を使用しています。
次に「cmp ecx, 0」という命令を使用しています。
これは先ほど説明したように、「ecxと0を比較しなさい」とい
う命令です。
比較した結果は「EFLAGS」レジスタに格納されます。
             EFLAGSレジスタ
      結果格納 ┌─────────┐
cmp ecx, 0─────→│         │
           └─────────┘
最後に「jne LOOP1」という命令があります。
これは初めて出てきた命令です。
jne LOOP1    ;ecx=0でなければLOOP1へジャンプ
比較しただけではいけないので、比較した結果により制御を移
行させる為の命令です。
「jne」(Jump Not Equal)という命令は比較した結果が「等し
くなければ」ジャンプしなさい、という意味になります。
つまり「ecx Not= 0」の時、LOOP1というラベルに制御が移行さ
れます。
このジャンプ命令を実行する時、インテルプロセッサ内部では
「EFLAGS」レジスタのゼロフラグを参照する処理が自動的に行
なわれます。
             EFLAGSレジスタ
       参照  ┌─────────┐
jne LOOP1 ←─────┤         │
           └─────────┘
ジャンプ命令はこの他にもさまざまな物があります。
代表的な物だけ以下に示します。
JE(Jump Equal):等しい時ジャンプする
例)
┌──────┬───────────────────┐
│cmp eax, ebx│                   │
│je LABEL  │eax=ebxの時LABELにジャンプする    │
└──────┴───────────────────┘
JNE(Jump Not Equal):等しくない時ジャンプする
例)
┌──────┬───────────────────┐
│cmp eax, ebx│                   │
│jne LABEL  │eax NOT= ebxの時LABELにジャンプする │
└──────┴───────────────────┘
JA(Jump Above):より大きい時ジャンプする
例)
┌──────┬───────────────────┐
│cmp eax, ebx│                   │
│ja LABEL  │eax > ebxの時LABELにジャンプする   │
└──────┴───────────────────┘
JAE(Jump Above Equal):以上の時ジャンプする
例)
┌──────┬───────────────────┐
│cmp eax, ebx│                   │
│jae LABEL  │eax >= ebxの時LABELにジャンプする  │
└──────┴───────────────────┘
JL(Jump Low):より小さい時ジャンプする
例)
┌──────┬───────────────────┐
│cmp eax, ebx│                   │
│jl LABEL  │eax < ebxの時LABELにジャンプする   │
└──────┴───────────────────┘
JLE(Jump Low Equal):以下の時ジャンプする
例)
┌──────┬───────────────────┐
│cmp eax, ebx│                   │
│jle LABEL  │eax <= ebxの時LABELにジャンプする  │
└──────┴───────────────────┘
以上は「ある条件に合った時」にジャンプする為の命令ですが、
無条件にジャンプする命令も当然あります。
JMP(Jump):無条件ジャンプ
例)
┌──────┬───────────────────┐
│jmp LABEL  │無条件にLABELにジャンプする      │
└──────┴───────────────────┘
●       ●
● ビルドする ●
●       ●
コマンドプロンプトを起動した後は必ず環境設定のバッチファ
イルを実行するようにしましょう。
env.bat
上記バッチファイル実行後、コマンドプロンプト上で以下の様
に入力してください。
sc cmpjmp.c -j
これでビルドされ、"cmpjmp.exe"という実行ファイルが作成さ
れるはずです。
●      ●
● 実行する ●
●      ●
コマンドプロンプト上で以下の様に入力してください。
cmpjmp.exe
例によって実行結果はいつもと同じです。(^^;
以前作成したプログラムと実行結果が同じなのでつまらないか
もしれませんが、コンペア命令とジャンプ命令を使用している
事を考えると少しはへー、と思いますよね?
(こればっかり・・・)
次回は関数って何?という説明をしたいと思います。