第九章 转移指令的原理
第九章 转移指令的原理9.0 绪论
1.可以修改IP。或者同时修改CS和IP的指令统称为转移指令
2.转移的分类
·只修改IP时,叫做段内转移
·同时修改CS和IP,叫做段间转移
2. 段内转移分为:
·短转移:IP的修改范围为-128~127
·近转移:IP的修改范围为-32768~32767
3.8086转移指令的分类
·无条件转移指令(如:jmp)
·条件转移指令
·循环指令(如loop)
·过程
·中断
9.1操作符offset
1. 功能:由编译器处理,取得标号的偏移地址
2. 用法:offset 标号
问题9.1
有如下的程序段,添加两条指令,使得运行中该程序将s处的一条指令复制到s0处
Program 9-Problem-1
assume cs:coding
codesg segment
s:mov ax,bx
mov si,offset s
mov di,offset s0
______________
______________
s0:nop
nop
codesg ends
end
注:nop的机器码占一个字节,此处为占位作用。答案:
mov cx,cs;[si]
mov cs:[di],cx
9.2~9.6 jmp指令
1.jmp为无条件转移指令,可以只修改IP,也可以同时修改CS和IP
2.jmp指令要给出的两条信息:
(1)转移的目的地址
(2)转移的距离(段间转移,段内短转移,段内近转移)
3.依据位移进行转移的jmp指令
·jmp short 标号(转到标号处执行指令)
该格式的指令进行段内短转移,对IP的修改范围为-128~127,即向前转移最多越过128个字节,向后转移最多可以越过127个字节
在debug下显示为jmp xxxx,但机器码中不包含转移的目的地址,因为CPU在执行jmp指令时不需要目的地址,而需要位移。
功能:(IP)=(IP)+8位位移
(1)8位位移=标号处的地址-jmp指令后的第一个字节的地址
(2) short指名为8位位移
(3) 位移用补码表示
(4) 位移在编译时算出
·jmp near ptr 标号
该指令实现段内近转移,与jmp short 标号 相似,但为16位位移,范围为-32768~32767
4.转移的目的地址在指令中的jmp指令
·jmp far ptr 标号
实现段间转移(远转移),功能:(CS)=标号所在的段地址 (IP)=标号所在段的偏移地址
标号指明了段地址和偏移地址
在debug中显示为jmp xxxx:xxxx
5. 转移指令在寄存器中
·jmp 16位reg
功能 (IP)=(16位reg)
6. 转移地址在内存中的jmp指令
·jmp word ptr 内存单元地址(段内转移)
功能:从内存单元地址处取出一个字,作为转移的目的地址
·jmp dword ptr 内存单元地址(段间转移)
功能:从内存单元中取出两个字,高地址处的字是转移的目的段地址,低地址处为转移的目的偏移地址。即:
(CS)=(内存单元地址+2)
(IP)=(内存单元地址)
检测点9.1
(1)程序如下。
assume cs:code
data segment
dw 2 dup (0)
data ends
code segment
start: mov ax,dtat
mov ds,ax
mov bx,0
jmp word ptr [bx+1]
code ends
end start
若要使jmp指令执行后,CS:IP指向程序的第一条指令,在data段中应该定义哪些数据?
答案①db 3 dup (0)
答案②dw 2 dup (0)
答案③dd 0
jmp word ptr [bx+1]为段内转移,要CS:IP指向程序的第一条指令,应设置ds:[bx+1]的字单元(2个字节)存放数据应为0,则(ip)=ds:[bx+1]=0
简单来说就是,只要ds:[bx+1]起始地址的两个字节为0就可以了
(2)程序如下。
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], word ptr 0 ;或mov [bx], offset start
mov [bx+2], cs ;或mov [bx+2], cs ;或mov [bx+2], seg code
jmp dword ptr ds:[0]
code ends
end start
补全程序,使用jmp指令执行后,CS:IP指向程序的第一条指令。
第一格可填①mov [bx],bx②mov [bx],word ptr 0③mov [bx],offset start等。
第二格可填①mov [bx+2],cs②mov [bx+2],cs③mov [bx+2],seg code等。
解析:
jmp dword ptr ds:[0]为段间转移,(cs)=(内存单元地址+2),(ip)=(内存单元地址),要CS:IP指向程序的第一条指令,第一条程序地址cs:0,应设置CS:IP指向cs:0
程序中的mov [bx],bx这条指令,是将ip设置为0
mov [bx+2],cs,将cs这个段地址放入内存单元
执行后,cs应该不变,只调整ip为0,(ip)=ds:[0]=0
(3)用Debug查看内存,结果如下:
2000:1000 BE 00 06 00 00 00 ......
则此时,CPU执行指令:
mov ax,2000h
mov es,ax
jmp dword ptr es:[1000h]
后,(cs)= 0006H ,(ip)= 00BEH
解析:
jmp dword ptr为段间转移,高位存放段地址,低位存放偏移地址
(cs)=(内存单元地址+2),(ip)=(内存单元地址)
根据书P16,对于寄存器AX,AH为高位(前1字节为高位),AL为低位(后1字节为低位)
推算出(内存单元地址)=00beh,(内存单元地址+2)=0006h
根据书P182,高位存放段地址(后2个字节为高位),低位存放偏移地址(前2个字节为低位)
(cs)=(内存单元地址+2),(ip)=(内存单元地址)
推算出(cs)=0006h,(ip)=00beh
9.7 jcxz指令
1.jcxz指令为有条件转移指令,所有的有条件转移指令都是短转移。在对应的机器码中包含转移的位移
2.格式:jcxz 标号(如果(cx)=0转移到标号处执行。)
3操作:当(cx)=0时,(IP)=(IP)+8位位移
当(cx)!=0时,程序向下执行,jcxz什么都不做
相当于:if((cx)==0)jmp short 标号;
检测点9.2
补全编程,利用jcxz指令,实现在内存2000H段中查找第一个值为0的字节,找到后,将它的偏移地址存储在dx中。
assume cs:code
code segment
start: mov ax,2000h
mov ds,ax
mov bx,0
s: mov ch,0
mov cl,[bx]
jcxz ok ;当cx=0时,CS:IP指向OK
inc bx
jmp short s
ok: mov dx,bx
mov ax ,4c00h
int 21h
code ends
end start
9.8 loop指令
1.loop为循环指令,所有的循环指令都是短转移,在对应的机器码中包含的是位移
2.loop标号 相当于
(cx)–;
if((cx)!=0)jmp short 标号;
检测点9.3
补全编程,利用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 cx
inc bx
loop s
ok:dec bx
mov dx,bx
mov ax,4c00h
int 21h
code ends
end start
执行loop s时,首先要将(cx)减1。
“loop 标号”相当于
dec cx
if((cx)≠0) jmp short 标号
9.9 根据位移进行转移的意义:方便程序段在内存中的浮动装配
9.10编译器对转移位移超界的检测:如果转移范围超界,编译器报错。形如jmp xxxx:xxxx的指令在debug下可用,但不能在源程序中写出。
实验8 分析一个奇怪的程序
该程序是否可以正常返回?
Program ex-8-1
assume cs:codesg
codesg segment
mov ax,4c00h
int 21h
start: mov ax,0 ;ax=0
s: nop ;占一字节,机器码90
nop ;占一字节,机器码90
mov di,offset s ;(di)=s偏移地址
mov si,offset s2 ;(si)=s2偏移地址
mov ax,cs:[si] ;(ax)=jmp short s1指令对应的机器码EBF6
mov cs:[di],ax ;jmp short s1覆盖s处指令2条nop指令
s0: jmp short s ;执行s???? 未执行到这里,直接跳回mov ax,4c00h了
s1: mov ax,0
int 21h
mov ax,0
s2: jmp short s1
nop
codesg ends
end start
<-debug下的内容
注意到所谓的jmp short s实际上是EBF6这样的指令,F6为位移。根据每条指令的长度计算出被覆盖的nop指令移动F6个字节后指向那一条指令。
关于jmp指令的位移内容
当指令执行到s0:jmp short s时,该指令得到执行,编译器算出的ip位移量为8-18h=-16(补码F0),(ip)=(ip)+位移量=18h+(-16)=8,cs:8指向s;
当指令执行到s标段jmp命令时,第1个字节中的机器码为EBF6,给出的ip位移量为-10(补码F6),(ip)=(ip)+位移量=ah+(-10)=0,cs:0指向第一条指令。
实验9 显示三行welcome to masm(根据材料编程)
编程:在屏幕中间分别显示绿色,绿底红色,白色蓝底的字符串’welcome to masm!’
材料:80x25彩色字符模式显示缓冲区的介绍
1.缓冲区所在的内存地址:B8000H~BFFFFH(共32KB)
2.向该地址空间写入数据,写入的内容立即出现在显示器上。
3.显示器可以显示25行,每行80个字符,每个字符可以有256中属性
4.一个字符在显示缓冲区中占两个字节,分别存放字符的ASCII码和属性,低位字节存放字符ASCII码,高位字节存放属性
5.80x25模式下,一屏的内容在显示缓冲区占4000个字节。
6.显示缓冲区共8页,每页4KB
7.显示器可以显示任意一页的内容,一般显示第0页(B8000H~B8F9FH)的内容
8.在一页显示缓冲区中:
偏移 | 对应显示器的行数 |
000~09F | 1 |
0A0~13F | 2 |
F00~F9F | 25 |
单元 | 对应显示器的列数 |
00 01 | 第1列 |
02 03 | 第2列 |
9E 9F | 第80列 |
9.在显示缓冲区中,偶地址存放字符,奇地址存放字符属性
10.属性字节的格式
R 红色
G 绿色
B 蓝色
常用:
红底绿字0100 0010B
红底闪烁绿字1100 0010B
红底高亮绿字0100 1010B
黑底白字0000 0111B
白底蓝字0111 0001B
Program ex-9-1 自主解法
assume cs:code,ds:data,ss:stack
data segment
db 'welcome to masm!'
db 00000010B ;绿色(16)
db 00100100B ;绿底红字(17)
db 01110001B ;白底蓝字(18)
data ends
stack segment
db 16 dup(0)
stack ends
code segment
start:
mov ax,0b800h
mov es,ax ;es=显示缓冲区
mov di,1660 ;第10行第40列
mov ax,data
mov ds,ax ;ds=data
mov ax,stack
mov ss,ax ;ss=stack
mov sp,16
mov si,0
mov cx,3
mov bx,16
s0: push cx
mov cx,16
s: mov ax,0
mov al,[si]
mov es:[di],ax
mov dl,[bx]
mov es:[di+1],dl
add di,2
inc si
loop s
sub di,32
add di,160
mov si,0
inc bx
pop cx
loop s0
mov ax,4c00h
int 21h
code ends
end start
Program ex-9-2 参考
assume cs:code,ds:data
data segment
db 'welcome to masm!'
data ends
code segment
start:mov ax,data
mov ds,ax
mov bx,0;ds:bx指向data字符串
mov ax,0b800h
mov es,ax
mov si,0;es:si指向显存
mov cx,16
s:mov al,[bx];字符赋值al
mov ah,02h;绿色
mov es:[si].720h,ax;写入第12行64列
mov ah,14h;绿底红色
mov es:[si].7c0h,ax;写入第13行64列
mov ah,71h;白底蓝色
mov es:[si].860h,ax;写入第14行64列
inc bx;指向下一字符
add si,2;指向下一显存单元
loop s
mov ax,4c00h
int 21h
code ends
end start
