2016-06-12 23 views
2

アセンブリコードを含む3つの参照引数を持つ関数があります。 私はその関数の結果を変数R、G、Bで次のように得たいと思います。例えばアセンブリ関数への参照引数の受け渡し

 void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B)  { 

    _asm { 
     push EAX; 
     xor EAX, EAX; 
     mov EAX,color; 
     mov R, AL; 
     mov G, AH; 
     shr EAX, 16; 
     mov B, AL; 
     pop EAX; 
    } 
    } 

DWORD color=RGB(76,0,0); 
uint8_t R, G, B; 
Get_RGB_color(color , R ,G ,B); 

コードは、2つの問題を抱えて、私は関数を使用した:

1-ライン のMOV EAX、色、EAXに間違った値を取得します。ライン

MOVのR、ALに

2-エラー 'オペランドサイズ競合'。 mov G、AH; mov B、AL;

なぜあなたはここにEAXを推進している

+1

参照が実際にあなたが必要とするポインタとして渡されます。セミコロンは実際にアセンブラでコメント区切り文字であるので、それがCに次のように書くようになるので、それは、しかし、重要ではない理由それらをそのまま扱う。 PS:これのためにアセンブリを使用する理由はありません。 – Jester

+0

私は変数のアドレスを取得すると思います、どうすれば変数の値を取得できますか? – SaeidMo7

+0

私はJesterがすでに言ったことを強調したいと思います。あなたは、このアセンブリがコンパイラ生成コードより高速であると思いますか? –

答えて

7
push EAX; 

私に助けてください?それをする必要はありません。 EAXは呼び出し元を保存するレジスタです。つまり、呼び出し先(、すなわち)は自由にそれをぶつけることができます。その価値を維持する必要はありません。
(EAX、EDX、およびECX発信者保存Win32のABIのレジスタである;他のものは、被呼保存されている)

レジスタをプッシュする唯一の他の理由は、スタックを整列することであろうが、それはここでも必要ではない。コントロールが関数に渡されると、スタックは既に正しく整列されています。

xor EAX, EAX; 

私はあなたが、これは(自分自身でそれをXOR)レジスタをクリアするために一般的に使用されるトリックであることを知っていると推定。ただし、値をMOVeする前に事前にレジスタをクリアする必要はありません。

mov EAX,color; 

この行は間違っています。アセンブラのエラーがあなたに伝えているものです。 colorはDWORDへの参照としてこの関数に渡されますが、参照はポインタとして実装されるため、実際にはDWORDへのポインタとして渡されます。これは、色の値に直接アクセスできないことを意味します。ポインタの間接参照(またはx86用語で"indirect addressing")を使用する必要があります。あなたは、インラインアセンブリを使用しているので、あなたは、コンパイラはあなたのためのスタック簿記をやらせることができ、ちょうど仮パラメータの名前でメモリ位置を参照してください:あなたが実際にはないので、もちろん

mov EAX, DWORD PTR [color] ; get the address of color 
mov EAX, DWORD PTR [EAX]  ; dereference it, storing the result in EAX 

colorをこの関数の中に変更すると、それを参照パラメータとして渡す理由はありません。実際に参照が必要な場合を除き、スカラー値(、たとえば、整数)は、は常にとなります。これはより効率的で読みやすくなります。最適化コンパイラはレジスタ内の値を渡し、このポインタを間接的にし、その付帯コストを完全に不要にします。ここで

mov R, AL; 

、アセンブラはあなたに "オペランドサイズの競合" エラーを与えています。 Rは実際には参照として実装されているため、ポインタは32ビットです。これは、メモリ内の8ビット位置を指す32ビットポインタです。したがって、8ビットの値(AL)を32ビットの場所(ポインタ)に移動しようとしています。オペランドはサイズが異なります。だからもう一度、間接アドレシングを使用する必要があります。

mov EDX, DWORD PTR [R] ; get the address of R 
mov BYTE PTR [EDX], AL ; dereference it so we can store AL in there 
:それは今 Rはバイトサイズであり、あなたは私たちがそこに着くために懸命に働いたEAXの値をつかう避けるために、スクラッチ・レジスタとして異なるレジスタを使用する必要がある以外は、ちょうど上記のコードのようになります。

EAXの下位バイト(ALと呼ぶことができる)をRで指定されたバイトサイズのメモリ位置に移動します。

EAXの上位バイト(AHと呼ばれる)を移動していることを除いて、次の行と同じことです。我々は再びその古い値を必要としないので、私たちは、今ここEDXを再利用することができます

mov EDX, DWORD PTR [G] ; get the address of G 
mov BYTE PTR [EDX], AH ; dereference it so we can store AH in there 
shr EAX, 16; 

これは正しいです。最初と同じ

mov B, AL; 

第三詩、。あなたが今知っているように、これは次のようになります。私たちが先頭にEAXをプッシュしていなかったので、

mov EDX, DWORD PTR [B] ; get the address of B 
mov BYTE PTR [EDX], AL ; dereference it so we can store AL in there 
pop EAX; 

ポッピングEAXは、今や不要です。一緒にすべてを置く


、そして、あなたは、命令の次のシーケンスを取得:

void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B) 
{ 
    __asm 
    { 
     mov EAX, DWORD PTR [color] 
     mov EAX, DWORD PTR [EAX] 

     mov EDX, DWORD PTR [R] 
     mov BYTE PTR [EDX], AL 

     mov EDX, DWORD PTR [G] 
     mov BYTE PTR [EDX], AH 

     shr EAX, 16 
     mov EDX, DWORD PTR [B] 
     mov BYTE PTR [EDX], AL 
    } 
} 

はしかし、これはコードを書くための最適な方法ではありません。 32ビットレジスタのローとハイの8ビットへのアクセスは許容されていますが、遅いです。最適化コンパイラはこれを避けるため、その過程で、シフト命令の必要性を回避します:

void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B) 
{ 
    __asm 
    { 
     mov EAX, DWORD PTR [color] ; get the address of color 
     mov EAX, DWORD PTR [EAX]  ; get the value in EAX 

     mov EDX, DWORD PTR [R]  ; get the address of R 
     mov CL, BYTE PTR [EAX]  ; get the value of the lowest byte (8 bits) of color 
     mov BYTE PTR [EDX], CL  ; dereference R and store that byte in it 

     mov EDX, DWORD PTR [G]  ; get the address of G 
     mov CL, BYTE PTR [EAX + 1] ; get the value of the second-to-lowest byte in color 
     mov BYTE PTR [EDX], CL  ; dereference G and store that byte in it 

     mov EDX, DWORD PTR [B]  ; get the address of B 
     mov CL, BYTE PTR [EAX + 2] ; get the value of the third-to-lowest byte in color 
     mov BYTE PTR [EDX], CL  ; dereference B and store that byte in it 
    } 
} 

しかし、物事を遅くするためにそこに潜む部分レジスタストールが残っています。だから、本当に賢いコンパイラはレジスタを事前にゼロまたはmovzxを使用していずれかの方法でそれらを排除するであろう:

void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B) 
{ 
    __asm 
    { 
      mov EAX, DWORD PTR [color] 
      mov EAX, DWORD PTR [EAX] 

      mov EDX, DWORD PTR [R] 
      movzx ECX, BYTE PTR [EAX] 
      mov BYTE PTR [EDX], CL 

      mov EDX, DWORD PTR [G] 
      movzx ECX, BYTE PTR [EAX + 1] 
      mov BYTE PTR [EDX], CL 

      mov EDX, DWORD PTR [B] 
      movzx ECX, BYTE PTR [EAX + 2] 
      mov BYTE PTR [EDX], CL 
    } 
} 

また、命令の順序を変更し、巧みに可能な限り3つの操作を並列化するレジスタを割り当てる可能性があります。確かにこれを行うためのより効率的な方法があります。

void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B) 
{ 
    R = (color & 0xFF); 
    G = ((color >> 8) & 0xFF); 
    B = ((color >> 16) & 0xFF); 
} 

最後にひとつ:あなたはアセンブリ言語プログラミングを学ぶためにしようとしている場合を除きが強くは、このようなコードを書くことを好む、(その場合には、インラインアセンブラを使用してもあまり意味がありません)注:インラインアセンブリ構文を使用する場合でも、各アセンブリ言語命令をセミコロンで終了する必要はありません。これはCとC++でのみ必要です。

int foo;/**/ 
+0

ご協力いただきありがとうございます。 – SaeidMo7

+2

@ SaeidMo7:この回答があなたを助け、問題を解決した場合は、回答を受け入れることを検討する必要があります。答えを受け入れる方法や方法については、http://meta.stackexchange.com/a/5235/271768を参照してください。私はまたあなたのために働く答えに対する答えを一度も受け入れたことがないことに気付きました。あなたは新しいようですが、あなたの他の質問に戻り、あなたの問題を解決した回答を受け入れることをお勧めします。 Stackoverflowへようこそ! –

関連する問題