VC++內(nèi)聯(lián)匯編,將MSDN里面關(guān)于內(nèi)聯(lián)匯編的幾乎全翻譯了,一上午的成果啊,哪翻譯錯(cuò)了,告我一聲啊。。。
【一】.在 __asm block中使用匯編語(yǔ)言
1.關(guān)鍵字__asm調(diào)用內(nèi)聯(lián)匯編語(yǔ)句
有三種方式可用
(1)__asm block 形式
例子:
// asm_overview.cpp
// processor: x86
void __declspec(naked) main()
{
// Naked functions must provide their own prolog...
__asm {
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
}
// ... and epilog
__asm {
pop ebp
ret
}
}
(2)將__asm放在每句匯編指令的開(kāi)頭
例子:
__asm push ebp
__asm mov ebp, esp
__asm sub esp, __LOCAL_SIZE
(3)應(yīng)為_(kāi)_asm 也是一個(gè)語(yǔ)句分隔符,所以可以將匯編指令放在同一行上
例子:
__asm push ebp __asm mov ebp, esp __asm sub esp, __LOCAL_SIZE
2.內(nèi)聯(lián)匯編指令集
VC++編譯器支持Pentium 4 和 AMD Athlon的所有指令,額外的被其他目標(biāo)處理器支持的指令
能夠被創(chuàng)造用_emit 偽指令。
附:_emit 偽指令說(shuō)明
_emit偽指令的MASM的DB指令相似,你能夠使用_emit在代碼段(text segment)的當(dāng)前位置
去定義一個(gè)字節(jié)的立即數(shù)。_emit 一次只能定義一個(gè)字節(jié),并且僅僅能夠再代碼段(text segment)
內(nèi)定義。
例子:
#define randasm __asm _emit 0x4A __asm _emit 0x43 __asm _emit 0x4B
.
.
.
__asm {
randasm
}
3.再內(nèi)聯(lián)匯編中的MASM表達(dá)式
內(nèi)聯(lián)匯編能夠使用任何的MASM的表達(dá)式,能夠使任何操作數(shù)和操作碼的組合。
4.內(nèi)聯(lián)匯編中的數(shù)據(jù)指令和操作
盡管__asm block能夠引用C/C++的數(shù)據(jù)類(lèi)型和對(duì)象(object),但是他不能定義數(shù)據(jù)對(duì)象用MASM的指令和操作,尤其,不能使用
DB,DW,DD,DQ,DT,DF 或者DUP,THIS。MASM中結(jié)構(gòu)體和記錄類(lèi)型也是不可用的,內(nèi)聯(lián)匯編不接受STRUC,RECORD,WIDTH,MASK操作.
5.EVEN 和 ALIGN 指令
盡管內(nèi)聯(lián)匯編不支持大多數(shù)MASM的指令,但是支持EVEN 和 ALIGN 指令,這兩個(gè)指令填充N(xiāo)OP在匯編代碼中去對(duì)其數(shù)據(jù)和指定的邊界,這樣能夠
CUP的數(shù)據(jù)訪(fǎng)問(wèn)更加高效.
6.內(nèi)聯(lián)匯編中的MASM宏指令
內(nèi)聯(lián)匯編不支持MAsm中的宏指令(MACRO, REPT, IRC, IRP, ENDM)或者宏操作符(<>, !, &, %, .TYPE)。
7.內(nèi)聯(lián)匯編中的段引用
再內(nèi)聯(lián)匯編中指定一個(gè)段只能通過(guò)寄存器,而不能通過(guò)名字(例如,段名_TEXT是不可用的),段超越必須顯式的使用寄存器,如ES:[BX].
8.內(nèi)聯(lián)匯編中的類(lèi)型和變量尺寸問(wèn)題
LENGTH, SIZE 和 TYPE 操作符有一個(gè)限定的意義再內(nèi)聯(lián)匯編中,他們不能被使用和DUP一起(因?yàn)樵賰?nèi)聯(lián)匯編中不能使用DUP命令),但是能夠使
用他們?nèi)サ玫紺/C++變量的尺寸和類(lèi)型.
*LENGTH 操作符返回一個(gè)數(shù)組的元素?cái)?shù)目,非數(shù)組變量返回1.
*Size 操作符返回C/C++變量的尺寸,一個(gè)變量的尺寸是LENGTH與TYPE相乘的結(jié)果.
*TYPE 操作符返回C/C++類(lèi)型或變量的尺寸,如果是一個(gè)數(shù)組變量返回?cái)?shù)組中單個(gè)元素的TYPE.
例子:
int arr[8];
__asm C Size
LENGTH arr sizeof(arr)/sizeof(arr[0]) 8
SIZE arr sizeof(arr) 32
TYPE arr sizeof(arr[0]) 4
9.內(nèi)聯(lián)匯編的注釋問(wèn)題
再__asm block 中可以使用匯編語(yǔ)言的注釋
例子:
__asm mov ax, offset buff ; Load address of buff
【二】.再__asm block 中使用C/C++
概述:應(yīng)為內(nèi)聯(lián)匯編能夠與C/C++語(yǔ)句混合使用,他夢(mèng)能夠使用C/C++的變量通過(guò)名字,還有C/C++語(yǔ)言的其他元素.
*符號(hào),包括標(biāo)號(hào),變量,函數(shù)名.
*常量,包括符號(hào)常量和枚舉(enum)
*宏,預(yù)處理命令
*注釋?zhuān)ò?/**/和//)
*類(lèi)型名稱(chēng)
*typedef名稱(chēng),一般都和PTR和TYPE一起使用或者去指定結(jié)構(gòu)體或聯(lián)合成員
1.在 __asm block 中使用操作符
再 __asm block 中不能使用 C/C++特有的操作符,例如<<。C/C++與匯編共用的操作符,如*,是被解釋為匯編操作符。
舉個(gè)例子來(lái)說(shuō),[]操作符在C語(yǔ)言里被解釋為數(shù)組的下標(biāo), C能夠自動(dòng)的轉(zhuǎn)換數(shù)組元素的尺寸,解釋為首地址+單個(gè)元素的長(zhǎng)度*方括號(hào)內(nèi)的值.
但是再__asm block中,他被看做 MASM索引操作符(index operator),解釋為首地址+方括號(hào)中的值.
下面的實(shí)例顯示了他們的不同。
int array[10];
__asm mov array[6], bx ; Store BX at array+6 (not scaled)
array[6] = 0; /* Store 0 at array+24 (scaled) */
能夠使用TYPE操作符去達(dá)到和C同樣的效果
__asm mov array[6 * TYPE int], 0 ; Store 0 at array + 24
array[6] = 0; /* Store 0 at array + 24 */
2.使用C/C++符號(hào)在__asm block 中
__asm塊能夠引用 C/C++在作用域中的符號(hào)(包括變量名,函數(shù)名,標(biāo)號(hào),不能調(diào)用C++的成員函數(shù))
在使用C/C++符號(hào)時(shí)有一些限制:
*每條匯編語(yǔ)句僅僅能夠包含一個(gè)C/C++的符號(hào)。在LENGTH, TYPE, 和 SIZE表達(dá)式中則可以使用多個(gè)C/C++符號(hào)。
*在__asm block中函數(shù)引用必須先聲明。否則編譯器不能區(qū)別在__asm block 中的標(biāo)號(hào)與函數(shù)名.
*不能使用與MASM保留字相同的符號(hào)名稱(chēng)(無(wú)論大小寫(xiě))。
*結(jié)構(gòu)體和聯(lián)合類(lèi)型不能別識(shí)別在__asm block中.
3.訪(fǎng)問(wèn)C/C++數(shù)據(jù)在__asm block中
在內(nèi)聯(lián)匯編中通過(guò)名稱(chēng)訪(fǎng)問(wèn)C/C++變量是十分方便的。在__asm block中能訪(fǎng)問(wèn)任何在作用域中符號(hào)。
例如,在其作用域中有一個(gè)C變量 var, __asm MOV EAX,var 存儲(chǔ)var的值在EAX中。
如果一個(gè)類(lèi),結(jié)構(gòu)體或者聯(lián)合結(jié)構(gòu)的成員是唯一的,在__asm block中引用他僅僅使用成員變量名,
而不用使用變量名或者typedef名在.操作符之前。如果成員名不是唯一的,無(wú)論如何,必須放置變量名或者typedef名在.操作符之前。
例子:
// InlineAssembler_Accessing_C_asm_Blocks.cpp
// processor: x86
#include <stdio.h>
struct first_type
{
char *weasel;
int same_name;
};
struct second_type
{
int wonton;
long same_name;
};
int main()
{
struct first_type hal;
struct second_type oat;
__asm
{
lea ebx, hal
mov ecx, [ebx]hal.same_name ; Must use 'hal'
mov esi, [ebx].weasel ; Can omit 'hal'
}
return 0;
}
在 __asm block中能夠訪(fǎng)問(wèn)C++ 的數(shù)據(jù)成員而不用去遵守訪(fǎng)問(wèn)限制,但是不能調(diào)用C++的成員函數(shù).
4.使用內(nèi)聯(lián)匯編寫(xiě)函數(shù)
略。沒(méi)啥好講的,直接看例子
int power2( int num, int power )
{
__asm
{
mov eax, num ; Get first argument
mov ecx, power ; Get second argument
shl eax, cl ; EAX = EAX * ( 2 to the power of CL )
}
// Return with result in EAX
}
5.使用和保存寄存器在內(nèi)聯(lián)匯編中
一般來(lái)說(shuō),不應(yīng)該假設(shè)寄存器將會(huì)有一個(gè)指定的值在__asm blok塊開(kāi)始時(shí),寄存器的值不保證在離開(kāi)了一個(gè)__asm block后被保存,如果你離開(kāi)
了一個(gè)asm塊并開(kāi)始了另一個(gè)asm塊,不應(yīng)該應(yīng)用在上一個(gè)塊中保存寄存器的值。An __asm block inherits whatever register values result
from the normal flow of control.
如果使用__fastcall調(diào)用約定,編譯器傳遞參數(shù)使用寄存器而不是堆棧,這可能產(chǎn)生一個(gè)問(wèn)題在應(yīng)用了__asm block的函數(shù)中,因?yàn)楹瘮?shù)無(wú)法知
道那個(gè)參數(shù)是在寄存器中。如果一個(gè)函數(shù)接受參數(shù)在EAX中,但是過(guò)后別立刻用來(lái)存儲(chǔ)其他的值,那摩這個(gè)原始的參數(shù)就丟失了。并且,在
__fastcall約定中,必須保存ECX寄存器的值。
去避免如此的寄存器沖突,不要使用__fastcall調(diào)用約定為那些包含__asm block的函數(shù),如果使用/Gr編譯器選項(xiàng)指定了全局的__fastcall約定
,那摩定義每個(gè)包含__asm block的函數(shù)用_stdcall或__cdecl。
當(dāng)使用__asm去寫(xiě)匯編語(yǔ)句在C/C++中,不需要去保存EAX,EBX,ECX,EDX,ESI,EDI。在使用EBX,ESI,EDI時(shí),你強(qiáng)迫編譯器去保存并回復(fù)這些寄存
器的值在函數(shù)的序言與結(jié)尾處.
也應(yīng)該保存使用的其他寄存器(如DS,SS,SP,BP,EFLAGS)對(duì)于這個(gè)__asm block的作用域.
也應(yīng)該保存ESP和EBP除非你有其他的改變他們的原因。(例如,堆棧轉(zhuǎn)換)
下面這段不太好翻譯,自己看吧:
Some SSE types require eight-byte stack alignment, forcing the compiler to emit dynamic stack-alignment code. To be able to
access both the local variables and the function parameters after the alignment, the compiler maintains two frame pointers.
If the compiler performs frame pointer omission (FPO), it will use EBP and ESP. If the compiler does not perform FPO, it will
use EBX and EBP. To ensure code runs correctly, do not modify EBX in asm code if the function requires dynamic stack
alignment as it could modify the frame pointer. Either move the eight-byte aligned types out of the function, or avoid using
EBX.
注意:如果在__asm block中改變了方向標(biāo)志,通過(guò)STD,CLD,那摩就要保存這些標(biāo)志的原始值.
6.在內(nèi)聯(lián)匯編中跳轉(zhuǎn)到指定標(biāo)號(hào)
像一般的 C/C++標(biāo)號(hào),在__asm block有函數(shù)作用域(在整個(gè)函數(shù)中可見(jiàn),而不僅僅是在定義的__asm block中),匯編指令與goto語(yǔ)句都能跳到
標(biāo)號(hào)處.
定義在__asm block中的標(biāo)號(hào)不是大小寫(xiě)敏感的,goto語(yǔ)句與匯編指令能夠引用整個(gè)標(biāo)號(hào)而不用考慮大小寫(xiě)。但是C/C++代碼中的標(biāo)號(hào)是大小寫(xiě)
敏感的當(dāng)使用goto語(yǔ)句時(shí),使用匯編語(yǔ)句不用考慮大小寫(xiě)問(wèn)題.
例子:
void func( void )
{
goto C_Dest; /* Legal: correct case */
goto c_dest; /* Error: incorrect case */
goto A_Dest; /* Legal: correct case */
goto a_dest; /* Legal: incorrect case */
__asm
{
jmp C_Dest ; Legal: correct case
jmp c_dest ; Legal: incorrect case
jmp A_Dest ; Legal: correct case
jmp a_dest ; Legal: incorrect case
a_dest: ; __asm label
}
C_Dest: /* C label */
return;
}
int main()
{
}
在__asm block中不要使用C庫(kù)的函數(shù)名作為標(biāo)號(hào)名稱(chēng)。
BAD TECHNIQUE: using library function name as label
jne exit
.
.
.
exit:
; More __asm code follows
在MASM中($)符號(hào)作為當(dāng)前的地址計(jì)數(shù)(current location counter)。他是當(dāng)前正在被編譯的指令的標(biāo)號(hào).在__asm block 中他的主要作用
是去作為一個(gè)長(zhǎng)的條件跳轉(zhuǎn).
jne $+5 ; next instruction is 5 bytes long
jmp farlabel
; $+5
.
.
.
farlabel:
【三】.在內(nèi)聯(lián)匯編中調(diào)用C函數(shù)
一個(gè)__asm block能夠調(diào)用C函數(shù),包括C庫(kù)函數(shù)。下面是調(diào)用printf的例子:
// InlineAssembler_Calling_C_Functions_in_Inline_Assembly.cpp
// processor: x86
#include <stdio.h>
char format[] = "%s %s\n";
char hello[] = "Hello";
char world[] = "world";
int main( void )
{
__asm
{
mov eax, offset world
push eax
mov eax, offset hello
push eax
mov eax, offset format
push eax
call printf
//clean up the stack so that main can exit cleanly
//use the unused register ebx to do the cleanup
pop ebx
pop ebx
pop ebx
}
}
【四】.定義__asm block作為宏
C語(yǔ)言的宏提供了一個(gè)簡(jiǎn)便的方式去插匯編代碼進(jìn)入源代碼。但是那需要額外的小心因?yàn)楹瓯粩U(kuò)展到一個(gè)單獨(dú)的邏輯行上(a single logical
line),去創(chuàng)建無(wú)錯(cuò)誤的宏,應(yīng)遵守下列規(guī)則:
*用{}包圍__asm block
*放_(tái)_asm 關(guān)鍵字在每條匯編指令的開(kāi)頭
*使用老式的注釋(/**/)代替匯編中的注釋(;)和單行注釋(//).
例子:
#define PORTIO __asm \
/* Port output */ \
{ \
__asm mov al, 2 \
__asm mov dx, 0xD007 \
__asm out dx, al \
}
一個(gè)__asm block寫(xiě)的宏能夠帶參數(shù),但是不能返回值,因此不要使用這樣的宏在C/C++表達(dá)式中.
【五】.內(nèi)聯(lián)匯編的優(yōu)化問(wèn)題
__asm block的存在會(huì)對(duì)優(yōu)化產(chǎn)生一些影響。首先,編譯器不會(huì)嘗試去優(yōu)化__asm block中的指令。第二,__asm block會(huì)對(duì)寄存器變量的存儲(chǔ)產(chǎn)
生影響,編譯器會(huì)避免去登記穿越__asm block的那些寄存器會(huì)被修改的變量.
posted on 2008-12-21 17:07
楊彬彬 閱讀(9307)
評(píng)論(1) 編輯 收藏 引用