《汇编语言》讲稿_10_call_和_ret_指令

上传人:yx****d 文档编号:243158051 上传时间:2024-09-17 格式:PPT 页数:77 大小:320.50KB
返回 下载 相关 举报
《汇编语言》讲稿_10_call_和_ret_指令_第1页
第1页 / 共77页
《汇编语言》讲稿_10_call_和_ret_指令_第2页
第2页 / 共77页
《汇编语言》讲稿_10_call_和_ret_指令_第3页
第3页 / 共77页
点击查看更多>>
资源描述
,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,本课件由汇编网()制作提供,第10章 call 和 ret 指令,10.1 ret 和 retf,10.2 call 指令,10.3 依据位移进行转移的call指令,10.4 转移的目的地址在指令中的call指令,10.5 转移地址在寄存器中的call指令,10.6 转移地址在内存中的call指令,10.7 call 和 ret 的配合使用,10.8 mul 指令,10.9 模块化程序设计,10.10 参数和结果传递的问题,10.11 批量数据的传递,10.12 寄存器冲突的问题,引言,call和ret 指令都是转移指令,它们都修改IP,或同时修改CS和IP。,它们经常被共同用来实现自程序的设计。,这一章,我们讲解call和ret 指令的原理。,10.1 ret 和 retf,ret指令用栈中的数据,修改IP的内容,从而实现近转移;,操作,retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移;,操作,10.1 ret 和 retf,CPU执行ret指令时,进行下面两步操作:,(1)(IP)=(ss)*16+(sp),(2)(sp)=(sp)+2,10.1 ret 和 retf,CPU执行retf指令时,进行下面两步操作:,(1)(IP)=(ss)*16+(sp),(2)(sp)=(sp)+2,(3)(CS)=(ss)*16+(sp),(4)(sp)=(sp)+2,10.1 ret 和 retf,可以看出,如果我们用汇编语法来解释ret和retf指令,则:,CPU执行ret指令时,相当于进行:,pop IP,CPU执行retf指令时,相当于进行:,pop IP,pop CS,10.1 ret 和 retf,示例程序,ret指令,程序中ret指令执行后,(IP)=0,CS:IP指向代码段的第一条指令。,retf指令,程序中retf指令执行后,CS:IP指向代码段的第一条指令。,特别提示,检测点10.1(p179),没有完成此检测点,请不要向下进行。,10.2 call 指令,CPU执行call指令,进行两步操作:,(1)将当前的 IP 或 CS和IP 压入栈中;,(2)转移。,call 指令不能实现短转移,除此之外,call指令实现转移的方法和 jmp 指令的原理相同,下面的几个小节中 ,我们以给出转移目的地址的不同方法为主线,讲解call指令的主要应用格式。,10.3 依据位移进行转移的call指令,call 标号(将当前的 IP 压栈后,转到标号处执行指令),CPU执行此种格式的call指令时,进行如下的操作:,(1) (sp) = (sp) 2,(ss)*16+(sp) = (IP),(2) (IP) = (IP) + 16位位移,10.3 依据位移进行转移的call指令,call 标号,16位位移=“标号”处的地址call指令后的第一个字节的地址;,16位位移的范围为 -3276832767,用补码表示;,16位位移由编译程序在编译时算出。,演示,10.3 依据位移进行转移的call指令,从上面的描述中,可以看出,如果我们用汇编语法来解释此种格式的 call指令,则:,CPU 执行指令“call 标号”时,相当于进行:,push IP,jmp near ptr 标号,特别提示,检测点10.2(p181),没有完成此检测点,请不要向下进行。,10.4 转移的目的地址在指令中的call指令,前面讲解的call指令,其对应的机器指令中并没有转移的目的地址 ,而是相对于当前IP的转移位移。,指令“call far ptr 标号”实现的是段间转移。,操作,10.4 转移的目的地址在指令中的call指令,CPU执行“call far ptr 标号”这种格式的call指令时的操作:,(1) (sp) = (sp) 2,(ss) 16+(sp) = (CS),(sp) = (sp) 2,(ss) 16+(sp) = (IP),(2) (CS) = 标号所在的段地址,(IP) = 标号所在的偏移地址,10.4 转移的目的地址在指令中的call指令,从上面的描述中可以看出,如果我们用汇编语法来解释此种格式的 call 指令,则:,CPU 执行指令 “call far ptr 标号” 时,相当于进行:,push CS,push IP,jmp far ptr 标号,特别提示,检测点10.3(p181),没有完成此检测点,请不要向下进行。,10.5 转移地址在寄存器中的call指令,指令格式:call 16位寄存器,功能:,(sp) = (sp) 2,(ss)*16+(sp) = (IP),(IP) = (16位寄存器),10.5 转移地址在寄存器中的call指令,指令格式:call 16位寄存器,汇编语法解释此种格式的 call 指令,CPU执行call 16位reg时,相当于进行:,push IP,jmp 16位寄存器,特别提示,检测点10.4(p182),没有完成此检测点,请不要向下进行。,10.6 转移地址在内存中的call指令,转移地址在内存中的call指令有两种格式:,(1),call word ptr 内存单元地址,(2),call dword ptr 内存单元地址,10.6 转移地址在内存中的call指令,(1) call word ptr 内存单元地址,汇编语法解释:,push IP,jmp word ptr 内存单元地址,示例,10.6 转移地址在内存中的call指令,(1) call word ptr 内存单元地址(示例),比如下面的指令:,mov sp,10h,mov ax,0123h,mov ds:0,ax,call word ptr ds:0,执行后,(IP)=0123H,(sp)=0EH,10.6 转移地址在内存中的call指令,(2) call dword ptr 内存单元地址,汇编语法解释:,push CS,push IP,jmp dword ptr 内存单元地址,示例,10.6 转移地址在内存中的call指令,(2) call dword ptr 内存单元地址(示例),比如,下面的指令:,mov sp,10h,mov ax,0123h,mov ds:0,ax,mov word ptr ds:2,0,call dword ptr ds:0,执行后,(CS)=0,(IP)=0123H,(sp)=0CH,特别提示,检测点10.5(p183),没有完成此检测点,请不要向下进行。,10.7 call 和 ret 的配合使用,前面,我们已经分别学习了 ret 和call指令的原理。现在我们看一下,如何将它们配合使用来实现子程序的机制。,问题10.1,10.7 call 和 ret 的配合使用,assume cs:code,code segment,start:mov ax,1,mov cx,3,call s,mov bx,ax,;(bx) = ?,mov ax,4c00h,int 21h,s: add ax,ax,loop s,ret,code ends,end start,问题10.1,右面程序返回前,bx中的值是多少?,思考后看,分析,。,10.7 call 和 ret 的配合使用,问题10.1分析,我们来看一下 CPU 执行这个程序的主要过程:,(1)CPU 将call s指令的机器码读入,IP指向了call s后的指令mov bx,ax,然后CPU执行call s指令,将当前的 IP值(指令mov bx,ax的偏移地址)压栈,并将 IP 的值改变为标号 s处的偏移地址;,(2)CPU从标号 s 处开始执行指令,loop循环完毕,(ax)=8;,10.7 call 和 ret 的配合使用,问题10.1分析(续),我们来看一下CPU执行这个程序的主要过程:,(3)CPU将ret指令的机器码读入,IP指向了ret 指令后的内存单元,然后CPU 执行 ret 指令 ,从栈中弹出一个值(即 call 先前压入的mov bx,ax 指令的偏移地址)送入 IP 中。则CS:IP指向指令mov bx,ax;,(4)CPU从 mov bx,ax 开始执行指令,直至完成。,10.7 call 和 ret 的配合使用,问题10.1分析(续),程序返回前,(bx)=8 。我们可以看出,从标号s 到ret的程序段的作用是计算2的N次方,计算前,N的值由CX提供。,我们再来看下面的程序,10.7 call 和 ret 的配合使用,10.7 call 和 ret 的配合使用,我们看一下程序的主要执行过程:,(1)前三条指令执行后,栈的情况如下:,10.7 call 和 ret 的配合使用,程序的主要执行过程:,(2)call 指令读入后,(IP) =000EH,CPU指令缓冲器中的代码为 B8 05 00;,CPU执行B8 05 00,首先,栈中的情况变为:,然后,(IP)=(IP)+0005=0013H。,10.7 call 和 ret 的配合使用,程序的主要执行过程:,(3)CPU从cs:0013H处(即标号s处)开始执行。,(4)ret指令读入后:(IP)=0016H,CPU指令缓冲器中的代码为 C3;CPU执行C3,相当于进行pop IP,执行后,栈中的情况为:,(IP)=000EH;,10.7 call 和 ret 的配合使用,程序的主要执行过程:,(5)CPU回到 cs:000EH处(即call指令后面的指令处)继续执行。,从上面的讨论中我们发现,可以写一个具有一定功能的程序段,我们称其为子程序,在需要的时候,用call指令转去执行。,10.7 call 和 ret 的配合使用,可是执行完子程序后,如何让CPU接着call指令向下执行?,call指令转去执行子程序之前,call指令后面的指令的地址将存储在栈中,所以可以在子程序的后面使用 ret 指令,用栈中的数据设置IP的值,从而转到 call 指令后面的代码处继续执行。,10.7 call 和 ret 的配合使用,这样,我们可以利用call和ret来实现子程序的机制。,子程序的框架,10.7 call 和 ret 的配合使用,子程序的框架:,标号:,指令,ret,具有子程序的源程序的框架:,10.7 call 和 ret 的配合使用,现在,可以从子程序的角度,回过头来再看一下本节中的两个程序。,10.8 mul 指令,因下面要用到,我们介绍一下mul指令,mul是乘法指令,使用 mul 做乘法的时候:,(1)相乘的两个数:要么都是8位,要么都是16位。,8 位: AL中和 8位寄存器或内存字节单元中;,16 位: AX中和 16 位寄存器或内存字单元中。,10.8 mul 指令,使用mul座乘法的时候:,(2)结果,8位:AX中;,16位:DX(高位)和AX(低位)中。,格式如下:,mul reg,mul 内存单元,10.8 mul 指令,内存单元可以用不同的寻址方式给出,比如:,mul byte ptr ds:0,含义为,: (ax)=(al)*(ds)*16+0);,mul word ptr bx+si+8,含义为,:,(ax)=(al)*(ds)*16+(bx)+(si)+8)结果的低16位;,(dx)=(al)*(ds)*16+(bx)+(si)+8)结果的高16位;,10.8 mul 指令,例如:,(1)计算100*10,100和10小于255,可以做8位乘法,程序如下:,mov al,100,mov bl,10,mul bl,结果: (ax)=1000(03E8H),10.8 mul 指令,例如:,(1)计算100*10000,100小于255,可10000大于255,所以必须做16位乘法,程序如下:,mov ax,100,mov bx,10000,mul bx,结果: (ax)=4240H,(dx)=000FH,(F4240H=1000000),10.9 模块化程序设计,从上面我们看到 ,call 与 ret 指令共同支持了汇编语言编程中的模块化设计。在实际编程中,程序的模块化是必不可少的。,因为现实的问题比较复杂,对现实问题进行分析时,把它转化成为相互联系、不同层次的子问题,是必须的解决方法。,10.9 模块化程序设计,而call和ret 指令对这种分析方法提供了程序实现上的支持。利用 call和ret指令,我们可以用简洁的方法,实现多个互相联系、功能独立的子程序来解决一个复杂的问题。,下面的内容中,我们来看一下子程序设计中的相关问题和解决方法。,10.10 参数和结果传递的问题,子程序一般都要根据提供的参数处理一定的事务,处理后,将结果(返回值)提供给调用者。,其实,我们讨论参数和返回值传递的问题,实际上就是在探讨,应该如何存储子程序需要的参数和产生的返回值。,10.10 参数和结果传递的问题,我们设计一个子程序,可以根据提供的N,来计算N的3次方。,这里有两个问题:,(1)我们将参数N存储在什么地方?,(2)计算得到的数值,我们存储在什么地方?,10.10 参数和结果传递的问题,很显然,我们可以用寄存器来存储,可以将参数放到 bx 中 ;因为子程序中要计算 NNN ,可以使用多个 mul 指令,为了方便,可将结果放到 dx 和 ax中。,子程序,10.10 参数和结果传递的问题,子程序:,说明:计算N的3次方,参数: (bx)=N,结果: (dx:ax)=N,3,cube:mov ax,bx,mul bx,mul bx,ret,10.10 参数和结果传递的问题,注意,我们在编程的时候要注意良好的风格,对于程序应有详细的注释。子程序的注释信息应该包含对子程序的功能、参数和结果的说明。,因为今天写的子程序,以后可能还会用到;自己写的子程序,也很可能要给别人使用,所以一定要有全面的说明。,10.10 参数和结果传递的问题,用寄存器来存储参数和结果是最常使用的方法。对于存放参数的寄存器和存放结果的寄存器,调用者和子程序的读写操作恰恰相反:,调用者将参数送入参数寄存器,从结果寄存器中取到返回值;,子程序从参数寄存器中取到参数,将返回值送入结果寄存器。,10.10 参数和结果传递的问题,编程:计算data段中第一组数据的 3 次方,结果保存在后面一组dword单元中。,assume cs:code,data segment,dw 1,2,3,4,5,6,7,8,dd 0,0,0,0,0,0,0,0,data ends,我们可以用到已经写好的子程序,程序代码,10.11 批量数据的传递,前面的例程中,子程序 cube 只有一个参数,放在bx中。如果有两个参数,那么可以用两个寄存器来放,可是如果需要传递的数据有3个、4个或更多直至 N个,我们怎样存放呢?,寄存器的数量终究有限,我们不可能简单地用寄存器来存放多个需要传递的数据。对于返回值,也有同样的问题。,10.11 批量数据的传递,在这种时候,我们将批量数据放到内存中,然后将它们所在内存空间的首地址放在寄存器中,传递给需要的子程序。,对于具有批量数据的返回结果,也可用同样的方法。,10.11 批量数据的传递,我们看一个例子,设计子程序,功能:将一个全是字母的字符串转化为大写。,分析,子程序,10.11 批量数据的传递,将一个全是字母的字符串转化为大写。,分析,这个子程序需要知道两件事,字符串的内容和字符串的长度。因为字符串中的字母可能很多,所以我们不便将整个字符串中的所有字母都直接传递给子程序。,10.11 批量数据的传递,将一个全是字母的字符串转化为大写。,分析(续),但是,我们可以将字符串在内存中的首地址放在寄存器中传递给子程序。因为子程序中要用到循环,我们可以用 loop 指令,而循环的次数恰恰就是字符串的长度。出于方便的考虑,可以将字符串的长度放到cx中。,10.11 批量数据的传递,编程:将data段中的字符串转化为大写。,assume cs:code,data segment,db conversation,data ends,code segment,start: ,code ends,end start,10.11 批量数据的传递,编程:将data段中的字符串转化为大写。,源程序代码,注意:除了寄存器传递参数外,还有一种通用的方法使用栈来传递参数。关于这种技巧请参看附注4。,10.12 寄存器冲突的问题,设计一个子程序:,功能:将一个全是字母,以0结尾的字符串,转化为大写。,程序要处理的字符串以0作为结尾符,这个字符串可以如下定义:,db conversation,0,10.12 寄存器冲突的问题,设计一个子程序:,功能:将一个全是字母,以0结尾的字符串,转化为大写。,分析,应用这个子程序 ,字符串的内容后面定要有一个0,标记字符串的结束。子程序可以依次读取每个字符进行检测,如果不是0,就进行大写的转化,如果是0,就结束处理。,由于可通过检测0而知道是否己经处理完整个字符串 ,所以子程序可以不需要字符串的长度作为参数。我们可以用jcxz来检测0。,10.12 寄存器冲突的问题,子程序设计:,说明:将一个全是字母,以0结尾的字符串,转化为大写。,参数:ds:si指向字符串的首地址;,结果:没有返回值。,子程序代码,我们来看一下这个子程序的应用,10.12 寄存器冲突的问题,子程序代码,10.12 寄存器冲突的问题,程序的应用,(1)将data段中字符串转化为大写,assume cs:code,data segment,db conversation,0,data ends,代码段中相关程序段如下:,mov ax,data,mov ds,ax,mov si,0,call capital,10.12 寄存器冲突的问题,子程序的应用,(2)将data段中字符串全部转化为大写,assume cs:code,data segment,db word,0,db unix,0,db wind,0,db good,0,data ends,可以看到,所有字符串的长度都是5(算上结尾符 0 ),我们使用循环 ,重复调用子程序capital完成对4个字符串的处理。,完整的程序代码,10.12 寄存器冲突的问题,问题10.2,前面的程序在思想上完全正确,但在细节上却有些错误,把错误找出来。,思考后看,分析,。,10.12 寄存器冲突的问题,问题10.2分析,问题在于cx的使用,主程序要使用cx记录循环次数,可是子程序中也使用了cx,在执行子程序的时候,cx中保存的循环计数值被改变,使得主程序的循环出错。,从上而的问题中,实际上引出了个一般化的问题:子程序中使用的寄存器,很可能在主程序中也要使用,造成了寄存器使用上的冲突。,10.12 寄存器冲突的问题,问题10.2分析(续),那么我们如何来避免这种冲突呢 ?粗略地看,我们可以有两个方案:,(1)在编写调用子程序的程序时 ,注意看看子程序中有没有用到会产生冲突的寄存器,如果有,调用者使用别的寄存器;,(2)在编写子程序的时候,不要使用会产生冲突的寄存器。,10.12 寄存器冲突的问题,问题10.2分析(续),我们来分析一下上面两个方案的可行性:,(1) 这将给调用子程序的程序的编写造成很大的麻烦,因为必须要小心检杳所调用的子程序中是否有将产生冲突的寄存器。,比如说,在上面的例子中,我们在编写主程序的循环的时候就得检查子程序巾是否用到了bx和cx,因为如果子程序中用到了这两个寄存器就会出现问题。,如果我们采用这种方案来解决冲突的话,那么在主程序的循环中,就小能使用cx寄存器,因为子程序中己经用到。,10.12 寄存器冲突的问题,问题10.2分析(续),分析一下上面两个方案的可行性:,(2) 这个方案是不可能实现的,因为编写子程序的时候无法知道将来的调用情况。,可见,我们上面所设想的两个方案都不可行。,10.12 寄存器冲突的问题,问题10.2分析(续),我们希望:,(1)编写调用了程序的程序的时候不必关心子程序到底使用了哪些寄存器;,(2)编写了程序的时候不必关心调用者使用了哪些寄存器;,(3)不会发生寄存器冲突。,10.12 寄存器冲突的问题,问题10.2分析(续),解决这个问题的简捷方法是,在子程序的开始将子程序中所有用到的寄存器中的内容都保存起来,在子程序返回前再恢复。我们可以用栈来保存寄存器中的内容。,10.12 寄存器冲突的问题,问题10.2分析(续),以后,我们编写子程序的标准框架如下:,子程序开始:子程序中使用的寄存器入栈,子程序内容,子程序使用的寄存器出栈,返回(ret、retf),我们改进一下,子程序capital的设计,10.12 寄存器冲突的问题,问题10.2改进的子程序capital的设计capital: push cx,push si,change: mov cl,si,mov ch,0,jcxz ok,and byte ptr si,11011111b,inc si,jmp short change,ok: pop si,pop cx,ret,要注意寄存器入栈和出栈的顺序。,小结,77,
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 大学资料


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!