汇编语言——基于x86处理器 笔记

目录

    • 安装环境
    • 第一章 基本概念
    • 第二章 x86处理器架构
    • 第三章 汇编语言基础
    • 第四章
    • 第五章 过程
        • 定义并使用过程
        • 链接到外部库
    • 第六章 条件处理
    • 第七章 整数运算
    • 第八章 高级过程

安装环境

  • 本书源代码 http://asmirvine.com/gettingStartedVS2017/Irvine.zip
  • https://blog.csdn.net/caipengbenren/article/details/88148018
brew install --cask dosbox
mkdir -p ~/dos/masm
# 把当前文件下的文件夹masm的所有文件复制到~/dos/masm中,这样就成功安装masm

# 打开dosbox
mount c ~/dos/masm # 挂载本地磁盘

# hello.asm 已在masm中
masm hello.asm # 一直回车到ok
link HELLO.OBJ # 一直回车到ok
hello.exe # 运行输出
  • nasm
brew reinstall nasm 
nasm -f macho64 -o hello.o hello64.asm # 64
ld -o hello -e _main hello.o -static

第一章 基本概念

  • 汇编器是一种程序,用于将源程序从汇编语言转换为机器语言。
  • 链接器是把汇编器生成的单个文件组合成一个可执行程序。
  • 调试器:为程序员提供一种途径来追踪程序的执行过程,并查看内存的内容。
  • 汇编语言和机器语言是一对一的关系,即一条汇编指令对应于一条机器指令,汇编语言不具有移植性,它与具体的处理器绑定。
  • 在展示计算机体系结构中的每一层如何表示为一个机器抽象时,虚拟机概念是一种有效的方式。每层可以用硬件或软件构成,其上编写的程序可以用其下一层进行翻译或解释。虚拟机概念可以与真实世界中的计算机层次相关,包括数学逻辑、指令集架构、汇编语言和高级语言。

第二章 x86处理器架构

  • CPU是进行算术和逻辑操作的部件,包含了有限数量的存储位置——寄存器(register),一个高频时钟、一个控制单元和一个算术逻辑单元。
    • 时钟对CPU内部操作与系统其他组件进行同步
    • 控制单元协调参与机器指令执行的步骤序列
    • 算术逻辑单元执行算术运算,如加法、减法,以及逻辑运算
  • 总线bus是一组并行线,用于将数据从计算机的一部分传送到另一部分
    • 数据总线:在CPU和内存之间传送指令和数据
    • I/O总线:在CPU和系统输入/输出设备之间传输数据
    • 控制总线:用二进制信号对所有连接在系统总线上设备的行为进行同步
    • 地址总线:当前执行指令在CPU和内存之间传输地址时,地址总线用于保持指令和数据的地址。
  • 指令执行周期:取值Fetch、译码Decode和执行Execute
  • CPU从内存读取一个值的步骤:
    • 将想要读取的值的地址放到地址总线上
    • 设置处理器RD(读取)引脚(改变RD的值)
    • 等待一个时钟周期给存储器芯片进行响应
    • 将数据总数据总线复制到目标操作数。
  • 程序加载器:加载程序到内存。
  • x86处理器操作模式
    • 保护模式:处理器原生状态,所有的指令和特性都是可用的。分配给程序的独立内存区域称为段,而处理器会阻止程序使用自身段范围之外的内存。
    • 虚拟8086模式:保护模式下,处理器可以在一个安全环境中直接执行实地址模式软件,如MS-DOS程序。
    • 实地址模式:早期的Intel处理器编程环境,但是增加了一些其他的特性,如切换到其他模式的功能。当程序需要直接访问系统内存和硬件设备时,这个模式就很有用。
    • 系统管理模式:向操作系统提供了实现诸如电源管理和系统安全等功能的限制
  • 基本程序执行寄存器
    • 通用寄存器:主要用于算术运算、数据传输和逻辑操作
      • EAX EBX ECX EDX。EAX ,16位 AX, 8位AH和AL
      • ESI EBP EDI ESP。只有16位 SI等,没有8位
      • 特殊用法
        • 乘法指令默认使用EAX,它常常被称为扩展累加器寄存器(extended accumulator)
        • CPU默认使用ECX为循环计数器
        • ESP用于寻址堆栈数据。它极少用于一般算术运算和数据传输,被称为扩展堆栈指针寄存器(extended stack register)。
        • ESI和EDI用于高速存储器传输指令,也被称为扩展源变址寄存器(extended source register)和扩展目的变址寄存器(extended destination register)。
        • 高级语言通过EBP来引用堆栈中的函数参数和局部变量。一般不用于算术运算和数据传输。也被称为扩展帧寄存器(extended frame register)
    • 段寄存器:16位段寄存器用于表示的是预先分配的内存区域的基址,这个内存区域称为段。保护模式中,段寄存器中存放的是段描述符表指针。
      • CS SS DS ES FS GS
    • 指令指针:EIP寄存器中包含下一条将要执行指令的地址。某些机器指令能控制EIP,使得程序分支转向一个新位置
    • EFLAGS寄存器:包含一个独立的二进制位,用于控制CPU的操作,或者反映一些CPU操作的结果。有些指令可以测试和控制这些单独的处理器标志位
    • 控制标志位:控制CPU的操作
    • 状态标志位:反映了CPU执行的算术和逻辑操作的结果。
      • 进位标志位(CF):无符号算术运算结果太大时,设置该标志位
      • 溢出标志位(OF):有符号算术运算太大或太小时,设置该标志位
      • 符号标志位(SF):算术或逻辑操作产生负结果时,设置该标志位
      • 零标志位(ZF):算术或逻辑操作产生结果为零时,设置该标志位
      • 辅助进位标志位(AC):算术操作在8位操作数中产生了位3向位4进位时,设置该标志位
      • 奇偶校验标志位(PF):结果的最低有效字节包含偶数个1时,设置该标志位,否则,清除该标志位。用于错误检测。
  • MMX寄存器:8个64位MMX寄存器支持称为SIMD的特殊指令。提升Intel的处理器性能
  • XMM寄存器:8个128位XMM寄存器,被用于SIMD流欧战指令集
    • 浮点单元FPU执行高速浮点算术运算
  • 内存管理
    • 保护模式是最可靠、最强大的,但是她对应用程序直接访问系统硬件有着严格的限制
    • 在实地址模式,只能寻址1MB内存。地址从00000H到FFFFFH。处理器一次只能运行一个程序,但是可以暂时中断程序来处理来自外围的请求(中断)。

第三章 汇编语言基础

  • 变量
.data
sum DWORD 0 ; 定义名为sum的变量
  • 保留字:有特殊意义并且只能在其正确的上下文中使用
  • 标识符:程序员选择的名称,它用于标识变量、常数、子程序和代码标签。
  • 伪指令(directive)是嵌入源代码中的命令,由汇编器识别和执行
    • DWORD伪指令告诉汇编器在程序中为一个双字节变量保存空间
    myVar DWORD 26
    mov   eax,myVar
    
    • .data:标识一个段可用于定义变量
    • .code:标识程序区段包含了可执行的指令
  • 指令:在程序汇编编译时变得可执行。[label:] mnemonic [operands] [;comment]
    • 标号:指令和数据的位置标记
    • 指令助记符:mov add等
    • 操作数:个数范围为1~3个。
    stc             ; 进位标志位置1
    inc eax         ; eax加1
    mov count,ebx   ; 将abx传送给变量count
    imul eax,ebx,5 ;将ebx与5相乘,结果存放到eax中
    
  • 注释:单行注释 “;”。块注释
COMMENT !
this is comment
this is comment
!
  • NOP 空操作指令,它在程序中占用一个字节,但是不做任何操作

  • DUP操作符:使用一个整数表达式作为计数器,为多个数据项分配存储空间。

BYTE 20 DUP(0) ; 20个字节,值都为0
BYTE 20 DUP(?) ; 20个字节,非初始化
BYTE 4 DUP("STACK") ; 20个字节
  • 汇编器读取源文件,并生成目标文件,即对程序的机器语言翻译

  • 链接器读取并检查目标文件,以便发现该程序是否包含了任何对链接库中过程的调用。链接器从链接库中复制任何被请求的过程,将它们与目标文件组合,以生成可执行文件。

  • 列表文件(listing file)包括了程序源文件的副本,再加上行号、每条指令的数字地址、每条指令的机器代码字节以及符号表。

  • 符号表中包含了程序中所有标识符的名称、段和相关信息。

  • 符号常量:为整数表达式或文本指定标识符创建符号常量。不预留存储空间,它只在汇编器扫描程序时运行。当前地址计数器符号 $

    • 等号伪指令:把一个符号名称与一个整数连接起来
    • EQU伪指令:把一个符号名称与一个整数或任意文本连接起来
    • TEXTEQU:文本宏。
COUNT = 500
mov eax,COUNT 

selfPtr DWORD $ ; 声明变量,初始化为该变量的偏移量

list BYTE 10,20,30,40
ListSize = ($ - list)

PI EQU <3.14159>

第四章

  • visual studio https://blog.csdn.net/weixin_43272781/article/details/104988520
  • 操作数
    • 立即数:使用数字文本表达式
    • 寄存器操作数:使用CPU内已命名的寄存器
    • 内存操作数:引用内存位置
  • movzx进行全零扩展并传送:将源操作数复制到目的操作数,并把目的操作数0扩展到16位或32位。这条指令只用于无符号数。
.data
byteVal BYTE 10001111b
.code
movzx ax,byteVal ; ax=000000000 10001111b
  • movsx进行符号扩展并传送。扩展到16位或32位,只用于有整数
.data
byteVal BYTE 10001111b
.code
movsx ax,byteVal ; ax=11111111 010001111b
  • LAHF 加载状态标志到AH:将EFLAGS寄存器的低字节复制到AH
  • SAHF:保存AH内容到状态标志位
  • XCHG:交换指令,交换两个操作数内容
XCHG reg,reg
XCHG reg,mem
XCHG mem,reg
  • 直接——偏移量操作数:变量名加上一个偏移量
arrayC WORD 1234h,2345h,3456h,5678h
mov ax,[arrayC+2]   ; AX=2345h
mov bx,[arrayC+4]   ; BX=3456h
  • CPU标志位
    • UP 方向 EI 中断
    • CY 进位,无符号整数溢出
    • OV 溢出 有符号整数溢出
    • ZR 零 意味着操作结果为0
    • PL 符号 意味着产生的结果为负数
    • PE 奇偶 在一条算术或布尔运算指令执行之后。立即判断目的操作数最低有效字节中1的个数是否为偶数
    • AC 辅助进位 1表示目的操作数最低有效字节中位3有进位
  • 加法和减法
    • INC 自增 INC reg/mem
    • DEC 自减
    • ADD 将长度相同的源操作数和目的操作数进行相加操作
    • SUB desc,source 从目的操作数中减去源操作数
    • NEG 非指令,把操作数转换为二进制补码
  • 与数据相关的运算符和伪指令
    • OFFSET 运算符返回的是一个变量与其所在段起始地址之间的距离 mov esi,OFFSET aVal
    • ALIGN伪指令将一个变量对其。ALIGN bound(1,2,4,8,16)
    • PTR 运算符可以重写操作符默认的大小类型。可大送小,也可小送大
    .data
    myDouble DWORD 12345678h
    wordList WORD 5678h,1234h
    .code
    mov ax,WORD PTR myDouble ; 5678h
    mov bx,WORD PTR [myDouble+2] ; 1234h
    ; x86是小端序,低地址存放高位。78 56 34 12
    ; 将较小的值送入较大的目的操作数
    mov eax,DWORD PTR wordList ;EAX=12345678h
    
    • TYPE运算符 返回变量单个元素的大小
    • LENGTHOF运算符 计算数组中元素的个数
    • SIZEOF运算符 = LENGTHOF * TYPE
    • LABEL伪指令可以插入一个符号,并定义它的大小属性,但是不为这个符号分配存储空间。LABEL常见用法是,为数据段中定义的下一个变量提供不同的名称和大小属性
    .data
    val16 LABEL WORD
    val32 DWORD 12345678h
    .code 
    MOV ax,[val16] ; 5678h
    MOV DX,[val16+2] ; 1234h
    
  • 间接寻址:寄存器作为指针
    • 任何一个32位通用寄存器(EAX,EBX,ECX,EDX,ESI,EDI,EBP,ESP)加上括号就能构成一个间接操作数。寄存器中存放的是数据的地址。
    mov eax,OFFSET myDouble
    mov ebx,[eax] ; 把myDouble的值写入ebx寄存器中
    mov ecx,888888
    mov [eax],ecx ; ecx内容写入寄存器eax存放值的内存地址
    
    • 变址操作数:在寄存器上加上常数产生一个有效地址
    .data
    arrayD DWORD 1,2,3,4
    arrayB BYTE 1,2,3,4
    mov esi,0
    mov al,arrayB[esi] ; arrayB[esi] = [arrayB+esi] al=1
    
    mov eax,arrayD[esi*4] ;eax=4
    mov eax,arrayD[esi*TYPE arrayD] ;eax=4
    
    • 指针:如果一个变量包含另一个变量的地址,则该变量称为指针。
    .data
    arrayB BYTE 10h,20h,30h,40h
    ptrB DWORD arrayB ; ptrB包含了arrayB的偏移量
    
    ptrB DWORD OFFSET arrayB
    
    • TYPEDEF运算符:可以创建用户自定义类型,这些类型包含了定义变量时内置类型的所有状态。
    PBYTE TYPEDEF PTR BYTE ;字节指针
    PWORD TYPEDEF PTR WORD  ;字指针
    PDWORD TYPEDEF PTR DWORD ; 双字指针
    
  • JMP和LOOP指令
    • 无条件转移:无论什么情况都会转移到新地址。JMP destination
    • 条件转移:满足某种条件,则程序出现分支。CPU基于ECX和标志寄存器的内容来解释真/假条件。
    • LOOP指令:正式称为按照ECX计数器循环,将程序块重复特定次数。ECX自动成为计数器,每循环一次计数值减1。
    .data
    count DWORD ? 
    .code
    main proc
        mov eax,0h
        mov edx,0h
        mov ecx,100h
        L1:
            inc eax
            mov count,ecx ; 外层L1循环次数保存变量
            mov ecx,20h
            L2:             ; 嵌套循环
                inc edx
                loop L2
            mov ecx,count ; 外层循环次数还原
        loop L1
    

第五章 过程

  • 堆栈的应用
    • 当寄存器用于多个目的时,堆栈可以作为寄存器的一个方便的临时保存区。在寄存器被修改后,还可以恢复其初始值。
    • 执行CALL指令时,CPU在堆栈中保存当前过程的返回地址。
    • 调用过程时,输入数值也被称为参数,通过将其压入堆栈实现参数传递。
    • 堆栈也为过程局部变量提供了临时存储区域。
  • PUSH POP
PUSH reg/mem16
PUSH reg/mem32

POP reg/mem16
POP reg/mem32
  • PUSHFD POPFD:把32位的EFLAGS寄存器压入栈或出栈
  • PUSHAD指令按照EAX ECX EDX EBX ESP(执行PUSHAD之前的值)、EBP ESI EDI的顺序入栈,POPAD按相反顺序出栈

定义并使用过程

  • 过程用PROC和ENDP伪指令定义。
  • 在程序启动过程之外创建一个过程时,就用RET指令来结束它,RET强制CPU返回到该过程被调用的位置
  • CALL指令调用一个过程,指挥处理器从新的内存地址开始执行。从物理上来说,CALL指令将其返回地址压入堆栈,再把调用过程的地址复制到指令指针寄存器。当过程准备返回时,它的RET指令从堆栈把返回地址弹回到指令指针寄存器。32位模式下,CPU执行的指令从EIP(指令指针寄存器)在内存中指出。
  • 向过程传递寄存器参数
.data
theSum DWORD ?
.code
main PROC
    MOV eax,1000h   ;参数
    mov ebx,2000h   ;参数
    mov ecx,3000h   ;参数
    call SumOf      ;EAX=EAX+EBX+ECX
    mov theSum,eax  ;保存和数
    ...
  • 保存和恢复寄存器值
    • USES运算符,让程序员列出过程中修改的所有寄存器名,USES告诉汇编器:
    • 在过程开始时生成PUSH指令,将寄存器保存到堆栈;
    • 在过程结束时生成POP指令,从堆栈恢复寄存器的值
    ; USES esi ecx 相当于注释的push和pop
    ArraySum proc USES esi ecx
    	;push  esi 				; save ESI, ECX
    	;push  ecx
    	mov   eax,0				; set the sum to zero
    
    L1:
    	add   eax,[esi]			; add each integer to sum
    	add   esi,TYPE DWORD	; point to next integer
    	loop  L1				; repeat for array size
    	;pop   ecx				; restore ECX, ESI
    	;pop   esi
    	ret 					; sum is in EAX
    ArraySum endp
    
    • 当过程利用寄存器(通常时EAX)返回数值时,保存使用寄存器的惯例就出现了一个例外。在这种情况下,返回寄存器不能被压入和弹出堆栈。

链接到外部库

  • 链接库是一种文件,包含了已经汇编为机器代码的过程(子程序)。链接库开始时一个或多个源文件,这些文件再被汇编成目标文件。目标文件插入到一个特殊格式文件,该文件由链接器工具识别。
  • 假设程序要调用过程WriteString在控制台窗口显示一个字符串。该过程源代码必须包含PROTO伪指令来标识WriteString过程。
  • 当程序进行汇编时,汇编器不指定CALL指令的目标地址,它知道这个地址将由链接器指定。链接器在链接库中寻找WriteString,并把库中适当的机器指令复制到程序的可执行文件中。同时,它把WriteString的地址插入到CALL指令。如果被调用过程不在链接库中,链接器就发出错误信息。且不会生成可执行文件。

第六章 条件处理

  • TEST指令 不修改操作数的and比较
  • AND指令 OR NOT XOR
  • CMP 比较整数;执行从目标操作数中减去源操作数的隐含减法操作,并且不修改任何操作。
  • 条件跳转
    • JZ/JNZ 为零跳转/非零跳转
    • JC/JNC 进位跳转/无进位跳转
    • JO/JNO 溢出跳转
    • JS/JNS 符号跳转
    • JP/JNP 偶校验/奇校验跳转
    • 相等性比较
      • JE/JNE 相等跳转
      • JCXZ CX=0跳转
    • 基于无符号比较
      • jA 大于跳转 JAE 大于等于
      • JB 小于跳转 JBE
      • JNA 不大于跳转
      • JNB 不小于跳转
    • 有符号比较
      • JG 大于跳转 JGE 大于等于
      • JL 小于跳转 JLE
      • JNL 不小于跳转
      • JNG 不大于跳转
  • 条件循环指令
    • LOOPZ和LOOPE 为零跳转 相等跳转
    • LOOPNZ LOOPNE
  • 条件结构
    • if
    if (op1>op2){x=1;y=2;}else{z=3;}
        mov eax,op1
        cmp eax,op2
        jAE L1
        mov x,1
        mov y,1
        jmp L2
    L1: mov z,3
    L2:
    
  • 表驱动选择:用查表来代替多路选择结构的一种方法。
  • 有限状态机 FSM

第七章 整数运算

  • LSB最低有效位 ;MSB最高有效位
  • 逻辑移位,空出来的位用0填充。SHL SHR
  • 算数移位,空出来的位用原数据的符号位填充。SAL SAR
    • SAL 低位用0填充
    • SAR 高位用符号位填充
  • 位元循环:以循环方式来移位
    • ROL 循环左移。把所有位都向左移
    • ROL 循环右移
    • 位组交换:利用ROL卡伊交换一个字节的高四位和第四位 rol al,4
  • 带进位位元循环
    • RCL带进位循环左移:把每一位都向左移,进位标志位复制到LSB,而MSB复制到进位标志位
    • RCR 带进位循左移:
    mov bl,88h  ; CF,BL=0 10001000b
    rcl bl,1    ; CF,BL=1 00010000b
    rcl bl,1    ; CF,BL=0 00100001b
    
  • 乘法
    • MUL 无符号乘法
    • IMUL 有符号乘法
    IMUL reg/mem8   ; AX = AL * reg/mem8
    IMUL reg/mem16  ; DX:AX = AX * reg/mem16
    IMUL reg/mem32  ; EDX:EAX = EAX * reg/mem32
    
  • 除法
    • DIV 无符号除法
    DIV reg/mem8    ; 被除数AX      商AL        余数AH
    DIV reg/mem16   ; 被除数DX:AX   商 AX       余数 DX
    DIV reg/mem32   ; 被除数EDX:EAX 商 EAX      余数 EDX
    
    • IDIV 有符号除法
  • CBW(字节转字)指令把AL的符号位扩展到AH寄存器。CDQ(双字转四字)指令把EAX的符号位扩展到EDX寄存器。CWD(字转双字)指令把AX的符号位扩展到DX寄存器。
  • 扩展精度加减法:基本没有大小限制的加减法
    • ADC(带进位加法)将源操作数和进位标志位的值都与目的操作数相加。
    mov dl,0
    mov al,0ffh
    add al,0ffh     ; AL = feh
    adc dl,0        ;  DL/AL = 01FEh ==> {DL 0000001(1) AL 11111110(FEh)}
    
    • SBB(带借位减法):从目的操作数中减去源操作数和进位标志位的值

第八章 高级过程

  • 大多数现代编程语言在调用子程序之前都会把参数压入堆栈。反过来,子程序也常常把它们的局部变量压入堆栈。
  • 堆栈帧(或活动记录):是一块堆栈保留区域,用于存放被传递的实际参数、子程序的返回值、局部变量以及被保存的寄存器。创建步骤如下
    • 被传递的实际参数。如果有,压入堆栈
    • 当子程序被调用时,使该子程序的返回值压入堆栈。
    • 子程序开始执行时,EBP被压入栈
    • 设置EBP等于ESP。从这时开始,EBP就变成了该子程序所有参数的引用基址。
    • 如果有局部变量,修改ESP以便在堆栈中为这些变量预留空间
    • 如果需要保存寄存器,就将它们压入堆栈
  • 寄存器参数:采用寄存器传递参数,调用子程序前需要把寄存器入栈,子程序返回需要出栈恢复寄存器。缺点,额外的出栈入栈不仅使代码混乱,还有可能消除性能优势。
  • 值传递:当一个参数通过数值传递时,该值的副本会被压入堆栈。
.data
val1 DWORD 5
val2 DWORD 6
.code
push val2
push val1
call AddTwo
  • 引用传递:通过引用来传递的参数包含的是对象的地址(偏移量)
push OFFSET val2
push OFFSET val1
call Swap
  • 基址——偏移量寻址:访问堆栈,EBP是基址寄存器,偏移量是常数。
int AddTwo(int x,int y)
{
    return x+y;
}

AddTwo PROC
    push ebp
    mov ebp,esp         ; 堆栈帧的基址
    mov eax,[ebp+12]    ; 第二个参数
    add eax,[ebp+8]     ; 第一个参数
    pop ebp
    ret
AddTwo PROC
  • 调用规范
    • C调用规范:子程序的参数逆序入栈。程序调用子程序时,在CALL指令的后面紧跟一条语句使堆栈指针(ESP)加上一个数,该数的值即为子程序参数锁占用堆栈空间的总和。
    ; AddTwo(5,6)
    Example1 PROC
        push 6
        push 5
        call AddTwo
        add esp,8 ; 从堆栈移除参数
        ret
    Example1 ENDP
    
    • STDCALL 调用规范:给RET指令添加一个整数参数param,使得程序在返回到调用过程时,ESP会加上数值param。
    AddTwo PROC
        push ebp
        mov ebp,esp         ; 堆栈帧的基址
        mov eax,[ebp+12]    ; 第二个参数
        add eax,[ebp+8]     ; 第一个参数
        pop ebp
        ret 8               ; 清除堆栈
    AddTwo PROC
    
    • STDCALL不仅减少了子程序调用产生的代码量(减少了一条指令),还保证了调用程序永远不会忘记清除堆栈。
    • C语言调用规范则允许子程序声明不同数量的参数,主调程序可以决定传递多少个参数
    • 通常,子程序在修改寄存器之前要将它们的当前值保存到堆栈。
  • 局部变量:高级语言中,在单一子程序内新建、使用和撤销的变量被称为局部变量。局部变量创建于运行时堆栈,通常位于基址指针(EBP)之下。尽管不能在汇编时给它们分配默认值,但是能在运行时初始化它们
void MySub(){
    int X = 10;
    int Y = 20;
}

x_local EQU DWORD PTR [ebp-4] ; 局部变量符号
y_local EQU DWORD PTR [ebp-8]
MySub PROC
    push ebp
    mov ebp,esp
    sub esp,8                   ; 创建局部变量
    mov x_local,10    ; X
    mov y_local,20    ; Y
    mov esp,ebp                 ; 从堆栈在删除局部变量
    pop ebp
    ret
MySub ENDP
  • 引用参数通常是由过程用基址——偏移量寻址(从EBP)方式进行访问
  • LEA指令返回间接操作数地址。lea esi,[ebp-30]
  • ENTER和LEAVE指令。ENTER指令为被调用的过程自动创建堆栈帧。LEAVE指令结束一个堆栈帧。
MySub PROC
    ENTER 8,0                  
    mov x_local,10    ; X
    mov y_local,20    ; Y
    LEAVE
    ret
MySub ENDP
  • LOCAL伪指令,代替ENTER
MySub PROC
    LOCAL x_local:DWORD,y_local:DWORD             
    mov x_local,10    ; X
    mov y_local,20    ; Y
    ret
MySub ENDP
  • 递归子程序:用局部变量在堆栈中保存数据
  • INVOKE伪指令,只用于32位模式,将参数入栈并调用过程。ADDR运算符,在INVOKE调用过程中,它可以传递指针参数。
INVOKE FillArray,ADDR myArray, LENGTHOF myArray
  • PROC
  • PROTO伪指令为现有过程创建了原型(prototype)。原型声明了过程的名称和参数列表。它允许在定义过程之前对其进行调用,并验证参数的数量和类型是否与过程定义相匹配。

你可能感兴趣的:(汇编,masm)