时光地下铁

走走停停,看看风景

0%

汇编:第一个课程设计

这是王爽老师出版的《汇编语言》中第一个课程设计,做了一天多的时间。
题目描述经过我简化之后是这样的:

  • data segment中存储了年份(db),销售额(dd),和人数(dw),共21组数据;
  • table segment中初始化了21个字符串year summ ne ??,分别用来存储年份,销售额,人数,和平均销售额(销售额/人数,dw类型,商为整形,不考虑余数)
  • 将data segment中的数据,填充到table segment中,并计算出平均销售额,然后显示在屏幕上。

效果图如下:

我的思路:

  • 大的循环为21次,使用dx表示循环的次数,行号=dx+1。
  • 使用es:[bx]来对data进行寻址,使用ds:[si]来对table进行寻址。
  • 分别复制年份,销售额,人数到table segment上。
  • 计算平均销售额并填充到table segment上
  • 利用cache segment来缓存显示到屏幕上的内容。写一个函数来显示cache中的内容,再写一个函数来清空cache中的内容。
  • 年份可直接复制到cache中,显示出来。
  • 销售额,人数,平均销售额为数字,使用dtoc函数,来转换数字为字符串,并存到cache中显示出来。

以下是代码实现:

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
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
assume cs:codesg,ss:stacksg,ds:data 

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'

dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 34590,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000

dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800

data ends

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

cache segment ;cache用来缓存显示在屏幕上的字符串
db 10 dup (0)
cache ends

stacksg segment
db 80 dup (0)
stacksg ends

codesg segment
start: mov ax,stacksg
mov ss,ax
mov sp,80
mov cx,21
mov dx,0 ;dx表示行号

line: mov si,0
mov bx,0
mov ax,data
mov es,ax ;使用es:[bx]来对data进行寻址
mov ax,table
mov ds,ax ;使用ds:[si]来对table进行寻址

push cx ;将year放到table中
mov cx,4
mov ax,0
mov al,4
mul dl
mov bx,ax

mov ax,0
mov al,16
mul dl
add si,ax
year: mov al,es:[bx]
mov ds:[si],al
inc si
inc bx
loop year
pop cx

sub si,4
sub bx,4
push cx
mov cx,2
sum: mov ax,es:[bx+84] ;将销售额放到table
mov ds:[si+5],ax
add bx,2
add si,2
loop sum
pop cx

sub si,4
mov al,2
mul dl
mov bx,ax
mov ax,es:[bx+168] ;将人数放到table
mov ds:[si+10],ax

push dx
push cx
mov ax,ds:[si+5] ;计算平均数
mov dx,ds:[si+7]
div word ptr ds:[si+10]
mov ds:[si+13],ax
pop cx
pop dx

call syear ;显示年份
call ssum ;显示销售额
call speo ;显示人数
call saver ;显示平均数
inc dx
loop line

mov ax,4c00h
int 21h

syear: ;显示year
call ccache ;清理之前缓存的cache

push es ;将年复制到cache中,然后显示到屏幕中
push bx
push cx
push si
mov bx,0
mov ax,cache
mov es,ax
mov cx,4
scy: mov al,ds:[si]
mov es:[bx],al
inc si
inc bx
loop scy
pop si
pop cx
pop bx
pop es

push cx
mov cx,2
call showc
pop cx

ret

;显示销售额
ssum: call ccache
push ax
push dx
mov ax,ds:[si+5]
mov dx,ds:[si+7]

push ds
push si
push bx
mov bx,cache
mov ds,bx
mov si,0
call dtoc
pop bx
pop si
pop ds
pop dx
pop ax

push cx
mov cx,14
call showc
pop cx

ret
;显示人数
speo: call ccache
push ax
push dx
mov ax,ds:[si+10]
mov dx,0

push ds
push si
push bx
mov bx,cache
mov ds,bx
mov si,0
call dtoc
pop bx
pop si
pop ds
pop dx
pop ax
push cx
mov cx,26
call showc
pop cx

ret
;显示平均数
saver: call ccache
push ax
push dx
mov ax,ds:[si+13]
mov dx,0

push ds
push si
push bx
mov bx,cache
mov ds,bx
mov si,0
call dtoc
pop bx
pop si
pop ds
pop dx
pop ax
push cx
mov cx,38
call showc
pop cx

ret

;显示cache中的字符串,其中cl表示列号
showc: push ds
push si
push bx
push dx
push cx
mov bx,dx
inc bx ;次数加1。因为从0开始的。行数从1开始。
mov si,0
mov ax,cache
mov ds,ax
mov dh,bl
mov dl,cl
mov cl,2
call show_str
pop cx
pop dx
pop bx
pop si
pop ds
ret

;清理cache
ccache: push ds ;清理cache
push si
push cx
push ax
mov si,0
mov ax,cache
mov ds,ax
ccstart:
mov cx,ds:[si]
jcxz ccend
mov byte ptr ds:[si],0
inc si
jmp short ccstart

ccend: pop ax
pop cx
pop si
pop ds
ret

;dtoc将dword型数转变为表示十进制数的字符串,字符串以0位结尾符
dtoc: push bx
push cx
push di
push si

mov di,0

dtocl: mov cx,0ah
call divdw
mov bx,cx
mov cx,ax
add bx,30h
push bx
inc di
jcxz dtocp
jmp short dtocl

dtocp: mov cx,di
jcxz dtoce
sub di,1
pop bx
mov [si],bl
inc si
jmp short dtocp

dtoce: pop si
pop di
pop cx
pop bx
ret

;divdw进行不会产生溢出的除法运算
divdw: push bx
push ax
mov ax,dx
mov dx,0
div cx
mov bx,ax

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

pop bx
ret

show_str:
push ax
push bx
push bp
push es

mov ax,0b800h
mov es,ax

mov al,dh
mov ah,0
mov bl,0a0h
mul bl
mov bp,ax

mov ax,0
mov al,dl
mov bl,2
mul bl
add bp,ax

s: push cx
mov cl,[si]
mov ch,0
jcxz ok
pop cx
mov al,[si]
mov ah,cl
mov es:[bp],ax
inc si
add bp,2
jmp short s

ok: pop cx
pop es
pop bp
pop bx
pop ax
ret

codesg ends
end start

这是运行后的效果图:

这个和原题中效果图不一样的地方就是没有把屏幕中的其他字符给清空。清空也是可以做的,我没这么多时间,这里就写一下思路

  • 空格的ASCII码为32,即0x20
  • 根据列号和长度在屏幕上填充多个空格来覆盖其他字符
  • 在转换销售额,人数,平均销售额时,可返回当前转换后字符串的长度。根据返回的长度可算出数字字符串后应填充多少空格