函数名:
push ebp
mov ebp, esp
sub esp, xx
.
.
.
leave
ret
第一,前3句虽然可以用enter x,0来替代,但enter指令的开销比较大,没有这几个指令的效率高,而且enter指令中的嵌套我们是用不到的。
第二,leave指令就是 mov esp,ebp ; pop ebp 这个条指令,这个指令一条就有两条指令的功能,效率要高一点,少了取指令的时间。
第三,函数不保存寄存器里的值,退出也不恢复,有需要调用者有义务自己保存;
第四,参数由函数弹出,如果调用者需要查看参数的变化,请记住参数已在堆栈指针之上。调用者有义务保证函数的参数个数的正确性。
第五,函数默认为远调用,在对参数的读取上采取的是远调用的方法。
其中 第三行指令为开避局部存储空间,即局部变量。注意,xx为4的倍数,以便数据对齐。
低位> |--------|
| 0 |
| 0 |
| 局部 |< esp
| 变量 |
| ebp |< ebp
| eip |
| cs |
| 参数 |
高位> |--------|
取参数为 dword [ebp+12+x*4] ,x 为参数序号,以0开始,最后的参数先入栈,第一个参数最后入栈。
取局部变量为 dword [ebp-4-x*4],x 为局部变量序号,以0开始。
在读取参数与局部变量时,可以在函数开始处定义宏,返回前取消宏定义,以方便阅读。
如:
%define _$InC01 dword [ebp+12] ; 定义为第一个参数
%define _$Loc01 dword [ebp-4] ; 定义为第一个局部变量
在ret前
%undef _$InC01
%undef _$Loc01
以上的默认格式在实际运用过程中发现有些问题,改为
;###########################################################################################
; 子程序,此代码适用于中汇或nasm
;###########################################################################################
; 参数 :
; 返回值:
;------------------------------------------------------------------------------------------
%macro @pro 1
push %1
call _@pro
add esp, 4
%endmacro
;------------------------------------------------------------------------------------------
align 4
_@pro:
pushad
mov ebp, esp
%define _#@参数1 [ebp+36]
; 局部变量定义
; sub esp, 4
; %define _#@变量1 [ebp-4]
;----------------------------------------------------------------------------------
; 代码从这里开始
;----------------------------------------------------------------------------------
.end:
%undef _#@参数1
; %undef _#@变量1
; mov [ebp+36], eax ; 返回值
mov esp, ebp
popad
ret
;===========================================================================================
首先定义一个宏,方便函数的调用,同时也兼容C语言的调用,在函数体内的pushad、popad是保证了各寄存器的正常使用与恢复,而上面用到的
push ebp
mov ebp, esp
...
leave
则被改为
mov ebp, esp
...
mov esp, ebp
是因为在pushad中已经将ebp入栈,不需再入一次,同时减少一个指令,可以提高代码运行的效率,所以在函数体内ebp是不得修改的,只用于参数与局部变量的提取。
PS: 由于pushad 的执行效率问题,C语言没有使用,就是因为堆栈的开销太大了,而且每次需要入栈8个寄存器,所以不用。看到以前上面写的东西觉得自己好傻好天真。呵呵!