汇编 第九章 转移指令的原理

第九章 转移指令的原理

8086CPU的转移指令分为以下几类:
1.无条件跳转指令(如:jmp)
2.条件跳转指令
3.循环指令(如:loop)
4.过程,就像C语言中的函数
5.中断

9.1 操作符offset

​ 操作符offset(伪指令)在汇编语言中由编译器处理,它的功能是取标号的偏移地址。

assume cs :codesg
codesg segment
start : mov ax,offset start;相当于mov ax,0
s : 	mov ax,offset s;相当于mov ax,3
codesg ends
end start

在上面的程序中,offset操作符取得了标号start和s的偏移地址0和3,所以指令:mov ax,offset start相当于指令mov ax,0,因为start是代码段中的标号,它所标记的指令是代码段中的第一条指令,偏移地址为0;
mov ax,offset s相当于指令mov ax,3,因为s是代码段中的标号,它所标记的指令是代码段中的第二条指令,第一条指令长度为3个字节,则s的偏移地址为3。

使该程序在运行中将s处的一条指令复制到s0处。
分析:
(1) s 和s0 处的指令所在的内存单元的地址是多少? cs:offset s和 cs:offset s0。

(2)将s 处的指令复制到s0 处,就是将cs:offset s 处的数据复制到cs:offset s0处。

(3)段地址已知在cs 中,偏移地址 offset s和 offset s0已经送入si和di中。

(4)要复制的数据有多长? mov ax,bx指令的长度为两个字节,即1个字。

assume cs : codesg
codesg segment
s : mov ax,bx
	mov ax,bx的机器码占两个字节
	mov si, offset s
	mov di, offset s0
	mov ax,cs: [si]
	mov cs: [di] ,ax
s0 :nop ; nop的机器码占一个字节
	nop
codesg ends
end s
9.2 jmp指令

​ 1.无条件转移,可以只修改ip,也可以同时修改cs和ip
​ 1.【jmp 段地址:偏移地址】 可以用来同时修改CS和IP
​ 指令中的段地址修改CS
​ 偏移地址修改IP
​ 这种用法编译器不认识,只能做在debug中使用
​ 2.【jmp 某一合法的寄存器】 仅修改IP的内容
​ 比如:jmp ax 或者 jmp bx(类似于mov IP ax)
​ 2.jmp指令要给出两种信息:
​ 1.转移的目的地址
​ 2.转移的距离(段间转移、段内短转移、段内近转移)

9.3 依据位移进行转移的jmp指令

​ 1.jmp short 标号【转到标号处执行指令,段内短转移】
​ 此格式实现的是:段内短转移,它对ip的修改范围为-128~127
​ 2.也就是说,它向前转移时可以最多越过128个字节,负数使用补码表示
​ 向后转移可以最多越过127个字节

assume cs : codesg ;程序9.1
codesg segment
start : mov ax,0
		jmp short s
		add ax,1
s: 		inc ax
codesg ends
end start

上面的程序执行后,ax中的值为1,因为执行 jmp short s后,越过了add ax,1,IP指向了标号s处的inc ax。也就是说,程序只进行了一次ax加1操作。

汇编指令jmp short s对应的机器指令应该是什么样的呢?我们先看一下别的汇编指令和其相对应的机器指令。

在这里插入图片描述

assume cs : codesg ;程序9.2
codesg segment
start : mov ax ,0
        mov bx, 0
        jmp short s
        add ax,1
s : 	inc ax
codesg ends
end start

在这里插入图片描述

(1) (CS)-0BBDH,(IP)-0006H,CS:IP指向EB 03(jmp short s 的机器码);(2)读取指令码EB 03进入指令缓冲器;
(3) (IP)=(IP)+所读取指令的长度=(IP)+2-0008H,CS:IP指向add ax,1;(4)CPU执行指令缓冲器中的指令EB 03;
(5)指令EB03执行后,(IP)=000BH,CS:IP指向inc ax。
从上面的过程中我们看到,CPU 将指令EB 03读入后,IP指向了下一条指令,即CS:0008处的add ax,1,接着执行EB 03。如果EB03没有对P进行修改的话,那么,接下来CPU将执行add ax,1,可是,CPU执行的EB 03却是一条修改IP的转移指令,执行后(IP)=000BH,CS:IP指向inc ax,CS:0008处的add ax,1没有被执行。

​ 3.CPU不需要目的地址就可以实现对ip的修改
​ jmp指令的机器码中不包含目的地址,但是可以实现跳转
​ 实现的方式,是在原地址的基础上进行一个偏移量,即位移

在这里插入图片描述

f7 = 11110111 减一11110110 再取反 00001001 这里是9(-9的补码是f7),所以jmp s本来下一条指令是0009指向的nop,但是收到了修改ip的指令,EB F7,0009-9 =0 跳到了s处地址的位置。

​ 4.还有一种和指令“jmp short 标号”功能类似的指令格式:
​ jmp near ptr 标号,它实现的是段内近转移
​ 功能为:(ip)=(ip)+16位位移
​ jmp short 标号是8位的位移(范围为-128~127),而jmp near ptr 标号是16位位移(范围为-32768~32767)

9.4 转移的目的地址在指令中的jmp指令

​ 前面讲的jmp指令,其对应的机器码中并没有转移的目的地址,而是相对于当前ip的转移位移
​ 1.指令“jmp far ptr 标号”
​ 实现的是段间转移,又称为远转移,这时机器码中应该明确给出【段地址】
​ 2.指令“jmp far ptr 标号”功能如下:
​ (CS)=标号所在段的段地址
​ (IP)=标号所在段中的偏移地址
​ far ptr 指明了指令用标号的段地址和偏移地址修改cs和ip

assume cs: codesg
codesg segment
start : mov ax, 0
		mov bx, 0
		jmp far ptr s
		db 256 dup (0)
s:		add ax,1
		inc ax
codesg ends
end start

在这里插入图片描述

如图中所示,源程序中的db 256 dup (0),被 Debug解释为相应的若干条汇编指令。这不是关键,关键是,我们要注意一下jmp far ptr s所对应的机器码:EA 0B 01 6A 07,其中包含转移的目的地址。“0B 01 6A 07 ”是目的地址在指令中的存储顺序,高地址的“ 6A 07”是转移的段地址: 07 6A H,低地址的“0B01”是偏移地址:010BH。

9.5 转移地址在寄存器中的jmp指令

​ 指令格式:jmp 16位寄存器
​ 功能:修改ip寄存器中的值,把16位寄存器中的值送入到ip寄存器中

9.6 转移地址在内存中的jmp指令

​ 转移地址在内存中的jmp指令有两种格式:
​ 1.jmp word ptr 内存单元地址(段内转移)
​ 功能:将内存中的那个字视为一个偏移地址,然后跳转到那个偏移地址
​ 与【jmp 寄存器】功能相似
​ 内存单元地址可用寻址方式的任意格式给出

mov ax,0123H
mov ds: [ 0] , ax
jmp word ptr ds : [ 0]
;执行后,(IP)=0123H.又比如,下面的指令:
mov ax,0123H
mov [bx ] ,ax
jmp word ptr [bx];执行后,(IP)=0123H

​ 2.jmp dword ptr 内存单元地址(段间转移)
​ (ip)=(内存单元地址) ;双字中的低位字是给ip的
​ (cs)=(内存单元地址+2) ;双字中的高位字是给cs的
​ 跟【jmp 段地址:偏移地址】功能类似
​ 内存单元地址可用寻址方式的任意格式给出

mov ax,0123H
mov ds: [0] ,ax
mov word ptr ds: [2],0
jmp dword ptr ds: [ 0]
;执行后,(CS)=0,(IP)=0123H,CS:IP指向0000:0123又比如,下面的指令:
mov ax,0123H
mov [bx ] ,ax
mov word ptr [bx+2],0
jmp dword ptr [bx]
;执行后,(CS)=0,(IP)=0123H,CS:IP指向0000:0123

补充:不能直接向内存单元中加入立即数要通过寄存器,把立即数加进去

9.7 jcxz指令

​ 1.有条件跳转指令,所有的有条件跳转指令都是短转移
​ 对应的机器码中包含转移的位移,而不是目的地址。对ip的修改范围都为:-128~127(用补码表示)
另一个有条件跳转指令【loop指令】
​ 2.指令格式:jcxz 标号
如果(cx)=0,则跳转到标号处执行
​ 3.jcxz 标号 指令的操作:
​ 1.当(cx)=0时,(ip)=(ip)+8位位移
​ 2.当(cx)!=0时,什么也不做(程序继续向下执行)

​ if ( (cx) ==0 jmp short标号;

;补全编程,利用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.循环指令,所有的循环指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址。对ip的修改范围都为:-128~127(用补码表示)
​ 2.指令格式:loop 标号
​ 3.指令的内部操作
​ 1.cx=cx-1
​ 2.如果cx!=0,(ip)=(ip)+8位位移,跳转
​ 3.(cx)=0,什么也不做,程序向下执行(和jcxz相反)
​ cx用来控制循环的次数

;利用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 ;要+1避免减之前就是0了。
        inc bx
        loop s
ok: 	dec bx;dec指令的功能和inc相反,dec bx进行的操作为:(bx)=(bx)-1
		mov dx , bx
		mov ax,4c00h
		int 21h
code ends
end start
9.9 根据位移进行转移的意义

​ 1.根据位移进行转移,这样设计,方便了程序段在内存中的浮动装配
​ 可以实现代码的复用
​ 2.如果在机器码中直接给出【段地址:偏移地址】,
​ 这段程序在内存中换一个位置,则会运行不正确
​ 3.段内近转移、段内短转移都是根据位移进行转移,一共有四种方式
​ 1.jmp short ptr 标号
​ 2.jmp near ptr 标号
​ 3.jcxz 标号
​ 4.loop 标号

这段程序装在内存中的不同位置都可正确执行,因为loop s在执行时只涉及s的位移(-4,前移4个字节,补码表示为FCH),而不是s的地址。如果 loop s的机器码中包含的是s 的地址,则就对程序段在内存中的偏移地址有了严格的限制,因为机器码中包含的是s的地址,如果s 处的指令不在目的地址处,程序的执行就会出错。而loop s的机器码中包含的是转移的位移,就不存在这个问题了,因为,无论s 处的指令的实际地址是多少,loop指令的转移位移是不变的。

9.10 编译器对转移位移超界的检测

​ 注意,根据位移进行转移的指令,他们的转移范围会受到限制
​ 如果在源程序中出现了转移范围超界的问题,在编译的时候,编译器将报错

比如,下面的程序将引起编译错误:

assume cs:code
code segment
start: 	jmp short s
		db 128 dup (0)
s : 	mov ax,0ffffh
code ends
end start

jmp short s的转移范围是-128~127,IP最多向后移动127个字节。

jmp short s的转移范围是-128~127,IP最多向后移动127个字节。

【实验八、九】

;实验8 分析下面的程序,在运行前思考:这个程序可以正确返回吗? 答案:可以。
assume cs:codesg
codesg segment
		mov ax,4c00h  ;最终跳到了这里
		int 21h
start:	mov ax,0
s:		nop  ;jmp short s1 F6EB H 也就是eb f6 -10的补码
        nop
        mov di, offset s
        mov si,offset s2
        mov ax,cs : [si] ; F6EB H
        mov cs: [di] ,ax ;jmp short s1 机器码放到了上面
s0: 	jmp short s
s1:		mov ax,0  ;jmp short s1本以为会跳回来,但是是加偏移地址-10
		int 21h
		mov ax,0
s2: 	jmp short s1  ;
		nop
codesg ends
end start

实验九 编程:在屏幕中间分别显示绿色、绿底红色、白底蓝色的字符串 ‘welcome tomasm!’。
编程所需的知识通过阅读、分析下面的材料获得。
80×25彩色字符模式显示缓冲区(以下简称为显示缓冲区)的结构:
内存地址空间中,B8000H~BFFFFH 共32KB的空间,为80×25彩色字符模式的显示缓冲区。向这个地址空间写入数据,写入的内容将立即出现在显示器上。
在80x25彩色字符模式下,显示器可以显示25行,每行80个字符(160byte),每个字符可以有256种属性(背景色、前景色、闪烁、高亮等组合信息)。
这样,一个字符在显示缓冲区中就要占两个字节,分别存放字符的ASCII码和属性。80x25模式下,一屏的内容在显示缓冲区中共占4000个字节。
显示缓冲区分为8页,每页4KB(~4000B),显示器可以显示任意一页的内容。一般情况下,显示第О页的内容。也就是说通常情况下,B8000H~B8F9FH中的4000个字节的内容将出现在显示器上。
在一页显示缓冲区中:
偏移00009F对应显示器上的第1行(80个字符占160个字节);偏移0A013F对应显示器上的第2行;
偏移140~1DF对应显示器上的第3行;

依此类推,可知,偏移F00~F9F对应显示器上的第25行。
在一行中,一个字符占两个字节的存储空间(一个字),低位字节存储字符的 ASCII码,高位字节存储字符的属性。一行共有80个字符,占160个字节
即在一行中:
0001单元对应显示器上的第1列;0203单元对应显示器上的第2列;04~05单元对应显示器上的第3列;
依此类推,可知,9E~9F单元对应显示器上的第80列。例:在显示器的0行0列显示黑低绿色的字符串’ABCDEF’('A’的ASCII码值为41H,02H表示黑底绿色)
显示缓冲区里的内容为:
在这里插入图片描述

注意,闪烁的效果必须在全屏 DOS方式下才能看到。

assume cs:code , ds:data , ss : stack
data segment
 	db 'welcome to masm!' ;定义要显示的字符串(共16字节加属性就是32字节)
	db 82h,24h,71h;定义三种颜色属性
data ends
stack segment
	dw 8 dup(0)
stack ends
code segment
start:
	mov ax ,data
	mov ds , ax
	mov ax,stack
	mov ss,ax
	mov sp ,10h
	xor bx, bx  ;bx清零,用来索引颜色
	mov ax ,0b872h  ;算出屏幕第12行中间的显存的段起始位置放入ax中 也就是屏幕中第一个w的位置。
	;B8000H 前11行*每行160byte + 64byte(一行中间的位置80-一半的字符串字节16) = 1760+64= 1824=720H+b8000h=b8720h
	mov cx,3  ;s3循环控制行数,外循环为3次,因为要显示三个字符串
s3:
	push cx;三个进栈操作为外循环s3保存相关寄存器的值
	push ax;以防止它们的值在内循环中被破坏
	push bx
	mov es,ax;此时es为屏幕第12行中间的显存的段起始位置
	mov si ,0;si用来索引代码列的字符
	mov di ,0;ai用来定位目标列
	mov cx,10h
	;s1循环控制存放的字符,内循环为10h次,因为一个字符串中含10h个字节
s1:
	mov al,ds: [si]
	mov es : [di] ,al
	inc si
	add di ,2
	loop s1;此循环实现偶地址中存放字符
	mov di,1;di的值设为1,从而为在显存奇地址中存放字符的颜色属性做准备
	pop bx
	mov al,ds : 10h [bx];取颜色属性
	inc bx
	mov cx,10h;第二个内循环也为10h次
s2:
	mov es:[di] , al
	add di ,2
	loop s2;此循环实现奇地址中存放宇符的颜色属性
	;以下4句为下一趟外循环做准备
	pop ax
	add ax ,0ah;将显存的段起始地址设为当前行的下一行
	; [在段地址中加oah,相当于在偏移地址中加了0a0h (=160d) ]
	pop cx
	loop s3
	mov ax,4c00h
	int 21h
code ends
end start

在这里插入图片描述
用cls清屏后的效果
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值