可以修改CS和IP的指令统称为转移指令,概括的讲,转移指令就是可以控制CPU执行内存中某处代码的指令.

一、分类

8086CPU的转移行为有以下几类:

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

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

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

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

  • 无条件转移指令
  • 条件转移指令
  • 循环指令
  • 过程
  • 中断

二、JMP指令

JMP指令为无条件转移指令,可以只修改IP,也可以修改CS和IP,有以下几种用法:

1.JMP short 标号(转到标号处执行,段内短转移)

assume cs:code
stack segment stack
    db 256 dup(0)
stack ends
code segment
start:
    mov ax,stack
    mov ss,ax
    mov sp,256
    mov ax,0
    jmp short s ;
    inc ax
s:  inc ax
    mov ax,4c00h
    int 21h 
code ends 
end start

容易看出,上述代表执行后ax寄存器的值为1,为了了解其工作原理,我们先看看代码段中的机器指令:

0B47:0000 B8370B        MOV     AX,0B37
0B47:0003 8ED0          MOV     SS,AX
0B47:0005 BC0001        MOV     SP,0100
0B47:0008 B80000        MOV     AX,0000
0B47:000B EB01          JMP     000E
0B47:000D 40            INC     AX
0B47:000E 40            INC     AX
0B47:000F B8004C        MOV     AX,4C00
0B47:0012 CD21          INT     21

注意到JMP指令的汇编代码EB01,并不能看出EB01是什么意思,修改原文件:

assume cs:code
stack segment stack
    db 256 dup(0)
stack ends
code segment
start:
    mov ax,stack
    mov ss,ax
    mov sp,256
    mov ax,0
    jmp short s ;
    inc ax
    inc ax
s:  inc ax
    mov ax,4c00h
    int 21h 
code ends 
end start
0B47:0000 B8370B        MOV     AX,0B37
0B47:0003 8ED0          MOV     SS,AX
0B47:0005 BC0001        MOV     SP,0100
0B47:0008 B80000        MOV     AX,0000
0B47:000B EB02          JMP     000F
0B47:000D 40            INC     AX
0B47:000E 40            INC     AX
0B47:000F 40            INC     AX
0B47:0010 B8004C        MOV     AX,4C00
0B47:0013 CD21          INT     21

这时候的汇编指令是EB02,也就是说JMP short指令不存储标号的地址,只存储标号到当前指令的位移(字节,inc指令只占用一个字节),此时的EB02是指,该指令执行后,IP的值增加2

我们得出结论,JMP short 指令只存储相对位移注意到02(EB02)只占用1个字节,也就是说JMP short能够跳转的位移是-128~127,是这样吗?我们增加跳转的位移

assume cs:code
stack segment stack
    db 256 dup(0)
stack ends
code segment
start:
    mov ax,stack
    mov ss,ax
    mov sp,256
    mov ax,0
    jmp short s ;
    db 256 dup(0)
    inc ax
    inc ax
s:  inc ax
    mov ax,4c00h
    int 21h 
code ends 
end start

此时编译出错

1.asm(11): error A2053: Jump out of range by 131 byte(s)

  50952 + 450712 Bytes symbol space free

      0 Warning Errors
      1 Severe  Errors

如何解决这一问题呢?这时我们需要使用jmp near ptr 指令

2.JMP near ptr 标号(转到标号处执行,段内近转移),修改代码:

assume cs:code
stack segment stack
    db 256 dup(0)
stack ends
code segment
start:
    mov ax,stack
    mov ss,ax
    mov sp,256
    mov ax,0
    jmp near ptr s ;
    db 256 dup(0)
    inc ax
    inc ax
s:  inc ax
    mov ax,4c00h
    int 21h 
code ends 
end start

机器指令如下:

0B47:0000 B8370B        MOV     AX,0B37
0B47:0003 8ED0          MOV     SS,AX
0B47:0005 BC0001        MOV     SP,0100
0B47:0008 B80000        MOV     AX,0000
0B47:000B E90201        JMP     0110

注意E90201,8086CPU是小端模式这里的0201实际上是16进制数0102,也就是位移.

如果位移超处-32768~32767,这时就需要短间转移指令

3.JMP far ptr 标号(短间转移指令)

assume cs:code
stack segment stack
    db 256 dup(0)
stack ends
code segment
start:
    mov ax,stack
    mov ss,ax
    mov sp,256
    mov ax,0
    jmp far ptr s ;
    db 256 dup(0)
    inc ax
    inc ax
s:  inc ax
    mov ax,4c00h
    int 21h 
code ends 
end start

机器指令:

0B47:0000 B8370B        MOV     AX,0B37
0B47:0003 8ED0          MOV     SS,AX
0B47:0005 BC0001        MOV     SP,0100
0B47:0008 B80000        MOV     AX,0000
0B47:000B EA1201470B    JMP     0B47:0112

这事JMP就是采用绝对地址了,注意小端模式

4 .JMP 标号(根据标号的位置,编译器会自动判断采用前面三种的某一种转移方式)

5 JMP word ptr 内存单元地址(直接修改IP,段内转移)

比如下面代码,执行后IP=0123H

mov ax,0123H
mov ds:[0],ax
jmp word ptr ds:[0]

6 JMP dword ptr 内存单元地址(直接修改CS和IP,段间转移)

比如下面代码,执行后IP=0123H,CS=0000H

mov ax,0123H
mov ds:[0],ax
mov word ptr ds:[2],0
jmp word ptr ds:[0]

又比如下面程序能够正常返回

assume cs:code
data segment
    dw 0,0,0,0
data ends
stk segment stack
    db 256 dup(0)
stk ends
stack segment
    mov ax,100
    jmp dword ptr [bx]
stack ends
code segment
start:
    mov ax,stk
    mov ss,ax

    mov bx,0
    mov ax,data
    mov ds,ax
    mov ax,stack
    mov [bx+6],ax 
    mov [bx+4],bx 

    mov ax,code

    mov [bx+2],ax
    mov ax,offset s 
    mov [bx],ax

    jmp dword ptr [bx+4] ;
s:  mov ax,4c00h
    int 21h 
code ends 
end start

三、LOOP指令

loop指令为循环指令,所有循环指令都是短转移,loop 标号 等价于下面两条指令:

  • cx=cx-1
  • if(cx!=0)jmp short 标号

四、根据位移进行转移的意义

前面我们讲到:

  • jmp short 标号
  • jmp near ptr 标号
  • loop 标号

这几个汇编指令,它们对IP的修改是根据转移目的地址和转移起始地址之间的位移来进行的,它们对应的机器码中不包含转移的目的地址,而包含的是目的地址的位移.

这种设计为了方便程序段在内存中的浮动装配,例如

   mov cx,6
    mov ax,10
s:  add ax,ax
    loop s

这段程序在内存中的任何位置都能正确执行,而如果loop跳转的是绝对地址,该程序只能在指定的内存地址中执行了

五、CALL和RET指令

  • RET指令相当于如下指令:
POP IP
  • RETF指令相当于如下指令:
POP IP
POP CS
  • CALL 标号 指令相当于如下指令:
PUSH IP
JMP near ptr 标号
  • CALL far ptr 标号 指令相当于如下指令:
PUSH CS
PUSH IP
JMP far ptr 标号
  • CALL 16位寄存器 指令相当于如下指令(其他以此类推):
PUSH IP
JMP 16位寄存器

 

例如如下代码执行后AX为E

0B47:0008 B80000        MOV     AX,0000
0B47:000B E80100        CALL    000F
0B47:000E 40            INC     AX
0B47:000F 58            POP     AX

例如下面程序执行后AX的值为3(不要用DEBUG调试,中断操作会影响栈的值)

assume cs:codesg

stack segment 

 dw 8 dup (0)

stack ends 

codesg segment 
start: 
    mov ax,stack
    mov ss,ax
    mov sp,16
    mov ds,ax
    mov ax,0
        call word ptr ds:[0EH]
    inc ax
    inc ax
    inc ax
    mov ax,4c00h
    int 21h

codesg ends 

end   start

注意:执行到 call word ptr ds:[0EH] 的时候 , 具体的流程如下 :

  • CPU取指令 : (call word ptr ds:[0EH])
  • ip自增上述指令的长度 , 指向了下一条指令 (inc ax)
  • 开始执行该指令
    • push ip ; 将 ip 压入栈 , 也就是 : ss:[0EH] 保存 ip 的低 8 位 , ss:[0FH] 保存高 8 位
    • jmp ds:[0EH] ( (ip) = ds:[0EH] , 也就是说 , 程序又从 ds:[0EH] 中取出数据赋值给 ip , 然后继续执行 )
  • 现在其实就开始执行 ip 之前保存的地址的指令了 , 也就是三个 inc ax
  • 因此最终 ax 值为 3

例如下面程序执行后AX为1,BX为0(不要用DEBUG调试,中断操作会影响栈的值)

assume cs:code 
stack segment 
   dw 8 dup(0) 
stack ends 

code segment 
   start:mov ax,stack 
         mov ss,ax 
         mov sp,16 
         mov word ptr ss:[0],offset s 
         mov ss:[2],cs 
         call dword ptr ss:[0] 
         nop   
       s:mov ax,offset s 
         sub ax,ss:[0ch] 
         mov bx,cs 
         sub bx,ss:[0eh] 
         mov ax,4c00h 
         int 21h 
code ends 
end start 

请按照上个例子的思路分析

分类: 编程