汇编(从键盘接收若干个 N 位的十进制数值(0~65535),并以二进制、十进制、十六进制三种数制形式显示其和。)


题目

从键盘接收若干个 N 位的十进制数值(0~65535),并以二进制、十进制、十六进制三种数制形式显示其和。
要求:
(1)用子程序实现一个 N 位十进制数值的输入,在主程序的循环结构
中调用该子程序;
(2)当用户未输入数值,直接回车时,结束输入;
(3)输出的数据为多位十进制数据,而机器内部计算的和是十六进制形式,需要进行数制转换,然后以十进制字符串的形式输出结果;
(4)程序中要求有必要的提示信息。


运行示例

在这里插入图片描述


代码

DATA SEGMENT
	STR1 DB "Please input a number: $"
	STR2 DB "The sum is: $"
	CRLF DB 0DH,0AH,'$'   ;换行
	COUNT DW 0   ;保存所有输入的真实和
	DIVNUM DW 10  
	DIVNUM1 DW 16
	MULNUM DW 10
	RESULT DB 5 DUP(?)
	RESULT1 DB 5 DUP(?)
	KONGGE DB 32,32,32,32,32,32,32,32,32,32,32,32,'$' ;输出多个空格,对齐时使用
	TEMP DW ?
	ARRAY DB '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' ;用于输出十六进制形式时,用下标进行对照
DATA ENDS
CODE SEGMENT 
ASSUME CS:CODE,DS:DATA
START:
	MOV AX,DATA
	MOV DS,AX
	
LOOP1:
	CALL GET
	CMP CX,0
	JZ ENDINPUT
	ADD COUNT,BX
	JMP LOOP1
	
ENDINPUT:
	LEA DX,CRLF
	MOV AH,9
	INT 21H
	
	LEA DX,STR2
	MOV AH,9
	INT 21H

TWO:
	MOV BX,COUNT
	MOV TEMP,0
LOOPTWO:
	CMP TEMP,16
	JZ TEN
	SAL BX,1
	JNC OUT0
	MOV AH,02H
	MOV DL,'1'
	INT 21H
	ADD TEMP,1
	JMP LOOPTWO
	
OUT0:
	MOV AH,02H
	MOV DL,'0'
	INT 21H
	ADD TEMP,1
	JMP LOOPTWO

	
TEN:
	MOV AH,02H
	MOV DL,'B'
	INT 21H
	
	LEA DX,CRLF
	MOV AH,9
	INT 21H
	
	LEA DX,KONGGE
	MOV AH,9
	INT 21H
	
	MOV AX,COUNT
	MOV SI,0
	MOV DX,0
LOOP3:
	DIV DIVNUM; AX...DX
	MOV RESULT[SI],DL   ;因为余数小于10,所以DH=0
	CMP AX,0
	JZ NEXT
	MOV DX,0
	ADD SI,1
	JMP LOOP3
	
NEXT:	
	ADD SI,1
	MOV CX,SI
	SUB SI,1

LOOP4:
	ADD RESULT[SI],30H
	MOV AH,02H
	MOV DL,RESULT[SI]
	INT 21H
	SUB SI,1
	LOOP LOOP4
	
	
	MOV AH,02H
	MOV DL,'D'
	INT 21H
	
	LEA DX,CRLF
	MOV AH,9
	INT 21H
	
	LEA DX,KONGGE
	MOV AH,9
	INT 21H
	
	
SIXTEEN:	
	MOV AX,COUNT
	MOV CX,4
	MOV SI,0
	MOV DX,0
LOOPM:
	DIV DIVNUM1; AX....DX
	MOV RESULT1[SI],DL   ;虽说余数放在DX里,但是余数不会超过十五,所以DH为0
	CMP AX,0
	JZ T
	MOV DX,0
	ADD SI,1
	LOOP LOOPM
T:	
	ADD SI,1
	MOV CX,SI
	SUB SI,1
	
LOOP5:
	MOV BL,RESULT1[SI]
	MOV BH,0
	MOV DI,BX
	
	MOV AH,02H
	MOV DL,ARRAY[DI]
	INT 21H
	SUB SI,1
	LOOP LOOP5
	

	MOV AH,02H
	MOV DL,'H'
	INT 21H
	
	LEA DX,CRLF
	MOV AH,9
	INT 21H
	
	
	MOV AX,4C00H
	INT 21H
	
GET PROC ;无输入值,无输出,但是将结果存放在BX中
	LEA DX,STR1
	MOV AH,9
	INT 21H
	
	MOV CX,0;CX用于计数,用于判断这次输入是不是只输入了回车
	MOV BX,0;用BX保存输入的数字最后的真实值
LOOPT:
	MOV AH,1
	INT 21H
	
	CMP AL,0DH
	JZ OVER
	
	MOV CX,1
	MOV AH,0
	SUB AL,30H
	ADD AX,BX
	MUL MULNUM;DW类型和DW类型相乘,高十六位放在DX中的,第十六位放在AX
	MOV BX,AX
	JMP LOOPT
	
OVER:
	MOV AX,BX
	DIV DIVNUM
	MOV BX,AX
	RET
GET ENDP
		
CODE ENDS
END START

解释

1、输入部分

LOOP1:
	CALL GET
	CMP CX,0
	JZ ENDINPUT
	ADD COUNT,BX ;将每次真实的NUM添加到COUNT里
	JMP LOOP1
	
ENDINPUT:
	LEA DX,CRLF;换行
	MOV AH,9
	INT 21H
	
	LEA DX,STR2;输出The sum is:
	MOV AH,9
	INT 21H
GET PROC ;无输入值,无输出,但是将结果存放在BX中
	LEA DX,STR1
	MOV AH,9
	INT 21H
	
	MOV CX,0;CX用于计数,用于判断这次输入是不是只输入了回车
	MOV BX,0;用BX保存输入的数字最后的真实值
LOOPT:
	MOV AH,1
	INT 21H
	
	CMP AL,0DH
	JZ OVER
	
	MOV CX,1
	MOV AH,0
	SUB AL,30H
	ADD AX,BX
	MUL MULNUM;DW类型和DW类型相乘,高十六位放在DX中的,第十六位放在AX
	MOV BX,AX
	JMP LOOPT
	
OVER:
	MOV AX,BX
	DIV DIVNUM
	MOV BX,AX
	RET
GET ENDP

整体思想

这一题和上一题不一样的地方就是现在输入的不是单个十进制(0~9)的数
这里我用的是接收用户输入的一个一个字符,比如输入6552,接收第一个字符6,把6乘10,放在一个寄存器BX中;再接收5,将BX+5,那么现在BX中的值就是65了,再将BX乘10(这里的乘10及后面的乘都借助AX);再接收5,将BX+5,那么现在BX中的值就是655了,再将BX乘10;再接收2,将BX+2,现在BX为6552,将BX乘10,
因为我是

MOV AX,BX
MUL MULNUM  
MOV BX,AX

(如果乘10之后没有超过65535的话,就用不到DX),现在DX:AX中的值是65520 。现在已经接收完了,用户输入回车,退出输入字符的循环,将DX:AX除10即可

逐步解释

LOOP1:
	CALL GET
	CMP CX,0
	JZ ENDINPUT
	ADD COUNT,BX
	JMP LOOP1

首先一个循环CALL GET
看看GET子程序

	LEA DX,STR1
	MOV AH,9
	INT 21H
	
	MOV CX,0;CX用于计数,用于判断这次输入是不是只输入了回车
	MOV BX,0;用BX保存输入的数字最后的真实值

输出STR1,这里我用CX来作为判断,因为用户只输入一个回车的话代表用户结束了整个输入部分,如果用户输入6553再输入回车,代表只是结束这一次输入,会继续进行下一个数的输入。所以我这里就用CX判断回车之前有没有输入别的数

LOOPT:
	MOV AH,1 ;用户输入字符
	INT 21H
	
	CMP AL,0DH ;判断是不是回车,如果是回车则退出循环,这是退出循环的唯一条件
	JZ OVER
	
	MOV CX,1 ;不是回车,证明现在已经有数字的输入了,所以CX为1,下一次用户还会再进行输入一个完整的NUM
	MOV AH,0
	SUB AL,30H ;AX保存输入字符的真实值
	ADD AX,BX ;
	MUL MULNUM;DW类型和DW类型相乘,高十六位放在DX中的,第十六位放在AX
	MOV BX,AX
	JMP LOOPT
	
OVER:
	MOV AX,BX
	DIV DIVNUM
	MOV BX,AX

看注释,和刚刚的整体思想中已经说清楚了


2、输出十六进制

以输出十六进制为例
其实输出部分都蛮简单的

SIXTEEN:	
	MOV AX,COUNT;将和赋给AX
	MOV CX,4;让CX为4作为循环次数,因为是16位的,转换成十六进制就是4位
	MOV SI,0 ;用做变址寄存器,作为指针
	MOV DX,0 
LOOPM:
	DIV DIVNUM1; AX....DX
	MOV RESULT1[SI],DL   ;虽说余数放在DX里,但是余数不会超过十五,所以DH为0
	CMP AX,0
	JZ T
	MOV DX,0
	ADD SI,1
	LOOP LOOPM
T:	
	ADD SI,1
	MOV CX,SI
	SUB SI,1
	
LOOP5:
	MOV BL,RESULT1[SI]
	MOV BH,0
	MOV DI,BX
	
	MOV AH,02H
	MOV DL,ARRAY[DI]
	INT 21H
	SUB SI,1
	LOOP LOOP5
	

	MOV AH,02H
	MOV DL,'H'
	INT 21H

逐步解释

LOOPM:
	DIV DIVNUM1; AX....DX
	MOV RESULT1[SI],DL   ;虽说余数放在DX里,但是余数不会超过十五,所以DH为0
	CMP AX,0
	JZ T
	MOV DX,0
	ADD SI,1
	LOOP LOOPM

DIV DIVNUM1时候要注意的,除数是DX类型的时候,被除数默认为DX:AX,商放在AX,余数放在DX中;除数是DB类型的时候,被除数默认为AX,商放在AL,余数放在AH。那么这里为什么要让除数是DW类型的呢,因为比如6552除10之后,商是655,余数是2,AL是8位,655超出范围了,这就会出现错误了。所以用DW类型的除数
循环取余,放在一个数组里面
如果AX(AX保存的是商)为0,证明已经全部放完了,没有了,即可退出循环了,到输出十六进制的部分了。
注意每次循环末尾要MOV DX,0 ,因为DX会做一会被除数的高位,而刚才余数也是放在DX里面的

T:	
	ADD SI,1
	MOV CX,SI ;CX作为循环次数,等于数组最后一个数的下标加1
	SUB SI,1
	
LOOP5:
	MOV BL,RESULT1[SI]
	MOV BH,0
	MOV DI,BX
	
	MOV AH,02H
	MOV DL,ARRAY[DI]
	INT 21H
	SUB SI,1
	LOOP LOOP5
	

	MOV AH,02H
	MOV DL,'H'
	INT 21H

逆序输出即可,无难点


Bug

在这里插入图片描述
输入一个字符之后没法删除重新输入,输入的时候,比如我这个数输错了,554我输成555了,我没法进行修改。

拟解决方案

用字符串来承接用户输入的数,比如用户输入6553,我就定义一个字符串,把用户的输入的65535放进去,再调用一个子程序,把这个字符串转换成真正的数

只有输入部分发生了改变

MOV COUNT,0
LOOP1:
	CALL INPUT   ;这个问题非常关键,就是在这里我有一点问题就是不确定在正常输入的时候,按下回车,回车会不会保存到NUM里面
	MOV SI,2
	CMP NUM[SI],0DH ;回车会被保存在NUM数组的第三个字节位置
	JZ ENDINPUT
	MOV SI,1
	MOV CX,word ptr NUM[SI] ;将NUM的真实长度送个CX,作为循环次数
	SUB CX,1  ;因为真实长度还有除去回车
	
	ADD CX,1
	MOV SI,CX   ;这三行是为了,把SI定位对NUM数组的倒数第二个位置(最后一个是回车)
	SUB CX,1
	 
	MOV BX,0
	CALL TURN   ;将字符串NUM转换成真正有意义的NUM
	ADD COUNT,BX
	
	LEA DX,CRLF
	MOV AH,9
	INT 21H
	JMP LOOP1
INPUT PROC
	LEA DX,STR1
	MOV AH,9
	INT 21H
	
	LEA DX,NUM
	MOV AH,0AH
	INT 21H
	
	RET
INPUT ENDP

TURN PROC
	PUSH AX
	
	MOV TEMP,1
	
	;我想用BX来暂时NUM数组中的真实数据
LOOPN:
	MOV AL,NUM[SI]
	SUB AL,30H
	MOV AH,0
	MUL TEMP ;AX*TEMP(DW)->DX:AX    因为我知道COUNT最大容量为16位,所以必然用不到DX
	ADD BX,AX
	SUB SI,1
	
	MOV AX,TEMP
	MUL MULNUM
	MOV TEMP,AX
	LOOP LOOPN

	
	POP AX
	RET

这个办法也基本上能成功的,但就是有一个问题没有得到解决,就是我每次输入字符串,我在DATA SEGMENT里面只能定义一个字符串,那么下一次输入会覆盖这个字符串。如果这次输入的字符串,比上次的短,那么字符串保存的真实长度就不准确,且会多出上一个字符串没有被覆盖的部分。
所以总的来说,就是如何将字符串置空的问题。


如果有错误,欢迎指正啊,感谢观看!!!

  • 23
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

开心星人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值