分かりやす〜い
コンピュータ技術情報

TOPに戻る
▼Processor
簡単なアセンブラ言語
┣ 高級、低級の意味と
┃ レジスタ、mov、add

┣ 計算を行う
┣ プログラムを作る
┣ loop命令
┣ Indexレジスタの
┃ 役割とレジスタの
┃ 大きさ
┣ リピートプリフィック
┃ ス・ストリング命令

┣ inc・dec命令
┣ MMX技術
┣ MMXレジスタ
┣ MMX・SIMD・SSE
┣ CMP・JMP命令
┣ 関数とパラメータ
┣ スタック領域
┣ ESPレジスタ
┣ セグメント・
┃   call・ret

┣ コードファイルを作る
┣ コードファイル説明1
┣ コードファイル説明2
┗ マイクロコード

Copyright(C) 2001-2002.ugpop. All rights reserved.




■デジタル用語辞典:
■ inc・dec命令 ■

前回はアセンブラ言語を使用して、データをコピーするプログ
ラムを効率の良い方法で作成してみました。
ちょっと復習してみましょう。


LOOP1:
    mov al, [esi]  ;←データbをレジスタに移動
    mov [edi], al  ;←レジスタの中身をデータaに移動
    add edi, 1    ;←データbのアドレスを1加算
    add esi, 1    ;←データaのアドレスを1加算
    loop LOOP1    ;←ecxの回数分ループ

というプログラムが全て

    rep movsb

の1行に収まってしまう事の説明をしました。
そして、インテルプロセッサではこの「rep」命令等の事を総
称して「リピートプリフィックス命令」と呼び、「movsb」等
の命令の事を総称して「ストリング命令」と呼ぶ事の説明も行
ないました。

また、このように1つの命令で複数の仕事を行ってくれるプロ
セッサの事を「CISC(Complex Instruction Set Computer)複合
命令セットコンピュータ」と呼び、それに対して単純な命令だ
けしか持たないプロセッサの事を「RISC(Reduced Instruction
Set Computer)単純命令セットコンピュータ」と呼ぶ事の説明
も行ないました。

今回は、データをコピーするのではなく、データを加工するプ
ログラムを作ってみたいと思います。

データを加工する、というのは、画像処理や音声処理等の、マ
ルチメディア処理に用いられる事が多いのですが、ここではそ
のような難しい物は作りません。

少し前に作成したプログラムですが、"loop2.c"を流用してし
まいます。
"loop2.c"はデータbをデータaにコピーするプログラムでした。

まずは"loop2.c"をもう1度見てみましょう。

─↓loop2.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, [esi]  ;←データbをレジスタに移動
    mov [edi], al  ;←レジスタの中身をデータaに移動
    add edi, 1    ;←データaのアドレスを1加算
    add esi, 1    ;←データbのアドレスを1加算
    loop LOOP1    ;←ecxの回数分ループ
  }
}

─↑ここまで──────────────────────

上のプログラムのサブ関数は、データbをデータaにコピーする
だけのものです。

今回は、データaにデータbを「加算する」プログラムを作って
みましょう。


まず、"loop2.c"をコピー&貼り付けして、新しくできたファ
イルを"inc.c"という名前に変更しておいてください。

今回も変更点はそれほど多くありません。

まず、メイン関数はそのままです。変更しません。

次にサブ関数ですが、以下の様に変更してください。
変更部分にはコメントを付けておきます。

─↓ここから──────────────────────

  _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に移動
    add edi, 1
    add esi, 1
    loop LOOP1
  }

─↑ここまで──────────────────────

今回はデータを移動するのではなくて、加工しなければならな
い為、前回のプログラムとは微妙に動作が変わっています。

まずデータaとデータbをそれぞれレジスタに移動しています。

 mov al, [edi] ;←データaをレジスタalに移動
 mov bl, [esi] ;←データbをレジスタblに移動


そしてレジスタの値を加算します。

 add al, bl   ;←データa+データbをalレジスタに格納


最後に答えをデータaに移動します。

 mov [edi], al ;←alレジスタをデータaに移動


変更部分はたったこれだけです。

以上で、データbをデータaに加算するプログラムは作成完了で
す。


●       ●
● ビルドする ●
●       ●

コマンドプロンプト上で以下の様に入力してください。

sc inc.c -j

これでビルドされ、"inc.exe"という実行ファイルが作成され
るはずです。



●      ●
● 実行する ●
●      ●

コマンドプロンプト上で以下の様に入力してください。

inc.exe

これでサブ関数を呼び出す前のデータaとデータbの中身と、サ
ブ関数を呼び出した後のデータaとデータbの中身がそれぞれ表
示されたはずです。

サブ関数を呼び出した後はデータbがデータaに加算されている
のが分かりますね。

実行結果:
a[0]:0 b[0]:10
a[1]:1 b[1]:11
a[2]:2 b[2]:12
a[3]:3 b[3]:13
a[4]:4 b[4]:14
a[5]:5 b[5]:15
a[6]:6 b[6]:16
a[7]:7 b[7]:17
sub routine called
a[0]:10 b[0]:10
a[1]:12 b[1]:11
a[2]:14 b[2]:12
a[3]:16 b[3]:13
a[4]:18 b[4]:14
a[5]:20 b[5]:15
a[6]:22 b[6]:16
a[7]:24 b[7]:17



では、このプログラムをほんの少しだけ効率の良い物に変更し
てみましょう。

はじめに言っておきますが、計算命令(足し算、引き算、掛け
算、割り算等)の命令に対して、ストリング・リピートプリフ
ィックス命令を使用する事は出来ません。

ですので、前回出てきたように「rep」命令を使用して効率の
良い物に変更しようとしても、それは出来ないのです。

では、どうすれば良いのでしょうか?

ここではアドレスに「1を加算する」という処理に注目しまし
ょう。


    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に移動
●これ→add edi, 1    ;←データbのアドレスを1加算
●これ→add esi, 1    ;←データaのアドレスを1加算
    loop LOOP1    ;←ecxの回数分ループ


これは、実はもっと効率よくする事が出来ます。
「1を加算する」という処理を以下の様に書き換えてみてくだ
さい。

    inc edi    ;←データbのアドレスを1加算
    inc esi    ;←データaのアドレスを1加算

「add」命令の部分を「inc」(インクリメント)という命令に
書き換えました。
これをビルド&実行してみてください。
結果は先ほどと同じになるはずです。

なんだ、さっきと同じ結果か。
しかもさっきと同じ2行じゃないか。

と思う方も多いかもしれません。
しかし、「1を加算する」という処理を行なわせるには、こち
らの命令を使用した方が速く処理が行なわれるのです。

これはインテルプロセッサの仕様です。

「1を加算する時は」add命令よりinc命令を使用した方が処理
が速いのです。

そして「inc」命令はループ処理の中で使われる事が多いです。
今回作ったプログラムは8回ループするだけですが、これが大
量にループする処理だとすると結構な違いになってきます。

また、「1減算する」命令としては「dec」(デクリメント)
という命令があります。
こちらも「sub edi, 1」等とするよりは「dec edi」とした方
が処理が速くなります。


今回は「inc」命令と「dec」命令を紹介しました。


実はこのプログラム、もっと速くする事が出来ます。
しかも「inc」命令を使用した時のように、「ちょっとだけ」
速くなる、という程度の効果ではありません。

ヒントが少しだけ出ているので既に分かっている方も多いかも
しれませんが、次回に説明します。



▲このページの上へ

▲このページの上へ

←前に戻る    ▲このページの上へ    続きを読む→

▲このページの上へ