项目情景
任务:将实验7中的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
4text 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
45printf:
; 名称: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
43wtoc:
; 名称: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 | divdw: |
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
45dtoc:
; 名称: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
4stack segment
db 64 dup (0)
; 64字节的超大栈段
stack ends
定义代码段code
:1
2
3
4
5code segment
start:
...
code ends
end start
初始化段寄存器:1
2
3
4
5
6
7mov 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
8mov 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
21mov 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
18mov 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
12mov 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
16mov 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
2mov 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
;
运行结果如图所示:
可以看到,与题目的要求完全一致。
项目完成