汇编语言实验总结
实验一
四、实验结论
1. 教材实验1(P45)
(1)使用Debug,将下面的程序段写入内存,逐条执行,观察每条指令执行后CPU中相关寄存器的内容变化
使用e命令修改内存:

使用a命令修改内存:


(2)将下面3条指令写入从2000:0开始的内存单元中,利用这三条指令计算2的8次方

单步调试:
开始部分:

结束部分:ax=0100H

(3)查看内存中的内容
从FFF00开始查看

生产日期应为01/01/92。尝试修改生产日期:

使用e命令修改内存后发现数据未被修改。由此可知生产日期在只读的地址空间,所以只能读取,不能写入。结合当前时间来看ROM的生产日期是虚拟的。
(4)向内存从B8100H开始的单元填写数据

向B8100H地址写入数据后在窗口中出现了图像。翻阅教材1.15节得知A0000H~BFFFFH为显存地址空间。向这些内存单元写数据,就是向显存中写入数据,这些数据会被显示卡输出到显示器上。
2. 教材实验2(P74)
(1)使用Debug,将下面的程序段写入内存,逐条执行,根据指令执行后的实际情况填空
首先使用 e 命令修改 内存单元0022:0~0022:f 中的数据,及修改后使用d命令查看是否正确写入

使用 a 命令输入汇编指令并填空

单步调试




push ax后2200:00FE中的内容
![]()
push bx后2200:00FC中的内容
![]()
push [4]后2200:00FE中的内容
![]()
push [6]后2200:00FC中的内容
![]()
与之前的理论填空结果相比较,可知结果正确
(2)进行以下实验,并分析:为什么2000:0~2000:f中的内容会发生改变?
图 3.19 中使用 a命令输入的 7 行指令,使用 e 命令修改 2000:0~2000:f 的值,及修改后查看的部分

单步调试每一行汇编指令的截图。每一条指令单步调试后,都使用d命令查看2000:0~2000:f 的值。

观察上图,发现执行momv ss, ax与mov sp, 10后2000:0~2000:f中的内容发生改变,继续调试


观察上图可知,红框中内容没有发生改变,且其中073FH与CS寄存器的值相对应,①的值与AX寄存器的值相对应,②的值与IP寄存器的值相对应,③为栈中存储的数据。通过了解,知道DOS系统中只有一个栈,系统会用该栈暂存相关数据。
以文字方式陈述:
1)前三行汇编指令的功能是什么?特别是,mov sp, 10意味着什么?初始时栈顶和栈底分别是?
mov ax, 2000 ; 将2000H送至ax寄存器 mov ss, ax ; 将ax寄存器的值送至ss栈顶段地址寄存器 mov sp, 10 ; 将0010H送至sp栈顶偏移地址寄存器
初始时栈顶为2000:0010,栈底为2000:000e。
2)基于单步调试观察到的变化,给出你对此的思考及可能原因分析
通过单步调试以及查阅资料,知道DOS系统中只有一个栈,系统会用该栈暂存相关数据,因此内存单元中的值会被修改。
实验二
四、实验结论
1. 实验任务1
给出ex1.asm源代码
; ex1.asm
assume cs:code
code segment
mov ax, 0b810h
mov ds, ax
mov byte ptr ds:[0], 1
mov byte ptr ds:[1], 1
mov byte ptr ds:[2], 2
mov byte ptr ds:[3], 2
mov byte ptr ds:[4], 3
mov byte ptr ds:[5], 3
mov byte ptr ds:[6], 4
mov byte ptr ds:[7], 4
mov ah, 4ch
int 21h
code ends
end
给出使用masm、link工具汇编、链接的命令行及运行结果截图

给出使用debug调试的截图。结合可执行文件加载后寄存器CX的值,使用u命令精确反汇编截图

查看PSP的命令及截图

使用g命令执行到line16退出执行之前,操作截图

2. 实验任务2
给出ex2.asm源代码
; ex2.asm
assume cs:code
code segment
mov ax, 0b810h
mov ds, ax
mov bx, 0
mov ax, 101H
mov cx, 4
s: mov [bx], ax
add bx, 2
add ax, 101H
loop s
mov ah, 4ch
int 21h
code ends
end
给出使用masm、link工具汇编、链接的命令行及运行结果截图
图不放了
给出使用debug调试的截图。结合可执行文件加载后寄存器CX的值,使用u命令精确反汇编截图

灵活使用t命令/p命令、g命令,对ex2.exe进行调试的截图


回答问题
结合上述实验和观察,分析、对比ex2.asm和ex1.asm,它们实现的是否是相同的功能和效果?在具体实现上有什么不同?
答:作用相同,都是向b810:0000开始的内存空间中写入0101 0202 0303 0404四个字。在具体实现上,ex1.asm使用8行(几乎)重复的代码完成写入操作,而ex2.asm使用了loop结构完成写入操作。
3. 实验任务3
给出源代码
; ex3.asm
assume cs:code
code segment
mov ax, 0b800h
mov ds, ax
mov bx, 07b8h
mov cx, 16
s: mov [bx], 0237h
inc bx
inc bx
loop s
mov ah, 4ch
int 21h
code ends
end
给出运行结果截图
屏幕中间绿色的7
把填充的字数据,从0237H 改成0239H,再次保存后,汇编、链接、运行,观察结果。
屏幕中间绿色的9
把填充的字数据,从0237H 改成0437H,再次保存后,汇编、链接、运行,观察结果。
屏幕中间红色的7
猜测并分析,这个字数据中高位字节里存放的是什么信息,低位字节里存放的是什么信息。
容易想到,37h为字符'7'对应的ASCII码,39h为字符'9'对应的ASCII码。在网络上检索到相关资料:
Reference:(Belal Hashmi, Assembly Language Programming Lecture Notes, Chap. 6)
The video device is seen by the computer as a memory area containing the ASCII codes that are currently displayed on the screen ...... Therefore if that appropriate block of the screen is cleared, the screen will be cleared. If the ASCII of ‘A’ is placed somewhere in that block, the shape of ‘A’ will appear on the screen at a corresponding place ...... This correspondence must be defined as the memory is a single dimensional space while the screen is two dimensional having 25 rows and 80 columns. The memory is linearly mapped on this two dimensional space, just like a two dimensional is mapped in linear memory. There is one word per character in which a byte is needed for the ASCII code and the other byte is used for the character’s attributes discussed later. Now the first 80 words will correspond to the first row of the screen and the next 80 words will correspond to the next row. By making the memory on the video controller accessible to the processor via the system bus, the processor is now in control of what is displayed on the screen ...... It was fixed at the physical memory location of B8000. The first byte at this location contains the ASCII for the character displayed at the top left of the video screen ...... The second byte in the word designated for one screen location holds the foreground and background colors for the character. This is called its video attribute. So the pair of the ASCII code in one byte and the attribute in the second byte makes the word that corresponds to one location on the screen. The lower address contains the code while the higher one contains the attribute. The attribute byte as detailed below has the RGB for the foreground and the background.
由于0x07b8 = 1976 = 988*2 = (12*80+28)*2,因此向b800:07b8写入数据就相当于向显示器第13行第29个字符位置写入数据。同时,0x0237就表示前景颜色为绿色的字符'7';0x0239就表示前景颜色为绿色的字符'9';0x0437就表示前景颜色为红色的字符'7'。从下图也可以看出第一个字符出现的位置正是显示器第13行第29个字符。

同理,0x1737就表示前景颜色为白色、背景颜色为蓝色的字符'7'。
图片
4. 实验任务4
编写完整汇编源程序,实现向内存0:200~0:23F依次传送数据0~63(3FH)。
Tips:
这道练习,本质上就是把一组连续的字节数据(常数),送到指定的连续的字节单元。
如果利用栈实现,借助push和loop实现连续入栈操作。需要注意:
① 初始时ss和sp的设置
② 8086的入栈操作,是从高地址单元→低地址单元方向的;
③ 8086的入栈操作,只能以字为单元。但这里是字节数据,如何灵活处理?
程序源代码
1 ; ex4.asm
2 assume cs:code
3 code segment
4 mov ax, 0
5 mov ds, ax
6
7 mov bx, 200h
8 mov al, 0
9 mov cx, 40h
10
11 ; clear memory
12 t: mov byte ptr [bx], 0
13 inc bx
14 loop t
15 mov bx, 200h
16 mov cx, 40h
17
18 s: mov byte ptr [bx], al
19 inc bx
20 inc al
21 loop s
22
23 mov ah, 4ch
24 int 21h
25 code ends
26 end
汇编、链接无误后,灵活使用debug的t命令、g命令、p命令调试,用d命令查看0:200~0:23F,确认是否将0~63传送至此段内存区域。

首先执行至Line 17,确保0:200h附近的空间数据为0;然后执行至Line 23,查看内存。

可见0~63被正确传送至此段内存区域。
5. 实验任务5
教材实验4(3)(P121)
Tips:
这道练习,本质仍然是复制,只不过复制的是自身代码。填空的关键是,如何确定复制多少字节。
填空以后的源代码
1 ; ex5.asm
2 assume cs:code
3 code segment
4 mov ax, cs
5 mov ds, ax
6
7 mov ax, 20h
8 mov es, ax
9
10 mov bx, 0
11 mov cx, 0
12
13 s: mov al, [bx]
14 mov es:[bx], al
15 inc bx
16 loop s
17
18 mov ah, 4ch
19 int 21h
20 code ends
21 end
以文字方式说明空白处填写依据
注意Line 11中cx首先被置为0。为了确定Line 1 - 16的指令长度,cx首先被置为0,接着通过对程序进行debug查看Line 1 - 16的指令长度,再将Line 11修改为该长度。将ax设为cs是因为确定代码段起始位置。

由上图看出,将cx修改为16h即可。修改之后再次编译、链接。

首先查看20:0附近的内存,可以看出内存中的数据仍为实验4中移动的数据。程序执行至Line 17,再次查看内存。


可以看出程序被成功复制。
6. 实验任务6
在linux环境下,编写、汇编、运行、调试一个32位的Intel风格的x86汇编程序。
源代码
1 ; example.asm
2
3 global _start
4
5 section .data
6 msg db "a simple test", 0xa
7 len equ $ - msg
8
9 section .text
10 _start:
11 mov eax, 4
12 mov ebx, 1
13 mov ecx, msg
14 mov edx, len
15 int 0x80
16
17 mov eax, 1
18 mov ebx, 7
19 int 0x80
对example.asm进行汇编、链接、运行。

将example.asm中line18行中寄存器ebx的值改为7,重新汇编、链接、运行。

五、实验总结
关于DOS程序运行结束后的返回
在以上ex1-ex5中,程序均包含以下两行代码
mov ah, 4ch
int 21h
翻阅书本,在P81有
这两条指令所实现的功能就是程序返回。
在目前阶段,我们不必去理解int 21h指令的含义,和为什么要在这条指令的前面加上指令mov ax, 4c00h。我们只要知道,在程序的末尾使用这两条指令就可以实现程序返回。
此两行代码涉及DOS中Interrupt(中断)的知识,详细内容可在书本Chap. 12-13被参照。此处仅做最基本的介绍。int为一条x86指令,其作用为产生软件中断,
在计算机科学中,中断(Interrupt)是指处理器接收到来自硬件或软件的信号,提示发生了某个事件,应该被注意,这种情况就称为中断。软件中断。是一条CPU指令,用以自陷一个中断。由于软中断指令通常要运行一个切换CPU至内核态(Kernel Mode/Ring 0)的子例程,它常被用作实现系统调用。
int指令的格式为
int X
其中X用于指出具体的中断。int 21h表示调用系统服务。该指令根据ax寄存器的高字节(ah)中的数据,又可实现不同的功能。当ah为4ch时,表示终止程序。故程序均包含该两行代码以正常退出。对于其它的中断,读者可参照该网站以及该pdf获取更多信息。
实验三
四、实验结论
1. 实验任务1
附上源代码文件task1.asm
1 assume cs:code, ds:data
2 data segment
3 db 'Nuist'
4 db 5 dup(2)
5 data ends
6
7 code segment
8 start:
9 mov ax, data
10 mov ds, ax
11
12 mov ax, 0b800H
13 mov es, ax
14
15 mov cx, 5
16 mov si, 0
17 mov di, 0f00h
18 s: mov al, [si]
19 and al, 0dfh
20 mov es:[di], al
21 mov al, [5+si]
22 mov es:[di+1], al
23 inc si
24 add di, 2
25 loop s
26
27 mov ah, 4ch
28 int 21h
29 code ends
30 end start
附上运行结果截图
不贴了
可以看到在窗口左下角出现了绿色的字符串“NUIST”。
附上在debug里调试到程序返回前的截图
首先反汇编,查看指令地址


可以看出程序退出前的指令地址为27。使用g命令执行至程序退出前。

修改line4里5个字节单元的值,重新汇编、链接、运行,观察结果。
db 5 dup(2)
--> 改成:
db 2,3,4,5,6
运行结果为

可以看出字符颜色改变。
分析源代码中data段line4的字节数据的用途
在上一次实验的实验任务3中,我已经查阅了相关资料并得到了结论。这里再次对结论进行简要的陈述。
Line4中的5个字节数据为Line3中5个字符的显示属性(video attribute),用于控制对应的字符的显示效果。显示属性的8个比特位的含义如下图所示。
图前面有。
所以2(10B)代表前景颜色为绿色;3(11B)代表前景颜色为蓝绿色;4(100B)代表前景颜色为红色;5(101B)代表前景颜色为紫色;6(110B)代表前景颜色为黄色。与实际结果相符。
2. 实验任务2
编写程序,在屏幕上以十进制整数形式打印输出这5个两位数。
1个两位数如何输出:
具体处理步骤如下:
第1步,利用除法指令div,计算出每个数位上的数值。
第2步,利用数值和数字字符之间ascⅡ码的关系,把数值→数字字符
第3步,利用系统功能调用int 21h中的2号子功能,输出单个字符。
int 21h中的2号子功能说明如下:
功能:输出单个字符
用法:
mov ah, 2 mov dl, ×× ; ××是待输出的字符,或其ASCII码值 int 21h
附上源代码文件task2.asm
1 ; task2.asm
2 assume cs:code, ds:data
3 data segment
4 db 23, 50, 66, 71, 35
5 data ends
6 code segment
7 start: mov ax, data
8 mov ds, ax
9
10 mov bl, 10
11 mov cx, 5
12 mov si, 0
13 s: mov ax, 0
14 mov al, [si]
15 div bl
16 add al, 30h
17 mov dl, al
18 mov bh, ah
19 mov ah, 2
20 int 21h
21 mov al, bh
22 add al, 30h
23 mov dl, al
24 int 21h
25 inc si
26 loop s
27
28 mov ah, 4ch
29 int 21h
30 code ends
31 end start
附上运行结果截图

3. 实验任务3
教材「实验5 编写、调试具有多个段的程序」(1)
附上源代码文件task3.asm
1 assume cs:code, ds:data, ss:stack
2 data segment
3 dw 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h
4 data ends
5
6 stack segment
7 dw 0, 0, 0, 0, 0, 0, 0, 0
8 stack ends
9
10 code segment
11 start: mov ax,stack
12 mov ss, ax
13 mov sp,16
14
15 mov ax, data
16 mov ds, ax
17
18 push ds:[0]
19 push ds:[2]
20 pop ds:[2]
21 pop ds:[0]
22
23 mov ax,4c00h
24 int 21h
25
26 code ends
27 end start
回答教材中①②③三个问题。
① data段中的数据没有改变,为0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h。
② cs = 076ch, ss = 076bh, ds = 076ah。
③ data段的段地址为X-2,stack段的段地址为X-1。
附上回答①时,反汇编查看data段中数据的截图。验证观察到的结果和理论分析是否一致。
首先对程序反汇编,查看指令地址

运行至程序退出前,查看data段中的数据。

可以看出与理论结果相符合。
附上回答②时反汇编调试截图,即:在debug中,执行到程序返回前的调试截图。

4. 实验任务4
教材「实验5 编写、调试具有多个段的程序」(2)
附上源代码文件task4.asm
1 assume cs:code, ds:data, ss:stack
2 data segment
3 dw 0123h, 0456h
4 data ends
5
6 stack segment
7 dw 0, 0
8 stack ends
9
10 code segment
11 start: mov ax,stack
12 mov ss, ax
13 mov sp,16
14
15 mov ax, data
16 mov ds, ax
17
18 push ds:[0]
19 push ds:[2]
20 pop ds:[2]
21 pop ds:[0]
22
23 mov ax,4c00h
24 int 21h
25
26 code ends
27 end start
回答教材中①②③④四个问题。
① data段中数据不变,为0123h,0456h。
② cs = 076ch, ss = 076bh, ds = 076ah。
③ data段的段地址为X-2,stack段的段地址为X-1。
④ 该段实际占有的空间为(N/16+1)*16。
附上回答②时反汇编调试截图,即:在debug中,执行到程序返回前的调试截图。

5. 实验任务5
教材「实验5 编写、调试具有多个段的程序」(3)
附上源代码文件task5.asm
1 assume cs:code, ds:data, ss:stack
2
3 code segment
4 start: mov ax,stack
5 mov ss, ax
6 mov sp,16
7
8 mov ax, data
9 mov ds, ax
10
11 push ds:[0]
12 push ds:[2]
13 pop ds:[2]
14 pop ds:[0]
15
16 mov ax,4c00h
17 int 21h
18
19 code ends
20 data segment
21 dw 0123h, 0456h
22 data ends
23
24 stack segment
25 dw 0,0
26 stack ends
27 end start
回答教材中①②③三个问题。
① data段中数据不变,为0123h, 0456h。
② cs = 076ah, ss = 076eh, ds = 076dh。
③ data段的段地址为X+3,stack段的段地址为X+4。
附上回答②时反汇编调试截图,即:在debug中,执行到程序返回前的调试截图。

6. 实验任务6
教材「实验5 编写、调试具有多个段的程序」(4)
回答教材中提出的问题,并按要求说明原因。
若将“end start”改为“end”,则只有task 5可以正确执行。因为修改之后,不指明程序入口,则程序从头开始执行。但是task 3与4开头均为数据段与栈段,这里的数据会被当作机器指令执行,所以无法得到预期的结果;而task 5开头即为代码段,所以从头执行能够得到正确的结果,同时在代码段结束时调用系统中断退出程序,从而能够得到预期的结果。
7. 实验任务7
教材「实验5 编写、调试具有多个段的程序」(5)
注*: 如果在集成软件环境下编写、汇编这个实验任务,请将段名为c的逻辑段,名称改为c1或别的标识
符。由于早期一些指令集中使用c指代寄存器,如不更正,有些汇编器在汇编时会报错。
附上完整源代码文件task7.asm
1 assume cs:code
2 a segment
3 db 1,2,3,4,5,6,7,8
4 a ends
5
6 b segment
7 db 1,2,3,4,5,6,7,8
8 b ends
9
10 c1 segment ; 在集成软件环境中,请将此处的段名称由c→改为c1或其它名称
11 db 8 dup(0)
12 c1 ends
13
14 code segment
15 start:
16 mov ax, a
17 mov ds, ax
18
19 mov cx, 8
20 mov bx, 0
21 s: mov al, [bx]
22 add al, [bx+10h]
23 mov [bx+20h], al
24 inc bx
25 loop s
26
27 mov ah, 4ch
28 int 21h
29 code ends
30 end start
附上在debug环境中,执行到程序返回前,查看逻辑段c的数据的截图,以此验证所编写的程序正确实现了题目要求。
首先查看相加之前c段中的数据(d 20 2f),可以看出均为0。

反汇编以查看指令地址,并执行至程序退出前,再次查看内存(d 20 2f)。

可以看出程序正确执行。
8. 实验任务8
教材「实验5 编写、调试具有多个段的程序」(6)
附上完整源代码文件task8.asm
1 assume cs:code
2
3 ; ds:0
4 a segment
5 dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0eh,0fh,0ffh
6 a ends
7
8 ; ds:20h
9 b segment
10 dw 8 dup(0)
11 b ends
12
13 ; ds:30h
14 code segment
15 start:
16 mov ax, a
17 mov ds, ax
18
19 add ax, 2
20 mov ss, ax
21 mov sp, 10h
22
23 mov cx, 8
24 mov bx, 0
25 s: push [bx]
26 add bx, 2
27 loop s
28
29 mov ah, 4ch
30 int 21h
31 code ends
32 end start
附上在debug环境中,执行到程序返回前,查看逻辑段c的数据的截图,以此验证所编写的程序正确实现了题目要求。
首先查看栈操作之前b段中的数据(d 20 2f),可以看出均为0。

反汇编以查看指令地址,并执行至程序退出前,再次查看内存(d 20 2f)。

可以看出程序正确执行。
实验四
四、实验结论
1. 实验任务1
教材「实验9 根据材料编程」(P187-189)
编程:在屏幕中间分别显示绿色、绿底红色、白底蓝色的字符串'welcome to masm!'。
Tips
这道编程练习需要考虑的两个关键问题:
- 屏幕中间对应的显存位置的计算 —— 第×行第×列对应的显存地址单元
- 字符串颜色属性的设置
上述两个问题,请参考教材188-189或第9章课件,内有:
- 80×25彩色字符模式显示缓冲区结构说明
- 字符属性字节的设置依据
字符串及其颜色属性信息,可以提前定义在数据段中。
要显示字符串,本质上,就是把字符及其颜色属性信息,复制到相应的显存空间,借助寻址方式和循环实现。
附: 本练习中显示字符串所对应的显存空间偏移地址
80×25的彩色字符模式,即一屏25行80列。
每一行显示一个字符需要两个字节(低位字节存放字符的ASCⅡ码值,高位字节存放字符的显示属性),一共160个字节。
默认,在显存第0列显示时,对应的显存空间为B8000H ~ B8F9FH。如果以B800H作为段地址,则一屏25行对应的偏移地址如下。请据此计算出屏幕中间位置对应的显存地址。

附上源程序代码
1 ; task1.asm
2 assume cs:code, ds:data
3 data segment
4 db 'welcome to masm!', 0
5 color db 2, 24h, 71h ; store color attributes here
6 data ends
7 code segment
8 start:
9 mov ax, data
10 mov ds, ax
11 mov bx, 720h ; 11(line)*160(bytes/line)+32(column)*2(bytes/column)
12 mov cx, 3 ; loop 3 times for 3 lines
13 mov di, 0 ; offset of color attributes in color segment
14 mov si, 0
15
16 s: mov ah, color[di] ; store color attribute in ah, and character in al
17 call print ; print the string at position es:[bx+si]
18 add bx, 0a0h ; switch to next line (+160bytes)
19 inc di ; switch to next color attribute
20 loop s
21
22 mov ah, 4ch
23 int 21h
24
25 print:
26 push es ; push registers so that they can be used in subprogram
27 push dx
28 push di
29 push si
30 push ax
31 mov dx, 0b800h ; video memory
32 mov es, dx
33 mov di, 0
34
35 sprint: mov al, ds:[si] ; store color attribute in ah, and character in al
36 cmp al, 0 ; if string ends, then return
37 je ed
38 mov es:[bx+di], ax ; print the character
39 inc si
40 add di, 2
41 jmp sprint
42
43 ed: pop ax
44 pop si
45 pop di
46 pop dx
47 pop es
48 ret
49
50 code ends
51 end start
附上运行结果截图
图不贴了
2. 实验任务2
编写子程序printStr,实现以指定颜色在屏幕上输出字符串。调用它,完成字符串输出。
子程序printSar
- 功能:以指定颜色在屏幕上(从屏幕左上角开始)输出字符串
- 要求:字符串以0结尾
- 入口参数
- 字符串的起始地址—> ds: si (其中,字符串所在段的段地址—> ds, 字符串起始地址的偏移地址—> si
- 字符串颜色—> al
- 出口参数:无
附上源程序代码
1 assume cs:code, ds:data
2 data segment
3 str db 'try', 0
4 data ends
5
6 code segment
7 start:
8 mov ax, data
9 mov ds, ax
10
11 mov si, offset str
12 mov al, 2
13 call printStr
14
15 mov ah, 4ch
16 int 21h
17
18 printStr:
19 push bx
20 push cx
21 push si
22 push di
23
24 mov bx, 0b800H
25 mov es, bx
26 mov di, 0
27 s: mov cl, [si]
28 mov ch, 0
29 jcxz over
30 mov ch, al
31 mov es:[di], cx
32 inc si
33 add di, 2
34 jmp s
35
36 over: pop di
37 pop si
38 pop cx
39 pop bx
40 ret
41
42 code ends
43 end start
附上运行结果截图

修改Line3与Line12后运行结果截图:

line19-22, line36-39,这组对称使用的push、pop,这样用的目的是什么?
在子程序执行之前将寄存器的数据保存至栈中,子程序执行结束返回之前再将寄存器的数据从栈中恢复,可以保护主程序在栈中存放的数据不被修改。
line30的功能是什么?
将要显示的数据移动至显存区域,以在屏幕上显示。
3. 实验任务3
子程序num2str:
- 功能:把0~2559之间的任意整数转换成数字字符串,例如,把1984转换成'1984'
- 入口参数
- 要转换的整数 —> ax
- 数字字符串的起始地址 —> ds:di (其中:数字字符串所在段的段地址—> ds,字符串起始地址的偏移地址—>di)
- 出口参数:无
- 子任务1
对task3.asm进行汇编、链接,得到可执行程序后,在debug中使用u命令反汇编,使用g命令执行到line15(程序退出之前),使用d命令查看数据段内容,观察是否把转换后的数字字符串'1984'存放在数据段中str标号后面的单元。
- 子任务2
对task3.asm源代码进行修改、完善,把task2.asm中用于输出以0结尾的字符串的子程序加进来,实现对转换后的字符串进行输出。
附上子任务1的反汇编截图
程序运行前首先查看内存,再反汇编

可以看出此时内存中数据为0,程序结束前的指令地址为076c:000e。使用g命令执行至程序结束前

可以看出1984被成功存放在数据段中str标号后面的单元。
附上子任务2修改、完善后的完整汇编源代码,及,运行测试截图
将task2.asm中的printStr添加至task3.asm后
1 assume cs:code, ds:data
2 data segment
3 x dw 1984
4 str db 16 dup(0)
5 data ends
6
7 code segment
8 start:
9 mov ax, data
10 mov ds, ax
11 mov ax, x
12 mov di, offset str
13 call num2str
14
15 mov si, offset str
16 mov al, 70h
17 call printStr
18
19 mov ah, 4ch
20 int 21h
21
22 num2str:
23 push ax
24 push bx
25 push cx
26 push dx
27
28 mov cx, 0
29 mov bl, 10
30 s1:
31 div bl
32 inc cx
33 mov dl, ah
34 push dx
35 mov ah, 0
36 cmp al, 0
37 jne s1
38 s2:
39 pop dx
40 or dl, 30h
41 mov [di], dl
42 inc di
43 loop s2
44
45 pop dx
46 pop cx
47 pop bx
48 pop ax
49
50 ret
51
52 printStr:
53 push bx
54 push cx
55 push si
56 push di
57
58 mov bx, 0b800H
59 mov es, bx
60 mov di, 0
61 s: mov cl, [si]
62 mov ch, 0
63 jcxz over
64 mov ch, al
65 mov es:[di], cx
66 inc si
67 add di, 2
68 jmp s
69
70 over: pop di
71 pop si
72 pop cx
73 pop bx
74 ret
75 code ends
76 end start
运行结果:

4. 实验任务4
task4.asm中用到的两个系统功能调用
int 21h中的1号子功能
- 功能:从键盘输入单个字符
- 用法:
mov ah, 1 int 21h ; 出口参数为al, 从键盘输入的字符保存在al中
int 21h中的2号子功能
- 功能:向屏幕输出单个字符
- 用法:
mov ah, 2 mov dl, ; ××是待输出的字符,或其ASCⅡ码值 int 21h
附上程序源代码
1 ; task4.asm
2 assume cs:code, ds:data
3 data segment
4 str db 80 dup(?)
5 data ends
6
7 code segment
8 start:
9 mov ax, data
10 mov ds, ax
11 mov si, 0
12
13 s1:
14 mov ah, 1
15 int 21h
16 mov [si], al
17 cmp al, '#'
18 je next
19 inc si
20 jmp s1
21 next:
22 mov cx, si
23 mov si, 0
24 s2: mov ah, 2
25 mov dl, [si]
26 int 21h
27 inc si
28 loop s2
29
30 mov ah, 4ch
31 int 21h
32 code ends
33 end start
附上运行测试截图

line13-20实现的功能是?
从键盘中读取字符存入data segment中,直到遇到'#'结束。
line22-28实现的功能是?
将用户输入的串在屏幕上打印。
5. 实验任务5
1 // task5.c
2 #include <stdio.h>
3
4 int sum(int, int);
5
6 int main() {
7 int a = 2, b = 7, c;
8 c = sum(a, b);
9 return 0;
10 }
11
12 int sum(int x, int y) {
13 return (x + y);
14 }
由于我的电脑上未安装Visual Studio环境,下面使用gcc环境进行反汇编等操作。使用gcc命令进行编译得到task5.o文件,然后使用objdump命令进行反汇编。
(base) PS D:\> gcc -g -c task5.c
(base) PS D:\> objdump -d -M intel -S task5.o >> task5.s
使用这种方式得到的.s文件同时包含.c文件源代码以及反汇编指令代码,阅读起来较为方便,使用以下命令同样也可以进行反汇编,但是得到的.s文件仅有反汇编指令代码。
gcc -S task5.c
查看task5.s中的内容
1 task5.o: file format pe-i386
2
3
4 Disassembly of section .text:
5
6 00000000 <_main>:
7 // task5.c
8 #include <stdio.h>
9
10 int sum(int, int);
11
12 int main() {
13 0: 55 push ebp
14 1: 89 e5 mov ebp,esp
15 3: 83 e4 f0 and esp,0xfffffff0
16 6: 83 ec 20 sub esp,0x20
17 9: e8 00 00 00 00 call e <_main+0xe>
18 int a = 2, b = 7, c;
19 e: c7 44 24 1c 02 00 00 mov DWORD PTR [esp+0x1c],0x2
20 15: 00
21 16: c7 44 24 18 07 00 00 mov DWORD PTR [esp+0x18],0x7
22 1d: 00
23 c = sum(a, b);
24 1e: 8b 44 24 18 mov eax,DWORD PTR [esp+0x18]
25 22: 89 44 24 04 mov DWORD PTR [esp+0x4],eax
26 26: 8b 44 24 1c mov eax,DWORD PTR [esp+0x1c]
27 2a: 89 04 24 mov DWORD PTR [esp],eax
28 2d: e8 0b 00 00 00 call 3d <_sum>
29 32: 89 44 24 14 mov DWORD PTR [esp+0x14],eax
30 return 0;
31 36: b8 00 00 00 00 mov eax,0x0
32 }
33 3b: c9 leave
34 3c: c3 ret
35
36 0000003d <_sum>:
37
38 int sum(int x, int y) {
39 3d: 55 push ebp
40 3e: 89 e5 mov ebp,esp
41 return (x + y);
42 40: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
43 43: 8b 45 0c mov eax,DWORD PTR [ebp+0xc]
44 46: 01 d0 add eax,edx
45 48: 5d pop ebp
46 49: c3 ret
47 4a: 90 nop
48 4b: 90 nop
- 参数传递:Line24-27可以看出在调用sum子函数前,main函数将两个参数放在了栈顶上方,即[esp]与[esp+0x4],在sum子函数里,再从[esp+0x8]与[esp+0xc]处取得。这里猜测是调用sum函数时,将CS与EIP压入了栈中,所以ESP减小了0x8。
- 返回值传递:Line29与Line44可以看出返回值存放在EAX寄存器中。
- 参数顺序:比较Line19-21与Line24-27可以看出,在调用c=sum(a, b)时,b首先被存至栈顶上方一个int处,接着a再被存至栈顶处。所以参数的处理顺序为从右至左。

浙公网安备 33010602011771号