▶ 书中第八章的程序,主要讲了子过程调用过程中寄存器、堆栈的使用
● 代码,简单的加法,使用两种调用规范
1 INCLUDE Irvine32.inc 2 3 .data 4 word1 WORD 1234h 5 word2 WORD 4111h 6 7 .code 8 main PROC 9 call Example1 ; 先用两种规范的子过程计算了 5 + 6,然后在主过程中计算 1234h + 4111h 10 call Example2 11 12 movzx eax, word1 13 push eax 14 movzx eax, word2 15 push eax 16 call AddTwo 17 call DumpRegs ; 没有清空堆栈 18 call WaitMsg 19 exit 20 21 main ENDP 22 23 Example1 PROC ; C 规范 24 push 5 25 push 6 26 call AddTwo_C 27 add esp, 8 ; 主调函数中清理栈 28 call DumpRegs 29 ret 30 Example1 ENDP 31 32 AddTwo_C PROC 33 push ebp 34 mov ebp, esp 35 mov eax, [ebp + 12] 36 add eax, [ebp + 8] 37 pop ebp 38 ret 39 AddTwo_C ENDP 40 41 Example2 PROC ; STDCALL 规范 42 push 5 43 push 6 44 call AddTwo 45 call DumpRegs 46 ret 47 Example2 ENDP 48 49 AddTwo PROC 50 push ebp 51 mov ebp, esp 52 mov eax, [ebp + 12] 53 add eax, [ebp + 8] 54 pop ebp 55 ret 8 ; 被调函数中清理栈 56 AddTwo ENDP 57 58 END main
● 输出结果
EAX=0000000B EBX=010D1000 ECX=013C105A EDX=013C105A ESI=013C105A EDI=013C105A EBP=012FFA50 ESP=012FFA3C EIP=013C36D1 EFL=00000206 CF=0 SF=0 ZF=0 OF=0 AF=0 PF=1 EAX=0000000B EBX=010D1000 ECX=013C105A EDX=013C105A ESI=013C105A EDI=013C105A EBP=012FFA50 ESP=012FFA3C EIP=013C36EB EFL=00000202 CF=0 SF=0 ZF=0 OF=0 AF=0 PF=0 EAX=00005345 EBX=010D1000 ECX=013C105A EDX=013C105A ESI=013C105A EDI=013C105A EBP=012FFA50 ESP=012FFA40 EIP=013C36B4 EFL=00000202 CF=0 SF=0 ZF=0 OF=0 AF=0 PF=0
● 代码,数组填充。堆栈传参的典型过程。
1 INCLUDE Irvine32.inc 2 3 .data 4 count = 100 5 array WORD count DUP(?) 6 7 .code 8 main PROC 9 push OFFSET array 10 push COUNT 11 call ArrayFill 12 call WaitMsg 13 exit 14 main ENDP 15 16 ArrayFill PROC 17 push ebp ; 先调整寄存器,压栈,再处理参数 18 mov ebp, esp 19 pushad ; 栈中依次(地址递减)为:OFFSET array,COUNT,WaitMsg 地址,ebp 20 mov esi, [ebp+12] 21 mov ecx, [ebp+8] 22 cmp ecx, 0 ; 数组长度为 0 ,已经完成 23 je L2 24 25 L1: 26 mov eax, 10000h 27 call RandomRange 28 mov [esi], ax ; 生成的随机数塞进数组 29 add esi, TYPE WORD ; esi 指向数组中下一个元素 30 loop L1 31 32 L2: 33 popad ; 恢复寄存器 34 pop ebp ; ebp 恢复全体压栈以前的状态 35 ret 8 ; 清空栈clean up the stack 36 ArrayFill ENDP 37 38 END main
● 代码,使用局部数组,即强行在堆栈顶开辟一块空间使用。
1 INCLUDE Irvine32.inc 2 3 .code 4 main PROC 5 call makeArray 6 mov eax, 0 7 call WaitMsg 8 exit 9 main ENDP 10 11 makeArray PROC 12 push ebp 13 mov ebp, esp 14 sub esp, 32 ; 为了对齐双字边界,30 向上取整到 4 的倍数 15 lea esi, [ebp-32] ; 可以使用 [ebp-30] 16 mov ecx, 32 17 L1: 18 mov BYTE PTR [esi], '*' ; 填充 19 inc esi 20 loop L1 21 22 add esp, 32 ; 恢复栈顶指针和寄存器 23 pop ebp 24 ret 25 makeArray ENDP 26 27 END main
● 代码,递归计算 1 + 2 + ... + N,使用同一个寄存器存储每一次递归的计算结果。
1 INCLUDE Irvine32.inc 2 .data 3 N = 10 4 5 .code 6 main PROC 7 mov ecx, N ; 计算参数 8 mov eax, 0 ; 计算结果 9 call CalcSum ; 调用函数 10 L1: 11 call WriteDec ; 输出结果 12 call Crlf 13 call WaitMsg 14 exit 15 main ENDP 16 17 CalcSum PROC 18 cmp ecx, 0 ; 递归脱出条件,类似手工循环 19 jz L2 20 add eax, ecx ; 其余情况,计算并进行下一次递归 21 dec ecx 22 call CalcSum 23 L2: 24 ret 25 CalcSum ENDP 26 27 END Main
● 代码,递归计算阶乘,注意递归过程仅用于建栈,脱出过程才进行计算。
1 INCLUDE Irvine32.inc 2 3 .code 4 main PROC 5 push 5 ; 向函数传输的参数 6 call Factorial ; 调用函数 7 call WriteDec 8 call Crlf 9 call WaitMsg 10 exit 11 main ENDP 12 13 Factorial PROC 14 push ebp 15 mov ebp, esp 16 mov eax, [ebp+8] ; 获取参数 17 cmp eax, 0 ; 参数大于零,跳转 L1 计算,否则置 eax = 1,返回 18 ja L1 19 mov eax, 1 20 jmp L2 21 22 L1: 23 dec eax 24 push eax 25 call Factorial 26 27 ReturnFact: ; 栈全部压好了,在返回路径上逐步计算 28 mov ebx, [ebp+8] 29 mul ebx 30 31 L2: 32 pop ebp 33 ret 4 34 Factorial ENDP 35 36 END main
● 代码,交换数组元素,重点在说明 PROC 的传参过程,类似 C
1 INCLUDE Irvine32.inc 2 3 Swap PROTO, ; 声明过程 4 pValX:PTR DWORD, 5 pValY:PTR DWORD 6 7 .data 8 array DWORD 10000h,20000h 9 10 .code 11 main PROC 12 mov esi,OFFSET array ; 交换前显示数组元素 13 mov ecx, 2 14 mov ebx,TYPE array 15 call DumpMem 16 17 INVOKE Swap, ADDR array, ADDR [array+4] ; 实参列表 18 19 call DumpMem 20 call WaitMsg 21 exit 22 main ENDP 23 24 Swap PROC USES eax esi edi, pValX:PTR DWORD, pValY:PTR DWORD ; 形参列表为两个指针 25 mov esi, pValX ; 指针元素放入寄存器 26 mov edi, pValY 27 mov eax,[esi] ; 数组元素放入寄存器 28 xchg eax,[edi] ; 交换寄存器中元素和内存中的元素 29 mov [esi],eax ; 寄存器元素放回内存 30 ret 31 Swap ENDP 32 33 END main