本篇文章,是我们大二的汇编语言实验课的考核作业,在此记录下我的三百行代码吧hhh

1、实验目的

掌握通过键盘输入接收字符串、乘法运算、2/16 进制数与ASCII 码之间相互转换以及数据显示输出的程序设计方法。

2、实验内容

从键盘输入2个0990–99 之间的整数,对其做乘法运算,在屏幕上显示出该乘积对应的二进制和十六进制数。

3、实验要求

  1. 在屏幕提示:"please input the first number(0–99): "后,输入一个0–99 之间的整数。若所输入的数不在此范围内,或输入其它字符,则屏幕提示:"input an invalid number, input again!"
  2. 在屏幕提示:"please input the second number(0–99):"后,输入一个0–99 之间的整数。若所输入的数不在此范围内,或输入其它字符,则屏幕提示:"input an invalid number, input again!"
  3. 2 个整数做乘法运算。
  4. 在屏幕上显示出乘积所对应的二进制和十六进制数;
  5. 显示部分功能要求用子程序实现。

我的思路:

  • 通过 DOS 的第 0AH 号功能,键盘输入缓冲区,能够保证读入的数字只能在两位或一位。然后对输入的数字的高位与低位分别判断其合法性(即判断它对应的ASCII码是否在0-9),若不合法,则要求用户重新输入,直到用户输入合法的数字为止。对合法的数字ASCII码,通过位运算,将其转换为数字并存放到预先定义的存储单元中。

  • 获取到两个合法数字之后,两者进行乘法运算,得到的乘积结果存放到寄存器中。接着,分解寄存器中数字的高八位及低八位,再将其分解成高4位及低4位,依次地从数字转换为16进制所对应的ASCII码,并一位一位地输出。

  • 对于二进制输出,通过掩码,判断特定位是否为1, 然后对其进行打印。循环过程,掩码不断右移一位(缩小),相应位的数字也会打印出来,直至掩码为0,循环终止。

  • 对于十进制输出,可以将原来得到的乘积结果不断地除10运算。将得到的余数存放到堆栈,将得到的商继续地进行除10运算。直到商为0时,再从堆栈中pop出之前存放的所有余数,由此便能够得到对应数字的十进制表示。

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
datasg segment
outstr1 db 0dh,0ah,'please input the first number(0-99):',0dh,0ah,'$'
outstr2 db 0dh,0ah,'please input the second number(0-99):',0dh,0ah,'$'
outstr3 db 0dh,0ah,'input an invalid number, input again!',0dh,0ah,'$'
endstr db 0dh,0ah,'$'
instr1 db 3 ;包括回车符
db ?
db 3 dup(0)
instr2 db 3 ;包括回车符
db ? ;不包括回车符
db 3 dup(0)
a dw ? ;存放第一个操作数
b dw ? ;存放第二个操作数
nozero db 0 ;nozero为0,说明当前最高位仍是0;nozero为1,说明当前最高位不是0
datasg ends

stacks segment
dw 256 dup(?)
stacks ends

codesg segment
assume cs:codesg, ds:datasg, ss:stacks ;告诉汇编程序,段地址将要存放在哪个段寄存器中
start:
mov ax, stacks ;装填段地址
mov ss, ax
mov ax, datasg
mov ds, ax

mov ah, 09h ;输出“输入第一个操作数”的提示语句
mov dx, offset outstr1
int 21h
input1:
mov ah, 0ah ;输入操作数
mov dx, offset instr1
int 21h

mov si, offset instr1
call judgeNum ;判断是否合法数字
and dl, dl
jnz next1 ;zf=0,说明返回参数为true

mov ah, 09h
mov dx, offset outstr3 ;输出“重新输入”的提示语句
int 21h
jmp input1 ;跳转回原来的输入语句
next1: ;第一个操作数输入与第二个操作数输入的过渡部分
mov si, offset instr1
call transToNum ;ASCII转换为数字,并存到al中
mov a, ax ;将转换的数字存放到a这个存储单元

mov ah, 09h
mov dx, offset outstr2 ;输出“输入第二个操作数”的提示语句
int 21h
input2:
mov ah, 0ah ;输入第二个操作数
mov dx, offset instr2
int 21h

mov si, offset instr2
call judgeNum
and dl, dl
jnz next2

mov ah, 09h
mov dx, offset outstr3;
int 21h
jmp input2
next2:
mov si, offset instr2
call transToNum
mov b, ax;将转换的数字存放到b这个存储单元

mov ax, a
mul b ;ax=a*b

push ax ;保护ax
mov dx, offset endstr;先换行,不然会覆盖之前输入的字符
mov ah, 09h
int 21h
pop ax

push ax;暂存ax

test ax, ax ;如果ax为0,直接输出字符0即可
jnz printNormal
push ax ;保护ax
mov ah, 02h
mov dl, '0' ;直接输出0
int 21h
pop ax
jmp next3
printNormal: ;正常输出数字
mov bx, ax ;放到bx,对bx进行输出处理
mov cl, 8
shr bx, cl
call printHex ;先输出bx高8位
mov bx, ax
and bx, 00ffh
call printHex ;再输出bx低8位
next3:
mov ah, 02h
mov dl, 'H' ;打印'H',表明16进制数打印成功
int 21h
mov ah, 09h
mov dx, offset endstr ;换行
int 21h

mov nozero, 0 ;前导零重新初始化
pop ax ;恢复ax的值
push ax ;同时再次备份ax

test ax, ax
jnz printNormal2
mov ah, 02h
mov dl, '0' ;直接输出0
int 21h
jmp next4
printNormal2:
mov bx, ax
call printBin ;输出数字的二进制表示
next4:
mov ah, 02h
mov dl, 'B' ;打印'B',表明二进制数打印成功
int 21h
mov ah, 09h
mov dx, offset endstr ;换行
int 21h

pop ax
call printDec
mov ah, 02h
mov dl, 'D' ;打印'D',表示十进制数打印成功
int 21h
mov ah, 09h
mov dx, offset endstr ;换行
int 21h

mov ah, 4ch ;结束当前正在执行的程序,返回操作系统,屏幕显示系统操作符
int 21h

;将输入得到的字符分别进行合法性判断,入口参数:si(指向输入字符串的地址),返回参数:dl(表示是否合法)
judgeNum proc
add si, 2
mov dl, [si]
cmp dl, '0'
jb false
cmp dl, '9'
ja false

cmp byte ptr [si - 1], 1 ;[si-1]为实际输入的字符个数
jz then ;zf=1,说明输入的字符只有1位

mov dl, [si + 1]
cmp dl, '0'
jb false
cmp dl, '9'
ja false
mov dl, 1 ;返回参数为1
jmp then
false:
mov dl, 0 ;返回参数为0
then:
ret
judgeNum endp

;将输入得到的两个字符转换为数字,入口参数:si(指向输入字符串的地址),返回参数:al(存放转换得到的数字)
transToNum proc
push dx
mov ax, 0h
add si, 2 ;字符串中第一个数字(高位数字)
mov al, ds:[si]
and al, 0fh ;屏蔽该数字的前4位(ASCII转换为对应的数字)
cmp byte ptr [si - 1], 1
jz then2 ;若zf=1,说明输入的字符只有一位

mov dl, 10
mul dl;高位乘10
mov dl, ds:[si + 1] ;获得个位上的字符
and dl, 0fh ;ASCII转换为数字
add al, dl ;得到最终数字
then2:
pop dx
ret
transToNum endp

;直接转换法,将数字转换为ASCII码,同时打印数字,入口参数:dl(需要转换的两位数字),返回参数:无
transToASCII proc
push ax
or dl, 30h
cmp dl, 39h
jbe output
add dl, 7 ;说明当前位的数字超过9
output:
mov ah, 2
int 21h
pop ax
ret
transToASCII endp

;分解两位16进制数,同时处理前导零的输出,入口参数:bl(需要输出的两位数字),返回参数:无
printHex proc
push ax
push bx
push cx ;保护
push bx ;暂存
;先处理bl的高4位
mov dl, bl
mov cl, 4
shr dl, cl

mov bl, dl
test bl, bl
jnz goTrans ;说明当前位不为0,肯定要输出;若当前位为0,先看看是否仍为前导零
and nozero, 1
jz nextstep ;zf=0时,不需要输出此前导零
goTrans:
mov nozero, 1 ;说明当前位不属于前导零
call transToASCII ;此时需要将当前位输出
nextstep:
pop dx ;暂存的bx赋值到dx
and dx, 000fh
;将dl的前4位置零
mov bl, dl
test bl, bl
jnz goTrans2
and nozero, 1
jz nextstep2
goTrans2:
mov nozero, 1
call transToASCII
nextstep2:
pop cx
pop bx
pop ax
ret
printHex endp

;将输入的数字按二进制进行输出,入口参数:bx(需要输出的四位数字),返回参数:无
printBin proc
push ax
push cx
push dx
mov ax, 8000h ;D15置1,其余位置0
again:
test bx, ax ;判断最高位是否为0
jz moveZero
mov dl, '1'
mov nozero, 1 ;确定当前位不是前导零
jmp printNum
moveZero:
mov dl, '0'
printNum:
and nozero, 1 ;判断当前最高位是否仍存在前导零
jz thus ;说明仍有前导0,无需输出
push ax
mov ah, 02h
int 21h
pop ax
thus:
shr ax, 1 ;ax进行右移
test ax, ax
jnz again ;循环继续
pop dx
pop cx
pop ax
ret
printBin endp

;将输入的数字按十进制输出,入口参数:ax,返回参数:无
printDec proc
push bx
push cx
push dx
mov cx, 0 ;记录10进制位数
mov bx, 10D ;存放除数
mov dx, 0 ;初始化余数
goDivide:
inc cx
mov dx, 0 ;余数一定要记得清零,否则会与ax一起运算
div bx ;为了将余数存放到dx,用bx作为除数
push dx
test ax, ax
jnz goDivide
goOutput:
pop dx
or dl, 30h
mov ah, 02h
int 21h
loop goOutput

pop dx
pop cx
pop bx
ret
printDec endp
codesg ends
end start