`
zzc1684
  • 浏览: 1229588 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

学 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, 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
分享到:
评论

相关推荐

    汇编言语——进栈出栈指令PPT学习教案.pptx

    本PPT学习教案主要讲解汇编语言中的进栈出栈指令,包括PUSH指令和POP指令的格式、功能、说明和使用方法。下面是本教案的详细知识点: 1. PUSH指令: * 格式:PUSH d * 功能:先将SP的值减去2,然后将操作数d指定的...

    汇编语言之 两个多位十进制数相加

    汇编语言之两个多位十进制数相加 本实验旨在学习数据传送和算术运算指令的用法,并熟悉在 PC 机上建立、汇编、链接、调试和运行 8086 汇编语言程序的过程。 一、实验目的: * 了解数据传送和算术运算指令的用法 *...

    汇编指令大全 汇编语言的学习

    - `PUSHA` 和 `POPA` / `PUSHAD` 和 `POPAD`:快速压栈和出栈一组寄存器,简化数据管理。 - `BSWAP`:用于交换32位寄存器内的字节顺序,常用于字节序转换。 - `XCHG`:交换两个操作数的值,可以是寄存器或存储器...

    常用汇编指令(常用汇编指令)

    汇编语言基础知识点 汇编语言是基于 CPU 指令集的低级编程语言,它使用符号表示来表示机器语言指令,从而使得编程更加灵活和高效。下面是常用的汇编指令分类: 1. 通用数据传送指令 MOV:传送字或字节 MOVSX:先...

    汇编语言中山大学本科教学课件

    - 堆栈操作:PUSH和POP用于数据的压栈和出栈,常用于函数调用保存现场和恢复现场。 7. **输入输出** - 输入/输出(I/O)指令:如IN和OUT,用于与外部设备通信。 - BIOS和DOS中断:通过INT指令调用,提供系统服务...

    汇编语言第三版 王爽

    - 处理器控制指令:如PUSH(压栈)、POP(出栈)、MOV(移动数据)等。 4. 实模式与保护模式: - 实模式:早期的8086处理器工作模式,无内存保护,程序可以直接访问所有内存。 - 保护模式:引入了段机制和特权级...

    新版汇编语言程序设计钱晓捷习题答参照.pdf

    堆栈的两种基本操作是压栈和出栈,对应的指令是 PUSH 和 POP。 堆栈的工作原则 在习题 2.4 中,我们可以看到堆栈的工作原则是先进后出。例如,push ax 指令将 AX 寄存器的值压栈,pop bx 指令将堆栈顶部的值弹出到...

    汇编语言程序设计(钱晓捷)课后答案

    * 堆栈的基本操作:压栈和出栈 知识点4:Flags 和状态标志 * AL 寄存器的赋值,如 AL = 89h 等 * CF、ZF、SF、OF、PF 等标志的概念和使用 * 如何根据 AL 寄存器的值设置状态标志 知识点5:算术逻辑操作 * 加减...

    汇编指令查询器

    4. 存储操作指令:如PUSH(压栈)、POP(出栈)、LEA(取地址)等,用于管理内存中的数据。 二、汇编指令查询的重要性 1. 学习理解:汇编指令查询器可以帮助初学者快速查找和理解各种指令的功能和用法,深入学习...

    单片机设计案列单片机汇编例程学习

    堆栈操作指令如PUSH、POP用于数据压栈和出栈。 四、流程控制与循环 汇编语言中的流程控制包括条件跳转(如JZ、JNE)、无条件跳转(如AJMP、SJMP)、子程序调用(LCALL)和返回(RET)。通过这些指令,可以实现循环...

    8080汇编手册.pdf

    - `PUSH`和`POP`:用于数据入栈和出栈,常用于保存和恢复寄存器的状态。 - `PUSHA`和`POPA`:一次性将一组寄存器压入或弹出堆栈,节省了单独操作每个寄存器的时间。 - `PUSHAD`和`POPAD`:类似于`PUSHA`和`POPA`...

    AT & T汇编语言

    这本书《Professional Assembly Language》由Richard Blum编写,是市面上少见的专门讲解AT&T格式汇编语言的书籍之一。 #### 二、AT&T汇编语言特点 - **寄存器命名**:在AT&T汇编语言中,寄存器的命名方式是从左到右...

    汇编-使用gcc进行汇编内容查看.pdf

    - **push**/ **pop**:压栈和出栈指令,用于管理函数调用时的局部变量和参数。 - **lea**:加载有效地址指令,用于计算内存地址。 - **示例解读**:对于`func()`函数,其汇编代码可能包含类似`mov $42, %eax`的...

    AT&T汇编语言手册2005老版本

    - 存储和内存访问指令:如`mov`(移动数据)、`push`(压栈)、`pop`(出栈)等。 - 控制流程指令:如`call`(调用子程序)、`ret`(返回)等。 4. **汇编程序设计**: - 静态链接和动态链接:静态链接在编译时...

    80x86汇编指令大全总结珍藏版

    - `PUSHA` 和 `POPA`:一次性将所有通用寄存器压栈或出栈,简化了数据保存和恢复过程。 - `PUSHAD` 和 `POPAD`:与`PUSHA`和`POPA`类似,但针对32位寄存器。 - `BSWAP`:用于交换32位寄存器中的字节顺序,例如在...

    部分8086汇编指令集

    8086汇编指令集是一个广泛使用的汇编指令集,主要应用于 Intel 8086 微处理器。这些指令可以分为四大类:数据传输指令、输入输出端口传送指令、目的地址传送指令和标志传送指令。 数据传输指令: * MOV:传送字或...

    汇编指令查询器(汇编)

    4. **处理内存和寄存器的指令**:如MOV(移动数据)、LEA(获取内存地址)、PUSH(压栈)、POP(出栈),用于数据的存储和传输。 5. **输入/输出指令**:如IN(从端口读取数据)、OUT(向端口写入数据),用于与...

    汇编语言指令大全和王爽教程

    4. **处理器控制指令**:如PUSH(压栈)、POP(出栈)、MOV(移动)等,用于管理寄存器和内存。 王爽的《汇编语言》第二版则更注重理论与实践的结合,书中可能会详细讲解: 1. **基本概念**:如地址、寄存器、指令...

    最常用汇编指令大全---学汇编不发愁

    - `PUSHF`/`POPF`: 用于将标志寄存器压栈/出栈,32位版本为`PUSHD`/`POPD`。 5. **算术运算指令** - `ADD`和`ADC`: 用于加法运算,`ADC`考虑进位。 - `INC`和`DEC`: 对数据进行加1和减1操作。 - `SBB`和`SUB`: ...

Global site tag (gtag.js) - Google Analytics