程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 學 Win32 匯編 [17]: 關於壓棧(PUSH)與出棧(POP) 之一

學 Win32 匯編 [17]: 關於壓棧(PUSH)與出棧(POP) 之一

編輯:Delphi

 記得剛學多線程的時候, 碰到一個結構:

//Delphi 的語法描述 
PContext = ^TContext; 
_CONTEXT = record 
 ContextFlags: DWord; 
 Dr0: DWord; 
 Dr1: DWord; 
 Dr2: DWord; 
 Dr3: DWord; 
 Dr6: DWord; 
 Dr7: DWord; 
 FloatSave: TFloatingSaveArea; 
 SegGs: DWord; 
 SegFs: DWord; 
 SegEs: DWord; 
 SegDs: DWord; 
 Edi: DWord; 
 Esi: DWord; 
 Ebx: DWord; 
 Edx: DWord; 
 Ecx: DWord; 
 Eax: DWord; 
 Ebp: DWord; 
 Eip: DWord; 
 SegCs: DWord; 
 EFlags: DWord; 
 Esp: DWord; 
 SegSs: DWord; 
end; 

  從這個結構中可以基本洞察多線程的基本原理:

  1、在切換到另一個線程之前, 先把當前線程在寄存器中的數據保存在這個結構;

  2、重新切回線程時, 再才這個結構中讀出相關數據到寄存器, 從而繼續運行...

  壓棧、出棧也是類似的道理.

  一個程序包含若干子程序, 子程序中一般會有自己的參數或局部變量.

  在執行這個子程序前, 應該先把寄存器中的相關數據暫存一下(子程序也要使用寄存器), 這就是所謂的壓棧(PUSH);

  等子程序執行完畢, 再把之前壓到棧中的數據取回(而讓程序繼續執行), 這就是所謂的出棧(POP).

  什麼是 "棧"?

  程序把內存劃分了若干區域, 其中有 "全局數據區" 和 "局部數據區".

  全局數據所在的位置叫 "堆";

  局部數據(局部變量、局部常量、子程序參數)所在的位置叫 "棧", 也叫 "堆棧".

 對 "堆" 和 "棧", 前人給出了不同的使用規則:

  "堆" 中的數據一般是由下到上排列;

  "棧" 的數據則完全相反, 是由下到上排列.

  驗證 "堆" 與 "棧" 不同的數據排列方式:

; Test17_1.asm 
.386 
.model flat, stdcall 
 
include  Windows.inc 
include  kernel32.inc 
include  masm32.inc 
include  debug.inc 
includelib kernel32.lib 
includelib masm32.lib 
includelib debug.lib 
 
.data? 
   GlobalVal1 dd ? 
   GlobalVal2 dd ? 
   GlobalVal3 dd ? 
.code 
 
main proc 
   LOCAL LocalVal1:dword, LocalVal2:dword, LocalVal3:dWord 
   
  ;獲取全局變量地址(地址是順序遞增的): 
   PrintHex offset GlobalVal1 ;00403054 
   PrintHex offset GlobalVal2 ;00403058 
   PrintHex offset GlobalVal3 ;0040305C 
   
  ;獲取局部變量地址(地址是順序遞減的): 
   lea eax, LocalVal1 
   PrintHex eax           ;0012FFBC 
   lea eax, LocalVal2 
   PrintHex eax           ;0012FFB8 
   lea eax, LocalVal3 
   PrintHex eax           ;0012FFB4 
   ret 
main endp 
end main 

  壓棧與出棧的順序:

.386 
.model flat, stdcall 
 
include  Windows.inc 
include  kernel32.inc 
include  masm32.inc 
include  debug.inc 
includelib kernel32.lib 
includelib masm32.lib 
includelib debug.lib 
 
.data 
   val1 dd 111 
   val2 dd 222 
   val3 dd 333 
.code 
main proc 
   push val1 
   push val2 
   push val3 
  ;壓棧完畢, 接著出棧 
   pop val1 
   pop val2 
   pop val3 
  ;查看取回的數據: 
   PrintDec val1 ;333 
   PrintDec val2 ;222 
   PrintDec val3 ;111 
  ;怎麼反了? 這就是常說的 "棧中的數據是先進後出"! 讓後進的先出就好了. 
   ret 
main endp 
end main 

根據 "棧" 先進後出的特點, 寫一個變量換值的程序:

; Test17_3.asm 
.386 
.model flat, stdcall 
 
include  Windows.inc 
include  kernel32.inc 
include  masm32.inc 
include  debug.inc 
includelib kernel32.lib 
includelib masm32.lib 
includelib debug.lib 
 
.data 
   val1 dd 111 
   val2 dd 999 
.code 
 
main proc 
   push val1 
   push val2 
   pop val1 
   pop val2 
  ;現在 val1 和 val2 的值已經交換 
   PrintDec val1 ;999 
   PrintDec val2 ;111 
   ret 
main endp 
end main 

  如果僅是交換變量的值, 可以使用 XCHG 指令:

; Test17_4.asm 
.386 
.model flat, stdcall 
 
include  Windows.inc 
include  kernel32.inc 
include  masm32.inc 
include  debug.inc 
includelib kernel32.lib 
includelib masm32.lib 
includelib debug.lib 
 
.data 
   val1 dd 111 
   val2 dd 999 
.code 
 
main proc 
  ;xchg va1, val2 ;指令都不支持對兩個變量直接操作, 需要用個寄存器中轉下 
   mov eax, val1 
   xchg eax, val2 
   mov val1, eax 
   PrintDec val1  ;999 
   PrintDec val2  ;111 
   ret 
main endp 
end main 

  根據上面的原理, 也可以方便寫出一個翻轉字符串的函數:

; Test17_5.asm 
.386 
.model flat, stdcall 
 
include  Windows.inc 
include  kernel32.inc 
include  masm32.inc 
include  debug.inc 
includelib kernel32.lib 
includelib masm32.lib 
includelib debug.lib 
 
.data 
   szText db 'Hello World!', 0 
.code 
 
main proc 
  ;把字符串中的字符逐個壓入棧中 
   mov ecx, sizeof szText - 1 ;把字符串長度(將要反復的次數)給 ecx, 沒包括結束記號 
   xor esi, esi           ;清空 esi, 准備用作數組索引 
@@: movzx eax, szText[esi]    ;循環讀出並壓棧 
   push eax 
   inc esi 
   loop @B 
   
  ;從棧中逐個取出並寫入字符串 
   mov ecx, sizeof szText - 1 
   xor esi, esi 
@@: pop eax 
   mov szText[esi], al 
   inc esi 
   loop @B 
   
   PrintString szText ;!dlroW olleH 
   ret 
main endp 
end main 
;做這個程序也有更好的方案, 譬如用 movs 




  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved