课程设计1 显示Power idea公司的数据

项目情景

任务:将实验7中的Power idea公司的数据按照下图所示的格式在屏幕上显示出来。
效果

Power idea公司的数据

分析

需要用到实验7的数据,为了偷懒就直接在实验7的source.asm的基础上写了。

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

data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
db '1993','1994','1995'
;以上是表示21年的21个字符串

dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;以上是表示21年公司收入的21个dword型数据

dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
;以上是表示21年公司雇员人数的21个word型数据
data ends

table segment
db 21 dup ('year summ ne ?? ')
table ends

将实验7里的table段改为text段,用于存储需要打印的字符串。
1
2
3
4
text segment
db 160 dup (0)
; 160字节的超大text段
text ends

可以按照年份收入雇员人均收入的顺序依次处理不同类型的数据,并分四次打印到屏幕上,效率比较高。
text段中存储时,可以定义以0结尾,以0ah(即LF)为换行符,便于在打印时对字符串进行操作。

程序实现

printf子程序

在程序中需要实现在屏幕上打印字符串的功能,可以用printf子程序实现:

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
printf:
; 名称:printf
; 功能:在屏幕指定位置打印字符串,0A解释为\n,以0结尾
; 参数:text段存储需要打印的字符串
; (si)存储位置
push di
push cx
push es
push ds
push si
push si
; 保护寄存器内容,下各子程序同

mov di,0
mov cx,text
mov ds,cx
mov cx,0b800h
mov es,cx
s0:
mov ch,0
mov cl,ds:[di]
jcxz printf_cmpd ; 0标志结束
sub cx,0ah
jcxz lineFeed ; LF标志换行
add cx,0ah
mov ch,7h ; 白底黑字
mov es:[si],cx
add si,2
s1:
add di,1
jmp short s0
lineFeed:
; 换行
pop si
add si,0a0h
push si
jmp short s1
printf_cmpd:
pop si
pop si
pop ds
pop es
pop cx
pop di
ret

wtoc子程序

需要将word类型的数据(雇员人数、人均收入)转化为字符串,可以用wtoc子程序实现:

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
wtoc:
; 名称:wtoc
; 功能:数据(word) -> 字符
; 参数: (ax)=数据
; ds:si -> 字符串的开始地址
; si(输出) -> 字符串的结束地址
push ax
push bx
push cx
push dx
push di

mov di,0
divide_w:
mov dx,0
mov bx,10
div bx
; 原理:通过反复除以10得到各位的数字,再+30h得到对应的ASCII编码,后面的dtoc同
add dx,30h
mov dh,0
push dx
inc di
mov cx,ax
jcxz wtoc_cmpd ; 商为0跳出循环
jmp short divide_w
wtoc_cmpd:
; 将上述步骤得到的字符倒序排列
mov cx,di
s7:
pop ax
mov es:[si],al
inc si
loop s7
mov byte ptr es:[si],0ah
; 结尾加上LF
inc si

pop di
pop dx
pop cx
pop bx
pop ax
ret

divdw子程序

dword类型的数据(收入)也需要一个类似于wtoc的子程序,但是dword数据在进行div时容易溢出,所以编写一个不会溢出的除法子程序。
原理公式如下:

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
divdw:
; 名称:divdw
; 功能:不会溢出的除法
; c=a/b, a:dword, b:word, c:dword
; 参数:(ax)=a的低16位
; (dx)=a的高16位
; (cx)=b
; 返回:(ax)=c的低16位
; (dx)=c的高16位
; (cx)=余数
; 原理: (H//N)*65536+[(H%N)*65536+L]/N
push bx

mov bx,ax
mov ax,dx
mov dx,0
div cx
push ax
push bx
pop ax
pop bx
; 利用栈实现ax、bx互换
div cx
mov cx,dx
mov dx,bx

pop bx
ret

dtoc子程序

利用前面编写的dword子程序,将dword类型的数据(总收入)转化为字符串,原理同wtoc:

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
dtoc:
; 名称:dtoc
; 功能:dword转化为字符
; 参数:(ax)=dword(低16位)
; (dx)=dword(高16位)
; ds:si -> 字符串的开始地址
; si(输出) -> 字符串的结束地址
push ax
push bx
push cx
push dx
push di
push bp

mov di,0
divide:
mov cx,10
call divdw ; 把div换成divdw
mov bp,ax
add cx,30h
mov ch,0
push cx
inc di
or ax,dx
mov cx,ax
mov ax,bp
jcxz dtoc_cmpd
jmp short divide
dtoc_cmpd:
mov cx,di
s4:
pop ax
mov es:[si],al
inc si
loop s4
mov byte ptr es:[si],0ah
inc si

pop bp
pop di
pop dx
pop cx
pop bx
pop ax
ret

主程序

好了,写了这么多子程序,终于可以开始主程序了。
指定各段段寄存器:

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

定义栈段stack:
1
2
3
4
stack segment
db 64 dup (0)
; 64字节的超大栈段
stack ends

定义代码段code:
1
2
3
4
5
code segment
start:
...
code ends
end start

初始化段寄存器:
1
2
3
4
5
6
7
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,32
mov ax,text
mov es,ax

接下来清个屏,把显存用0720h(即黑底白字的空格)填满:
1
2
3
4
5
6
7
8
mov ax,0b800h
mov es,ax
mov cx,2000
mov bx,0
clear:
mov word ptr es:[bx],0720h
add bx,2
loop clear

打印年份,本来就是字符串,写入text段后直接用printf打印,不需要做什么特殊的处理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mov ax,text
mov es,ax
mov di,0
mov cx,21
mov si,0
s2:
push cx
mov cx,4
s3:
mov al,ds:[di]
mov es:[si],al
inc di
inc si
loop s3
mov byte ptr es:[si],0ah ; 每一个年份换一次行
inc si
pop cx
loop s2
mov byte ptr es:[si],0 ; 结尾加个0,表示字符串结束
mov si,0a0h ; 位置:(0,1)
call printf

接下来打印收入,通过dtoc先转化成字符再用printf打印:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mov ax,data
mov ds,ax
mov ax,text
mov es,ax

mov di,0
mov cx,21
mov si,0
s5:
mov ax,ds:[di+54h] ; ax存储低16位
mov dx,ds:[di+56h] ; dx存储高16位
call dtoc
add di,4
loop s5
mov byte ptr es:[si],0
mov si,0a0h
add si,40 ; 位置(20,1)
call printf

雇员人数,使用wtoc转化成字符,再用printf打印:
1
2
3
4
5
6
7
8
9
10
11
12
mov di,0
mov cx,21
mov si,0
s6:
mov ax,ds:[di+0a8h]
call wtoc
add di,2
loop s6
mov byte ptr es:[si],0
mov si,0a0h
add si,80 ; 位置(40,1)
call printf

关于为什么是+54h+56h+0a8h实验7已经分析过了,此处不再赘述。

人均收入,可以先通过除法算出人均收入,再用wtoc转化为字符,最后用printf输出:
(由于实验7中人均收入是2字节,所以这里我们不必担心溢出问题,可以直接用div计算)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mov di,0
mov cx,21
mov bx,0
mov si,0
s8:
mov ax,ds:[bx+54h]
mov dx,ds:[bx+56h]
div word ptr ds:[di+0a8h]
call wtoc
add di,2
add bx,4
loop s8
mov byte ptr es:[si],0
mov si,0a0h
add si,120 ; 位置(60,1)
call printf

最后返回:
1
2
mov ax,4c00h
int 21h

完整的程序如下:
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
; p1.asm
assume cs:code,ds:data,ss:stack

data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
db '1993','1994','1995'
;The above is 21 strings representing 21 years

dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;The above is 21 dwords representing the incomes of 21 years

dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
;The above is 21 words representing the numbers of employees
data ends

text segment
db 160 dup (0)
text ends

stack segment
db 64 dup (0)
stack ends

code segment
start:
; segReg init
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,32
mov ax,text
mov es,ax

; Clear the screen
mov ax,0b800h
mov es,ax
mov cx,2000
mov bx,0
clear:
mov word ptr es:[bx],0720h
add bx,2
loop clear

; Years
mov ax,text
mov es,ax
mov di,0
mov cx,21
mov si,0
s2:
push cx
mov cx,4
s3:
mov al,ds:[di]
mov es:[si],al
inc di
inc si
loop s3
mov byte ptr es:[si],0ah
inc si
pop cx
loop s2
mov byte ptr es:[si],0
mov si,0a0h ;(0,1)
call printf

; Incomes
mov ax,data
mov ds,ax
mov ax,text
mov es,ax

mov di,0
mov cx,21
mov si,0
s5:
mov ax,ds:[di+54h]
mov dx,ds:[di+56h]
call dtoc
add di,4
loop s5
mov byte ptr es:[si],0
mov si,0a0h
add si,40 ;(20,1)
call printf

; Employees
mov di,0
mov cx,21
mov si,0
s6:
mov ax,ds:[di+0a8h]
call wtoc
add di,2
loop s6
mov byte ptr es:[si],0
mov si,0a0h
add si,80 ; (40,1)
call printf

; PCI
mov di,0
mov cx,21
mov bx,0
mov si,0
s8:
mov ax,ds:[bx+54h]
mov dx,ds:[bx+56h]
div word ptr ds:[di+0a8h]
call wtoc
add di,2
add bx,4
loop s8
mov byte ptr es:[si],0
mov si,0a0h
add si,120 ; (60,1)
call printf

; Teminate
mov ax,4c00h
int 21h

printf:
; Name:printf
; Function:Print the strings at the specified position,0A -> \n,ending in 0
; Parameter:text seg -> content to be printed
; (si) -> position
push di
push cx
push es
push ds
push si
push si

mov di,0
mov cx,text
mov ds,cx
mov cx,0b800h
mov es,cx
s0:
mov ch,0
mov cl,ds:[di]
jcxz printf_cmpd
sub cx,0ah
jcxz lineFeed
add cx,0ah
mov ch,7h
mov es:[si],cx
add si,2
s1:
add di,1
jmp short s0
lineFeed:
pop si
add si,0a0h
push si
jmp short s1
printf_cmpd:
pop si
pop si
pop ds
pop es
pop cx
pop di
ret
divdw:
; Name:divdw
; Function:Dividing without overflow
; c=a/b, a:dword, b:word, c:dword
; Parameter:(ax)=a low 16 bits
; (dx)=a high 16 bits
; (cx)=b
; Return:(ax)=c low 16 bits
; (dx)=c high 16 bits
; (cx)=change
; Principle: (H//N)*65536+[(H%N)*65536+L]/N
push bx

mov bx,ax
mov ax,dx
mov dx,0
div cx
push ax
push bx
pop ax
pop bx
div cx
mov cx,dx
mov dx,bx

pop bx
ret
dtoc:
; Name:dtoc
; Function: dword -> char
; Parameter: (ax)=dword(low 16bits)
; (dx)=dword(high 16bits)
; ds:si(in) -> Starting adress of string
; si(out) -> Ending adress of string
push ax
push bx
push cx
push dx
push di
push bp

mov di,0
divide:
mov cx,10
call divdw
mov bp,ax
add cx,30h
mov ch,0
push cx
inc di
or ax,dx
mov cx,ax
mov ax,bp
jcxz dtoc_cmpd
jmp short divide
dtoc_cmpd:
mov cx,di
s4:
pop ax
mov es:[si],al
inc si
loop s4
mov byte ptr es:[si],0ah
inc si

pop bp
pop di
pop dx
pop cx
pop bx
pop ax
ret
wtoc:
; Name:wtoc
; Function: number(word) -> char
; Parameter: (ax)=Word
; ds:si -> Starting adress of string
; si(out) -> Ending adress of string
push ax
push bx
push cx
push dx
push di

mov di,0
divide_w:
mov dx,0
mov bx,10
div bx
add dx,30h
mov dh,0
push dx
inc di
mov cx,ax
jcxz wtoc_cmpd
jmp short divide_w
wtoc_cmpd:
mov cx,di
s7:
pop ax
mov es:[si],al
inc si
loop s7
mov byte ptr es:[si],0ah
inc si

pop di
pop dx
pop cx
pop bx
pop ax
ret
code ends
end start

想不到居然能写282行(

运行结果

编译、链接,生成p1.exe;
编译
运行结果如图所示:
运行结果
可以看到,与题目的要求完全一致。
项目完成

~~~~~~~~~~~~~~ 本文已结束 感谢您的阅读 ~~~~~~~~~~~~~~