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

时间:2016-06-12 14:53:00

标签: c++ assembly x86

我有一个包含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 );

代码有两个问题:

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

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

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

请帮帮我

1 个答案:

答案 0 :(得分:7)

push EAX;

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

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

xor EAX, EAX;

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

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位。它是一个32位指针,指向内存中的8位位置。因此,您尝试将8位值(AL)移动到32位位置(指针)。操作数大小不同。所以再一次,你必须使用间接寻址。它看起来就像上面的代码,除了现在R是字节大小的,你需要使用一个不同的寄存器作为临时寄存器,以避免破坏我们努力工作的EAX中的值:< / p>

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

这将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;

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

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,因为我们一开始并没有推动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位和高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
    }
}

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

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 ++中必不可少的。之所以无关紧要,是因为分号实际上是汇编程序中的 comment 分隔符,所以就像在C中编写以下代码一样:

int foo;/**/
相关问题