实现功能

  • 键盘输入任意小于65536的数字,输出对应二进制、八进制、十六进制形式;
  • 自定义字体颜色、背景色以及输出在显存的位置;
  • 对输入数据执行去除前缀零、长度校验、大小校验等处理,并对累加过程中的五位数加法溢出进行过滤;
  • 数据无效或累加过程出现溢出允许用户重新输入;
  • 允许用户对合法数据进行连续累加。

源码

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
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
DATA SEGMENT
MYSTR DB 20H DUP(?) ; FFFF = 65535
NEWSTR DB 20H DUP(?) ; 待增加的数字
MSG DB 'Please input your number: $'
ADD_MSG DB 'Input the number you wanna add: $'
OUTPUT_B DB 'The Binary form is: ' ; 20 + 16(MAX)
OUTPUT_O DB 'The Octal form is: ' ; 20 + 6
OUTPUT_H DB 'The Hex form is: ' ; 20 + 4
ERROR_MSG DB 'ERROR: Your input is invalid, please enter an integer between 0 and 65535$'
AGAIN DB 'Enter Y/y to continue Or any other letter to exit:$'
ADDMORE DB 'Enter Y/y to continue to add a new number Or any other letter to exit:$'
MAX_NUM DB '65535'
RES DW 0 ; 字符串转换后的结果
NEWLINE DB 0AH, 0DH, '$'
CLEAR DB 4000 DUP(' '), '$'
FRAME_CHAR DB '*'
COLOR_MSG DB 24h ; 绿底红字
COLOR_NUM DB 4Ah ; 红底绿字高亮
COLOR_FRAME DB 1CH ; 蓝底红字高亮
DATA ENDS

STACK SEGMENT STACK
DB 20 DUP(?)
TOP LABEL WORD ; 栈顶指针
STACK ENDS

CODE SEGMENT
ASSUME CS: CODE, DS: DATA, SS: STACK
START:
MOV AX, DATA
MOV DS, AX
MOV AX, STACK
MOV SS, AX
LEA SP, TOP
INPUT:
LEA DX, MSG ; 输出提示信息
MOV AH, 09H
INT 21H
LEA DX, NEWLINE ; 换行
MOV AH, 09H
INT 21H
LEA DX, MYSTR ; 接收输入
MOV BX, DX
MOV AL, 20
MOV DS:[BX], AL ; 字符串第一个字节存储最大字符个数
MOV AH, 0AH
INT 21H
XOR CH, CH
MOV SI, DX ; 字符串存储地址
MOV CL, [SI + 1] ; 实际字符个数
ADD SI, 2 ; 指向第一个字符
READY:
CALL CLEAR_FRONT ; 消除前缀0
CALL CHECK_LEN ; 检测数据长度
CALL CHECK_FIVE_DIGITS ; 检测五位数是否大于65535
SUB CL, 1 ; 循环LEN - 1次
XOR DX, DX ; 存放和
CMP CL, 0
JZ LAST ; 一位数不需要乘, 会导致CX = 0FFH
STR_TO_NUM:
MOV BL, [SI]
INC SI
SUB BL, 30H ; 不减先加可能会对CF造成影响
CALL CHECK_NUM ; 检测每一位数是否合规
ADD DL, BL
ADC DX, 0 ; 进位
MOV AX, 0AH
MUL DX
MOV DX, AX
LOOP STR_TO_NUM
LAST:
MOV BL, [SI]
SUB BL, 30H ; 不减先加可能会对CF造成影响
CALL CHECK_NUM ; 检测每一位数是否合规
MOV AL, [SI]
SUB AL, 30H
ADD DL, AL ; 最后一个数不用乘
ADC DX, 0
MOV RES, DX
CLEAN:
LEA DX, CLEAR ; 清空整页
MOV AH, 09H
INT 21H
FRAME_UP:
MOV AX, 0B866H ; 跳过前10行,跳过21个字符(80 - 36 - 2 >> 1)
MOV ES, AX
LEA SI, FRAME_CHAR
MOV DI, 0AH ; 偏移地址
MOV CX, 38 ; 38个'*'
PAINT_FRAME_UP:
MOV AL, [SI]
MOV ES:[DI], AL
INC DI
MOV AL, COLOR_FRAME
MOV ES:[DI], AL
INC DI
LOOP PAINT_FRAME_UP
FRAME_DOWN:
MOV AX, ES
ADD AX, 28H ; 加四行,段地址需要除以16
MOV ES, AX
MOV DI, 0AH
MOV CX, 38
PAINT_FRAME_DOWN:
MOV AL, [SI]
MOV ES:[DI], AL
INC DI
MOV AL, COLOR_FRAME
MOV ES:[DI], AL
INC DI
LOOP PAINT_FRAME_DOWN
FRAME_SIDE:
MOV AX, 0B870H
MOV ES, AX
MOV DI, 0AH
LEA SI, FRAME_CHAR
MOV CX, 3 ; 3行
PAINT_FRAME_SIDE:
PUSH CX
PUSH DI
PUSH AX
MOV CX, 2
MOV ES, AX
PAINT_ONE_LINE:
MOV AL, [SI]
MOV ES:[DI], AL
INC DI
MOV AL, COLOR_FRAME
MOV ES:[DI], AL
ADD DI, 72 ; 跳过36个字符
INC DI
LOOP PAINT_ONE_LINE
POP AX
ADD AX, 0AH ; 0A * 16 = 160
POP DI
POP CX
LOOP PAINT_FRAME_SIDE
MOV AX, 0B870H ; 段地址,跳过前11行,跳过22个字符
MOV ES, AX
LEA SI, OUTPUT_B
MOV DI, 0CH
MOV CX, 20
COLORFUL_OUTPUT_B: ; 输出二进制提示信息
MOV AL, [SI]
MOV ES:[DI], AL
INC DI
MOV AL, COLOR_MSG
MOV ES:[DI], AL
INC SI
INC DI
LOOP COLORFUL_OUTPUT_B
MOV CX, 16
MOV BX, RES
PRINT_B:
ROL BX, 1 ; 循环左移一位,将最高位移到最低位
MOV AX, BX
AND AL, 01H ; 将除了最低位的其他位置清零
ADD AL, 30H
MOV ES:[DI], AL
INC DI
MOV AL, COLOR_NUM
MOV ES:[DI], AL
INC DI
LOOP PRINT_B
CLC ; CF清零
MOV AX, 0B87AH ; 段地址,跳过前12行,跳过22个字符
MOV ES, AX
LEA SI, OUTPUT_O
MOV DI, 0CH
MOV CX, 20
COLORFUL_OUTPUT_O: ; 输出八进制提示信息
MOV AL, [SI]
MOV ES:[DI], AL
INC DI
MOV AL, COLOR_MSG
MOV ES:[DI], AL
INC SI
INC DI
LOOP COLORFUL_OUTPUT_O
MOV AX, RES ; 第一个八进制
ROL AX, 1
CLC ; CF清零
AND AL, 01H
ADD AL, 30H
MOV ES:[DI], AL
INC DI
MOV AL, COLOR_NUM
MOV ES:[DI], AL
INC DI
MOV CX, 5
MOV BL, 0 ; 4,7,10,13,16
PRINT_O:
PUSH CX
MOV AX, RES ; 第二三四五六对应八进制
MOV CL, 4
ADD CL, BL
ROL AX, CL
ADD BL, 3
AND AL, 00000111B
ADD AL, 30H
MOV ES:[DI], AL
INC DI
MOV AL, COLOR_NUM
MOV ES:[DI], AL
INC DI
POP CX
LOOP PRINT_O
CLC ; CF清零
MOV AX, 0B884H ; 段地址,跳过前13行,跳过22个字符
MOV ES, AX
LEA SI, OUTPUT_H
MOV DI, 0CH
MOV CX, 20
COLORFUL_OUTPUT_H: ; 输出十六进制提示信息
MOV AL, [SI]
MOV ES:[DI], AL
INC DI
MOV AL, COLOR_MSG
MOV ES:[DI], AL
INC SI
INC DI
LOOP COLORFUL_OUTPUT_H
MOV CX, 4
MOV BL, 4 ; 步幅
PRINT_H:
PUSH CX
MOV DX, RES
MOV CL, BL
ADD BL, 4
ROL DX, CL
CLC ; CF清零
AND DL, 00001111B
CALL CONVERT
MOV ES:[DI], DL
INC DI
MOV DL, COLOR_NUM
MOV ES:[DI], DL
INC DI
POP CX
LOOP PRINT_H
ADDAGIN:
LEA DX, ADDMORE
MOV AH, 09H
INT 21H
LEA DX, NEWLINE ; 换行
MOV AH, 09H
INT 21H
MOV AH, 01H
INT 21H
CMP AL, 'Y' ; 比较输入之前换行会修改AL
JZ INPUT_AGAIN
CMP AL, 'y'
JZ INPUT_AGAIN
OVER:
LEA DX, NEWLINE ; 换行
MOV AH, 09H
INT 21H
MOV AH, 4CH
INT 21H
INPUT_AGAIN:
LEA DX, NEWLINE ; 输入Y/y跳转之后再换行
MOV AH, 09H
INT 21H
LEA DX, ADD_MSG
MOV AH, 09H
INT 21H
LEA DX, NEWLINE ; 换行
MOV AH, 09H
INT 21H
LEA DX, NEWSTR
MOV BX, DX
MOV AL, 20 ; 设置最大字符数
MOV DS:[BX], AL
MOV AH, 0AH
INT 21H
MOV SI, DX ; 指向新输入的字符串
MOV CL, [SI + 1] ; 新输入的字符串长度
ADD SI, 2 ; 指向第一个字符
CALL CLEAR_FRONT_NEW ; 清除新字符串的前缀零
ADD_NEW_TO_FIVE:
CMP CL, 05H ; 比较新字符串是否是五位数
JZ ADD_OLD_TO_FIVE
JA ERROR
PUSH SI ; 存储新字符串起始位置
PUSH SI
ADD SI, 4
MOV DI, SI ; DI指向五位数最后一位
POP SI
XOR CH, CH
PUSH CX
DEC CX
ADD SI, CX ; SI指向当前字符串最后一位
POP CX
XOR CH, CH
PUSH CX
MOVE:
MOV AL, [SI]
MOV [DI], AL ; 依次后移
DEC SI
DEC DI
LOOP MOVE
POP CX
POP SI
PUSH SI
MOV AL, 5
SUB AL, CL ; AL = 5 - CL -> 补零个数
MOV CL, AL ; 剩余位置补零
XOR CH, CH
ADD_ZERO:
MOV BYTE PTR [SI], 30H
INC SI
LOOP ADD_ZERO
POP SI ; 新字符串的起始位置已经在CLEAR_FRONT中存入了最大长度字段
MOV CL, 05H ; 最终长度为5
LEA DI, NEWSTR
MOV [DI + 1], CL ; 实际长度修改为5
ADD_OLD_TO_FIVE:
LEA SI, MYSTR
MOV CL, [SI + 1] ; 取出旧字符串实际长度
CMP CL, 05H
JZ ADD_NEW_TO_OLD
MOV SI, DS:[SI] ; SI指向旧字符串第一个字符
AND SI, 0FFH ; 高位字节清零
PUSH SI
PUSH SI
ADD SI, 4
MOV DI, SI ; DI指向五位数最后一位
POP SI
PUSH CX
DEC CL
XOR CH, CH
ADD SI, CX ; SI指向当前字符串最后一位
POP CX
XOR CH, CH
PUSH CX
MOVE_OLD:
MOV AL, [SI]
MOV [DI], AL ; 依次后移
DEC SI
DEC DI
LOOP MOVE_OLD
POP CX
POP SI
PUSH SI
XOR CH, CH
MOV AL, 5
SUB AL, CL
MOV CL, AL ; 剩余位置补零
ADD_OLD_ZERO:
MOV BYTE PTR [SI], 30H
INC SI
LOOP ADD_OLD_ZERO
POP SI
MOV CL, 05H ; 最终长度为5
LEA DI, MYSTR
MOV [DI + 1], CL ; 实际长度修改为5
ADD_NEW_TO_OLD:
LEA SI, NEWSTR
MOV SI, [SI] ; 指向第一个字符
AND SI, 0FFH
LEA DI, MYSTR
MOV DI, [DI] ; 指向第一个字符
AND DI, 0FFH
ADD SI, 4 ; SI指向新字符串第五位字符
ADD DI, 4 ; DI指向旧字符串第五位字符
MOV CX, 05H
PUSH SI
SUB_NEW_TO_NUM: ; 避免因为新旧字符串都有30H导致对CF造成影响
SUB BYTE PTR [SI], 30H ; 如果是WORD PTR, 30H高位为0,也没有影响
DEC SI
LOOP SUB_NEW_TO_NUM
POP SI
CMP BYTE PTR [SI-4], 6 ; 判断五位数第一个数字是否合法,只要保证合法就不会发生五位数溢出
JA ERROR
MOV CX, 05H
ADD_NEW_AND_OLD:
MOV AL, [SI]
ADD [DI], AL
MOV AL, [DI]
CMP AL, 39H
JA ABOVE_NINE
NEXT:
DEC SI
DEC DI
LOOP ADD_NEW_AND_OLD
XOR CH, CH
LEA SI, MYSTR ; 字符串存储地址
MOV CL, [SI + 1] ; 实际字符个数
MOV SI, [SI] ; 指向第一个字符
AND SI, 0FFH
JMP READY
ABOVE_NINE: ; 超出39,该位减十,高位加一
SUB AL, 0AH
MOV [DI], AL
MOV BX, DI
DEC BX
INC BYTE PTR [BX]
JMP NEXT
CONVERT:
CMP DL, 0AH
JB BELOW
ADD DL, 07H
BELOW:
ADD DL, 30H
RET
CLEAR_FRONT:
CMP CL, 01H
JZ FINISH
MOV AL, [SI]
CMP AL, 30H ; '0'
JNE FINISH
INC SI ; 跳过当前字符
DEC CL ; 长度减一
JMP CLEAR_FRONT
FINISH:
LEA DI, MYSTR
MOV [DI], SI ; 将第一个字符的位置存储到字符串的最大容量字段
MOV [DI + 1], CL ; 将修改后的长度填入字符串实际长度字段
RET
CLEAR_FRONT_NEW:
CMP CL, 01H
JZ FINISH_NEW
MOV AL, [SI]
CMP AL, 30H ; '0'
JNE FINISH_NEW
INC SI ; 跳过当前字符
DEC CL ; 长度减一
JMP CLEAR_FRONT_NEW
FINISH_NEW:
LEA DI, NEWSTR
MOV [DI], SI ; 将第一个字符的位置存储到字符串的最大容量字段
MOV [DI + 1], CL ; 将修改后的长度填入字符串实际长度字段
RET
CHECK_LEN:
CMP CL, 05H ; 最大65535,5位
JA ERROR
RET
CHECK_FIVE_DIGITS:
PUSH CX ; 数据长度, PUSH至少是WORD
PUSH SI ; 字符串指针
CMP CX, 05H
JB CHECK_FIVE_FINISH ; 不到五位不用比较
LEA DI, MAX_NUM
MOV CX, 05H
CHECK_FIVE:
MOV AL, [DI]
CMP AL, [SI]
JB ERROR
JA CHECK_FIVE_FINISH
INC DI
INC SI
LOOP CHECK_FIVE
CHECK_FIVE_FINISH:
POP SI
POP CX
RET
CHECK_NUM:
CMP BL, 09H ; 保证每位数字在0~9
JA ERROR
RET
ERROR:
LEA DX, NEWLINE ; 换行, 保留用户输入
MOV AH, 09H
INT 21H
LEA DX, ERROR_MSG ; 错误提示
MOV AH, 09H
INT 21H
LEA DX, NEWLINE ; 换行
MOV AH, 09H
INT 21H
LEA DX, AGAIN
MOV AH, 09H
INT 21H
LEA DX, NEWLINE ; 换行
MOV AH, 09H
INT 21H
MOV AH, 01H
INT 21H
CMP AL, 'Y'
JZ RESTART
CMP AL, 'y'
JZ RESTART
LEA DX, NEWLINE ; 换行
MOV AH, 09H
INT 21H
JMP OVER
RESTART:
LEA DX, CLEAR ; 清空整页
MOV AH, 09H
INT 21H
JMP INPUT
CODE ENDS
END START

运行截图

① 输入合法数据:3456

img

② 在屏幕中间输出进制转换结果并提示选择累加或退出

wps2

③ 输入Y选择累加,输入累加数据:00003456

wps3

④ 输出相加后的转换结果并提示选择累加或退出

wps4

⑤ 输入累加数据:65535,触发溢出,提示用户选择重新开始或退出wps5

⑥ 测试非法字符:A2134

wps6

⑦ 测试非法数值:65536

wps7

⑧ 测试非法长度:123456

img

⑨ 测试五位数累加溢出:先输入数据65535(带前缀0也可以),再输入可以造成溢出的数据99000,检测到溢出wps9

wps10

⑩ 输入Y和y之外的任意字符退出程序

img

常用功能

01号功能

  • 功能:字符输入
  • 出口:AL

02号功能

  • 功能:字符输出
  • 入口:DL

09号功能

  • 功能:字符串输出
  • 入口:DX

0A号功能

  • 功能:字符串输入
  • 出口:DX
  • 注意:DX指向字符串地址,需要提前初始化(主要是初始化字符串的第一个字节,这个字节代表了该字符串可以容纳字符的个数,设置为FF表示无限制存储),第一位是最大字符数,第二位是实际字符数,第三位开始是真正的字符串

反思

  • 初始化栈段时要记得初始化栈顶指针SP
  • 使用0A号功能接受字符串要注意提前设置最大容量
  • 换行会导致寄存器被修改,例如刚刚使用01号功能接收字符就使用换行会导致AL被修改丢失数据
  • 使用内存操作数养成习惯规定PTR,避免使用字类型寄存器接收字节内存数据导致数据出错

拓展练习

  • 累加过程中提示用户当前可输入的最大值
  • 累加发生溢出允许用户撤销此次输入并回退到上一个合法输出