1 / 22

汇编语言

汇编语言. 数学科学学院计算机应用技术专业. 修兴强 北京师范大学数学科学学院 E-mail: xq_xiu@yahoo.com.cn. 第 5 章 过程. 堆栈操作. 堆栈 (stack) 也被称为后进先出结构 ( LIFO structure, last-in, first-out) ,这是因为最后压入堆栈的值总是最先被取出。 堆栈数据结构遵循相同的 规则 :新值总是被加到堆栈的顶端,数据也总是从堆栈的最顶端取出。. 顶部. 8. 7. 6. 5. 4. 3. 底部. 2. 1.

cade
Download Presentation

汇编语言

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 汇编语言 数学科学学院计算机应用技术专业 修兴强 北京师范大学数学科学学院 E-mail: xq_xiu@yahoo.com.cn 汇编语言

  2. 第5章 过程 汇编语言

  3. 堆栈操作 • 堆栈(stack)也被称为后进先出结构(LIFO structure, last-in, first-out),这是因为最后压入堆栈的值总是最先被取出。 • 堆栈数据结构遵循相同的规则:新值总是被加到堆栈的顶端,数据也总是从堆栈的最顶端取出。 顶部 8 7 6 5 4 3 底部 2 1 汇编语言

  4. 运行时栈(runtime stack)是由CPU内部硬件直接支持的,也是实现过程调用和过程返回机制的基本组成部分。 • 运行时栈是由CPU直接管理的内存数组,它使用两个寄存器:SS和ESP。在保护模式下,SS寄存器存放的是段选择器,用户模式程序不应对其进行修改。ESP寄存器存放的是指向堆栈内特定位置的一个32位偏移值。 • 我们很少需要直接操纵ESP的值,相反,ESP寄存器的值通常是由CALL,RET,PUSH和POP等指令间接修改的。 偏移 ESP 00001000 00000006 00000FFC NASM汇编编译器允许PUSH指令使用特定的寄存器,如EAX。 00000FF8 00000FF4 00000FF0 汇编语言

  5. 压栈操作和出栈操作 压栈之后 偏移 偏移 压栈之前 00001000 00000006 ESP 00001000 00000006 ESP 000000A6 00000FFC 00000FFC 00000FF8 00000FF8 00000FF4 00000FF4 00000FF0 00000FF0 出栈之后 出栈之前 偏移 偏移 00001000 00000006 00001000 00000006 000000A5 000000A5 00000FFC 00000FFC 00000001 ESP 00000001 00000FF8 00000FF8 ESP 0000002 00000FF4 00000FF4 00000FF0 00000FF0 汇编语言

  6. 在程序中堆栈有几种重要的用途: • 寄存器在用做多种用途的时候,堆栈可方便地作为其临时保存区域,在寄存器使用完毕之后,可恢复其原始值。 • CALL指令执行的时候,CPU用堆栈保存当前过程的返回地址。 • 调用过程的时候,可以通过堆栈传递输入值(参数)。 • 过程内部的局部变量在堆栈上创建,过程结束时,这些变量被丢弃。 汇编语言

  7. PUSH和POP指令 • PUSH指令首先减小ESP的值,然后将一个16位或32位的源操作数拷贝至堆栈上。对于16位操作数,ESP值将减2;对于32位操作数,ESP值将减4。PUSH指令有以下三种格式: • 保护模式下的立即数总是32位的。在实地址模式下,如果未使用.386(或更高)处理器伪指令,默认的立即数是16位的。 • POP指令首先将ESP所指的堆栈元素拷贝到16位或32位的目的操作数中,然后增加ESP的值。如果操作数是16位的,ESP值将加2;如果操作数是32位的,ESP值将加4。其格式如下: PUSHr/m16 PUSH r/m32 PUSH imm32 POPr/m16 POP r/m32 汇编语言

  8. PUSHFD,PUSHF,POPFD和POPF指令 • PUSHFD指令在堆栈上压入32位的EFLAGS寄存器的值,POPFD指令将堆栈顶部的值弹出并送至EFLAGS寄存器: • 实地址模式程序使用PUSHF指令在堆栈上压入16位的FLAGS寄存器的值,使用POPF指令从堆栈顶部弹出16位值并送到FLAGS寄存器: PUSHFD POPFD PUSHF POPF 汇编语言

  9. 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指令则以相反顺序弹出这些寄存器。 • 例子:反转字符串RevStr.asm。 汇编语言

  10. 过程的定义和使用 PROC伪指令 • 过程使用PROC和ENDP伪指令来声明,另外还必须给过程起一个名字(一个有效的标识符)。创建除了程序启动过程之外的其他过程时应以RET结束,以强制CPU返回到过程被调用的地方。 • 例子:三个整数之和 假设合适的整数在调用过程之前已经存放在EAX,EBX和ECX寄存器中了,函数将在EAX中返回相加的和: SumOfPROC add eax,ebx add eax,ecx ret SumOf ENDP 汇编语言

  11. CALL和RET指令 • CALL指令指示处理器在新的内存地址执行指令,以实现对过程的调用。在过程中使用RET(return from procedure)指令使处理器返回到程序中调用过程的地方继续执行。 • 从底层细节角度来讲,CALL指令将返回地址压入堆栈并将被调用过程的地址拷贝到指令指针寄存器中;当程序返回时,RET指令从堆栈中弹出地址并送到指令指针寄存器中。CPU总是执行指令指针寄存器EIP(在16位模式下是IP)所指向的内存地址处的指令。 • 调用和返回的例子 SumOfPROC 00000040 add eax,ebx add eax,ecx ret SumOf ENDP main PROC 00000020 call SumOf 00000025 mov ebx,eax ESP EIP ESP EIP 00000025 00000040 00000025 00000025 汇编语言

  12. 局部标号和全局标号 • 默认情况下,代码标号(以单个冒号结尾)有一个局部域,使得它对其所在过程内的语句可见,这阻止了跳转或循环语句转移到当前过程之外的标号。在极少数情况下,如果必须将控制转移到当前过程之外的标号处,标号必须被声明为全局的。声明全局标号,要在标号后跟两个冒号。例如: mainPROC jmp L2 ;错误! L1:: ;全局标号 exit main ENDP sub PROC L2: ;局部标号 jmp L1 ;正确 ret sub ENDP 汇编语言

  13. 向过程传递寄存器参数 • 如果过程执行某些诸如整数数组求和之类的标准操作,那么在过程之内引用特定的变量名并不是什么好主意。如果准备那么做的话,该过程就不可能用于其他数组了。 • 一个较好的方法是向过程传递数组的偏移,再传递一个整数来表示数组元素的数目。我们称这些为参数(arguments)或输入参数(input arguments)。在汇编语言中,常通过通用寄存器来传递参数。 • 例子: .data theSum DWORD ? .code main PROC mov eax,10000h ;参数 mov ebx,20000h ;参数 mov ecx,30000h ;参数 call SumOf ;EAX=(EAX+EBX+ECX) mov theSum,eax ;保存和 exit ;启动过程结束 main ENDP 汇编语言

  14. 例子:对整数数组求和 ;----------------------------------------------------- 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 ecx ;还原ECX和ESI的值 pop esi ret ;通过EAX返回结果 ArraySum ENDP 调用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 汇编语言

  15. 保存和恢复寄存器 • ArraySum过程的开始处ECX和ESI被压入堆栈,过程结束的时候又被弹出,绝大多数修改寄存器的过程都是用这种方式。修改寄存器值的过程应该总是保存和恢复寄存器值,以确保调用程序本身的寄存器值不被覆盖。 • 与PROC伪指令配套使用的USES操作符允许列出被过程修改的所有寄存器,它只是编译器做两件事:首先,在过程的开始处生成PUSH指令在堆栈上保存寄存器值;其次,在过程的结束处生成POP指令恢复这些寄存器值。USES操作符应紧跟PROC伪指令,其后跟由空格或制表符(不是逗号)分隔的寄存器列表。 • 例外:在过程要使用寄存器作为返回值得时候,千万不要将用于返回值得寄存器压栈和弹出,否则过程的返回值很可能丢失。 汇编语言

  16. USES操作符例子 ArraySum PROCUSESesi ecx mov eax,0 L1: add eax,[esi] add esi,4 loop L1 ret ArraySum ENDP ArraySum PROC push esi push ecx mov eax,0 L1: add eax,[esi] add esi,4 loop L1 L2: pop ecx pop esi ret ArraySum ENDP 汇编语言

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

  18. Irvine32.lib链接库中包含的过程 • Irvine32.inc • Irvine32.asm 汇编语言

  19. 使用过程进行程序设计 • 任何稍微复杂一点的程序应用都会包含一些不同的步骤。把所有的程序代码都写在一个过程之内是可能的,但是这样的程序很难阅读和维护,相反我们最好把各种编程任务划分为独立的过程。所有的过程可在同一源文件中,也可在多个文件中。 • 开始写程序时,用一份说明书来详细列出程序究竟要做什么是非常有帮助的,这通常是仔细分析要解决的问题的结果。一说明书为起点,就可以开始设计升序了。 • 一种标准的设计方法是将整体的问题分割成独立的任务,每个任务都可以在一个过程中实现。将问题细分为任务的过程通常称为功能分解(functional decomposition),或自顶向下的设计(up-down design)。 汇编语言

  20. 下面是自顶向下设计方法的一些假设: • 大问题更容易分解成小问题。 • 如果每个过程都可以独立测试的话,程序将更易于维护。 • 自顶向下设计能清楚地表现过程之间的相互关系。 • 在明确了总体设计之后,更容易集中精力解决细节问题和编写实现每个过程代码。 汇编语言

  21. 整数求和程序(设计) • 写一个程序提示用户输入一个或多个32位整数,将其保存在数组中,计算数组的和并在屏幕上显示总合。 汇编语言

  22. 第二次作业 • 阅读Irvine32.asm中的代码,选择至少3个过程,对每一行代码写注释,要求说明清楚过程的作用和方法。3周内提交。 汇编语言

More Related