0%

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

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

实验 5 编写、调试具有多个段的程序

实现功能:利用栈,将程序中定义的数据逆序存放

在代码段中使用数据和栈


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
29
30
31
32
33
assume cs:codesg

codesg segment

dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
;用 dw 定义 16 个字型数据,在程序加载后,将取得 16 个字的
;内存空间,存放这 16 个数据。在后面的程序中将这段
;空间当作栈来使用

start: mov ax,cs
mov ss,ax
mov sp,30h ;将设置栈顶 ss:sp 指向 cs:30

mov bx,0
mov cx,8
s: push cs:[bx]
add bx,2
loop s ;以上将代码段 0~15 单元中的 8 个字型数据依次入栈

mov bx,0
mov cx,8
s0: pop cs:[bx]
add bx,2
loop s0 ;以上依次出栈 8 个字型数据到代码段 0~15 单元中

mov ax,4c00h
int 21h

codesg ends

end start ;指明程序的入口在 start 处

我们用 dw 在程序中写入了 16 个字型数据,即开辟了 32 个字节的内存空间(可以通过定义数据来取得一定容量的内存空间),在之后的程序中,我们设置 ss:sp 指向 cs:30,把这段内存空间(cs:10 ~ cs:2F)当作栈空间,注意 ss:sp 指向的是栈底,入栈时,栈顶从高地址向低地址方向增长

我们知道,在单任务系统中,可执行文件中的程序执行过程如下:

  1. 由其他的程序(Debug、command 或其他程序)将可执行文件中的程序加载入内存
  2. 设置 CS:IP 指向程序的第一条要执行的指令(即程序的入口),从而使程序得以运行
  3. 程序运行结束后,返回到加载者

我们在程序中用伪指令 end 指明程序的入口在何处,end start 即说明程序的入口在标号 start 处,通过这种方式使得 CS:IP 能够正确地指向程序的入口

在 8086 模式下,一个段的容量不能大于 64 KB,再者,将所有的数据、栈和代码都放在同一个段中使得程序显得混乱,考虑用多个段来存放数据、代码和栈


实现功能:利用栈,将程序中定义的数据逆序存放

将数据、代码、栈放入不同的段


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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
assume cs:code,ds:data,ss:stack

data segment

dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

data ends

stack segment

dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

stack ends

code segment

start: mov ax,stack
mov ss,ax
mov sp,20h ;设置栈顶 ss:sp 指向 stack:20

mov ax,data
mov ds,ax ;ds 指向 data 段

mov bx,0 ;ds:bx 指向 data 段中的第一个单元
mov cx,8

s: push [bx]
add bx,2
loop s ;以上将 data 段中的 0~15 单元中的 8 个字型数据依次入栈

mov bx,0
mov cx,8

s0: pop [bx]
add bx,2
loop s0 ;以上依次出栈 8 个字型数据到 data 段的 0~15 单元中

mov 4c00h
int 21h
code ends

end start

在一个段中,数据的段地址可由段名代表,程序中对段名的引用将被编译器处理为一个表示段地址的数值,因此在编写程序时不能将其直接送入段寄存器,数据的偏移地址取决于它在段中的位置

注意,伪指令 assume 并不能使 CPU 将 cs 指向 codeds 指向 datass 指向 stack,CPU 到底如何处理我们定义的段中的内容,是当作指令执行,当作数据访问,还是当作栈空间,完全是由程序中具体的汇编指令和其对 CS:IPSS:SPDS 等寄存器的设置来决定的

实验任务(1)

将下面的程序编译、连接,用 Debug 加载、跟踪,然后回答问题


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
29
30
assume cs:code, ds:data,ss:stack

data segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
data ends

stack segment
dw 0,0,0,0,0,0,0,0
stack ends

code segment

start: mov ax,stack
mov ss,ax
mov sp,16

mov ax,data
mov ds,ax

push ds:[0]
push ds:[2]
pop ds:[2]
pop ds:[0]

mov ax,4c00h
int 21h

code ends

end start
用 Debug 跟踪程序运行

程序将 data 段的数据入栈再出栈,在顺序上也对应,因此不会改变 data 段数据的值


CPU 执行程序,程序返回前,data 段中的数据为多少?
CPU 执行程序,程序返回前,cs = ____ ss = ____ ds = ____。
设程序加载后,code 段的段地址为 X, 则 data 段的段地址为 ____ ,stack 段的段地址为 ____。

data 段中的数据未改变
cs = 076EH, ss = 076DH, ds = 076CH
code 段的段地址为 X,则 data 段的段地址为 X-2, stack 段的段地址为 X-1

实验任务(2)

将下面的程序编译、连接,用 Debug 加载、跟踪,然后回答问题


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
29
30
assume cs:code,ds:data,ss:stack

data segment
dw 0123H,0456H
data ends

stack segment
dw 0,0
stack ends

code segment

start: mov ax,stack
mov ss,ax
mov sp,16

mov ax,data
mov ds,ax

push ds:[0]
push ds:[2]
pop ds:[2]
pop ds:[0]

mov ax,4c00h
int 21h

code ends

end start
用 Debug 跟踪程序运行


CPU 执行程序,程序返回前,data 段中的数据为多少?
CPU 执行程序,程序返回前,cs = ____ ss = ____ ds = ____。
设程序加载后,code 段的段地址为 X, 则 data 段的段地址为 ____ ,stack 段的段地址为 ____。
对于如下定义的段:

1
2
3
name segment
...
name ends

如果段中的数据占 N 个字节,则程序加载后,该段实际占有的空间为 ____。

data 段中的数据未改变
cs = 076EH, ss = 076DH, ds = 076CH
code 段的段地址为 X,则 data 段的段地址为 X-2, stack 段的段地址为 X-1

修改程序,改变数据与栈的长度


用 Debug 跟踪修改后的程序的运行,观察到 data 段的起始地址为 076C:0stack 段的起始地址为 076E:0code 段的起始地址即程序第一条指令的起始地址为 0770:0,发现虽然 data 段中只存放了 24 个字节、stack 段也只开了 20 个字节作为栈空间,但实际上,两个段都占有 32 个字节的空间

可以得出,实际占有空间为 $⌈\dfrac{N}{16}⌉ · 16$ ( ⌈…⌉ 为向上取整 )

实验任务(3)

将下面的程序编译、连接,用 Debug 加载、跟踪,然后回答问题


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
29
30
assume cs:code,ds:data,ss:stack

code segment

start: mov ax,stack
mov ss,ax
mov sp,16

mov ax,data
mov ds,ax

push ds:[0]
push ds:[2]
pop ds:[2]
pop ds:[0]

mov ax,4c00h
int 21h

code ends

data segment
dw 0123H,0456H
data ends

stack segment
dw 0,0
stack ends

end start
用 Debug 跟踪程序运行


CPU 执行程序,程序返回前,data 段中的数据为多少?
CPU 执行程序,程序返回前,cs = ____ ss = ____ ds = ____。
设程序加载后,code 段的段地址为 X, 则 data 段的段地址为 ____ ,stack 段的段地址为 ____。

data 段中的数据未改变
cs = 076CH, ss = 0770H, ds = 076FH
code 段的段地址为 X,则 data 段的段地址为 X+3, stack 段的段地址为 X+4

实验任务(4)

如果将(1) (2) (3)中的最后一条伪指令 end start 改为 end (也就是说,不指明程序的入口),则哪个程序仍然可以正确执行?请说明原因。

(3) 仍可以正确执行
end start 改为 end 后,cs:ip 指向程序的首地址,只有 (3) 中的程序加载后 cs:ip 指向的是代码段

实验任务(5)

程序如下,编写 code 段的代码,将 a 段和 b 段中的数据依次相加,将结果存到 c 段中。

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

a segment
db 1,2,3,4,5,6,7,8
a ends

b segment
db 1,2,3,4,5,6,7,8
b ends

c segment
db 0,0,0,0,0,0,0,0
c ends

code segment

start:
?

code ends

end start

完整程序如下


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
29
30
31
32
33
34
assume cs:code

a segment
db 1,2,3,4,5,6,7,8
a ends

b segment
db 1,2,3,4,5,6,7,8
b ends

c segment
db 0,0,0,0,0,0,0,0
c ends

code segment

start: mov ax,a
mov ds,ax
mov bx,0
mov cx,8

s: mov al,ds:[bx]
add al,ds:[bx+16]
mov ds:[bx+32],al

inc bx
loop s

mov ax,4c00h
int 21h

code ends

end start

在编写程序时,我们通过前面的实验知道了如何推算一个段具体占有多少内存空间,那么当我们知道 a 段的起始地址为 a:0 时,我们就可以推出 b 段的起始地址为 a:0+16c 段的起始地址为 a:0+32,从而使得在我们的程序中只需要用寄存器取一个段地址即可

注意,dw 的含义是定义字型数据,dwdefine word,字型数据可以直接放入寄存器中去,因为数据寄存器的大小也是一个字的大小,而 db 的含义是定义字节型数据,dbdefine byte,字节型数据应该使用数据寄存器的高 8 位或是低 8 位进行存放

用 Debug 跟踪程序运行

实验任务(6)

程序如下,编写 code 段的代码,用 push 指令将 a 段中的前 8 个字型数据,逆序存储到 b 段中。

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

a segment
dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0eh,0fh,0ffh
a ends

b segment
dw 0,0,0,0,0,0,0,0
b ends

code segment

start:
?

code ends

end start

完整程序如下


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
29
30
31
32
33
assume cs:code

a segment
dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0eh,0fh,0ffh
a ends

b segment
dw 0,0,0,0,0,0,0,0
b ends

code segment

start:
mov ax,a
mov ds,ax

mov ax,b
mov ss,ax
mov sp,16

mov bx,0 ;sub bx,bx
mov cx,8

s: push ds:[bx]
add bx,2
loop s

mov ax,4c00h
int 21h

code ends

end start

b 段视为栈空间,将 a 段中的前 8 个字型数据在循环中顺序压入栈中即可

用 Debug 跟踪程序运行


汇编语言实验合集

汇编语言实验合集

实验 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