8086汇编学习之转移指令的原理分析

(一)、8086汇编学习之基础知识、通用寄存器、CS/IP寄存器与Debug的使用
(二)、8086汇编学习之DS寄存器、SS/SP寄存器
(三)、8086汇编学习之[BX],CX寄存器与loop指令,ES寄存器等
(四)、8086汇编学习之代码段、数据段、栈段与段地址寄存器
(五)、8086汇编学习之寻址方式、数据类型以及几个数据操作指令

一、offset伪指令:

offset 标号:可以获取标号所在位置指令的偏移地址(即IP值)。
比如下面这段程序:

;将第一条指令复制到start_two的两个字节的位置
assume cs:codeseg

codeseg segment
    start:
    mov ax,bx
    mov si,offset start ;获取入口地址IP偏移值
    mov di,offset start_two ;获取目标地址IP偏移值
    mov dx,cs:[si] ;将第一条指令放到中间寄存器中
    mov cs:[di],dx ;从中间寄存器完成到目标地址的复制

    start_two:
    ;nop指令(No Operation)空操作,只占用一个字节不进行任何操作,并使IP加1。
    ;在Intelx86系列CPU中,其机器指令为0X90,所以可以看到起所在字节内存值均为90
    ;nop指令一般用来进行指令内存对齐的操作
    nop 
    nop

    mov ax,4C00H
    int 21H
codeseg ends
end start

测试结果:
这里写图片描述

二、jmp指令的几种格式:

1、描述:

jmp指令的本质是修改CS:IP,CS:IP修改以后自然会跳转到指定位置执行指令

;jmp的几种写法与作用:
jmp short 标号    ;转移到编号处执行指令(段内短转移),(IP)=(IP)+8位位移,该式表明新IP是旧IP的偏移,而与段地址无关        
jmp near 标号 ;转移到编号处执行指令(段内近转移),(IP)=(IP)+16位位移
jmp far 标号  ;转移到编号处执行指令(段间转移),(CS):(IP)=标号处的CS:IP
        ;(Debug中查看jmp对应机器指令的地址部分是完整的CS:IP地址,eg:EA0B01BD0B==>cs:IP=0BBD:010B)

jmp 16位reg      ;修改IP
jmp word ptr 内存单元地址 ;(段内转移)修改IP
jmp dword ptr 内存单元地址    ;(段间转移)修改CS:IP(高两位为CS,低两位为IP)

“jmp X 标号”看似是跳转,其本质还是修改CS:IP

jmp near 标号     ;修改IP的为标号标识地址的偏移部分
jmp far 标号      ;修改CS为标号标识地址的段地址部分,修改IP的为标号标识地址的偏移部分
jmp ax          ;修改IPax的值
jmp word ptr [0]    ;修改IPds:[0]为地址的一个字的内容
jmp dword ptr [6]   ;修改IPds:[6]为地址的一个字的内容,修改CSds:[8]为地址的一个字的内容

小结:所谓段间段内的区别在于:段间修改IP,而段内修改CS:IP

注意:
jmp nearjmp far可以在debug中运行,但是jmp word ptr [bx]与jmp dword ptr [bx]就不行。因为worddword还有ptr对于debug调试器来说不认识,(Debug只认识指令与立即数不认识伪指令)

我们说jmp的本质是修改CS:IP,但是修改IP的方式不一定是给出指令的地址,而是给出跳转地址相对于当前跳转指令的下一个指令首地址的偏移值。比如:

addr1   mov ax,1
addr2   jmp s
addr3   mov ax,2
addr4   s:
addr5   mov ax,3

机器指令并不是EB “addr5”,而是EB “addr5-addr3”,并且是以差值的补码形式存储。关于补码,可以参考:计算机的编码问题与数据上溢

2、简单应用:

如何使得程序执行jmp指令后,CS:IP重新指向第一条指令。如下:

;在数据段给定入合适的值,使得jmp指令执行后,CS:IP指向第一条指令
assume cs:codeseg

data segment
    db 16 dup (0)
data ends
codeseg segment 
    start:
    mov ax,data
    mov ds,ax
    mov bx,0
    jmp word ptr [bx+1] ;读取ds:[1]与ds:[2]放到IP中,那么IP就变为0,从第一条指令重新开始
codeseg ends

end start

另外一种方式:

;在空出的位置填入合适的内容,使得jmp指令执行以后CS:IP指向第一条指令:mov ax,data
assume cs:codeseg

data segment
    dd 12345678H
data ends
codeseg segment 
    start:
    mov ax,data
    mov ds,ax
    mov bx,0
    mov [bx],__ ;IP填0:mov [bx],bx
    mov [bx+2],__ ;CScs即可:mov [bx+2],cs

    mov [bx+2],cs
    jmp dword ptr ds:[0]
codeseg ends

end start

三、jcxz与loop的原理:

1、jcxz指令:

jcxz指令:为有条件转移指令,所有的有条件转移指令都是短转移,在对应的机器码中,包含转移的位置,而不是目的地址。对IP的修改范围为-128~127(用补码表示)。

格式:“jcxz 标号”
如果(cx)=0,就跳到标号处执行,否则继续向下执行。
操作:

①当(cx)=0时,(IP)=(IP)+8位位移
②8位位移=标号处地址-jcxz指令后第一个字节地址。
③编译器在编译时便算出来偏移地址大小,用补码表示。

举例:
寻找指定数据段开始的第一个内容为0的字节,将其偏移地址放到dx寄存器中。

;利用jcxz指令实现在内存2000H段中查找第一个值为0的字节找到后将他的偏移地址存放在dx中。
assume cs:codeseg

codeseg segment
    start:
    mov ax,2000H
    mov ds,ax
    mov bx,0
    ;寻找单字节值为0的地址
    again:
    mov cx,0
    mov cl,ds:[bx]
    jcxz ok ;当找到的值存到cx中,找到0,存入CX的也为0,那么会自动跳到ok处
    inc bx
    jmp short again

    ;找到后跳到该处将地址放到DX中
    ok:
    mov dx,bx

    mov ax,4C00H
    int 21H
codeseg ends
end start

测试结果:
这里写图片描述

2、loop指令:

loop则与jcxz是相反的道理,当cx不等于0时jmp执行,等于零时越过loop指令顺序执行。编译器在编译时,遇到转移位移越界的问题会报编译错误。
利用loop实现与上面程序相同的功能:

;利用loop指令实现在内存2000H段中查找第一个值为0的字节找到后将他的偏移地址存放在dx中。
assume cs:codeseg

codeseg segment
    start:
    mov ax,2000H
    mov ds,ax
    mov bx,0
    ;寻找单字节值为0的地址
    again:
    mov cx,0
    mov cl,ds:[bx]
    inc cx  ;当cx为0时,自加一次为1,在loop时自减一次为0就结束循环
    inc bx
    loop again

    ;找到后跳到该处将地址放到DX中
    ok:
    dec bx;与inc指令作用相反,自减:(bx)=(bx)-1
    mov dx,bx

    mov ax,4C00H
    int 21H
codeseg ends
end start

**再强调一遍:**jmp指令修改的是CS:IP寄存器,并且在段内转移时IP是相对值(相对jmp之后第一个字节的值),而不是相对CS:0的值。在段间转移时则是直接给定绝对地址CS:IP。jcxz与loop都属于段内转移(且都是段内短转移,修改范围为-128~127),所以其机器指令中的地址值,都是跳转目的地址相对jcxz/loop指令后的第一个字节的字节数。jcxz是CX不为0时进行jcxz的跳转,loop是CX为0时跳出循环。

四、一个“混乱”的程序与分析:

下面的程序能否正常结束?

assume cs:codeseg 

codeseg 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

codeseg ends
end start

按理来说,由于入口在start处,而

mov ax,4C00H
int 21H

在入口之前,并且从程序上看来,没有jmp指令直接跳转到第一条指令处,因为第一条指令处没有标号。但是我们知道除了使用jmp加标号的形式跳转,也可以采用jmp加非标号的形式跳转,至于没有标号就一定不能执行前两条指令是不成立的。我们仔细来分析:
我们程序在编译过程中产生一个“.lst”文件,我们就其部分内容进行分析,如下图:
这里写图片描述

将s2处的数据”EB F6”复制到了s处,而不是将s2处的“jmp short s1”复制到了s处,EBF6的作用是修改IP使得IP减8,所以复制到s处的数据被解析同样的效果,从s(0008)处减8即减到0000处,因而是可以正常执行退出指令的,程序可以正常退出。

我们也可以在Debug中调试查看,结果分析如下:
这里写图片描述

我们在可执行文件中查看二进制机器码,与编译完成后从cs:0地址开始的数据是相同的。
这里写图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
第1章 基础知识 1.1 机器语言 1.2 汇编语言的产生 1.3 汇编语言的组成 1.4 存储器 1.5 指令和数据 1.6 存储单元 1.7 CPU对存储器的读写 1.8 地址总线 1.9 数据总线 1.10 控制总线. 1.11 内存地址空间(概述) 1.12 主板 1.13 接口卡 1.14 各类存储器芯片 1.15 内存地址空间 第2章 寄存器 2.1 通用寄存器 2.2 字在寄存器中的存储 2.3 几条汇编指令 2.4 物理地址 2.5 16位结构的CPU 2.6 8086CPU给出物理地址的方法 2.7 “段地址x16+偏移地址=物理地址”的本质含义 2.8 段的概念 2.9 段寄存器 2.10 CS和IP 2.11 修改CS、IP的指令 2.12 代码段 实验1 查看CPU和内存,用机器指令汇编指令编程 第3章 寄存器(内存访问) 3.1 内存中字的存储 3.2 DS和[address] 3.3 字的传送 3.4 mov、add、sub指令 3.5 数据段 3.6 栈 3.7 CPU提供的栈机制. 3.8 栈顶超界的问题 3.9 push、pop指令 3.10 栈段 实验2 用机器指令汇编指令编程 第4章 第—个程序 4.1 —个源程序从写出到执行的过程 4.2 源程序 4.3 编辑源程序 4.4 编译 4.5 连接 4.6 以简化的方式进行编译和连接. 4.7 1.exe的执行 4.8 谁将可执行文件中的程序装载进入内存并使它运行? 4.9 程序执行过程的跟踪 实验3 编程、编译、连接、跟踪 第5章 [BX]和loop指令 5.1 [BX] 5.2 Loop指令 5.3 在Debug中跟踪用loop指令实现的循环程序 5.4 Debug和汇编编译器masm对指令的不同处理 5.5 loop和[bx]的联合应用 5.6 段前缀 5.7 —段安全的空间 5.8 段前缀的使用 实验4 [bx]和loop的使用 第6章 包含多个段的程序 6.1 在代码段中使用数据 6.2 在代码段中使用栈 6.3 将数据、代码、栈放入不同的段 实验5 编写、调试具有多个段的程月 第7章 更灵活的定位内存地址的方法 7.1 and和or指令. 7.2 关于ASCII码 7.3 以字符形式给出的数据 7.4 大小写转换的问题 7.5 [bx+idata]. 7.6 用[bx+idata]的方式进行数组的处理 7.7 SI和DI. 7.8 Lbx+si]和[bx+di] 7.9 [bx+si+idata]和[bx+di+idata] 7.10 不同的寻址方式的灵活应用 实验6 实践课程中的程序 第8章 数据处理的两个基本问题 8.1 bx、Sl、di币口bp 8.2 机器指令处理的数据在什么地方 8.3 汇编语言中数据位置的表达 8.4 寻址方式 8.5 指令要处理的数据有多长 8.6 寻址方式的综合应用 8.7 div指令 8.8 伪指令dd. 8.9 dUp 实验7 寻址方式在结构化数据访问中的应用 第9章 转移指令原理 9.1 操作符offset 9.2 jmp指令 9.3 依据位移进行转移的jmp指令. 9.4 转移的目的地址在指令中的jmp指令 9.5 转移地址在寄存器中的jmp指令. 9.6 转移地址在内存中的jmp指令. 9.7 jcxz指令 9.8 loop指令 9.9 根据位移进行转移的意义 9.10 编译器对转移位移超界的检测 实验8 分析—个奇怪的程序 实验9 根据材料编程 第10章 CALL和RET指令 10.1 ret和retf. 10.2 call指令 10.3 依据位移进行转移的call指令 10.4 转移的目的地址在指令中的call指令 10.5 转移地址在寄存器中的call指令 10.6 转移地址在内存中的call指令. 10.7 call和ret的配合使用 10.8 mul指令 10.9 模块化程序设计 10.10 参数和结果传递的问题 10.11 批量数据的传递 10.12 寄存器冲突的问题 实验10 编写子程序 课程设计1 第11章 标志寄存器 11.1 ZF标志 11.2 PF标志 11.3 SF标志 11.4 CF标志 11.5 0F标志 …… 第12章 内中断 第13章 int指令 第14章 端口 第15章 外中断 第16章 直接定址表 第17章 使用BIOS进行键盘输入和磁盘读写

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值