0%

《汇编语言》(第四版) 实验 8

《汇编语言(第四版)》 . 王爽著 . 清华大学出版社 . 2019

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

可以修改 IP,或同时修改 CS 和 IP 的指令统称为转移指令

8086CPU 的转移行为:
段内转移:只修改 IP
段间转移:同时修改 CS 和 IP

由于转移指令对 IP 的修改范围不同,段内转移又分为:短转移和(80 ~ 7F)近转移(8000 ~ 7FFF)

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

1
2
jmp short 标号                  ;段内短转移 (IP)=(IP)+8 位位移
jmp near ptr 标号 ;段内近转移 (IP)=(IP)+16 位位移

转移的目的指令在指令中的 jmp 指令

1
2
jmp far ptr 标号                ;段间转移 / 远转移
;用标号的段地址与偏移地址修改 CS 和 IP

8086CPU 的转移指令分为:
无条件转移指令
条件转移指令
循环指令
过程
中断

检测点 9.1

1
2
jmp word ptr 内存单元地址       ;段内转移
jmp dword ptr 内存单元地址 ;段间转移


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
assume cs:code

data segment
dw 0,0,0,0,0,0,0,0
data ends

code segment

start: mov ax,data
mov ds,ax
mov bx,0
jmp word ptr [bx+1]

code ends

end start

段内转移,CS:IP 要指向程序的第一条指令,则目的偏移地址为 0 即可



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
assume cs:code

data segment
dd 12345678H
data ends

code segment

start: mov ax,data
mov ds,ax
mov bx,0
mov [bx],0 ;mov [bx],offset start
;操作符 offset 可以取得标号的偏移地址
mov [bx+2],cs
jmp dword ptr ds:[0]

code ends

end start

段间转移,内存单元高地址处的字是转移的目的段地址,低地址处是转移的目的偏移地址


CS=0006H, IP=00BEH

检测点 9.2

1
jcxz 标号                       ;if((cx)==0) jmp short 标号;


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
assume cs:code

code segment

start: mov ax,2000H
mov ds,ax
mov bx,0

s: mov cl,ds:[bx]
mov ch,0
jcxz ok
inc bx
jmp short s

ok: mov dx,bx
mov ax,4c00h
int 21h

code ends

end start

这里能否将补全的前两句指令替换成 mov cx,ds:[bx],它们之间的区别是什么?

题目要求在内存 2000H 段中查找第一个值为 0 的字节,而 cx 中存放的是字,直接传送是字操作,因此不能直接传送到 cx 中去,应该传送字节给 cx 的低 8 位 cl,再给其高 8 位 ch 赋 0

检测点 9.3

1
2
3
4
loop 标号

;(cx)--;
;if((cx)≠0) jmp short 标号


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 ;dec 指令的功能和 inc 相反,dec bx 进行的操作为:(bx)=(bx)-1
mov dx,bx
mov ax,4c00h
int 21h

code ends

end start

loop 指令先要执行 (cx)=(cx)-1 再判断 cx 是否为 0,所以应该先对 cx 加 1

实验任务

分析下面的程序,在运行前思考:这个程序可以正确返回吗?

运行后思考:为什么会是这种结果?

通过这个程序加深对相关内容的理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
assume cs:codesg

codesg 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

codesg ends

end start

这个程序可以正确返回

程序读上去的确非常奇怪,但是其中蕴含着一个简单而又深刻的原理,那就是此处 CPU 在执行 jmp 指令时并不需要转移的目标地址

我们来看程序

1
2
mov ax,cs:[si]
mov cs:[di],ax

执行完这两句指令后,我们可能会错误地以为,cs:[si] 处的指令 jmp short s1 通过 ax 寄存器传送到了 cs:[di] 处,即标号 s 处,然后接下来程序转移到标号 s 处,再转移到标号 s1 处,似乎程序无法正确返回

而实际上,通过 ax 转移的不是 jmp short s1 这句指令,而是这句指令的编译后的机器码,我们用 Debug 跟踪可知,编译后的机器码为 EBF6,其中 EB 代表 jmp 指令,F6 则不是转移的目标地址,而是要转移的位移

这个位移我们显然是可以计算出来的,回忆一下 CPU 执行指令的过程:
1)从 CS:IP 指向内存单元读取指令,读取的指令进入指令缓冲器
2)(IP)=(IP)+所读取指令的长度,从而指向下一条指令
3)执行指令,转到 1),重复这个过程

1
2
3
4
5
6
s1:     mov ax,0                ;B80000
int 21h ;CD21
mov ax,0 ;B80000

s2: jmp short s1 ;EBF6
nop ;90

所以这个位移不是标号 s2 到标号 s1 的差值,而是 jmp short s1 后一条指令的地址与标号 s1 的差值,这个差值即为中间指令机器码的长度(包括 jmp short s1 的长度),此处为 10 个字节,又因为是向低地址方向转移,所以位移为 -10,它的二进制补码即为 F6

知道这点后,我们就明白,当程序跳转到标号 s 处时,CPU 读取程序复制过来的内容,然后使 (IP)=(IP)+2,然后执行向低地址方向作 10 个字节的位移(短转移),此时 CS:IP 指向 mov ax,4c00h,于是程序得以正确返回

用 Debug 跟踪程序运行


补充材料:《深入理解计算机系统》

3.6.4 跳转指令的编码

在汇编代码中,跳转目标用符号标号书写。汇编器,以及后来的链接器,会产生跳转目标的适当编码。跳转指令有几种不同的编码,但是最常用都是 PC 相对的PC-relative)。也就是,它们会将目标指令的地址与紧跟在跳转指令后面那条指令的地址之间的差作为编码。这些地址偏移量可以编码为 1、2 或 4 个字节。第二种编码方法是给出“绝对”地址,用 4 个字节直接指定目标。汇编器和链接器会选择适当的跳转目的编码。

更深入的:7.7.2 重定位符号引用


汇编语言实验合集

汇编语言实验合集

实验 1 查看 CPU 和内存,用机器指令和汇编指令编程
实验 2 用机器指令和汇编指令编程
实验 3 编程、编译、连接、跟踪
实验 4 [bx] 和 loop 的使用
实验 5 编写、调试具有多个段的程序
实验 6 实践课程中的程序
实验 7 寻址方式在结构化数据访问中的应用
实验 8 分析一个奇怪的程序
实验 9 根据材料编程
实验 10 编写子程序
课程设计 1
实验 11 编写子程序
实验 12 编写 0 号中断的处理程序
实验 13 编写、应用中断例程
实验 14 访问 CMOS RAM
实验 15 安装新的 int9 中断例程
实验 16 编写包含多个功能子程序的中断例程
实验 17 编写包含多个功能子程序的中断例程
课程设计 2