インラインアセンブラでの配列要素の加算について

このQ&Aのポイント
  • インラインアセンブラを使ってunsigned char型の配列の要素を加算するプログラムが正常に動作しない問題が発生しています。
  • 配列ansに正しい解が代入されず、全ての要素が205になってしまいます。
  • 問題の原因が特定できず、質問者は自力で問題点を見つけることができません。ご指摘をお願いします。
回答を見る
  • ベストアンサー

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

現在、インラインアセンブラを学んでいるものです。 開発環境は 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; }

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

  • ベストアンサー
  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.3

★アドバイス >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  }  と記述できますね。

その他の回答 (2)

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.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; }

seven_star
質問者

お礼

Oh-Orange様、返信ありがとうございました。 適切な回答と、参考ページまで紹介いただき、とても感謝しています。 おっしゃる通り、ポインタをレジスタに一度格納しておかなければなりませんでした。 結果、正常に動作するようプログラムを組むことが出来ました。 ありがとうございました。 最後にひとつ気になったことがあるのですが、 ご紹介にいただいた2番目の参考ページについてです。 アセンブラ部分を見ると      MovQ mm0, [eax][ecx * 8] このような表記があります。 ソースオペランドに引数を2つもっているのですが、これはどのような意味なのでしょう?

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.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→『インラインアセンブリ』

関連するQ&A

  • 2次元配列の動的確保

    ある画像を読み込むため、その画像を格納できる幅、高さを持った配列を動的に確保しようと考えています。 幅をxsize、高さをysizeで次のように記述しました。 unsigned char **src; int i; src = (unsigned char**)malloc(sizeof(unsigned char*) * ysize); src[0] = (unsigned char*)malloc(sizeof(unsigned char) * xsize * ysize); for(i=1; i<ysize; i++) src[i] = (src[0] + i * xsize); わざわざポインタのポインタを使用したのは、動的に確保した配列を2次元的なアクセスをしたかったためです。 画像の読み込み時は fread(src[0], sizeof(unsigned char), xsize * ysize, fp); としています。 上に記述したソースは問題なく動作しました。 しかし、上の場合だと全ての配列を連続して確保することができません。つまりsrcでmallocを一回、src[0]でmallocを一回使っているため、ポインタの配列の直後に配列を確保する保障がありません。そこでいっぺんに確保することを考えました。 unsigned char **src; int i; src = (unsigned char **)malloc(sizeof(unsigned char *) * ysize + sizeof(unsigned char) * xsize * ysize); for(i=0; i<ysize; i++) src[i] = (unsigned char *)(src + sizeof(unsigned char *) * ysize + i * xsize); このように組み上げ、読み込み時は上のfreadと同様に記述したところエラーが出てしまいました。 やはり一行目のmallocで無理やりsizeof(unsigned char *) * ysize + sizeof(unsigned char) * xsize * ysize分確保するのは失敗だったのでしょうか?

  • 画像を読み込む配列の確保。

    C言語について質問です。 画像を読み込む時、その画像の幅、高さを入力し、そのサイズに見合った配列を確保します。その後ファイル名を入力し、配列に読み込むプログラムを作成しました。 メインの部分のみ記述します。 int xsize, ysize, i; unsigned char **src; char filename[30]; FILE *fp; printf("ファイル名を入力してください:"); scanf("%s", filename); printf("画像の幅:");scanf("%d", &xsize); printf("画像の高さ:");scanf("%d", &ysize); src = (unsigned char **)malloc(sizeof(unsigned char *) * ysize); for(i=0; i<ysize; i++) src[i] = (unsigned char *)malloc(sizeof(unsigned char) * xsize); fp = fopen(filename, "rb"); fread(src[0], sizeof(unsigned char), xsize * ysize, fp); このように記述し、エラーもなく実行できたのですが、srcをこのまま出力すると変?な画像となって出力されてしまいました。 上のように記述した場合、矛盾する場所はあるでしょうか? そして、この方法以外に配列を確保する方法はあるでしょうか?

  • 3次元配列でのポインタ

    唐突ですみません。 サイズが640*480の画像を180枚読み込むプログラムをポインタを使って作成しようと考えています。 以下で示すプログラムは画像を読み込むための作成したものですが、エラーが出てしまい実行することができません。 間違えている箇所があればご指摘お願いします。 また、そのほかに効率の良いやり方などがありましたらご教授願います。 #include <stdio.h> #include <stdlib.h> #include <math.h> #define xsize 640 #define ysize 480 #define round 180 #include "Input.h" void Input_task(unsigned char ***In); void main() {   static unsigned char ***In;   int i,j;   In=(unsigned char***)malloc(sizeof(unsigned char)*round);   for(i=0;i<round;i++)   {     In[i]=(unsigned char**)malloc(sizeof(unsigned char)*ysize);     for(j=0;j<ysize;j++)     {       In[i][j]=(unsigned char*)malloc(sizeof(unsigned char)*xsize);     }   }   Input_task(In); } Input.hの中身 void Input_task(unsigned char ***In) {   char filename[30];   int i,j,k;   FILE *fp;   for(i=0;i<round;i++)   {     sprintf(filename,"b20_%04d.raw",i);     fp=fopen(filename,"rb");    for(j=0;j<ysize;j++)     {       for(k=0;k<xsize;k++)       {         *(*(*(In+i)+j)+k)=(unsigned char)getc(fp);       }     }    fclose(fp);   } }

  • 「動的確保した2次元配列のメモリ解放」を関数化したい

    質問タイトルの通りですが、 「動的確保した2次元配列のメモリ解放」をC言語で関数化したいと思っています。しかし、関数の引数には動的確保した配列の先頭アドレスのみ渡す形にしたいです。そのような場合の関数化は可能ですか? どうもうまくいかず、困っています。 以下、具体的に、サンプルソースを記述します。 わかる方、よろしくお願いします。 //====================================================// #include<stdio.h> unsigned char** AllocByteArray2d(int column, int row); void FreeByteArray2d(unsigned char** box); int main(voidls){ unsigned char array**; array = AllocByteArray2d(2, 3); FreeByteArray2d(array); return 0; } unsigned char** AllocByteArray2d(int column, int row){ unsigned char* box; box = (unsigned char**)malloc( sizeof(unsigned char*)*column ) int i; for(i=0; i<column; i++){ box[i] = (unsigned char*)calloc( row, sizeof(unsigned char)); if(box[i] == NULL) exit(EXIT_FAILURE); } return box; } //引数では配列の先頭アドレスだけ渡す形にしたい void FreeByteArray2d(unsigned char** box){ //ここをどう書いたらいいかわからない }

  • ポインタ配列の動的確保

    ポインタの配列の動的確保について教えてください。 入力した数値をポインタ配列に入れるプログラムです。 下記のように書いてみました。(見づらくてごめんなさい) #include<stdio.h> #include<stdlib.h> #define kensu 3 main() { char abc[kensu+1]={'A','B','C','\0'}; char *ptr[kensu]; int i; printf("3つの整数を入力して下さい。\n"); for(i=0;i<kensu;i++){ ptr[i]=(char*)malloc(sizeof(char)*10); if(ptr[i]==NULL){ printf("メモリの取得に失敗しました"); exit(1); } printf("整数%c:",abc[i]); fgets(ptr[i],10,stdin); if(ptr[i][strlen(ptr[i])-1]=='\n') ptr[i][strlen(ptr[i])-1]='\0'; } for(i=0;i<kensu;i++) free(ptr[i]); } ちゃんと動いているようです。 しかし、ポインタ配列の動的確保をネットで調べてみると、ポインタのポインタ(?)を使って、下記のように2度mallocしています。 #include <stdio.h> #include <stdlib.h> #define N 3 int main(void) { char** arr; int i,j; arr = (char**)malloc(N * sizeof(char*)); /* ポインタ配列を確保 */ /* 配列の要素それぞれにつき、メモリ領域を確保 */ for(i=0;i<N;i++) arr[i] = (char*)malloc(N * sizeof(char));   ・・・ ポインタの配列を宣言して、配列の各要素に動的確保するのと ポインタのポインタを宣言し、ポインタ配列を動的確保して、再度配列の要素に動的確保するのとでは、何か違いがあるのでしょうか? ポインタのポインタを宣言し、ポインタ配列を確保する必要性が良く分かっていないのです。 ネット等で調べて見たのですが、理解力がないのかよく分かりませんでした。 どうか教えてください。

  • 困りました。

    現在SDKにて画像処理ソフトを作成しています。 そして最近フィルタリングの機能をつけてみようと思いました。 知識として3×3の配列を利用するフィルタ処理で、ラプラシアン、ノイズ除去などを行うやりかた(アルゴリズム)は知っているのですが、配列の確保に問題が出てきました。 プログラムの仕様として、入力画像の輝度情報は入力時にその情報量分のメモリを確保し、ポインタとして格納しています。つまり配列として扱うには1次元配列となってしまいます。もちろん配列をあやつる変数部分を変えるだけで、2次元的な表現はできるのですが、フィルタリング処理をする際複雑になってしまい、コーディングするのが困難になってしまっています。 そこでポインタとしての1次元配列を2次元配列に格納したいのですが、単に格納だけならできるのですが、格納する2次元配列も動的にメモリ確保を行いたいのです。 色々とごちゃごちゃした言い回しになってしまいましたが、第一の問題は2次元配列として扱えるよう動的にメモリを確保したいということです。つまり src[i][j] このように扱えるようメモリを確保したいということです。 以前メモリ確保についてここで質問させてもらいましたが、2次元配列の場合、宣言として (*src)[256] とした場合少なくとも縦が256の画像を読み込むことができます。しかしこの場合は”256”と決まった量を与えているため記述できるのですが、自分が行いたいのは関数の引数で画像サイズを取得し、その量のメモリを動的に確保したいため、画像サイズに応じて確保するメモリの量が変わってきてしまいます。そのため(*src)[256]と書くことができませんが、しかし自分では確保したメモリ配列を2次元配列として扱いたいため(*src)[256]と書かなければなりません。 つまりこれが壁になっています。 関数の引数で受け取った画像サイズ分のメモリ確保を行い、かつそのメモリ空間をを2次元配列として扱いたい場合、どのように記述すればよいでしょうか? ひとつ考えたことが **src としてポインタのポインタを使用し、 src = (unsigend char **)GlobalAlloc(GHND, sizeof(unsigned char *) * height); for(i=0; i<height; i++)   src[i] = (unsigned char *)GlobalAlloc(GHND, sizeof(unsigned char) * width); for(i=0; i<height; i++){     for(j=0; j<width; j++){ src[i][j] = Input[width * i + j];     } } このように定義してみたんですけど、src[i][j]と記述はできたのですが実行時にエラーが出てしまいました。 なにか効率の良い方法はないでしょうか?

  • 構造体内のポインタのポインタにアクセスするには?

    たとえば、 struct a { char **name } という構造体があったとして、 struct *a; a->name = malloc(sizeof(char *) * 3); としたときに、 for(i = 0; i < 3; i++){ a->name[i] = malloc(sizeof(char) * 10); } とするとエラーになります。 a->name配列の各要素をmallocするにはどうすればよいのでしょうか?

  • RGBをCMYKに変換するプログラム

    Windows7 VS2008 SP1 RGBからCMYKに変換するプログラムを以下の様に書いている ([詳解]画像処理プログラミング という本に載っている ソースです) のですがうまくいきません。 おそらく型の扱い??だと思うのですが・・ 具体的にどの部分を修正すればいいのかご指摘お願いします。 INPUT:24bpp raw int RawToCMYK(WCHAR *filename,int width,int height) { FILE *fpt; FILE *fpt_C,*fpt_M,*fpt_Y,*fpt_K; unsigned char *layer; unsigned char *C,*M,*Y,*K; int i,j; _wfopen_s(&fpt,filename,L"rb"); if(fpt==0x00) { MessageBox(NULL,L"RawToCMYK Error",L"RawToCMYK Error",MB_OK); return -1; } else { layer=(unsigned char*)malloc(3*width*height); C=(unsigned char *)malloc(width*height); M=(unsigned char *)malloc(width*height); Y=(unsigned char *)malloc(width*height); K=(unsigned char *)malloc(width*height); fread(&layer[0],sizeof(unsigned char),3*width*height,fpt); fclose(fpt); _wfopen_s(&fpt_C,L"RawToC.raw",L"wb"); _wfopen_s(&fpt_M,L"RawToM.raw",L"wb"); _wfopen_s(&fpt_Y,L"RawToY.raw",L"wb"); _wfopen_s(&fpt_K,L"RawToK.raw",L"wb"); for(i=0;i<3*width*height;i+=3*width) { for(j=0;j<3*width;j+=3) { C[i/3+j/3]=255-layer[i+j]; M[i/3+j/3]=255-layer[i+j+1]; Y[i/3+j/3]=255-layer[i+j+2]; K[i/3+j/3]=min3(C[i/3+j/3],M[i/3+j/3],Y[i/3+j/3]); if(K[i/3+j/3]==0xff) { C[i/3+j/3]=0x00; M[i/3+j/3]=0x00; Y[i/3+j/3]=0x00; } else { C[i/3+j/3]=(C[i/3+j/3]-K[i/3+j/3])*255/(255-K[i/3+j/3]); M[i/3+j/3]=(M[i/3+j/3]-K[i/3+j/3])*255/(255-K[i/3+j/3]); Y[i/3+j/3]=(Y[i/3+j/3]-K[i/3+j/3])*255/(255-K[i/3+j/3]); } } } fwrite(&C[0],sizeof(unsigned char),width*height,fpt_C); fwrite(&M[0],sizeof(unsigned char),width*height,fpt_M); fwrite(&Y[0],sizeof(unsigned char),width*height,fpt_Y); fwrite(&K[0],sizeof(unsigned char),width*height,fpt_K); fclose(fpt_C); fclose(fpt_M); fclose(fpt_Y); fclose(fpt_K); free(C); free(M); free(Y); free(K); return 0;

  • C言語のキャストについて

    お世話になります。 CRC-16の計算プログラムをC言語でつくりました。 例えば・・・1F 08 00 00 12 34 なら“1F0800001234”と入力すると【EEC2】と表示するプログラムです。 ただ・・・.Net SDKでコンパイルするとできたのですが、Visual C++2008でコンパイルするとエラーが出てしまいます。 (48) : error C2664: 'strlen' : 1 番目の引数を 'unsigned char [256]' から 'const char *' に変換できません。(新しい機能 ; ヘルプを参照) 1> 指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。 (52) : error C2664: 'strtol' : 1 番目の引数を 'unsigned char [3]' から 'const char *' に変換できません。(新しい機能 ; ヘルプを参照) 1> 指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。 型変換が必要ってことまではわかったのですが・・・必要なのはわかって行き詰まり状態です。。。 どのようしたらよいのでしょうか?ご教授をよろしくお願いします。 ソースは以下の通りです。 #include "stdafx.h" #include <stdio.h> #include <string.h> #include <stdlib.h> unsigned short crc_cal(unsigned short lng, unsigned char *str) { unsigned short crc, i, j, t; t= 0x0000; crc = 0xffff; for (i = 0; i < lng ; i++) { crc ^= (unsigned short) str[i]; t = (unsigned short) str[i]; for (j = 1; j <= 8; j++) { if (crc & 1) { // carry bit on crc = crc >> 1; crc ^= 0xa001; } else { // carry bit off crc = crc >> 1; } } } return crc; } int main(void) { unsigned char str[256],data[128],hexstr[3]; unsigned short crc,CRC,len; while(1) { printf("Please input key (HEX)\n"); scanf("%255s",str); hexstr[2]='\0'; for(len=0; len<(strlen(str)/2) ;len++) { hexstr[0]=str[len*2]; hexstr[1]=str[len*2+1]; data[len]=(unsigned char)strtol(hexstr, NULL, 16); } crc = crc_cal(len,data); CRC = (crc>>8) | (crc<<8); printf("\nCRC16 = %04X\n\n", CRC); } return 0; }

  • 入れ子になった構造体について

    以下のように定義した、2重に入れ子になった構造体があります。 これを、mallocを使ってエリアを確保した後、初期化しています。 例では、各構造体の項目数が少ないのですが、 項目が増えた場合、下記のような初期化方法だと面倒だと思います。 もっとよい方法があるのだろうと思っているのですが・・・。 下記は私の試行錯誤結果なので、「普通はこうやるんじゃないの?」という方法などがあればご教授願います。 /* -- 構造体定義 -- */ typedef struct {   char data[16] ; }D_TAG ; typedef struct {   char name[16];   D_TAG *d ; }F_TAG ; typedef struct {   char name[16] ;   F_TAG *f ; }T_TAG ; /* -- 変数定義 -- */ T_TAG *t ; /* -- エリア確保 -- */ t=(T_TAG*)malloc(sizeof(T_TAG)*10); for(i=0;i<10;i++) {   t[i].f=(F_TAG*)malloc(sizeof(F_TAG)*10);   for(j=0;j<10;j++) {     t[i].f[j].d = (D_TAG*)malloc(sizeof(D_TAG)*10);   } } /* -- 初期化 -- */ for(i=0;i<10;i++) {   memset(t[i].name , 0x00, sizeof(t[i].name));   for(j=0;j<10;j++) {     memset( t[i].f[j].d, 0x00, sizeof(D_TAG)*10);     memset( t[i].f[j].name , 0x00, sizeof(t[i].f[j].name));   } } よろしくお願いいたします。

専門家に質問してみよう