学 Win32 汇编[17]: 关于压栈(PUSH)与出栈(POP) 之一


记得刚学多线程的时候, 碰到一个结构:
//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, stdcallinclude    windows.incinclude    kernel32.incinclude    masm32.incinclude    debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.data?    GlobalVal1 dd ?    GlobalVal2 dd ?    GlobalVal3 dd ?.codemain 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    retmain endpend main

压栈与出栈的顺序:
.386.model flat, stdcallinclude    windows.incinclude    kernel32.incinclude    masm32.incinclude    debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.data    val1 dd 111    val2 dd 222    val3 dd 333.codemain proc    push val1    push val2    push val3    ;压栈完毕, 接着出栈    pop val1    pop val2    pop val3    ;查看取回的数据:    PrintDec val1  ;333    PrintDec val2  ;222    PrintDec val3  ;111    ;怎么反了? 这就是常说的 "栈中的数据是先进后出"! 让后进的先出就好了.    retmain endpend main

根据 "栈" 先进后出的特点, 写一个变量换值的程序:
; Test17_3.asm.386.model flat, stdcallinclude    windows.incinclude    kernel32.incinclude    masm32.incinclude    debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.data    val1 dd 111    val2 dd 999.codemain proc    push val1    push val2    pop val1    pop val2    ;现在 val1 和 val2 的值已经交换    PrintDec val1  ;999    PrintDec val2  ;111    retmain endpend main

如果仅是交换变量的值, 可以使用 XCHG 指令:
; Test17_4.asm.386.model flat, stdcallinclude    windows.incinclude    kernel32.incinclude    masm32.incinclude    debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.data    val1 dd 111    val2 dd 999.codemain proc    ;xchg va1, val2 ;指令都不支持对两个变量直接操作, 需要用个寄存器中转下    mov  eax, val1    xchg eax, val2    mov  val1, eax    PrintDec val1   ;999    PrintDec val2   ;111    retmain endpend main

根据上面的原理, 也可以方便写出一个翻转字符串的函数:
; Test17_5.asm.386.model flat, stdcallinclude    windows.incinclude    kernel32.incinclude    masm32.incinclude    debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.data    szText db 'Hello World!', 0.codemain 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    retmain endpend main;做这个程序也有更好的方案, 譬如用 movs

posted on 2010-04-10 18:16 万一 阅读(3295) 评论(0) 编辑 收藏©著作权归作者所有:来自51CTO博客作者JLee79的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 学 Win32 汇编[32] - 子程序进阶
  2. 宝塔强行卸载mysql数据库
  3. jQuery中的$.ajax方法,以及基本的vue指令操作
  4. IHS Markit:85%的运营商计划部署智能端局(CO)
  5. 全息政府行业数据安全解决方案
  6. Jaeger-实践处理相关计划
  7. 【PHP连接MySQL】我来手把手教你PHP怎么连接Mysql数据库
  8. 浅谈 Integer 类
  9. Pinpoint-技术专区-全流程学习

随机推荐

  1. android 屏幕方向切换 锁定方向
  2. Caused by: java.lang.IllegalStateExcep
  3. Android 基础 源码 工具
  4. Android 呼吸灯流程分析(一)
  5. android中校验email是否合法
  6. Android中设置控件透明度的方法
  7. 封装的一个android底部操作弹出窗
  8. Android文档阅读03—开发工具
  9. [置顶] Android修改源代码控制不锁屏
  10. android 系统状态栏的隐藏和显示