基础概念

可以修改IP,或同时修改CS和IP的指令统称为转移指令。转移指令就是可以控制CPU执行内存中某处代码的指令
分为以下几类

  • 只修改IP,称为段内转移,比如 jmp ax
  • 同时修改CS和IP,称为段间转移,比如 jmp 1000:0

由于转移指令对IP修改范围不同,段内转移又分为:短转移和近转移

  • 短转移IP修改范围-128~127
  • 近迁移IP修改范围-32768~32767

8086CPU转移指令分为以下几类

  • 无条件转移指令(jmp)
  • 条件转移指令
  • 循环指令(loop)
  • 过程
  • 中断

这些转移的基本原理是相同的

操作符offset

它的功能是取得标号的偏移地址,比如下面程序
assume scs:codesg
codesg segment
start:mov ax,offset start
s:mov ax,offset s
codesg ends
end start
上面程序中,offset操作符取得了标号start和s的偏移地址0和3
所以mov ax,offset start 相当于指令mov ax,0

jmp指令

jm为无条件转移指令,要给出两种信息

  • 转移的目的地址
  • 转移的距离(段间转移,段内短转移,段内近转移)

不同的给出目的地址的方法,和不同的的转移位置,对应有不同格式的jmp指令

根据位移进行转移的jmp指令

  • 段内短转移

jmp short 标号(转到标号处执行指令)
这种格式的jmp指令实现的是段内短转移,它的IP修改范围为-128~127,short符号说明指令进行的是短转移,标号是代码段中的标号,说明了指令要转移的目的地,转移指令结束后,CS:IP应该指向标号处的指令
assume cs:codesg

codesg segment
start: mov ax,0
jmp short s
add ax,1
s: inc ax
codesg ends
end start
上面的程序执行后,ax中值为1

在jmp short 标号 指令所对应的机器码中,并不包含专一的目的地址,而 包含的是转移的位移

  • 段内近转移

jmp near ptr 标号 (IP) = (IP) + 16位位移 16位位移的范围是-32768~32767 用补码表示

  • 段间转移(远转移)

jmo far ptr 标号
功能是 (CS) = 标号所在段的段地址 (IP) = 标号在段中的偏移地址
far ptr 指明了指令用标号的段地址和偏移地址修改CS和IP

assume cs:codesg
codesg segment
start: mov ax,0
mov bx,0
jmp far ptr s
db 256 dup (0)
s: add ax,1
inc ax
codesg ends
end start
源程序中的db 256 dup (0) 被debug解释为相应的若干条汇编指令。jum far ptr s 所对应的机器码:EA 0B 01 BD 0B 其中包含了专一的目的地址,高地址BD 0B 是转移的段地址, 低地址的0B 01 是偏移地址

转移地址在寄存器中的jmp指令

指令格式:jmp 16位reg
功能:(IP) = (16位reg) ,例如用ax中的值设置IP

转移地址在内存中的jmp指令

有两种格式

  • jmp word ptr 内存单元地址(段内转移)

功能:从内存单元地址处开始存放着一个字,是转移的目的偏移地址

内存单元地址可用寻址方式的任一格式给出
比如—
mov ax,0123H
mov ds:[0],ax
jmp word ptr ds:[0]
执行后,(IP) = 0123H

  • jmp dword ptr 内存单元地址(段间转移)

功能:从内存单元地址处开始存放着两个字,高地址处的字是转移的目的段地址,低地址处是转移的目的偏移地址
(CS) = (内存单元地址+2)
(IP) = (内存单元地址)

内存单元地址可用寻址方式的任一格式给出
比如
mov ax,0123H
mov ds:[0],ax
mov word ptr ds:[2],0
jmp dword ptr ds:[0]
执行后,(CS) = 0, (IP) = 0123H

注意:形如 jmp 2000:0100 的转移指令,是在debug中使用的汇编指令,汇编编译器并不认识,如果在源程序中使用,编译时也会报错

jcxz指令

条件转移指令,所有的条件转移指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址,对IP修改范围都为-128~127

指令格式:jcxz 标号 (如果(cx) = 0 ,转移到标号处执行)
操作:当(cx)=0,(IP)=(IP)+8位位移
当(cx) != 0,什么也不做,程序向下执行
jcxz 标号 功能相当于
if((cx) == 0) jmp short 标号

loop 指令

loop 指令为循环指令,所有循环指令都是短转移,对IP修改范围都是-128~127
loop 标号 功能相当于
(cx) = (cx)-1
如果(cx) != 0, (IP) = (IP)+8位位移
如果(cx) == 0 什么也不做,程序向下执行

RET指令

ret

用栈中的数据,修改IP内容,从而实现近转移
分以下两步

  • (IP) = ((ss)*16+(sp))
  • (sp) = (sp)+2

相当于 pop ip

例子

  1. assume cs:code
  2. stack segment
  3. db 16 dup (0)
  4. stack ends
  5. code segment
  6. mov ax,4c00h
  7. int 21h
  8. start: mov ax,stack
  9. mov ss,ax
  10. mov sp,16
  11. mov ax,0
  12. push ax
  13. mov bx,0
  14. ret
  15. code ends
  16. end start

ret指令执行后,(IP) = 0, CS:IP 指向代码段的第一条指令

retf

用栈中的数据,修改CS和IP的内容,从而实现远转移
分以下四步

  • (IP) = ((ss)*16+(sp))
  • (sp) = (sp)+2
  • (cs) = ((ss)*16+(sp))
  • (sp) = (sp)+2

相当于
pop ip
pop cs

例子

  1. assume cs:code
  2. stack segment
  3. db 16 dup (0)
  4. stack ends
  5. code segment
  6. mov ax,4c00h
  7. int 21h
  8. start: mov ax,stack
  9. mov ss,ax
  10. mov sp,16
  11. mov ax,0
  12. push cs
  13. push ax
  14. mov bx,0
  15. retf
  16. code ends
  17. end start

retf 执行执行后,CS:IP 指向代码段的第一条指令

CALL指令

分两步操作

  • 将当前的IP或CS和IP压入栈中
  • 转移

call 指令不能实现短转移,除此之外,call指令实现转移的方法和jmp指令的原理相同

根据位移进行转移的call指令

call 标号 (将当前的IP压入栈后,转到标号处执行指令)
CPU进行如下操作

  • (sp)=(sp)-2
  • ((ss)*16+(sp))=(ip)
  • (ip)=(ip)+16位位移

相当于
push IP
jmp near ptr 标号

转移的目的地址在指令中的call指令

前面的call指令,其对应的机器指令中没有转移的目的地址,而是相对于当前IP的转移位移
call far ptr 标号 实现的是段间转移
CPU执行这种格式的call指令时,进行如下操作

  • (sp)=(sp)-2
  • ((ss)*16+(sp))=(cs)
  • (sp)=(sp)-2
  • ((ss)*16+(sp))=(ip)
  • (cs)=标号所在段的段地址
  • (ip)=标号所在段中的偏移地址

相当于
push CS
push IP
jmp far ptr 标号

转移地址在寄存器中的call指令

call 16位reg
进行如下操作

  • (sp)=(sp)-2
  • ((ss)*16+(sp))=(IP)
  • (IP)=(16位reg)

PUSH IP
jmp 16位reg

转移地址在内存中的call指令

有两种格式

  1. call word ptr 内存单元地址

相当于
push IP
jmp word ptr 内存单元地址

  1. call dword ptr 内存单元地址

相当于
push CS
push IP
jmp dword ptr 内存单元地址