简介
转移指令可以理解为转移程序执行位置的指令。转移指令通常会修改IP或(IP与CS)。
转移指令分类
无条件转移指令(jmp)
其使用方式根据跳转的距离(修改IP的范围)分为三种
段内短转移——跳转的IP范围为 -128~127
jmp [short] 标号
jmp (16位reg)
jmp word ptr 内存单元地址
段内近转移——跳转的IP范围为 -32768~32767
jmp [near] 标号
jmp (16位reg)
jmp word ptr 内存单元地址
段间转移——没有限制
jmp far ptr 标号
jmp dword ptr 内存单元地址
段内短转移指令分析
其中带有[]内的内容其实可以忽略,因为这个大多数是用于编译器检查的;如果不加支持两种范围的段内转移
大家可以试试这段代码验证一下猜想
assume cs:codesg
codesg segment
mov ax, 32767
; 使用下面这行代码就会报错
; jmp short temp
; 使用下面这行代码就不会报错,但是会有个警告,说接近64K(即65536)
jmp temp
db 65535 dup (0)
temp:
mov ax,4c00H
int 21H
codesg ends
end
段内近转移指令分析
第二种指令仅支持 16位reg ,大家也不用考虑 8位reg~~~;第二种指令其实和第一种的原理是一样的,后续讲解原理的时候会讲到。
段间转移指令分析
第三种指令对于段内转移来说就是换一种方式取数据;而对于段间转移的话,高位地址保存CS数值;低位地址保存IP的数值
段内转移原理
注意jmp指令,在汇编代码里是jmp 0004,而在二进制里是EB02,为什么这里是02,而不是04呢?
对于段内转移来说,jmp指令是根据偏移量来定位的,比如上述的例子:jmp指令后的第一条指令与标号之间的偏移量,也就是——(076A:0004 - 076A:0002 = 02)
段间转移原理
注意此处加入了一行db 128 dup (0)指令,主要是让标号temp超出段内短转移的范围(否则即使使用jmp far ptr 标号,编译器也会进行优化。)
这里的表示方法就比较简单粗暴,易于看懂,这里就不多加解释了。
有条件转移指令(jcxz、jne、je等等)
有条件转移指令包含了以下几个:
jcxz:当 cx==0时,跳转到指定标号处
jne:当 zf==0时(两个数不相等时),跳转到指定标号处
je:当zf==1时(两个数相等时),跳转到指定标号处
jb:当cf==1时(第一个数小于第二个数),跳转到指定标号处
jnb:当cf==0时(第一个数不小于第二个数),跳转到指定标号处
ja:当cf0&&zf0(第一个数大于第二个数),跳转到标号处
jna:当cf0||zf1(第一个数不大于第二个数),跳转到标号处
这里着重分析第一条jcxz指令,该指令是当cx==0时,跳转到标号处的指令,该指令的操作数范围为 -128~127,可以理解为带有条件的jmp short 标号,操作数范围为 -128~127
循环指令(loop)
loop指令主要执行以下操作:
--cx
if(cx != 0) 跳转到标号处执行
if(cx == 0) 继续向下执行
该指令是短转移(所有的循环指令都是短转移),在对应的机器码中包含转移的位移而不是目的地址,操作数范围为 -128~127
注意点
如果对 偏移在-128~127之间的标号使用 偏移距离大于127的转移指令(段内近转移或者段间转移),就会生成nop(可以理解为对齐吧),看下面的示意图
这里纯属个人猜测:由于jmp near ptr 标号的范围是 -32768~32767(FFFF) ,所以编译器对于范围在 -128~127内的偏移量会自动进行转换,比如 段内近转移一般要3个字节,段内短转移只需要2个字节,所以使用段内近转移表示段内短转移的偏移量,会多生成一个字节凑够3个字节
课后检测点