8086汇编-CALL和RET指令(10)
引言
1.程序之间的加载和返回
2.call和ret指令都是转移指令,它们大都修改IP,或者同时修改CS和IP
3.它们经常被同用来实现自程序的设计
1.ret和retf 指令
1.ret指令是用栈中的数据,修改IP的内容,从而实现进转移
2.在CPU执行ret指令的时候
1 | 1.(IP)=((SS)*16+(SP)) |
3.retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移
4.在CPU执行retf指令的时候
1 | 1. |
2.call指令
1.call指令经常和ret指令配合使用,因此CPU执行call指令
1 | 1.当前的IP或者CS和IP压入栈中 |
2.call指令不能实现段转移,除此之外,call指令实现转移的方法和jmp指令的原理相同
3.依据位移进行转移的call指令
1.格式:call 标号
1 | 将当前的IP压入栈后,转移到标号处指令指令 |
2.CPU
1 | 1. (SP)=(SP)-2 |
4.转移的目的地址在指令中的call指令
1.call far ptr 标号
1 | 1. |
5.转移地址在寄存器中的call指令
1.格式:call 16位寄存器
2.功能
1 | ()=(sp)-2 |
6转移地址在内存中的call指令
1.call word ptr 内存单元地址
安装字型的数据来索引【短转移】
2.call dword ptr 内存单元地址
高cs 低 ip
7.call和ret的配合使用
1 | assume cs:code |
让我们来看看程序的主要执行过程
1,前三条指令执行后,栈的情况如下:
2.call 指令读入后,(IP)=00EH,cpu指令缓冲器中的代码是 B8 05 00
cpu执行B8 05 00;首先栈中的变化为:
然后,(IP)=(IP)+0005=0013H
3.CPU从cs:0013H处(即标号s处)开始执行
4.ret指令读入后:(IP)=0016H【IP永远都是指向的下一个指令,将要执行的IP】,cpu指令缓冲区中的代码为C3
当CPU执行C3,相当于进行pop IP,执行后,栈中的情况为
(IP)=000EH
5.CPU回到cs:000EH处(即call指令后面的指令处)继续执行
从上面的讨论中我们发现,可以写一个具有一定功能的程序段,我们称其为子程序,在需要的时候,用call指令转去执行
可是执行完子程序后,如何让CPU接着call指令向下执行?
没错就是ret
call指令后面的指令的地址存储在栈中,所以可以在子程序后面使用ret指令,用栈中的数据设置IP的值,从而转到call指令后面的代码继续执行
这样,我们可以利用call和ret来实现子程序的机制【函数】
8.mul 指令
1.因下面要用到,所以我们就来介绍一下mul指令,mul是乘法指令,使用mul做乘法的时候
1 | 1.相乘的2个数,要么都是8位,要么都是16位 |
2.结果
1 | 8位:AX中 |
3.格式
1 | mul reg【寄存器】 |
1.mul byte ptr ds:[0]
1 | 8位的 |
2.mul word ptr [bx+si+8]
1 | 16位: |
比如:100*10
1 | mov al,100 |
比如:100*10000
1 | mov ax,100 |
9.模块化程序设计
从上面我们看到,call和ret指令共同支持了汇编语言编程中的模块化设计,在实际编程中,程序的模块化是必不可少的
用call和ret指令==【面对过程的思想】
10.参数和结果传递的问题
子程序一般都要根据提供的参数处理一定的事务,处理后,将结果(返回值)提供给调用者
其实,我们讨论参数和返回值传递的问题,实际上是在探讨,应该如何存储子程序需要的参数和产生的返回值
我们设计一个子程序,可以根据提供的N,来计算N的3次方
1 | 1.我们将这个参数N存储在什么地方? |
1 | cube: mov ax,bx |
11.批量数据的传递
前面的例程中,子程序cube只有一个参数放在bx中。如果有两个参数,那么可以用两个寄存器来放,可是如果需要传递的数据有3个4个甚至多到N个呢,我们应该如何存放呢?
这个时候,我们将数据放在内存中,然后它们所在内存空间的首地址放在寄存器中,传递给需要的子程序。对于具有批量数据的返回结果,也可用同样的方法
1 | assume cs:code |
注意,除了用寄存器传递参数外,还有一种通用的方法是用栈来传递参数。
12.寄存器冲突的问题[10.12.play]
设计一个子程序,功能:将一个全是字母,以0结尾的字符串,转化为大写
程序要处理的字符串以0作为结尾符,这个字符串可以定义
1 | db 'conversation',0 |
应用这个子程序,字符串的内容后面一定要有一个 0 ,标记字符串的结束。子程序可以依次读取每一个字符进行检测,如果不是0 ,就进行大写的转化;如果是0,就结束处理。由于可通过检测0而知道是否处理完这整个字符串,所以子程序不需要字符串的长度作为参数。可以用jcxz来检测0
1 | capital: push cx |
实验10
编写子程序
1 | EXP 自己查看 |
自我总结一下最近学的把
1.乘法:
1 | 首先乘法:mul 乘数2 |
2.除法:
1 | 然后是除法:div 除数 |
3.循环的使用
1 | 1.已知或者能求出循环次数的循环 |
loop 如何使用呢?
1 | 这个很简单,设定循环的次数,cx |
jcxz 如何使用呢?
1 | 这个比较复杂,我已知2种办法 |