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

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.




■デジタル用語辞典:
■ MMX・SIMD・SSE ■

前回はMMX(Multi Media eXtension)技術が実際には浮動小数点
レジスタを使用する物である事を説明し、その動作概念を見て
みました。
MMXレジスタとしては64ビットの大きさを使用でき、いっき
に8バイトのデータを処理する事ができるのでした。

今回はMMX技術を使って実際にプログラムを作ってみることに
します。

以前に「inc.c」という、データbをデータaに加算するプログ
ラムを作成しました。
これにMMX技術を導入します。

まずは「inc.c」をもう1度見てみましょう。

─↓inc.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
  }
}
─↑inc.c───────────────────────


サブ関数はデータaにデータbを加算する処理を行なうのでした。

この「inc.c」にMMX技術を適用して、処理がもっと速く行なわ
れる様に変更してみます。

「inc.c」をコピー&貼り付けして、新しく出来たファイルを
「mmx.c」というファイル名に変更しておきましょう。
今回は「mmx.c」に変更を加えていきます。

まずメイン関数ですが、これは全く変更の必要はありません。

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

─↓ここから──────────────────────
int sub(char* a, char* b)
{
  printf("Sub Routine Called\n");
  _asm{
    mov edi, a
    mov esi, b
    movq mm0, [edi]  ;データaをmm0レジスタに移動
    movq mm1, [esi]  ;データbをmm1レジスタに移動
    paddd mm0, mm1  ;データa+データbをmm0レジスタに格納
    movq [edi], mm0  ;mm0レジスタをデータaに移動
    emms      ;MMX後処理
  }
}
─↑ここまで──────────────────────


まず、変更部分の最初の命令を見てみましょう。

movq mm0, [edi]  ;データaをmm0レジスタに移動

新しいレジスタが出てきました。
「mm0」レジスタはMMX技術を使用する為のレジスタで64ビッ
トの大きさがあります。(実際には浮動小数点レジスタです。)

また、新しい命令として「movq」という命令が出てきました。
これは64ビットの大きさのデータを移動する為の命令です。
32ビット以下のデータを移動する為の命令は全て「mov」で
良かったのですが、64ビットのデータを移動する時は「movq」
を使用しなければなりません。

つまり、この「movq mm0, [esi]」という命令は、「メモリか
らレジスタへ64ビット分のデータを移動しなさい」という意
味になります。
以下にこの命令の処理イメージを示します。


1.データaからレジスタへデータを移動する。

movq mm0, [edi]  ;データaをmm0レジスタに移動

        mm0レジスタ               メモリ(データa)
┌────────────────────────┐        ┌───┐
│  7  6  5  4  3  2  1  0│←───────┤  0│
└────────────────────────┘ ┌──────┤  1│
   ↑  ↑  ↑  ↑  ↑  ↑  ↑     │┌─────┤  2│
   │  │  │  │  │  │  └─────┘│┌────┤  3│
   │  │  │  │  │  └─────────┘│┌───┤  4│
   │  │  │  │  └─────────────┘│┌──┤  5│
   │  │  │  └─────────────────┘│┌─┤  6│
   │  │  └─────────────────────┘│┌┤  7│
   │  └─────────────────────────┘│
   └─────────────────────────────┘


レジスタの大きさが64ビットなので一気に8バイトもデータ
を移動しています。

次に同じ様にして今度はデータbをレジスタに移動します。


2.データbからレジスタへデータを移動する。

movq mm1, [esi]  ;データbをmm1レジスタに移動

        mm1レジスタ               メモリ(データb)
┌────────────────────────┐        ┌───┐
│ 17 16 15 14 13 12 11 10│←───────┤ 10│
└────────────────────────┘ ┌──────┤ 11│
   ↑  ↑  ↑  ↑  ↑  ↑  ↑     │┌─────┤ 12│
   │  │  │  │  │  │  └─────┘│┌────┤ 13│
   │  │  │  │  │  └─────────┘│┌───┤ 14│
   │  │  │  │  └─────────────┘│┌──┤ 15│
   │  │  │  └─────────────────┘│┌─┤ 16│
   │  │  └─────────────────────┘│┌┤ 17│
   │  └─────────────────────────┘│
   └─────────────────────────────┘


「mm1」レジスタも「mm0」レジスタと同じ64ビットの大きさ
です。
レジスタの大きさが64ビットなので一気に8バイトのデータ
を移動しています。


メモリからレジスタへデータを移動出来たので、今度は加算す
る処理を行ないます。

paddd mm0, mm1  ;データa+データbをmm0レジスタに格納

新たに「paddd」という命令が出てきました。
これは64ビットの大きさのデータをMMX技術を使用して足し
算しなさい、という命令です。
足し算した結果は「mm0」レジスタに格納されます。

以下にMMX命令での足し算のイメージを示します。


3.データaにデータbの値を加算する。

paddd mm0, mm1  ;データa+データbをmm0レジスタに格納

   ┌────────────────────────┐
mm0│ 24 22 20 18 16 14 12 10│
   └────────────────────────┘
      ↑  ↑  ↑  ↑  ↑  ↑  ↑  ↑
      +  +  +  +  +  +  +  +
      │  │  │  │  │  │  │  │
   ┌──┴──┴──┴──┴──┴──┴──┴──┴┐
mm1│ 17 16 15 14 13 12 11 10│
   └────────────────────────┘


MMX技術により一気に8個ものデータが足し算されています。
たった1つの命令でこれだけの事を行ってくれるのです。


足し算が行なわれたので答えをメモリに格納してあげます。
これも先ほどと同じ「movq」命令を使用します。


4.レジスタからデータaへデータを移動する。

movq [edi], mm0  ;mm0レジスタをデータaに移動

        mm0レジスタ                メモリ(データa)
┌────────────────────────┐         ┌───┐
│ 24 22 20 18 16 14 12 10├────────→│ 10│
└──┬──┬──┬──┬──┬──┬──┬───┘ ┌──────→│ 12│
   │  │  │  │  │  │  │     │┌─────→│ 14│
   │  │  │  │  │  │  └─────┘│┌────→│ 16│
   │  │  │  │  │  └─────────┘│┌───→│ 18│
   │  │  │  │  └─────────────┘│┌──→│ 20│
   │  │  │  └─────────────────┘│┌─→│ 22│
   │  │  └─────────────────────┘│┌→│ 24│
   │  └─────────────────────────┘│
   └─────────────────────────────┘


これでデータbをデータaに加算した結果がメモリに格納されま
した。

これでこのプログラムの処理終了のように思うかもしれません
が、最後にちょっと特別な事を行う必要があります。

emms      ;MMX後処理

「emms」という命令があります。
コメントには「MMX後処理」とあります。

これは何をしているのかと言うと、先ほど説明したようにMMX
で使用しているレジスタは実際には浮動小数点レジスタです。

通常、プログラムが動作している時はこのレジスタは「浮動小
数点レジスタ」として動作していますが、プロセッサはMMX命
令を見つけると、このレジスタを「MMXレジスタ」として解釈
するようになります。

つまり80ビットの大きさがある「浮動小数点」レジスタとし
てではなく、MMX命令用に動作する為に64ビットの大きさの
レジスタとして動作するようになるのです。

これをそのままにして放っておくと、MMX命令の動作が完了し
た後もずっとMMXレジスタとして動作してしまう事になり、本
当に「浮動小数点レジスタ」として動作させたい時に不具合が
起こる事になります。

その為、「emms」という命令を使用して「MMXの処理は終わり
ましたよ。」とプロセッサに教えてあげる必要があるのです。

「emms」という命令を見つけるとプロセッサは今まで「MMXレ
ジスタ」として動作していたのを、本来の「浮動小数点レジス
タ」として動作するようになります。


さて、プログラムを作り終わったらビルド&実行させてみまし
ょう。



●       ●
● ビルドする ●
●       ●

今まで書いてませんでしたが、コマンドプロンプトを起動した
後は必ず環境設定のバッチファイルを実行する必要があるので
忘れないようにしましょう。
「環境設定のバッチファイル」って何?という方はこちらをご
覧ください。

>>> academy002-039.htm

env.bat

上記バッチファイル実行後、コマンドプロンプト上で以下の様
に入力してください。

sc mmx.c -j

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



●      ●
● 実行する ●
●      ●

ここで気を付けて欲しいのですが、このプログラムはMMXに対
応したプロセッサでないと動作しません。
(MMX-Pentium以降であればOKです。)
対応していないプロセッサで無理やり実行させてもプロセッサ
の保護機能が動作し、強制終了されますのでご注意ください。

それでは実行してみましょう。
コマンドプロンプト上で以下の様に入力してください。

mmx.exe


これでデータ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



以前作成したプログラムと実行結果が同じなのでつまらないか
もしれませんが、MMX命令を使用している事を考えると少しは
へー、と思いますよね?!

しかも実行速度は単純計算で8倍になってます。
(体感は出来ませんが・・・。)



さて、MMX技術について説明してきました。

インテルが開発したMMX(Multi Media eXtension)技術の様に、
1つの命令を使用する事により、結果として複数のデータが出
来上がる技術の事を、「SIMD(Single Instruction Multiple
Data)」といいます。

SIMDというのはインテルプロセッサの用語ではなく、プロセッ
サ全般に関する用語です。

このSIMD技術(=MMX技術)ですが、ペンティアム3、ペンティア
ム4と新しいプロセッサが出る度に発展していきました。

まずペンティアム3により新しくレジスタが追加になりました。
これは浮動小数点レジスタと共用しない、全く新しいレジスタ
で「XMM0〜XMM7」というレジスタです。
このレジスタは128ビットの大きさがあります。
非常に大きなレジスタなので32ビットのデータも複数同時に
計算したりする事が可能になっています。

インテルではペンティアム3でのこの仕様変更の事を、SIMD技
術(=MMX技術)を「拡張した物」、という意味のStreaming SIMD
Expansion(SSE)と名付けました。

ペンティアム4ではMMX用のレジスタが増えたりする事はあり
ませんが、64ビットの浮動小数点演算を同時に2つ行なう事
が出来るようになっており、更に複数のSIMD用命令が追加され
ています。
こちらの技術にはSSE2という名前が付けられました。


さて、MMX技術について複数回に渡り説明してきました。
MMXの説明が分量としては多くなってしまいましたが、「簡単
なアセンブラ言語講座」として説明しなければならない事は、
あとほんのちょっとだけになりました。

次回はプロセッサに「比較」を行なわせ、その後は少し「関数」
について見てみようと思っています。



▲このページの上へ

▲このページの上へ

▲このページの上へ

▲このページの上へ

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