这里写自定义目录标题
介绍
10.1 ret和retf 原理
10.2call指令
10.3依据位移进行转移的call指令
10.4转移的目的地址在指令中的call指令
10.5转移地址在寄存器中的call指令
push ip
jmp ax
ip=6
执行call ax,此时ip=5,压栈。
call ax,执行,ip=6,cs:ip,指向,1000:6
mov bp,sp//fffeh,因为有一个压栈了。
add ax,[bp] //ss:[bp],把5+ax=5+6=0bh
10.6转移地址在内存中的call指令
Jmp word ptr 段寄存器:[偏移]如(jmp word ptr ds:[0]),该指令的功能是将IP修改为内存单元中所存的一个16位字
检测点10.5
(1)答:ax中的数值为3
(2)
ax=1
bx=0
第一题
该题中的程序的数据段和栈段使用了同一段内存 , 也就是说 ds 和 ss 是相同的
执行到 call word ptr ds:[0EH] 的时候 , 具体的流程如下 :
- CPU取指令 : (call word ptr ds:[0EH])
- ip自增上述指令的长度 , 指向了下一条指令 (inc ax)
- 开始执行该指令
3.1. push ip ; 将 ip 压入栈 , 也就是 : ss:[0EH] 保存 ip 的低 8 位 , ss:[0FH] 保存高 8 位
3.2. jmp ds:[0EH] ( (ip) = ds:[0EH] , 也就是说 , 程序又从 ds:[0EH] 中取出数据赋值给 ip , 然后继续执行 ) - 现在其实就开始执行 ip 之前保存的地址的指令了 , 也就是三个 inc ax
- 因此最终 ax 值为 3
第二题
这个程序定义了数据段 , 其实也是栈段和数据段使用了同一段内存
首先也是初始化栈 :
mov ax, data
mov ss, ax
mov sp, 16
然后执行 :
mov word ptr ss:[0], offset s
将 s 标号处两个字节的地址移动到 ss:[0] 处
mov ss:[2], cs
将代码段地址的值保存在到 ss:[2] 处 , 同样两个字节
call dword ptr ss:[0]
CPU首先读取该指令
然后将 ip 增加该指令的长度
然后准备开始执行 (由于是 dword 因此 cs 和 ip 都要压栈)
执行首先要将当前的 cs 压栈 (保存在 ss:[0EH])
然后 ip (也就是 call 指令下一个指令的地址 , 也就是 nop 的地址) 压栈 (保存在 ss:[0CH]) 处
然后读取 ss:[0] 中的四个字节的数据 , 相当于 : 高地址为段地址 , 低地址为偏移地址
也就说 cs 为 (ss:[2]) , ip 为 (ss:[0])
这个 cs 和 ip 组成的地址就是 second 的地址
现在开始执行 second
mov ax, offset second
读指令 , ip自增 , 然后执行 , 因此执行完后 , ax 为该指令的偏移地址
然后 sub ax, ss:[0CH]
通过之前的分析 :
ss:[0CH] 处保存的是 : call dword ptr ss:[0] 这条指令的下一条指令 nop 的相对于代码段偏移地址
ax = (mov ax, offset second 的偏移地址) - ( nop 的偏移地址)
也就是 nop 指令的长度 , 也就是 1
接下来 :
mov bx, cs
将代码段的基址赋值给 bx
bx = (bx) - (ss:[0EH])
(ss:[0EH]) = (cs)
因此 bx = 0
10.7call和ret的配合使用
计算2^N
call命令的理解,重要。
1、CPU将call s指令的机器码读入,
2、IP指向了call s后面的指令,比如mov bx,ax
3、CPU执行call s指令,将当前的IP值(指令mov bx,ax的偏移地址)压栈
4、并将IP的值改变为标号s处的偏移地址。
另外的例子,引出子程序的概念。