×[PR]この広告は3ヶ月以上更新がないため表示されています。
ホームページを更新後24時間以内に表示されなくなります。
![分かりやす〜い
コンピュータ技術情報](/contents/271/366/624.mime1)
■ 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
例によって実行結果はいつもと同じです。(^^;
以前作成したプログラムと実行結果が同じなのでつまらないか
もしれませんが、コンペア命令とジャンプ命令を使用している
事を考えると少しはへー、と思いますよね?
(こればっかり・・・)
次回は関数って何?という説明をしたいと思います。