Ret 和 call 也是转移指令,可是他们跟jmp不同的是,这两个转移指令都跟栈有关系。
<1> ret
用栈中的数据改动IP的地址,从而实现近转移
( ip ) = ( (ss)*16+ sp )
( sp ) =( sp ) + 2
相当于pop ip
<2>retf
用栈中的数据来改动CS以及IP的值,实现段间转移
( ip ) = ( (ss)*16+ sp )
( sp ) =( sp ) + 2
( cs ) = ( (ss)*16+ sp )
( sp ) =( sp ) + 2
相当于
Pop ip
Pop cs
<3> call xxx(行号)
先把当前IP压栈,然后跳转,相当于实现近转移
( sp ) = ( sp ) – 2
( (ss)*16+ sp ) = ( ip )
( ip ) = ( ip ) + 16位位移
相当于:
Push ip
Jmp near ptr xxx(行号)
<4>call far ptr
把CS。IP压栈处理,然后实现跳转,相当于段间转移。远转移
( sp ) = ( sp ) – 2
( (ss)*16+ sp ) = ( cs )
( sp ) = ( sp ) – 2
( (ss)*16+ sp ) = ( ip )
(cs) = 当前行号段地址
(ip) = 当前行号偏移地址
相当于:
Push cs
Push ip
Jmp far ptr xxx
<5> call reg(16bit)
跳转到16位寄存器上中存储的地址
( sp ) = (sp) – 2
( (SS)*16 + (sp) ) = (IP)
(IP) = ( 16bit Reg )
相当于:
Push IP
Jmp 16bit Reg
<6> call word ptr 内存单元地址
相当于
Push IP
Jmp word ptr 内存单元地址
如:call word ptr ds:[0]
<7> call dword ptr 内存单元地址
相当于
Push cs
Push ip
Jmp dword ptr 内存单元地址
比如:jmp dword ptr DS:[0];
<8>寄存器的冲突问题
主程序调用子程序段的时候,可能子程序会用到主程序中使用的寄存器的值。程序在设计的时候不可能做到不让子程序使用主程序的寄存器,由于两者是相互独立的。你永远不会还有一个会做什么。所以在子程序中採取不使用主程序中调用的寄存器的做法是不可行的。
解决方法:
把寄存器用到的东西,保存到堆栈里面。子程序调用完成。再将堆栈保存的东西弹出。