A: 在C代码中调用汇编程序
1..如果遵守C 调用约定 FOR C
global _add ;全局函数声明,表示这个函数要被外部文件调用
_add:
push ebp ;保护ebp指针
mov ebp,esp
mov eax,[ebp+8] ;第一个入口参数int a
mov ebx,[ebp+12];第二个入口参数int b
add eax,ebx
pop ebp ;恢复ebp指针
ret ;调用者平衡堆栈
在C文件中声明函数 extern "C" int _cdecl add(int a,int b);
最主要的是要在ASM的函数名称的前面加上一个 _(下划线) ,但是在C文件中声明的函数不用加下划线,并且一定要加上extern "C",并且用_cdecl 声明,这样以后就可以在C中调用ASM中的函数了。
2.如果遵守stdcall 调用约定 FOR C++
还用上面的那个两个数相加的例子
global _add@8 ;全局函数声明,表示这个函数要被外部文件调用
_add@8:
push ebp ;保护ebp指针
mov ebp,esp
mov eax,[ebp+8] ;第一个入口参数int a
mov ebx,[ebp+12];第二个入口参数int b
add eax,ebx
pop ebp ;恢复ebp指针
ret 8 ;被调用者平衡堆栈
在ASM文件的开头写上 global _myaddstdcall@8
在C文件中声明函数 extern "C" int _stdcall myaddstdcall(int a,int b);
这里要注意的是函数的名称问题,一般是_XXX@N ,也就是在开头加上一个下划线,@N中的N就是参数的大小。
B: 在汇编中调用C中的函数
1.遵守C调用约定
举个例子:
extern "C" void _cdecl myprint(int a)
{
printf("myprint %d\n",a);
}
在ASM中声明 extern _myprint ,然后就可以用 push xxx ,call myprint , add esp ,4 调用了
。要注意的是call 调用完后,一定要加上add esp ,X 来平衡堆栈,应为C调用约定规定是调用者平衡堆栈.
;extern _myprint
global _myprint_asm
_myprint_asm:
push ebp ;保护ebp指针
mov ebp,esp
mov eax,[ebp+8] ;第一个入口参数int VAR1
push eax ;保护ebp指针
call _myprint
add esp ,4 调用了
pop ebp ;恢复ebp指针
ret ;调用者平衡堆栈
2.遵守stdcall 调用约定
extern "C" void _stdcall myprintstdcall(int a)
{
printf("myprintstdcall %d\n",a);
}
在ASM中声明extern _myprintstdcall@4 ,然后用push xxx ,call _myprintstdcall@4 调动,这里就不用再加add esp,X了,有函数本身平衡堆栈.
extern _myprintstdcall@4
global _myprint_asm@4
_myprint_asm@4:
push ebp ;保护ebp指针
mov ebp,esp
mov eax,[ebp+8] ;第一个入口参数int VAR1
push eax ;保护ebp指针
call _myprintstdcall@4
pop ebp ;恢复ebp指针
ret 4 ;调用者平衡堆栈
这个就是入门的学习,非常简单的环境学习X86汇编。
入门回了,后面就是开始学习X86的基本指令,调试,以及学习MMX,.SSE!
下面是一个最简单的例子: Hello world
data segment ;数据段开始
str db 'Hello world. ', '$ ' ;字符串声明,以字节的存储形式,以‘$’结尾
data ends ;数据段结束
code segment ;代码段开始
assume cs:code,ds:data ;进行段的说明,说明一个对应的关系,之后再把段的首地址赋值给段寄存器,这样定义过的段才能被找到并被使用
main: ;程序代码段的开始标号
mov ax,data ;段段寄存器不能直接赋值,用ax做桥梁
mov ds,ax ;数据段寄存器
lea dx,str ;取str的有效地址
mov ah,09h ;中断调用入口参数
int 21h ;调用21h中断的09h号功能显示字符串
mov ah,4ch ;调用中断入口参数
int 21h ;调用21h中断的4ch功能功退出
code ends ;代码段结束
end main ;程序结束