汇编语言

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
main PROC 00000020 00000025 ESP
2007年10月
call SumOf mov ebx,eax 00000025 EIP 00000040
汇编语言
add eax,ecx ret SumOf ENDP ESP 00000025 EIP 00000025
11
局部标号和全局标号 默认情况下,代码标号(以单个冒号结尾)有一个局部域,使 得它对其所在过程内的语句可见,这阻止了跳转或循环语句 转移到当前过程之外的标号。在极少数情况下,如果必须将 控制转移到当前过程之外的标号处,标号必须被声明为全局 的。声明全局标号,要在标号后跟两个冒号。例如:
theSum DWORD ? .code main PROC mov eax,10000h mov ebx,20000h mov ecx,30000h call SumOf mov theSum,eax exit main ENDP 汇编语言
;参数 ;参数 ;参数 ;EAX=(EAX+EBX+ECX) ;保存和 ;启动过程结束来自百度文库
压栈之后
00001000
00000006
000000A6
00000FFC
00000FF8 00000FF4 00000FF0
ESP
偏移
00001000 00000FFC 00000FF8 00000FF4
出栈之前 00000006 000000A5
偏移
00001000 00000FFC 00000FF8
PUSHF POPF
2007年10月
汇编语言
8
PUSHAD,PUSHA,POPAD和POPA指令 PUSHAD指令在堆栈上按下列顺序压入所有的32位通用寄存器 :EAX,ECX,EDX,EBX,ESP的原始值,EBP,ESI和EDI; POPAD指令以相反的顺序从堆栈中弹出这些通用寄存器。 与之类似,80286处理器引入的PUSHA指令以同样的顺序压入 所有的16位寄存器(AX,CX,DX,BX,SP的原始值,BP,SI和 DI);POPA指令则以相反顺序弹出这些寄存器。
假设合适的整数在调用过程之前已经存放在EAX,EBX和ECX寄存器中 了,函数将在EAX中返回相加的和: SumOf PROC add eax,ebx add eax,ecx ret SumOf ENDP
2007年10月
汇编语言
10
CALL和RET指令 CALL指令指示处理器在新的内存地址执行指令,以实现对过 程的调用。在过程中使用RET(return from procedure)指令 使处理器返回到程序中调用过程的地方继续执行。 从底层细节角度来讲,CALL指令将返回地址压入堆栈并将被 调用过程的地址拷贝到指令指针寄存器中;当程序返回时, RET指令从堆栈中弹出地址并送到指令指针寄存器中。CPU总 是执行指令指针寄存器EIP(在16位模式下是IP)所指向的内存 地址处的指令。 SumOf PROC add eax,ebx 调用和返回的例子 00000040
出栈之后 00000006 000000A5 00000001
00000001
0000002
ESP
ESP
汇编语言
00000FF4 00000FF0
5
00000FF0
2007年10月
在程序中堆栈有几种重要的用途: 寄存器在用做多种用途的时候,堆栈可方便地作为其临时保 存区域,在寄存器使用完毕之后,可恢复其原始值。 CALL指令执行的时候,CPU用堆栈保存当前过程的返回地址。 调用过程的时候,可以通过堆栈传递输入值(参数)。 过程内部的局部变量在堆栈上创建,过程结束时,这些变量 被丢弃。
main PROC jmp L2 L1:: ;错误! ;全局标号
exit
main ENDP sub PROC L2: jmp L1 ret sub ENDP
2007年10月 汇编语言 12
;局部标号 ;正确
向过程传递寄存器参数 如果过程执行某些诸如整数数组求和之类的标准操作,那么 在过程之内引用特定的变量名并不是什么好主意。如果准备 那么做的话,该过程就不可能用于其他数组了。 一个较好的方法是向过程传递数组的偏移,再传递一个整数 来表示数组元素的数目。我们称这些为参数(arguments)或输 入参数(input arguments)。在汇编语言中,常通过通用寄存 器来传递参数。 例子: .data
保护模式下的立即数总是32位的。在实地址模式下,如果未使用 .386(或更高)处理器伪指令,默认的立即数是16位的。

POP指令首先将ESP所指的堆栈元素拷贝到16位或32位的目的 操作数中,然后增加ESP的值。如果操作数是16位的,ESP值 将加2;如果操作数是32位的,ESP值将加4。其格式如下:
2007年10月
调用ArraySum:
.data Array DWORD 0000h,20000h,30000h,40000h theSum DWORD .code main PROC mov esi,OFFSET array mov ecx,LENGTHOF array call ArraySum mov theSum,eax; exit main ENDP
偏移
00001000 00000FFC 00000FF8 00000006
ESP
NASM汇编编译器 允许PUSH指令使 用特定的寄存器 ,如EAX。
00000FF4
00000FF0
2007年10月 汇编语言 4

压栈操作和出栈操作
偏移
压栈之前 00000006
偏移 ESP
00001000
00000FFC 00000FF8 00000FF4 00000FF0
;将每个整数加入和 ;指向下一个整数 ;循环 ;还原ECX和ESI的值
ecx esi
;通过EAX返回结果
汇编语言
14
保存和恢复寄存器 ArraySum过程的开始处ECX和ESI被压入堆栈,过程结束的时 候又被弹出,绝大多数修改寄存器的过程都是用这种方式。 修改寄存器值的过程应该总是保存和恢复寄存器值,以确保 调用程序本身的寄存器值不被覆盖。 与PROC伪指令配套使用的USES操作符允许列出被过程修改的 所有寄存器,它只是编译器做两件事:首先,在过程的开始 处生成PUSH指令在堆栈上保存寄存器值;其次,在过程的结 束处生成POP指令恢复这些寄存器值。USES操作符应紧跟PROC 伪指令,其后跟由空格或制表符(不是逗号)分隔的寄存器列 表。 例外:在过程要使用寄存器作为返回值得时候,千万不要将 用于返回值得寄存器压栈和弹出,否则过程的返回值很可能 丢失。
2007年10月 汇编语言 15
USES操作符例子
ArraySum PROC USES esi ecx ArraySum PROC push push mov L1: add add loop L2: pop pop ret ArraySum ENDP esi ecx eax,0 eax,[esi] esi,4 L1 ecx esi
2007年10月
汇编语言
6
PUSH和POP指令 PUSH指令首先减小ESP的值,然后将一个16位或32位的源操作 数拷贝至堆栈上。对于16位操作数,ESP值将减2;对于32位 操作数,ESP值将减4。PUSH指令有以下三种格式:
PUSH r/m16 PUSH r/m32 PUSH imm32

汇编语言
2007年10月
汇编语言
1
第5章 过程
2007年10月
汇编语言
2
堆栈操作

堆栈(stack)也被称为后进先出结构 (LIFO structure, last-in, firstout),这是因为最后压入堆栈的值总 是最先被取出。 堆栈数据结构遵循相同的规则:新值 总是被加到堆栈的顶端,数据也总是 从堆栈的最顶端取出。
2007年10月
汇编语言
17
Irvine32.lib链接库中包含的过程 Irvine32.inc Irvine32.asm
2007年10月
汇编语言
18
使用过程进行程序设计



任何稍微复杂一点的程序应用都会包含一些不同的步骤。把 所有的程序代码都写在一个过程之内是可能的,但是这样的 程序很难阅读和维护,相反我们最好把各种编程任务划分为 独立的过程。所有的过程可在同一源文件中,也可在多个文 件中。 开始写程序时,用一份说明书来详细列出程序究竟要做什么 是非常有帮助的,这通常是仔细分析要解决的问题的结果。 一说明书为起点,就可以开始设计升序了。 一种标准的设计方法是将整体的问题分割成独立的任务,每 个任务都可以在一个过程中实现。将问题细分为任务的过程 通常称为功能分解(functional decomposition),或自顶向 下的设计(up-down design)。
POP r/m16 POP r/m32
2007年10月
汇编语言
7
PUSHFD,PUSHF,POPFD和POPF指令 PUSHFD指令在堆栈上压入32位的EFLAGS寄存器的值,POPFD指 令将堆栈顶部的值弹出并送至EFLAGS寄存器:
PUSHFD POPFD

实地址模式程序使用PUSHF指令在堆栈上压入16位的FLAGS寄 存器的值,使用POPF指令从堆栈顶部弹出16位值并送到FLAGS 寄存器:

例子:反转字符串RevStr.asm。
2007年10月
汇编语言
9
过程的定义和使用
PROC伪指令 过程使用PROC和ENDP伪指令来声明,另外还必须给过程起一 个名字(一个有效的标识符)。创建除了程序启动过程之外的 其他过程时应以RET结束,以强制CPU返回到过程被调用的地 方。 例子:三个整数之和
13
2007年10月
例子:对整数数组求和
;----------------------------------------------------ArraySum PROC ; ; Calculates the sum of an array of 32-bit integers. ; Receives: ESI points to the array, ECX = array size ; Returns: EAX = sum of the array elements ;----------------------------------------------------push esi ;保存ESI和ECX的值 push ecx mov eax,0 ;将和设置为0 L1: add eax,[esi] add esi,4 loop L1 pop pop ret ArraySum ENDP

8 7 6 5 4 3 2 1
顶部
底部
2007年10月
汇编语言
3



运行时栈(runtime stack)是由CPU内部硬件直接支持的,也 是实现过程调用和过程返回机制的基本组成部分。 运行时栈是由CPU直接管理的内存数组,它使用两个寄存器: SS和ESP。在保护模式下,SS寄存器存放的是段选择器,用户 模式程序不应对其进行修改。ESP寄存器存放的是指向堆栈内 特定位置的一个32位偏移值。 我们很少需要直接操纵ESP的值,相反,ESP寄存器的值通常 是由CALL,RET,PUSH和POP等指令间接修改的。
mov L1:
eax,0
add eax,[esi] add esi,4 loop L1
ret ArraySum ENDP
2007年10月
汇编语言
16
与外部库链接




链接库(link library)是包含已经编译成机器码的过程的文件。一 个或多个包含过程、常量和变量的源文件被编译成目标文件,然后 这些目标文件被插入到库中。 假设程序要调用名为WriteString的过程在控制台上显示字符串,那 么程序就必须包含一条PROTO伪指令声明要调用的程序。在 Irvine32.inc中可以找到如下伪指令: WriteString PROTO 接下来,用一条CALL指令执行WriteString过程: call WriteString 在程序被编译的时候,编译器为CALL指令的目的地址留出空白,该 空白随后由链接器填充。链接器在链接库中查找WriteString这个名 字,并从库中把合适的机器指令拷贝到程序的可执行文件中,并把 WriteString的地址插入到CALL指令中。
相关文档
最新文档