将引用参数传递给汇编函数

Sae*_*Mo7 2 c++ x86 assembly

我有一个带有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;
    }
  }
Run Code Online (Sandbox Code Playgroud)

例如我使用的函数

DWORD  color=RGB(76,0,0);
uint8_t R,   G, B;
Get_RGB_color(color , R ,G ,B );
Run Code Online (Sandbox Code Playgroud)

代码有两个问题:

1-在EAX中获取错误的值,行 mov EAX,color;

2-行中错误'操作数大小冲突'

mov R,AL; mov G,AH; mov B,AL;

请帮我

Cod*_*ray 7

push EAX;
Run Code Online (Sandbox Code Playgroud)

你为什么要在这里推EAX?没有必要这样做.EAX是一个调用者保存寄存器,这意味着被调用者(你的函数)可以自由地破坏它.您无需保留其价值.
(EAX,EDX和ECX是Win32 ABI中的调用者保存寄存器;其他是callee-save.)

推送寄存器的唯一其他原因是对齐堆栈,但这里也没有必要.当控制传递给您的函数时,堆栈已经正确对齐.

xor EAX, EAX;
Run Code Online (Sandbox Code Playgroud)

我认为你知道这是清除寄存器的常用技巧(与自身进行异或).但是,在向其中输入值之前,无需预先清除寄存器.

mov EAX,color;
Run Code Online (Sandbox Code Playgroud)

这条线错了; 这就是汇编程序错误告诉你的.color作为引用到DWORD传递给此函数,但在引擎下,引用实现为指针,因此它实际上作为指向DWORD的指针传递.这意味着您无法直接访问颜色值 - 您必须使用指针间接(或x86用语中的"间接寻址").由于您使用的是内联汇编,您可以让编译器为您执行堆栈簿记,并通过形式参数的名称引用内存位置:

mov EAX, DWORD PTR [color]   ; get the address of color
mov EAX, DWORD PTR [EAX]     ; dereference it, storing the result in EAX
Run Code Online (Sandbox Code Playgroud)

当然,由于您实际上没有在此函数内部进行修改 color,因此没有理由将其作为参考参数传递.通常,标量值(例如,整数)应始终通过值传递,而不是通过引用传递,除非您确实需要引用.这更高效,更可读 - 优化编译器将传递寄存器中的值,使得该指针间接及其伴随成本完全不必要.

mov R, AL;
Run Code Online (Sandbox Code Playgroud)

这里,汇编程序给出了"操作数大小冲突"错误.因为R它实际上是一个引用,实现为指针,它是32位.它是一个32位指针,指向内存中的8位位置.因此,您尝试将8位值(AL)移动到32位位置(指针).操作数大小不同.所以再一次,你必须使用间接寻址.它看起来就像上面的代码,除了现在R是字节大小的,你需要使用一个不同的寄存器作为临时寄存器,以避免破坏EAX中我们努力工作的价值:

mov EDX, DWORD PTR [R]    ; get the address of R
mov BYTE PTR [EDX], AL    ; dereference it so we can store AL in there
Run Code Online (Sandbox Code Playgroud)

这会将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
Run Code Online (Sandbox Code Playgroud)
shr EAX, 16;
Run Code Online (Sandbox Code Playgroud)

这是对的.

mov B, AL;
Run Code Online (Sandbox Code Playgroud)

第三节,与第一节相同.如您所知,这应该是:

mov EDX, DWORD PTR [B]    ; get the address of B
mov BYTE PTR [EDX], AL    ; dereference it so we can store AL in there
Run Code Online (Sandbox Code Playgroud)
pop EAX;
Run Code Online (Sandbox Code Playgroud)

弹出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
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,这不是编写代码的最佳方式.在允许的情况下,访问32位寄存器的低8位和高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
    }
}
Run Code Online (Sandbox Code Playgroud)

但是仍有部分寄存器停留在那里以减缓事情的发生.因此,一个非常聪明的编译器可以通过预先归零寄存器或使用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
    }
}
Run Code Online (Sandbox Code Playgroud)

它还可能对指令重新排序并巧妙地分配寄存器以尽可能地并行化这三个操作.毫无疑问,这样做的效率更高.除非您正在尝试学习汇编语言编程(在这种情况下,使用内联汇编程序没有多大意义),强烈希望编写如下代码:

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);
}
Run Code Online (Sandbox Code Playgroud)

最后要注意的是:即使使用内联汇编语法,也不需要用分号结束每个汇编语言指令.这只是在C和C++中必不可少的.之所以无关紧要,是因为分号实际上是汇编程序中的注释分隔符,所以就像在C中编写以下代码一样:

int foo;/**/
Run Code Online (Sandbox Code Playgroud)

  • @ SaeidMo7:如果这个答案对你有所帮助并解决了你的问题,你应该考虑接受答案.更多关于接受答案的方法和原因可以在这里找到:http://meta.stackexchange.com/a/5235/271768.我也注意到你从来没有接受任何适合你的答案的答案.你似乎是新人,我建议你回过头来解决你的问题并接受任何解决问题的答案.欢迎来到Stackoverflow! (2认同)