|
下面将举几个关于使用C\C++符号的例子。
如果先前C已经定义了一个变量var,那么则内联汇编可以访问这个变量如下:
__asm mov eax, var ;将变量var中的值赋给eax寄存器中。
如果有一个结构体first_type和一个实例hal:
struct first_type
{
char *weasel;
int same_name;
} hal;
在访问hal对象时,则必须如下:
__asm
{
mov ebx, OFFSET hal ;取得hal对象的首地址
mov ecx, [ebx]hal.same_name ;加上same_name偏移值,则可以访问到成员same_name
mov esi, [ebx]hal.weasel ;加上weasel偏移值。
}
下面是一个内联汇编如何实现一个函数的例子:
#include <stdio.h>
int power2( int num, int power );
void main( void )
{
printf( "3 times 2 to the power of 5 is %d\n", \
power2( 3, 5) );
}
int power2( int num, int power )
{
__asm
{
mov eax, num ; 取得第一个参数
mov ecx, power ; 取得第二个参数
shl eax, cl ; EAX = EAX * CL
}
//在函数中,返回值是由eax负责往回传递的。(顺便问一句ax与eax有什么不同啊?是不是一样的?)
}
因为内联函数中没有return,所以在上面的例子中,编译器会报出警告。还好,不像Java一样,少一个多一个return都会编译不通过。你可以使用宏#pragma warning来关掉警告器。在pascall式函数中堆栈的复位是由函数负责的,而不是调用者。在上面的例子中,由是在C函数中内部嵌入汇编来完成汇编函数的。在C函数出口处,C编译器会自动添加复栈指令,而不必自己添写。那反而会使系统混乱. 在内联汇编中跳转指令(包括条件跳转),可以跳转到C语言goto能到的所有地方。Goto也可以跳到内联汇编中定义的标签,示例如下:
void func( void )
{
goto C_Dest; /* Legal: correct case */
goto c_dest; /* Error: incorrect case在C中大小写区分。*/
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;
}
另外,在给标签起名时尽量避免与C内部的或已经使用了的标签名重名,如果那样的将会出现灾难性的程序错误。因此,在起名时最好追查一下是否这个名字已经被使用了。在引用函数时,应注意参数的从右向左方向地压栈。比如有一个函数是 int CAdd (int a,int b) 则应该如此调用:
__asm
{
mov eax,2;
push; 参数b等于2
mov eax,3;
push; 参数a等于3
call CAdd;调用CAdd函数
mov Result,eax;所有函数的返回值都被存放在eax。于是,Result等于5
}
注意内联汇编无法调用重载函数,因为被重载函数名与原函数名不一样。所以如果你需求调用的话, (我记得vckbase中有关于重载函数的文章),就不要定义重载函数,且C++函数必须使用extern "C"关键字来定义。因为C中的预处理指令#define是字符代换,所以你可以使用#define来定义一个汇编宏,例如:
#define PORTIO __asm \
/* Port output */ \
{ \
__asm mov al, 2 \
__asm mov dx, 0xD007 \
__asm out al, dx \
}
|
|