こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

インラインアセンブラについて

現在、インラインアセンブラを学んでいるものです。
開発環境は Visual Studio 2005 です。

プログラムは、unsigned char型の配列を3つ(src1, src2, ans)用意し、src1[i] + src2[i] = ans[i] というようにiを変数として、src1、src2それぞれの要素の加算を配列ansに格納する動作をインラインアセンブラで作成しています。 そして、高速化を目指しMMXを使用しているのですが、うまく動作しません。 動作結果としては配列ansに正しい解が代入されず、全ての要素が 205? になっています。
どこかに問題があるのですが、未熟な自分では間違いに気づくことができません。 ご指摘お願いいたします。 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LEN 64

int main(void){
unsigned char *src1, *src2, *ans;
unsigned char *t1, *t2, *t3;
int i, j;

src1 = (unsigned char *)malloc(sizeof(unsigned char) * LEN);
src2 = (unsigned char *)malloc(sizeof(unsigned char) * LEN);
ans = (unsigned char *)malloc(sizeof(unsigned char) * LEN);
t1 = src1;
t2 = src2;
t3 = ans;

i = LEN / 8;
for(j=0; j<LEN; j++){ //配列src1、src2の初期化
src1[j] = j;
src2[j] = j + 1;
}

_asm
{mov ecx, i
LOOPSTART:
movq mm0, src1
movq mm1, src2
paddusb mm0, mm1
movq ans, mm0
add src1, 8
add src2, 8
add ans, 8
loop LOOPSTART
emms
}

src1 = t1;
src2 = t2;
ans = t3;

for(i=0; i<LEN; i++)
printf("%d + %d = %d\n", src1[i], src2[i], ans[i]);

return 0;
}

投稿日時 - 2008-04-12 02:02:48

QNo.3941756

すぐに回答ほしいです

質問者が選んだベストアンサー

★アドバイス
>MovQ mm0, [eax][ecx * 8]
 ↑
 [ecx * 8]がオフセットになります。
 この記述を使えば
 max = (LEN / 8) - 1;
 
 _asm {
  mov   ecx, max
  mov   eax, t0
  mov   esi, t1
  mov   edi, t2
LOOPSTART:
  movq  mm1, [esi][ecx * 8]
  movq  mm2, [edi][ecx * 8]
  paddb  mm1, mm2
  movq  [eax][ecx * 8],mm1
  
  dec   ecx
  jns   LOOPSTART
  
  emms
 }
 と記述できますね。

投稿日時 - 2008-04-13 14:11:31

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(3)

ANo.2

★追記。
・ソースを良く見たらレジスタへのセットがおかしいようです。
 ans、src1、src2 のポインタを普通のレジスタにセットしてから
 movq で mm0、mm1、mm2 レジスタにセットします。
 下に全ソースを貼り付けて置きます。

#include <stdio.h>
#include <stdlib.h>

#define LEN (64)

// メイン関数
int main( void )
{
 unsigned char *ans, *src1, *src2;
 unsigned char *t0, *t1, *t2;
 int i, max;
 
 t0 = ans = (unsigned char *)malloc( sizeof(unsigned char) * LEN );
 t1 = src1 = (unsigned char *)malloc( sizeof(unsigned char) * LEN );
 t2 = src2 = (unsigned char *)malloc( sizeof(unsigned char) * LEN );
 
 // 配列ans、src1、src2の初期化
 for ( i = 0 ; i < LEN ; i++ ){
  ans[ i ] = (unsigned char)(0);
  src1[ i ] = (unsigned char)(i + 0);
  src2[ i ] = (unsigned char)(i + 1);
 }
 max = (LEN / 8);
 
 _asm {
  mov   ecx, max
LOOPSTART:
  // レジスタへの代入
  mov   eax, t0
  mov   esi, t1
  mov   edi, t2
  
  // MMX演算
  movq  mm1, [esi]
  movq  mm2, [edi]
  paddb  mm1, mm2
  movq  [eax],mm1
  
  // ポインタの加算
  add   t0, 8
  add   t1, 8
  add   t2, 8
  loop  LOOPSTART
  
  emms
 }
 for ( i = 0 ; i < LEN ; i++ ){
  printf( "%2d + %2d = %2d\n", src1[i], src2[i], ans[i] );
 }
 return 0;
}

投稿日時 - 2008-04-13 10:05:28

お礼

Oh-Orange様、返信ありがとうございました。

適切な回答と、参考ページまで紹介いただき、とても感謝しています。
おっしゃる通り、ポインタをレジスタに一度格納しておかなければなりませんでした。

結果、正常に動作するようプログラムを組むことが出来ました。
ありがとうございました。

最後にひとつ気になったことがあるのですが、
ご紹介にいただいた2番目の参考ページについてです。

アセンブラ部分を見ると

     MovQ mm0, [eax][ecx * 8]

このような表記があります。
ソースオペランドに引数を2つもっているのですが、これはどのような意味なのでしょう?

投稿日時 - 2008-04-13 12:18:36

ANo.1

★アドバイス
>高速化を目指しMMXを使用しているのですが、うまく動作しません。
 ↑
 paddusb命令は飽和演算を行います。
 多倍長演算には利用できないと思います。
 つまり加算・減算において 255 以上なら 255 になるためキャリーとして
 次の配列に演算を行わないため上手く動作しなくても正常だと思います。
・下に参考ページを載せておきますので MMX の paddusb 命令について
 もう一度動作の確認をして下さい。
 なお、使うなら paddb 命令の方ではないでしょうか?
 こちらならバイト区切りのオーバーフローとキャリーありで加算を行います。

参考ページ:
http://hp.vector.co.jp/authors/VA014520/asmhsp/chap8.html→『MMX命令による画像処理』
http://www7b.biglobe.ne.jp/robe/pf/pf009.html→『第九報:MMX 最適化』
http://www9.ocn.ne.jp/~mkisa/asm.htm→『インラインアセンブリ』

投稿日時 - 2008-04-13 09:08:51

あなたにオススメの質問