簡體   English   中英

將引用參數傳遞給匯編函數

[英]Passing the reference arguments to an assembly function

我有一個帶有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,顏色;

2-行中錯誤'操作數大小沖突'

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

請幫我

 push EAX; 

你為什么要在這里推EAX? 沒有必要這樣做。 EAX是一個調用者保存寄存器,這意味着被調用者( 你的函數)可以自由地破壞它。 您無需保留其價值。
(EAX,EDX和ECX是Win32 ABI中的調用者保存寄存器;其他是callee-save。)

推送寄存器的唯一其他原因是對齊堆棧,但這里也沒有必要。 當控制傳遞給您的函數時,堆棧已經正確對齊。

 xor EAX, EAX; 

我認為你知道這是清除寄存器的常用技巧(與自身進行異或)。 但是,在向其中輸入值之前,無需預先清除寄存器。

 mov EAX,color; 

這條線錯了; 這就是匯編程序錯誤告訴你的。 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

當然,由於您實際上並未修改此函數內部的color ,因此沒有理由將其作為參考參數傳遞。 通常,標量值( 例如 ,整數)應始終通過值傳遞,而不是通過引用傳遞,除非您確實需要引用。 這更高效,更可讀 - 優化編譯器將傳遞寄存器中的值,使得該指針間接及其伴隨成本完全不必要。

 mov R, AL; 

這里,匯編程序給出了“操作數大小沖突”錯誤。 因為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

這將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 ++中必不可少的。 之所以無關緊要,是因為分號實際上是匯編程序中的注釋分隔符,所以就像在C中編寫以下代碼一樣:

int foo;/**/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM