转移指令的原理
可以修改IP,或同时修改CS和IP的指令统称为转移指令。概括地讲,转移指令就是可以控制CPU执行内存中某处代码的指令。
8086CPU的转移行为有以下几类。
- 只修改IP时,称为段内转移,比如:jmp ax
- 同时修改CS和IP时,称为段间转移,比如:jmp 10000:0
由于转移执行对IP的修改范围不同,段内转移又分为:短转移和近转移。
- 短转移IP的修改范围为-128~127。
- 近转移IP的修改范围为-32768~32767。
而8086的转移指令分为以下几类。
- 无条件转移指令(如:jmp)
- 条件转移指令
- 循环指令(如:loop)
- 过程
- 中断
1. 操作符offset
操作符offset在汇编语言中是由编译器处理的符号,它的功能是取得标号的偏移地址。 例子如下:
assume cs:code
code segment
start: mov ax, offset start; 相当于 mov ax, 0
s: mov ax, offset s; 相当于 mov ax, 3
code ends
end start
问题:有如下程序段,添加两条指令,使该程序在运行中将s处的一条指令复制到s0处。
assume cs:code
code segment
s: mov ax, bx ;占两个字节
mov si, offset s
mov di, offset s0
_________________
_________________
s0: nop ;占一个字节
nop
code ends
end
answer:
mov ax, cs:[si]
mov cs:[di], ax
2. jmp指令
jmp为无条件转移指令,可以只修改IP,也可以同时修改CS和IP。它转移的时候会给出以下两类信息(其一):
(1) 转移的目的地址
(2) 转移的距离(段间转移,段内短转移,段内近转移)
2.1 依据位移进行转移的jmp指令
jmp short 标号(转到标号处执行指令)
这种格式的jmp指令实现的使段内短转移,它对IP的修改范围为-128~127。jmp指令中的"shrot"符号,说明指令进行的是短转移。例子如下:
assume cs:code
code segment
start: mov ax, 0
jmp short s
add ax, 1
s: inc ax
code ends
end start
上述代码在执行完之后,ax的值为1。
我们可以观察上述汇编指令对应的机器指令,jmp指令实际上就是把程序的执行跳转到位于CS:0008内存单元的指令执行。但它的机器指令"EB03"并无目标内存单元的地址。那么程序又是如何跳转过去呢?我们再看个例子。
assume cs:code
code segment
start: mov ax, 0
jmp short s
add ax, 1
add ax, 2
s: inc ax
code ends
end start
观察它的机器指令可知,这时候jmp指令的机器指令变成了"EB06",而一条"add"指令是三个字节,两条刚好是6个字节。说明"jmp short 标号"的机器指令中没有给出CPU要转移的目的地址,而是给出CPU要转移的位移,即当前的IP向后移动6个字节。
实际上,jmp short 标号"的功能为:(IP) = (IP) + 8位位移。
- 8位位移 = 标号处的地址 - jmp指令后的第一个字节的地址。
- short指明此处的位移为8位位移。
- 8位位移的范围为-128~127。
- 8位位移由编译程序在编译时算出。
还有一种类似指令:jmp near ptr 标号,它实现的是段内近转移。
"jmp near prt 标号"的功能为: (IP) = (IP) + 16位位移。
- 8位位移 = 标号处的地址 - jmp指令后的第一个字节的地址。
- near ptr指明此处的位移为16位位移,进行的是段内近转移。
- 16位位移的范围为-32768~32767,补码表示。
- 16位位移由编译程序再编译时算出。
2.2 转移的目的地址在指令中的jmp指令
"jmp far ptr 标号"实现的是段间转移,功能如下:
(CS) = 标号所在段的段地址, (IP) = 标号在段中的偏移地址。
far ptr 指明了指令用标号的段地址和偏移地址修改CS和IP。
assume cs:code
code segment
start: mov ax, 0
mov bx, 0
jmp far ptr s
db 256 dup (0)
s: add ax, 1
inc ax
code ends
end start
可以看到要转移的目的地址为:076C:010B,而对应的机器指令为"EA0B016C07",说明高地址存放转移的段地址,低地址存放偏移地址。
2.3 转移地址在内存中的jmp指令
转移地址在内存中的jmp指令有两种格式:
- jmp word ptr 内存单元地址(段内转移)
功能:只修改IP
mov ax, 0123H
mov ds:[0], ax
jmp word ptr ds:[0]
执行后,(IP) = 0123H
- Jmp dword ptr 内存单元地址(段间转移)
同时修改CS和IP,高地址内存单元存放转移的目的段地址,低地址内存单元存放转移的目的偏移地址。
mov ax, 0123H
mov ds:[0], ax
mov word ptr ds:[2], 0
jmp dword ptr ds:[0]
若要使程序中的jmp指令执行后,CS:IP指向程序的第一条指令,在data段中应该定义哪些数据?
assume cs:code
data segment
db 1, 0, 0, 2; 要填写的部分
data ends
code segment
start: mov ax, data
mov ds, ax
mov bx, 0
jmp word ptr [bx+1]
mov ax, 4c00H
int 21H
code ends
end start
解析:由于"jmp word ptr [bx+1]"是段内转移,只修改IP的内容。指向程序的第一条指令保持CS不变,IP为0即可。所以要求data段第二三个字节为0即可。
补全程序,是Jmp指令执行后,CS:IP指向程序的第一条指令。
assume cs:code
data segment
dd 12345678H
data ends
code segment
start: mov ax, data
mov ds, ax
mov bx, 0
mov [bx], bx ;要填写的部分
mov [bx+2], cs ;要填写的部分
jmp dword ptr ds:[0]
code ends
end start
解析:需要注意这里是段间转移,注意高低地址的内容即可。"mov [bx] __ " 这里肯定是填0的,并且是一个字单元。由于前面没有使用"word ptr"指定,必须使用寄存器bx来送入数据。
2.4 转移地址在寄存器中的jmp指令
指令格式: jmp 16位reg
功能: (IP) = (16位 reg)
2.5 jcxz有条件转移指令
所有的有条件转移指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址,对IP的修改范围都为:-128~127。
指令格式: jcxz 标号
操作: 当(cx) = 0 时, (IP) = (IP) + 8位位移
问题:利用jcxz指令,实现在内存2000H段中查找第一个值为0的字节,找到后,将它的偏移地址存储在dx中
assume cs:code
code segment
start:mov ax,2000h
mov ds,ax
mov bx,0
s:mov cl,[bx] ;需要注意找的是字节,而cx寄存器是16位的
mov ch,0
jcxz ok
inc bx
jmp short s
ok:mov dx,bx
mov ax,4c00h
int 21h
code ends
end start
3. loop指令
所有循环指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址,对IP的修改范围都为:-128~127。
指令格式: loop 标号
操作: (cx) = (cx) - 1
如果(cx)!=0,(IP) = (IP) + 8位位移
问题:补全程序,利用loop指令,实现在内存2000H段中查找第一个值为0的字节,找到后,将它的偏移地址存储在dx中。
assume cs:code
code segment
start:
mov ax,2000h
mov ds,ax
mov bx,0
s:mov cl,[bx]
mov ch,0
________
inc bx
loop s
ok:dec bx
mov dx,bx
mov ax,4c00h
int 21h
code ends
答案是: inc cx,loop指令的操作是: cx--; if (cx == 0) jmp short 标号。所以先要加1,才能正常退出循环
实验八 分析一个奇怪的程序
考虑如下程序是否可以正确返回?
assume cs:code
code segment
mov ax, 4c00H
int 21H
start: mov ax, 0
s: nop
nop
mov di, offset s
mov si, offset s2
mov ax, cs:[si]
mov cs:[di], ax
s0: jmp short s
s1: mov ax, 0
int 21H
mov ax, 0
s2: jmp short s1
nop
code ends
end start
这个程序的关键就是要理解jmp short s1的机器指令中并没有包含转移的目的地址,而是偏移地址,且偏移-10个字节。当把这条指令复制到s处时,刚好可以定位到mov ax, 4c00H这条指令。所以程序也就可以正确返回。
实验9 根据材料编程
assume cs:code, ds:data, ss:stack
data segment
db 'Welcome to masm!'
db 2,36,113 ;字符属性代码(十进制)
data ends
stack segment
db 16 dup(0)
stack ends
code segment
start:
mov ax,data
mov ds,ax
mov bx,0
mov di,16
mov ax,stack
mov ss,ax
mov sp,0
mov ax,0b800h
mov es,ax
mov si,160*12+32*2;将字符输出到屏幕中央
;(显示器可以显示25行,每行80个字符,每个字符两个字节)
mov cx,3
s1:
mov dh,ds:[di] ;高位放属性
push cx
mov cx,16
s0:
mov dl,ds:[bx] ;低位放ASCⅡ码
mov es:[si],dx ;放入缓冲区
add si,2
inc bx ;下一个字符
loop s0
pop cx
inc di
add si,80h ;移动到下一行
sub bx,bx ;字符串从头开始
loop s1 ;循环3次,输出三行
mov ax,4c00h
int 21h
code ends
end start