1、汇编语言程序设计_04_循环控制与过程大连理工大学软件学院_朱明2009年5月31日_ V1.1第三章提问 MOV指令要求源操作数和目的操作数的尺寸必须相同,其功能为将源操作数的值复制到目的操作数中,但有两个源操作数要比目的操作数尺寸小的类似功能的指令是什么?其功能是什么?CF(进位标志位)的判断方法是什么?OF(溢出标志位)的判断方法是什么?SIZEOF操作符、LENGTHOF操作符和TYPE操作符分别返回什么值?三者之间有什么关系?PTR操作符是否会改变操作数的原有类型?保护模式下程序可以访问的最大虚拟内存范围是多少?是如何计算的?什么操作符可取得其段内偏移地址?汇编语言程序设计-朱明2前
2、章回顾 前章一个小题目,10%成绩 如果我们要用这些指令对一个数组进行求和的操作 假设在程序运行之前,数组的大小(5个元素)和元素大小都是已知的(DWORD)要求你的程序仅使用今天以前讲过的指令完成 数组中有5个DWORD类型元素的 如果元素数目和大小未知怎么办?想办法知道元素的数目和大小 建立一个具有判断机制的循环体汇编语言程序设计-朱明3前章回顾arr1 DWORD 111h,222h,333h,444h,555hLOOP指令 组成原理实验中的实现循环方法到LOOP循环汇编语言程序设计-朱明4Section 1AX-1JC?AX=初始值CF(CY)作计数的寄存器ECX寄存器在与循环指令联合
3、使用时,被自动作为计数器,每次循环之后减去1标号:正常循环体ECX=初始值LOOP 标号LOOP指令 LOOP指令的执行过程汇编语言程序设计-朱明5标号:正常循环体ECX=初始值LOOP 标号 LOOP指令的执行包含两个步骤 ECX的值减1,并与0作比较 如果ECX不等于0,则跳转到对应标号地址处;如果ECX等于0则不跳转 与LOOP指令类似的有使用ECX作计数器的LOOPD和使用CX作为计数器的LOOPW LOOP的跳转范围必须在-128+127字节范围内(Offset=8位有符号)多层嵌套必须考虑好ECX的占用与保存问题Section 1JMP指令 无条件段内跳转指令 JMP的跳转是无条件
4、的,同时存在条件跳转指令 JG/JNG,大于则跳转/不大于则跳转 JC/JNC,进位则跳转/不进位则跳转.汇编语言程序设计-朱明6标号:正常循环体JMP 标号 JMP指令能够实现段内的无条件跳转 段内跳转不代表可以随意跳转 要考虑标号的作用域问题,即每一个标号只能在他所在的过程中(PROC)中可见 可使用全局标号Section 1问题处理 程序代码汇编语言程序设计-朱明7TITLESumming of Array;This is a example,loopong controlINCLUDEirvine32.inc.dataarr1DWORD 111h,222h,333h,444h,555h
5、val1DWORD?.codemainPROCmoveax,TYPE arr1movval1,eaxmovecx,LENGTHOF arr1moveax,0L1:;把arr1里面元素的值逐一加到一起的方法LOOPL1exitmainENDPENDmain取得数组相关信息把数据逐一相加间接寻址如何访问下一个数据Section 1间接寻址 在第三章中讲过的直接寻址 直接寻址的中存放的直接就是地址/偏移 间接寻址-间接操作数 通过间接操作数寻址时中存放的是寄存器的名称,而该寄存器中保存着数据的地址/偏移 间接寻址中的操作数(寄存器中的值)很容易修改,因此在操作内存中连续的数据(例如数组)时,可实现类
6、似数组下标的功能来访问不同元素汇编语言程序设计-朱明8moval,value1moval,esiSection 1间接寻址 通过间接操作数寻址访问数组 注意要访问的数组元素的大小汇编语言程序设计-朱明9arr2BYTE 10h,20h,BYTE 30h,40hmovesi,OFFSET arr2moval,esiincesimoval,esiincesimoval,esiSection 1假设此时esi=00040500esi=0004050110h20h30h40h00040500000405010004050200040503esi=00040502问题处理 加入间接寻址的方法汇编语言程序
7、设计-朱明10.dataarr1DWORD 111h,222h,333h,444h,555hval1DWORD?.codemainPROCmoveax,TYPE arr1movval1,eaxmovecx,LENGTHOF arr1movesi,OFFSET arr1moveax,0L1:addeax,esiaddesi,val1LOOPL1exitmainENDPENDmain间接寻址Section 1使用ESI问题处理 对于整数数组求和问题的总结与思考 若使用操作数间接寻址则要保存数组的起始偏移地址至某个用于寻址的寄存器中 取得数组种元素的数目并送ECX作为计数器初始值 选择另外一个寄存器
8、用于累加保存结果并将其清零 创建一个用于标示循环体的标号 在循环体中利用操作数间接寻址的方式进行累加操作 修改用于寻址的寄存器,使其指向数组的下一个元素 使用LOOP指令重复执行循环体内容汇编语言程序设计-朱明11Section 1Section 1间接寻址 通过间接操作数进行寻址 通过间接操作数寻址时,寄存器中存放的是地址/偏移 通过变址操作数进行寻址 通过变址操作数寻址时,寄存器中不直接存放地址/偏移,而存放一个相对于地址/偏移的增量汇编语言程序设计-朱明12addesi,2moval,esimovesi,2moval,aar1esimoval,aar1+esi间接寻址 通过变址操作数寻址
9、访问数组 注意要访问的数组元素的大小汇编语言程序设计-朱明13arr3BYTE 50h,60h,BYTE 70h,80hmovesi,0moval,arr3+esiaddesi,1moval,arr3+esiaddesi,1moval,arr3+esiSection 1esi=050h60h70h80h00040500000405010004050200040503假设arr3的存储为esi=1esi=2间接寻址 通过比例因子访问数组 其中的*并不是表示乘法,而是表示比例因子 IA-32处理器支持比例因子的计算方法来计算偏移地址 比例因子通常是数组元素的大小(1、2、4.),可以直接使用TYP
10、E操作符获得元素的大小汇编语言程序设计-朱明14arr4WORD 11h,12h,13hmovesi,2movax,esi*2Section 1movax,esi*TYPE arr4间接寻址 间接寻址另一个应用字符串复制汇编语言程序设计-朱明15thi.datastr1BYTE“this is the source string”,0str2BYTE(1).codemainPROCmovecx,(2)movesi,0L1:moval,str1esimov(3),al(4)LOOPL1exitmainENDPENDmainstr1str2Section 1(1)SIZEOF str1 DUP(?
11、)(2)SIZEOF str1(3)str2esi(4)inc esi回顾与练习 关于循环的基本操作练习 编写一个可以实现计算从1开始到1000以内的任意自然数连续相加的汇编语言程序,占成绩10%1+.+MaxVal(MaxVal是一个程序中定义的变量)下次课上课之前发送至姓名_班级_学号,附件形式发送代码 关于循环和间接寻址的一个练习 使用LOOP循环指令和间接寻址实现字符串的逆序复制汇编语言程序设计-朱明16.datastr1BYTE“this is the source string”,0str2BYTESIZEOF str1 DUP(?)回顾与练习thi回顾与练习关于循环和间接寻址的一
12、个练习 使用LOOP循环指令和间接寻址实现字符串的逆序复制汇编语言程序设计-朱明17sstr1str2ing0gnisiht0ESIEDI回顾与练习过程的定义 在汇编语言中也有类似高级语言的subroutine机制,称为过程(procedure)过程的定义 过程定义以PROC伪指令开头,以ENDP伪指令结尾 过程定义需要一个有效的标识符作为过程名 被调用过程内应该以RET指令结束,以完成返回;主过程(main)应该以exit结束汇编语言程序设计-朱明18Section 2sample PROC retsample ENDPmain PROC exitmain ENDP过程的定义 过程的写法与格
13、式 前面要求的三个过程定义的基本要求 过程的书写在格式上的要求,包含程序的功能说明、需要使用的参数、返回的结果等方面的注释的内容,程序书写上正确的、适合阅读的缩进结构等汇编语言程序设计-朱明19;-SumOf PROC;Calaulates and returns the sum of three 32-bit integers.;Receives:EAX,EBX,ECX;Return:EAX,saves the sum;-addeax,ebxaddeax,ecxretSumOf ENDPSection 2过程的调用 CALL指令指示处理器在新的内存地址执行指令汇编语言程序设计-朱明20cal
14、l foo.retEIP004000800040002000400025004000250040002000400080EIP被压栈跳转Section 2过程的返回 RET指令从栈中弹出EIP,并按照EIP继续执行程序汇编语言程序设计-朱明21call foo.retEIP004000800040002000400025004001100040002500400020004000800040011000400025EIP被压栈跳转Section 2跳转弹栈为EIP 一个过程在返回之前又调用了其他过程会造成嵌套PA返回后地址过程的嵌套汇编语言程序设计-朱明22call PA.Section 2c
15、all PBretcall PCretretPA返回后地址PB返回后地址PB返回后地址PA返回后地址PC返回后地址PC返回后地址PB返回后地址PA返回后地址PB返回后地址PA返回后地址PA返回后地址PC返回后地址PB返回后地址PA返回后地址过程的参数传递 过程中常常需要参数的传递 如果我需要对数组中的N个数求和 每次都通过寄存器传递?要传递若干次 C语言里面怎么做到的?汇编语言程序设计-朱明23;-SumOf PROC;Calaulates and returns the sum of three 32-bit integers.;Receives:EAX,EBX,ECX;Return:EAX
16、,saves the sum;-addeax,ebxaddeax,ecxretSumOf ENDPSection 2过程的参数传递 过程中常常需要参数的传递 例如可以传递一个数组偏移和一个元素的数量 数组偏移保存在ESI寄存器中 元素数量保存在ECX寄存器中汇编语言程序设计-朱明24ArraySum PROC;保护现场,压栈moveax,0;清除加法值L1:addeax,esi;执行加法操作addesi,TYPE DWORD;指向下一个整数loopL1;重复循环;恢复现场,弹栈ret;结果保存在EAX中ArraySum ENDPSection 2Section 2过程与堆栈 过程的现场保护实际
17、上就是栈的压栈操作 过程的现场恢复实际上就是栈的弹栈操作 堆栈的操作原则(结构):LIFO 新入栈的数据保存在栈的顶端(小地址)汇编语言程序设计-朱明25PA返回后地址PA返回后地址PB返回后地址PB返回后地址PA返回后地址PC返回后地址PB返回后地址PA返回后地址PB返回后地址PA返回后地址PA返回后地址PC返回后地址PB返回后地址PA返回后地址过程与堆栈 堆栈结构与堆栈指针 压栈操作(CALL指令和PUSH指令)堆栈指针ESP始终指向一个有数据的地址/偏移 堆栈指针ESP先修改自身的值(-4),然后再存入数据 IA-32处理器所管理的堆栈中的大地址作为栈底,小地址作为栈顶汇编语言程序设计-
18、朱明2600000FF000000FF400000FF800000FFC0000000600001000压栈前Section 2ESP压栈00000FF000000FF400000FF8000000A500000FFC0000000600001000压栈后ESP过程与堆栈 堆栈结构和堆栈指针 弹栈操作(RET指令和POP指令)对于处理器来说,比堆栈指针地址小的堆栈数据是无效的 堆栈先弹出数据,再修改堆栈指针ESP的指(+4)堆栈的运行是由CPU内部的硬件直接完成的,一般不需要用户修改ESP的值汇编语言程序设计-朱明2700000FF000000FF400000FF8000000A500000F
19、FC0000000600001000弹栈前ESP弹栈00000FF000000FF400000FF800000FFC0000000600001000弹栈后ESPSection 2过程与堆栈 堆栈的几个重要用途 寄存器在多种用途的时候,堆栈可以作为临时区域方便的临时保存或恢复原有的值 CALL指令执行时,堆栈用于保存其返回地址 在进行过程调用时,可以通过堆栈传递参数 过程内部的局部变量在堆栈上运行,过程结束时放弃 与堆栈操作相关的指令 压栈:CALL、PUSH、PUSHFD、PUSHAD、PUSHA 弹栈:RET、POP、POPFD、POPAD、POPA汇编语言程序设计-朱明28Section
20、2过程与堆栈 与堆栈操作相关的指令 PUSH reg/mem/imm,将寄存器的值或内存中的值或立即数压入堆栈 PUSHFD,将EFLAGS寄存器的值压入堆栈 PUSHAD,按照由先至后的顺序依次将EAX、ECX、EDX、EBX、ESP的原始值、EBP、ESI和EDI压入堆栈 PUSHA,与PUSHAD同样的顺序将16位寄存器的值压入堆栈 POP reg/mem,将栈顶的值弹出到寄存器或内存中 POPFD、POPAD和POPA分别与对应的PUSHFD、PUSHAD和PUSHA类似,执行弹栈操作,但顺序相反汇编语言程序设计-朱明29Section 2回顾与练习 关于堆栈和间接寻址的一个练习使用对
21、栈操作和间接寻址实现字符串的逆序复制30.datastr1BYTE“this is the source string”,0str2BYTESIZEOF str1 DUP(?)thisstr1汇编语言程序设计-朱明0000006800000074sihstr1t回顾与练习压栈弹栈Section 3过程化程序设计 自上而下的程序设计方法 将大问题分解成为小问题处理 每一个过程都可以进行独立的测试且便于维护 过程之间有明显而简洁的依赖关系 每个过程都更容易解决细节的问题和处理代码的实现 过程化的程序设计以程序的过程作为基本单位 main过程(主过程)DumpRegs过程(用于显示寄存器值的过程)整
22、数求和问题 写一个程序,要求用户从键盘输入3个32位整数,保存在数组中,并计算数组内元素的和并显示在屏幕上汇编语言程序设计-朱明31Section 3过程化程序设计 自上而下的程序设计方法整数求和问题汇编语言程序设计-朱明32用户输入系统计算输出结果运行流程图代码结构图用户输入系统计算输出结果读键盘输入数据屏幕显示运算结果主体程序显示提示显示提示Section 3过程化程序设计 自上而下的程序设计方法整数求和问题汇编语言程序设计-朱明33主体伪代码main;清除屏幕的内容;用户输入数据过程;提示用户输入;读取用户输入;计算过程;计算机过输出过程;提示结果输出;输出计算结果END main代码框
23、架;-main PROC;-exitmain ENDP;-GetNum PROC;-exitmain ENDP;-ArraySum PROC;-exitmain ENDP;-DispSum PROC;-exitmain ENDP过程化程序设计 自上而下的程序设计方法整数求和问题 完善框架中的代码内容 可能需要的调用一些功能过程 如何在屏幕上显示字符串?如何获取用户输入的数值?如何把计算结果显示在屏幕上?囧rz,其实这些过程不需要自己写的 在irvine32.inc链接库中都提供了这些功能 回顾一下第二章的内容,已经编译成为机器码的链接库是在哪一个阶段和源代码汇编生成的目标文件结合在一起的?汇编
24、语言程序设计-朱明34Section 3过程化程序设计 在屏幕上显示字符串的过程WriteString 在标准输出上显示一个以空字符结尾的字符串 要显示的字符串的偏移保存在EDX中 获取用户输入的过程ReadInt 从标准输入读取一个32位的有符号数并保存在EAX中汇编语言程序设计-朱明35Str1BYTE“Oh,the beautiful hawaii”,0movedx,OFFSET Str1callWriteStringintValSDWORD?callReadIntmovintVal,eaxSection 3过程化程序设计 在屏幕上显示计算的结果WriteInt 以十进制的形式在标准输出
25、上输出32位有符号整数 程序中其他功能的过程 输出一个我们意义上的换行,还记得是什么控制符?Crlf:向标准输出上输出0Dh和0Ah两个控制符 清除标准输出的内容,以便用户更好的观察程序输出 Clrscr:通常在程序的开始时候调用 程序中其他可能使用的指令 LOOP、POP以及PUSH相关指令汇编语言程序设计-朱明36moveax,14017088callWriteIntSection 3章节回顾 LOOP循环指令的应用方法和跳转要求 无条件跳转指令JMP的用法 操作数间接寻址和变址操作数间接寻址的形式 通过间接寻址实现对数组的访问 过程的定义要求、过程的写法与格式要求 过程的调用和过程的返回
26、,以及对应过程中堆栈的使用情况 堆栈的压栈操作和弹栈操作,以及相关的指令 过程化的程序设计方法及其相关步骤汇编语言程序设计-朱明37章节回顾章节回顾 以下的问题我们应当轻松回答 IA-32结构中的哪一个寄存器与LOOP联合使用作为计数器?该寄存器中的值为多少时LOOP不再跳转?通过寄存器进行操作数间接寻址和变址操作数间接寻址时,寄存器中应分别保存什么内容?如何定义一个过程的开始以及过程的结束?如何调用一个过程?过程中什么指令表示过程的返回?哪些指令涉及到堆栈的压栈和弹栈操作?保护模式下压栈的过程中,堆栈指针ESP的值和压栈的数据是按照什么顺序变化的?汇编语言程序设计-朱明38章节回顾思考问题 课后的一个作业 讲义中Page16页的问题 编写一个可以实现计算从1开始到1000以内的任意自然数连续相加的汇编语言程序,占成绩10%下次课上课之前发送至姓名_班级_学号,附件形式发送代码 自己尝试完成 在Page31所提出的问题 参考Page32Page36中所涉及的结构和过程汇编语言程序设计-朱明39思考问题