案例分析
案例一代码
1. bubbleSort proc 2. ;bubbleSort(int *arr,int len) call C style 3. ;input: 4. ;push len 5. ;push offset arr 6. push ebp 7. mov ebp,esp 8. pushad 9. 10. mov edx,[ebp+8] 11. mov ecx,[ebp+12] 12. mov esi,0;i=esi 13. sub ecx,1;ecx=n-1 14. again_1: cmp esi,ecx 15. jge final_1 16. mov edi,0;j=edi 17. mov ebx,ecx 18. sub ebx,esi 19. again_2: cmp edi,ebx;ebx=n-1-i 20. jge final_2 21. mov eax,[edx+4*edi] 22. cmp eax,[edx+4*edi+4] 23. jle next 24. push [edx+4*edi+4] 25. pop [edx+4*edi] 26. mov [edx+4*edi+4],eax 27. next: inc edi 28. jmp again_2 29. final_2: 30. inc esi 31. jmp again_1 32. final_1: 33. popad 34. pop ebp 35. 36. ret 8 37. bubbleSort endp 38. 39. output proc 40. ;output(int *arr ,int len) 41. ;input: 42. ;edx=arr 43. ;ecx=len 44. pushad 45. mov esi,0 46. again: cmp esi,ecx 47. jge final 48. mov eax,[edx+4*esi] 49. call writeint 50. mov al,' ' 51. call writechar 52. inc esi 53. jmp again 54. final: popad 55. ret 56. output endp
分析
push len
push offset arr
先将长度和首地址进行入栈
push ebp
mov ebp,esp
pushad
将ebp进栈方便对len和arr进行寻址,将各寄存器进栈
mov edx,[ebp+8]
mov ecx,[ebp+12]
此时edx,ecx分别为arr首地址和长度
进行逻辑运算后
popad
pop ebp
ret 8
将各寄存器出栈,将ebp出栈,ret 8让栈平衡
案例二代码
- 源代码
1. void insertionSort( int arr[], int n){ 2. int i; 3. for(i = 1; i < n; i++) { 4. insert(arr, i); 5. } 6. } 7. 8. void insert ( int arr[], int n){ 9. int key = arr[n]; 10. int i = n; 11. while (arr[i - 1] > key) { 12. arr[i] = arr[i-1]; 13. i--; 14. if(i == 0) break; 15. } 16. arr[i] = key; 17. }
- 汇编代码
1. INCLUDE Irvine32.inc 2. .data 3. arr dd 99, 2, 3, -11, 22, 88, 7, 77, 547, 717, -54 4. len dd ($-arr)/4 5. a dd ? 6. .code 7. insert PROC 8. ;arr,a(i) 9. push ebp 10. mov ebp,esp 11. pushad 12. mov ebx,[ebp+8];ebx=i(n) 13. mov eax,[ebp+12];eax=arr 14. mov ecx,[eax+ebx*4];ecx=key 15. again: 16. cmp [eax+4*ebx-4],ecx 17. jle final 18. mov edx,[eax+4*ebx-4] 19. mov [eax+4*ebx],edx 20. sub ebx,1 21. cmp ebx,0 22. je final 23. jmp again 24. final: 25. mov [eax+4*ebx],ecx 26. popad 27. pop ebp 28. ret 8 29. insert ENDP 30. 31. main PROC 32. mov a,1 33. again: 34. mov eax,a;a为下标(eax) 35. cmp eax,len;(len=n) 36. jae myout 37. push offset arr 38. push a 39. call insert 40. add a,1 41. jmp again 42. 43. myout: 44. mov ebx,0 45. again2: 46. cmp ebx,len 47. jae final2 48. mov eax,arr[ebx*4] 49. call WriteInt 50. add ebx,1 51. jmp again2 52. 53. final2: 54. exit 55. main ENDP 56. END main
案例三代码
include irvine32.inc
.data
arr dd 99, 2, 3, 1, 22, 88, 7, 77, 54
len dd ($-arr)/4
.code
slectSort proc
;;;;;void slectSort(int *arr,int n)
;;;;input:
;;;;;;;;;push n
;;;;;;;;;push offset arr
push ebp
mov ebp,esp
pushad
mov edx,[ebp+8];;edx:arr
mov esi,[ebp+12];;;;esi:i;i=n
again: cmp esi,1
jle final
push esi
push edx
call maxIndex;;;eax:j= maxIndex(arr,esi);
mov ebx,[edx+4*eax]
mov ecx,[edx+4*esi-4]
mov [edx+4*eax],ecx
mov [edx+4*esi-4],ebx
dec esi
jmp again
final: popad
pop ebp
ret 8
slectSort endp
maxIndex proc
;;;;maxIndex(int*arr,int n)
;;;;input:
;;;;;;;;;push n
;;;;;;;;;push offset arr
;;;;;;;;;return:eax
push ebp
mov ebp,esp
sub esp,4
pushad
mov edx,[ebp+8]
mov eax,0;;;eax:index
mov esi,0;;;esi:i
again: cmp esi,[ebp+12]
jge final
mov ebx,[edx+4*eax]
cmp ebx,[edx+4*esi]
jge next
mov eax,esi
next: inc esi
jmp again
final: mov [ebp-4],eax
popad
mov eax,[ebp-4]
add esp,4
pop ebp
ret 8
maxIndex endp
output proc
push esi
push ecx
push eax
mov esi,0
again: cmp esi,ecx
jge final
mov eax,[edx+4*esi]
call writeint
mov al,' '
call writechar
add esi,1
jmp again
final: pop eax
pop ecx
pop esi
ret
output endp
main proc
push len
push offset arr
call slectSort
mov edx,offset arr
mov ecx,len
call output
exit
main endp
end main
main() {
int arr[] = {99, 2, 3, 1, 22, 88, 7, 77, 54};
int i;
slectSort(arr, 9);
for (int i = 0; i < 9; i++)
cout << arr[i] << endl;
return 0;
}
分析
这里主要分析以下部分代码
1. sub esp,4 2. 3. final: 4. mov [ebp-4],eax 5. popad 6. mov eax,[ebp-4] 7. add esp,4 8. pop ebp 9. ret 8
由于要将运算的结果通过寄存器eax返回父函数,而eax在final的popad中被出栈导致数据丢失。此时我们在ebp进栈后通过sub esp,4开辟一个空间,运算结束后先将eax的值赋值给ebp-4这个空间,然后将popad,然后取出空间的值重新赋值给eax,然后将esp+4覆盖掉开辟的空间,最后弹出ebp,ret 8即可。
知识补充
- 栈传参的好处
子函数的运算不影响父函数寄存器存储的值,在递归中避免出现赋值的覆盖。还能避免传参的个数超出寄存器的数量。
当我们在执行函数调用时,寄存器不够用的时候,我们可以使用堆栈传参,比如说,我们要计算任意10个参数相加的和,如果按照之前文章的办法,我们会使用mov这个指令把参数传递到寄存器中进行计算,但是通用寄存器只有八个,我们现在要传递十个参数,这时候就可以使用栈传参了。
- 寄存器批量进(出)栈
- 数据交换
- 换行函数
call crlf
- 输出空格
mov al,' '
call writechar