《汇编语言》第9章 -转移指令的原理 实验8与实验9

本文深入探讨8086处理器的转移指令原理,包括段内与段间转移,短转移与近转移的区别,以及如何通过jmp、jcxz、loop等指令控制程序流程。详细解释了补码表示法在位移计算中的应用,以及masm编译器对jmp指令的处理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第9章 转移指令的原理

可以修改ip,或同时修改cs和ip的指令统称为转移指令。(就是可以控制CPU执行内存中某处代码的指令)
段内转移:只修改ip(如:jmp ax)
段间转移:同时修改cs和ip(如jmp 1000:0)

其中,段内转移又分为:
短转移:ip的修改范围是-128-127
近转移:ip的修改范围是-32768-32767

8086的转移指令分类
1.无条件转移指令(如:jmp)
2.条件转移指令
3.循环指令(如:loop)
4.过程
5.中断

1.操作符offset
“取得标号的偏移地址”

2.jmp指令
jmp指令要给出两种信息:
1)转移的目的地址
2)转移的距离(段间转移、段内短转移、段内近转移)

3.依据位移进行转移的jmp指令
CPU在执行jmp指令的时候并不需要转移的目的地址
cpu执行指令的过程简单回忆:
(1)cpu从cs:ip处读取指令,指令进入指令缓冲器;
(2)ip加1,从而指向下一条指令;
(3)cpu执行指令;
重复这个过程;
关于补码:
总结来说就是,补码是用来表示有符号的数的,可以区分正负号。8位补码范围是-128-127。
正数的补码等于其原码;
负数的补码等于原码按位取反再加1;
两个补码运算后的结果依然满足补码规则。
jmp short 标号的功能:
(ip)=(ip)+ 8位位移;
其中:
1)8位位移=标号处的地址-jmp指令后的第一个字节的地址;
2)short指明此处的位移为8位位移;
3)8位位移的范围是-128~127,用补码表示;
4)8位位移由编译器算出;
’jmp near ptr 标号‘实现的是段内近转移;(IP)=(IP)+ 16位位移;

4.转移的目的地址在指令中的jmp指令
jmp far ptr 标号” 实现的是段间转移,又称为远转移。
:(cs)=标号所在段的段地址;
:(IP)=标号所在段的偏移地址;
masm.exe对jmp的相关处理
1)向前转移
编译器中有一个地址计数器(AC),在翻译程序的时候,每读到一个字节AC就加1。在向前转移时,编译器读到标号s时,记下AC的值as,读到jmp … s时记下AC的值aj。这样编译器可以用as-aj算出位移量disp。
1.若disp属于[-128,127],则不管是jmp指令的哪一种,都将它转变为jmp short s所对应的机器码;EB disp(反码表示,占两个字节)
2.若disp属于[-32768,32767],则对于short s将编译出错;jmp s,jmp near ptr s所对应的机器码为:E9 disp(占3个字节);jmp far near ptr s为:EA 偏移,段地址(占5个字节)
2)向后转移
此种情况下,编译器先读到jmp …s,不知道s处的AC值,故都将jmp …s当作jmp short s来读取,记下jmp…s指令的位置,和AC的值aj。
当读的是jmp short s时,生成EB和1个nop(预留1个字节的位置,存放8位disp);
当读的是jmp s和jmp near ptr s时,生成EB和两个nop(预留2个字节位置,存放16位disp);
当读的是jmp far ptr s时,生成EB和4个nop(存放段地址和偏移地址)
然后读到标号s时,记下AC的值as,并作disp=as-aj.
1.若disp属于[-128,127],则均写作EB disp,注意此时若是jmp s和jmp near ptr s的话,后面还有一个nop指令;far s后面还有3个nop指令;
2.若disp属于[-32768,32767],则short s会出错;

5.转移地址在寄存器中的jmp指令
格式:jmp 16位reg
(IP)=(16位reg)
注:(仅修改ip的值)

6.转移地址在内存中的jmp指令
1)jmp word ptr 内存单元地址(段内转移)
:转移目的偏移地址到IP,可用任一格式的内存寻址方式;
2)jmp dword ptr 内存单元地址(段间转移)
:高地址处是目的段地址,低地址处是目的偏移地址。同样可用任一寻址方式。

7.jcxz指令
jcxz指令为有条件转移指令,所有的条件转移指令都是短转移,机器码中包含位移。
指令格式:jcxz 标号 (若(cx)=0,转移到标号处执行)
操作:当(cx)=0时,(ip)=(ip)+8位位移;
8位位移=标号处的地址-jcxz指令后的第一个字节的地址;用补码表示;
当(cx)≠0时,什么也不做(向下执行)
相当于if((cx)==0) {jmp short 标号;}

8.loop指令
loop指令为循环指令,同样为短转移。
指令格式:loop 标号
操作:(cx)=(cx)-1
若cx≠0,(IP)=(IP)+8位位移;
同样的,8位位移=标号处的地址(as)-loop指令后的第一个字节的地址(aj);
用反码表示,若cx=0,什么也不做,向下执行。
相当于:
cx- -;
if(cx≠0){jmp short s;}

9.根据位移进行转移的意义及超界检测
1)方便了程序在内存中的浮动装配;即在内存的任意位置都可正确执行。
2)例如jmp short s转移位移的范围是:-128-127,当发生超界时,编译器会报错。
3)jmp 2000:1000是在debug中使用的汇编指令,在源程序中使用,会报错。

检测点9.1
(1)使程序中的jmp指令执行后,cs:ip指向程序的第一条指令。

应在data段定义dw 0,0
因为程序的第一条指令的偏移地址是0,故只需将ds:[bx+1]处的字单元定义为0即可;
(2)补全程序,使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

:程序的jmp指令进行的是段间转移,ds:0双字单元的高2字节存放段地址,低2字节存放偏移地址。因为第一条指令的地址是:cs:0,故,可传送cs到[bx],传送0到[bx+2](此处不能直接传0进[bx],因为未指明传送的大小,编译会出错);
(3)CPU执行指令后:
(cs)=0006h,(IP)=00BEh
:高字位放段地址,低字位放偏移地址
检测点9.2

要补全的指令为:
s:
mov ch,0
mov cl,ds:[bx]
jcxz,ok
inc bx
jmp short s

解析:首先我们分析遍历2000段是按字节遍历;然后我们看要找的值是“0”,可以把遍历的值放入cx中,用jcxz来判断其是否为0(为0就直接跳转到ok标号处;不为0就往下走),最后做bx递增来进行字节遍历;以上为我们所补充内容。

检测点9.3

要补全的程序为:
loop ok

分析:我们遍历2000段,把每一个字节单元传入cl中,当检测到cx等于0时,loop ok不起作用,程序向下执行,使bx+1,而loop s同样不起作用,继续向下,把bx减一后就得到了其偏移地址。故,此处应补充loop ok;

实验8 分析一个奇怪的程序

首先我觉得程序不能正确返回,因为它的返回程序的开始处是在start标号处,而返回程序在标号之前,故不能执行,不能正确返回。
在这里插入图片描述在程序执行后,发现程序能够返回。
在这里插入图片描述
这是为什么呢???
我们利用debug重新深入分析一下代码:

在这里插入图片描述

assume cs:code
code segment
mov ax,4c00h
int 21h
start:mov ax,0	;;程序从此处开始执行;传送0进ax;(ip)=5;

s:nop			;预留一个字节的空间;(ip)=8;(as)=8;	
nop				;(ip)=9;
mov di,offset s
				;取s处的偏移地址(08)传入di;(ip)=0ah;
mov si,offset s2
				;取s2处的偏移地址(20)传入si;(ip)=0dh;
mov ax,cs:[si]
				;将cs:20h处的指令(EBF6)传入ax;(ip)=10h;
mov cs:[di],ax	
				;将ax中的数据(EBF6)传入cs:08处;(ip)=13h;
s0:jmp short s
				;先让ip指向下一条指令cs:18h处,再跳转到s标号处。;(aJ)=18h;
				此时,我们计算disp=as-aJ=8-18h=F0h(补码);所以机器码为EBF0;
				跳转到s处之后,s处的机器码指令是:EBF6,位移量就是10个字节。
				又读取s处得指令后,(IP)=(IP)+2=0ah,所以,执行s处得指令后,
				(IP)=0ah-0ah=0;于是,cs:ip就指向了偏移量为0的“mov ax,4c00h
				int 21h”使得程序能够正确返回。

s1:mov ax,0		
int 21h
mov ax,0

s2:jmp short s1
nop

code ends
end start

感悟: 通过本实验,我们应该能够很清楚的把握jmp跳转指令的原理了。
对jmp short s执行过程的说明:
(1)无论是向前转移还是向后转移,都是用标号s处的偏移量减去jmp指令处的下一条指令的偏移量aj得到位移量disp(补码表示在机器码中)即disp=as-aj;此过程由编译器得到;
(2)在cpu执行jmp指令时,先把jmp指令的机器码放入缓冲器,使IP指向下一条指令;然后再根据jmp机器码中的位移量对ip进行修改,从而使得cs:ip指向标号处。

实验9 根据材料编程

本书编者的话:此任务需在进行下面课程之前独立完成,因为后面的课程中,需要通过这个实验获得的编程经验。

编程:在屏幕中间分别显示绿色,绿底红色,白底蓝色的字符串‘welcome to masm!’
阅读材料。。。。。。。
以下为我的代码;

assume cs:code
data segment
db 'welcome to LOL!!'
db 02h,24h,71h
data ends
stack segment
dw 8 dup (0)
stack ends
code segment

start:
#使各寄存器指向内存各段;
mov ax,data
mov ds,ax		;将ds指向data段
mov ax,stack
mov ss,ax
mov sp,10h		;将ss指向stack段,ss:sp指向栈顶(此时栈空)
mov ax,0b800h
mov es,ax		;使es指向显示缓冲区。

#设置显示区域及初始化各控制变量
mov bp,6e0h		;用bx控制es段的行,从显示区的第12行开始(显示缓冲区的中心位置)
mov bx,10h		;用bx来保存颜色属性的偏移量
mov si,0		;用此si控制颜色属性的列
mov cx,3		;设置外层循环次数。


#外层循环
sw:
mov ah,ds:[bx].[si]	;颜色属性随行数改变,故放入外层中
push cx			;使外层cx进栈保护
push si			;使颜色属性列的选择入栈保护(此为问题关键所在,见下注)
mov cx,10h		;设置内层循环次数
mov di,40h		;用di控制es段的列(双字节列),从显示区的32列开始
mov si,0		;再用此si来控制字符的列
sn:
mov al,ds:[si]
mov es:[bp].[di],al	
				;data段的字符传入显示区段的奇数列(即双字节的高位)
mov es:[bp].1[di],ah
				;将颜色属性传入显示区段的偶数列
add di,2		;使列数(显示区的双字节列)递增
inc si			;使列数递增(data段的单字节列)	
loop sn

pop si			;使颜色属性控制变量出栈			
pop cx			;使外层循环次数出栈
add bp,0a0h		;显示区一行有80列,共160个字节,故控制行加1,应加0a0h
inc si			;使颜色属性更换为下一个。
loop sw

mov ax,4c00h
int 21h
code ends
end start

在这里插入图片描述
**吐槽:**此实验我做了3个多小时,有点累啊,在debug中整来整去,头脑发胀,最后参考了网上答案才找到了问题都出在了哪里?。



狗?北这厮最近有点飘,跟我说他学完了,还要给我在线教学?。让他来教,他又不来。不得不让我怀疑,不过又不必深究,给他留几分薄面。哈哈哈?
. !!!!,搞English!!!!难难难!!!!
北老师今天在线教学楼,可惜了,一世英名毁于ss:bp,哈哈哈,笑死我了,这不现在恼羞成怒骂我呢😁😀。----11.13更新。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值