您的位置:首页 > 其它

汇编语言基本概念(续8)

2011-11-22 11:04 330 查看
前面说到转移指令,通常转移到另外一个指令之后,继续接着执行。但是假如说我要转到另外的指令执行一段后然后再想回到跳转之间的指令该如何实现。汇编已经考虑到这个问题,设置了两个指令,CALL RET指令,它们都是转移指令,同前面jmp不同,它转移时除了修改CS和IP之外,还要将CS/IP入栈保存起来。以便后面继续使用。正因为有这两个指令,所以才有了子程序的概念。我们可以想像有一段代码,是比较独立功能的。可以被主程序调用多次的。显然,我们可以使用CALL和RET进行转移多次。
【1】 ret  指令用栈中的数据修改IP,实现近转移(16位) 相当于pop IP ,也就是两步,1)(IP)=((SS)*16+(sp))也就是栈顶的值。两个字节。 2)(sp)=(sp)+2 下移两个字节








 
【2】retf 指令用栈中的数据,修改CS和IP的值,实现远转移。相当于POP IP  和POP CS。也就是四步,1)(IP)=((SS)*16+(sp))也就是栈顶的值。两个字节。 2)(sp)=(sp)+2 下移两个字 3)(CS)=((SS)*16+(sp))也就是第二步栈顶的值。两个字节。 2)(sp)=(sp)+2 下移两个字








【3】call 标号 将当前IP压栈后,转到标号处执行指令。可以看出CALL与RET是一个反操作。CALL将当前的IP压栈,并转到标号处执行。而RET是弹出栈顶值到IP后,继续执行。CALL转移的标号是16位的位移,因此,不能短转移,是一个近转移。操作步骤:1) (sp)=(sp)-2 2) ((ss)*16+(sp))=(IP) 3)(IP)=(IP)+16位位移。16位位移范围-32768~32767 相当于从标号处的地址-CALL指令后第一个字节地址。也就是push IP  jmp near ptr 标号









【4】call far ptr 标号, 同前面指令类似,只是这个指令不是近转移,而是远转移,实现段间转移,同时将转移前的CS和IP入栈。相当于PUSH CS PUSH ip jmp far ptr 标号








【5】CALL 16位 reg 同CALL 标号类似,首先将IP入栈,接着将寄存器中的值赋于IP。  相当于PUSH IP, jmp 16位  reg






【6】call word ptr 内存单元地址 用内存单元中值来指定要转移的IP地址。相当于PUSH IP 和jmp word ptr 内存单元地址。



【7】call dword ptr 内存单元地址 相当于进行PUSH CS PUSH IP和jmp dword ptr内存单元地址。



前面我们都是单独使用CALL和RET指令,实际上我们是可以配合一起使用。我们首先看如下一张图,这张图显示了在CALL指令发生过程中IP修改和栈过程。



正因为CALL与RET实现了IP值的保存与恢复。所以可以基于RET实现一个子程序,而基于CALL实现一个调用。这样就形成了主程序与子程序框架。



从上面可以看,CALL与RET结合在一起,可以实现汇编语言编程中的模拟化设计。很显然,在实际编程中模块化设计是必不可少。而且模块化设计也简化了汇编程序的复杂性,可以将功能相同的指令放在一起作为一个子程序,让主程序进行多次调用,而不必重复相同的指令代码。这里有一个问题,就是前面没有解决的,就是如果你有C语言或者其它高级语言的经验,你一定会知道,对应的子程序或者函数,它一定是有参数和返回值的。那么如何在汇编程序中实现呢?首先我们要声明的一点,是汇编不提供专门的解决方法去解决参数和返回值问题。这一切都是由汇编程序来决定的。因此,我们可以很明显的得出一点,就是参数和结果都是数据,数据可以存在哪,参数和结果就可以存在哪。显然,有三个地方,内存、寄存器、和栈中。
首先学习一个新的指令mul,乘法,前面我们进行乘法都是忽悠大家,用的是加法。实际上汇编里程序同除法一样,是有的,而且也分位数。





这里内存字节单元,8位一般是mul byte ptr ds:[0] 而16位通常是mul word ptr ds:[0]
有了这个准备知识,我们来看下面的例子。



上面的例子对子程序而言只有一个可变是BX,如果说有多个可变量,那应该怎么办呢?显然光靠寄存器是不行的。使用内存是必然的选择。单纯的使用内存,我们知道需要额外的读定操作。所以现代编译器都使用栈。虽然栈也是在内存中,但是它是一次性取16位的,性能明显要高于普通的MOV操作。使用寄存器存在另外一个问题,就是如果说同一个寄存器在主程序和子程序中都使用的话,会产生一种问题,那就是共享问题。比喻说主程序使用这个寄存器暂存了某个值,这时进入子程序,如果子程序要用到这个寄存器就会修改了主程序暂存的值,造成结果错误。如下例所示:


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: