一、实验目的

1. 理解80×25彩色字符模式显示原理
2. 理解转移指令jmp, loop, jcxz的跳转原理,掌握使用其实现分支和循环的用法
3. 理解转移指令call, ret, retf的跳转原理,掌握组合使用call和ret/retf编写汇编子程序的方法,掌握
参数传递方式
4. 理解标志寄存器的作用
5. 理解条件转移指令je, jz, ja, jb, jg, jl等的跳转原理,掌握组合使用汇编指令cmp和条件转移指令实
现分支和循环的用法
6. 了解在visual studio/Xcode等环境或利用gcc命令行参数反汇编c语言程序的方法,理解编译器生成
的反汇编代码
7. 综合应用寻址方式和汇编指令完成应用编程

二、实验结论

1.实验任务1

教材「实验9 根据材料编程」(P187-189)
编程:在屏幕中间分别显示绿色、绿底红色、白底蓝色的字符串'welcome to masm!'。
源程序:
 1 assume cs:code
 2 data segment
 3     db 'welcome to masm!'
 4     db 02h,24h,71h  ; 要求的三个颜色对应的16进制代码
 5 data ends
 6 stack segment
 7     db 16 dup(0)
 8 stack ends
 9 code segment
10 start:    
11         ; 设置data段,以及ds:bx指向data段的第一个单元,
12         ; 即ds:[bx]的内容就是data段第一个单元的内容  
13         mov ax,data
14         mov ds,ax
15         
16         mov ax,0b800h ;设置起始缓存
17         mov es,ax        
18         
19         mov ax,stack
20         mov ss,ax
21         mov sp,10h  ;指向栈顶
22         
23         mov bx,780h ; 行 从12-14(注意:从第1行开始计数)
24         mov si,10h ; 颜色的偏移量,三次循环每次
25                     ; 增加 1h 指向下一个颜色
26         
27         mov cx,3    ; 三次循环改变行
28     s:     mov ah,ds:[si] ;颜色事先存放在ah中
29         push cx        
30         push si
31         
32         mov cx,16  ; 16次循环改变列
33         mov si,64 
34         mov di,0  
35                     
36     s0:    mov al,ds:[di] ;将date段中的字符一个一个传入es中
37         mov es:[bx+si],al ; 低位存放字符
38         mov es:[bx+si+1],ah ; 高位存放颜色
39         
40         add si,2  ;显示缓存区字符ASCII码偏移量为2
41         add di,1  ;data段字符的偏移量,每次加 1 
42         
43         loop s0
44         
45         pop si  
46         pop cx  ;后进先出,先出栈si,再出栈cx
47         
48         add si,1h  ;指向下一个颜色
49         add bx,0a0h ;指向下一行 160=0a0h
50         loop s
51 
52         mov ax,4c00h
53         int 21h
54 code ends
55 end start

mov si,64 从64开始是因为:(1)字符串为32字节,16字节ASCLL码,16字节属性

(2)每一行有160列,那么余下有 160-32=128列为空白,要使得字符串居中显示,那么字符串的左边

右边,都应该是64字节(128/2),而列数是从0开始计数,所以左边的64字节为0-63,所以这里偏移量

为64。

汇编、链接、执行后实验结果:

 

 

2.实验任务2

编写子程序printStr,实现以指定颜色在屏幕上输出字符串。调用它,完成字符串输出。
使用任意文本编辑器,录入汇编源程序task2.asm。
 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改为:

str db 'another try', 0
把line12改为:
mov al, 4

再次汇编、运行程序,观察运行结果:

基于运行结果,理解源代码,以及,组合使用转移指令call和ret实现子程序的原理与方法。具体地,在line18-40中:

line19-22, line36-39,这组对称使用的push、pop,目的是保护和恢复各寄存器的值,可以更好的利用寄存器空间。

line30的功能是读取目标数据存入es,将cx中存放的字符及其颜色属性送入显存中,实现在屏幕上的显示。

 3.实验任务3

使用任意文本编辑器,录入汇编源程序task3.asm。

子任务1

对task3.asm进行汇编、链接,得到可执行程序后,在debug中使用u命令反汇编,使用g命令执行
到line15(程序退出之前),使用d命令查看数据段内容,观察是否把转换后的数字字符串'1984'存放
在数据段中str标号后面的单元。

观察发现成功把转换后的数字字符串‘1984’存放在数据段中str标号后面的单元。

子任务2

对task3.asm源代码进行修改、完善,把task2.asm中用于输出以0结尾的字符串的子程序加进来,
实现对转换后的字符串进行输出。
源代码:
 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, 2
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 
76 code ends
77 end start

把task3.asm源代码中,line3中整数改成0~2559之间的任意数值,运行测试,观察结果。

 把line3中整数改为1314:

 

 4.实验任务4

使用任意文本编辑器,录入汇编源程序task4.asm。
 1 assume cs:code, ds:data
 2 data segment
 3         str db 80 dup(?)
 4 data ends
 5 
 6 code segment
 7 start:  
 8         mov ax, data
 9         mov ds, ax
10         mov si, 0
11 
12 s1:        
13         mov ah, 1
14         int 21h
15         mov [si], al
16         cmp al, '#'
17         je next
18         inc si
19         jmp s1
20 next:
21         mov cx, si
22         mov si, 0
23 s2:     mov ah, 2
24         mov dl, [si]
25         int 21h
26         inc si
27         loop s2
28 
29         mov ah, 4ch
30         int 21h
31 code ends
32 end start

汇编、链接、运行程序,输入一个字符串并以#结束(比如,2020, bye#)观察运行结果。

结合运行结果,理解程序功能,了解软中断指令。具体地:
line12-19实现的功能是:通过中断转移指令和条件跳转指令,从键盘中读取字符存入指定内存单元,直到遇到'#'结束。
line21-27实现的功能是:通过循环指令,依次输出保存在栈内的字符。

 5.实验任务5

在visual studio集成环境中,编写一个简单的包含有函数调用的c程序。代码如下:
 1 #include <stdio.h>
 2 int sum(int, int);
 3 
 4 int main() {
 5     int a = 2, b = 7, c;
 6 
 7     c = sum(a, b);
 8 
 9     return 0;
10 }
11 
12 int sum(int x, int y) {
13     return (x + y);
14 }

 在line7, line13分别设置断点:

在调试模式下,查看反汇编代码:

 

 分析反汇编代码,从汇编的角度,观察高级语言中参数传递和返回值是通过什么实现的,以及,参数入栈顺序,返回值的带回方式,等等。

高级语言中参数传递和返回值都是通过栈实现的,数据从ptr [a]、ptr [b]传入eax、ecx等寄存器。
调用函数时,形参参数入栈自右向左。先借助寄存器,将参数b 的地址压入堆栈寄存器eax,再将参数a 的值压入堆栈寄存器ecx。
用call命令调用sum函数,最后返回真正的值为eax,再传入双字数据ptr[c],函数的返回值放在eax寄存器中,调用ret指令返回。
posted on 2020-12-16 16:47  夏梦陆离  阅读(107)  评论(3编辑  收藏  举报