蒙珣的博客

活好当下,做好今天该做的事情。

0%

实验10 编写子程序

call 指令,组织数据和组织代码的方式,给我们的启发

设置参数 组织代码 输入
程序处理 组织代码 程序处理
程序的返回值 组织数据 输出

编写子程序

在这次实验中,我们要别写3个子程序,通过它们来认识几个常见的问题和掌握解决这些问题的方法。

1.显示子字符串

问题

显示字符串是现实工作中经常用到的功能,应该编写一个通用的子程序来实现这个功能。我们应该提供灵活的调用接口,使调用者可以决定显示的位置(行、列)、内容和颜色。

子程序描述

名称:show_str

功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串

参数:(dh)=行号(取值范围0~24),(dl)=列号(取值范围0~79)

​ (cl)=颜色,ds:si 指向字符串的首地址

返回:无

应用举例:在屏幕的8行3列,用绿色显示 data 段中的字符串

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
assume cs:code

data segment
db 'Welcome to masm!',0
data ends

code segment

start: mov dh,8
mov dl,3
mov cl,2
mov ax,data
mov ds,ax
mov si,0
call show_str

mov ax,4c00h
int 21h

show_str: .
.
.
.
.
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

;名称:show_str

;功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串

;参数:(dh)=行号(取值范围0~24),(dl)=列号(取值范围0~79),(cl)=颜色,ds:si 指向字符串的首地址

;返回:无

;应用举例:在屏幕的8行3列,用绿色显示 data 段中的字符串[201~)]

assume cs:code,ss:stack,ds:data

data segment
db 'Welcome to masm!',0
data ends

stack segment stack
db 128 dup (0)
stack ends

code segment

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

mov di,0
mov si,0
mov ax,0

call init_reg

call get_col
add di,ax ;拿到列的返回值
call get_row
add di,ax ;拿到行的返回值

mov cl,2
call show_reg

mov ax,4c00h
int 21h

;====================================
show_reg: push di
push si
push cx
push dx
push es

mov cx,0
mov dl,2

show_str: mov cx,ds:[si] ;如果cx不为0继续循环
jcxz show_Ret
mov es:[di],cx ;偶数显示字节
mov es:[di+1],dl ;奇数显示颜色,2为绿色
add di,2
inc si
loop show_str

show_Ret: pop es
pop dx
pop cx
pop si
pop di

ret

;====================================
get_col: mov dh,8
mov al,160 ;一行160个字节,乘以8就是8行
mul dh
ret

;====================================
get_row: mov dl,3
mov al,2 ;屏幕一个字符占两个字节
mul dl
ret

;====================================初始化寄存器
init_reg: mov bx,data
mov ds,bx

mov bx,0B800H
mov es,bx
ret

code ends

end start

2.解决除法溢出的问题

实验10.2 编程,解决除法溢出问题

名称:long_div(书上原名 divdw)

功能:进行不会产生溢出的除法运算,被除数 dword 型,除数为 word 型,结果为 dword 型

参数:ax = dword 型数据的 低 16 位

​ dx = dword 型数据的 高16 位

​ cx = 除数

返回:dx = 结果的高 16 位,ax = 结果的低16位

​ cx = 余数

应用举例:计算 1000000/10 (F4240H / 0AH) = 186A0H

1
2
3
4
5
mov ax,4240h
mov dx,000fh
mov cx,10

call long_div
1
2
3
4
5
6
7
8
9
10
11
公式:X/N = int(H/N)*65536 + [rem(H/N)*65536+L]/N

提示公式: dx ax
X:被除数,范围: [0,FFFF FFFFH]
N: 除数,范围:[0,FFFFH]
H: X的高16位
LX的低16位

int(): 描述性运算符,取商 比如 int(38/10) = 3

rem(): 描述性运算符,取余数 比如 rem(38/10) = 8
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
assume cs:code,ds:code,ss:stack

data segment
dd 1000000
data ends

stack segment stack
db 128 dup (0)
stack ends

code segment

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

; mov ax,4240h
; mov dx,0fh

mov bx,data
mov ds,bx

mov bx,0

mov ax,ds:[bx+0] ;参数 除数低16位 L
mov dx,ds:[bx+2] ;参数 除数高16位 M

mov cx,10

push ax
mov bp,sp

call long_div

;公式:X/N = int(H/N)*65536 + [rem(H/N)*65536+L]/N

mov ax,4c00h
int 21h

;============================================
long_div: mov ax,dx
mov dx,0
div cx ;ax = int(H/N) => dx dx = rem(H/N)*65536

push ax

mov ax,ss:[bp+0] ;L

div cx ;ax = [rem(H/N)*65536+L]/N 的商 dx = 余数

mov cx,dx

pop dx

ret


code ends

end start

3.数值显示

问题
编程,将data段中的数据以十进制的形式显示出来

1
2
3
data segment
dw 123,12666,1,8,3,38
data ends

子程序描述
名称:dtoc
功能:将 word 型数据转变为表示十进制的字符串,字符串以0为结尾符
参数:(ax)=word型数据,ds:si指向字符串的首地址
返回:无

应用举例:编程,将数据 12666 以十进制的形式在屏幕的 8行3列,用绿色显示出来。
在显示时我们调用本次实验中的第一个子程序show_str

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

data segment
db 10 dup (0)
data ends

code segment

start: mov ax,12666
mov bx,data
mob ds,bx
mov si,0
call dtoc

mov dh,8
mov dl,3
mob cl,2
call show_str
.
.
.

.
.
.

.
.
.

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
assume cs:code,ds:data,ss:stack

data segment
dw 1230,12666,1,8,3,38
data ends

string segment
db 10 dup('0'),0
string ends

stack segment stack
db 128 dup (0)
stack ends

code segment

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

call init_reg
call init_number

mov ax,4c00h
int 21h

;'00000 00000',0
;=================================
init_number: mov bx,0
mov si,9 ;es:[si],将得到的ASCII先放到个位

mov di,160*10 + 30*2
mov cx,6

show_number: call show_word
add di,160
add bx,2
loop show_number

ret

;=================================
show_word: push ax
push bx
push cx
push dx
push es
push ds
push di
push si

mov ax,ds:[bx] ;ax 和 dx 寄存器的值去参与运算
mov dx,0

call short_div
call init_reg_show_string

call show_string

pop si
pop di
pop ds
pop es
pop dx
pop cx
pop bx
pop ax
ret

;=================================
show_string: push cx
push ds
push es
push si
push di

mov cx,0

showString: mov cx,0
mov cl,ds:[si]
jcxz showStringRet
mov es:[di],cl
add di,2
inc si
jmp showString

showStringRet: pop di
pop si
pop es
pop ds
pop cx

ret
;=================================
init_reg_show_string:
mov bx,string
mov ds,bx

mov bx,0B800H
mov es,bx
ret
;=================================
short_div: mov cx,10
div cx
add dl,30H
mov es:[si],dl
mov cx,ax
jcxz shortDivRet
dec si ;从个位变到十位,到百位
mov dx,0
jmp short_div

shortDivRet: ret

;=================================
init_reg: mov bx,data
mov ds,bx

mov bx,string
mov es,bx
ret

code ends

end start