MAIN ENDP ;-------------------------------------------------------- ;子程序1:获取键盘输入。入口参数为键盘输入,出口参数为IFERROR(表示用户输入是否有错)、 INPUT[DI](用户输入的数字)、COUNT(数组长度) GETNUM PROC PUSH AX ;保存现场 PUSH BX PUSH CX PUSH DX PUSH DI MOV DI,0 ;数组偏移地址 MOV CX,0 ;记录输入单个数字的个数 GETLOOP: MOV AH,01H ;从键盘读取一个字符 INT 21H CMP AL,0DH ;和回车比较 JE EXIT1 ;如果该字符为回车则表示用户输入结束 CMP CX,0 ;数字位数为空 JE CMP0 CMP CX,1 ;数字位数为1 JE CMP1 CMP CX,2 ;数字位数为2 JE CMP2 CMP0: CMP AL,20H ;空格的ASCII码为20H JE GETLOOP ;数字位数为0且输入空格则忽略,继续输入 JMP ACQUIRE ;不是空格就读取该字符 CMP1: CMP AL,20H JE ERR2 ;数字位数为1且输入空格,说明输入的数字位数不够,输入错误 JMP ACQUIRE ;不是空格就读取该字符 CMP2: CMP AL,20H JNE ERR4 ;数字位数为2后,不输入空格则分隔符错误 XOR CX,CX ;如果输入的数字位数已经是2,且又输入空格,说明一个 合格的数字已经输入完毕,则将数字位数清零 JMP GETLOOP ;继续循环检测键盘输入 ACQUIRE:CMP AL,30H ;单个数字与0比较大小,0的ASCII码为30H JL ERR1 ;小于0则无效字符 CMP AL,39H ;单个数字与9比较大小,9的ASCII码为39H JG ERR1 ;大于9则是无效字符 MOV INPUT[DI],AL ;保存输入的数 INC COUNT ;统计总体的用户输入的数字个数,用来判断是否输入为两位数 INC DI ;向后移动数组 INC CX ;数字位数加1 JMP GETLOOP ;一直循环直到检测到回车字符结束 ;输出错误1:用户输入的字符不是0~9之间的数字 ERR1: MOV IFERROR,1 MOV DX,OFFSET ERROR1 CALL PRINT JMP RETURN ;检测出用户输入错误后均转移到RETURN,返回主程序后返回DOS ;输出错误2:用户输入的数字个数为奇数 ERR2: MOV IFERROR,1 MOV DX,OFFSET ERROR2 CALL PRINT JMP RETURN ;输出错误3:用户没有输入数字 ERR3: MOV IFERROR,1 MOV DX,OFFSET ERROR3 CALL PRINT JMP RETURN ;输出错误4:输入数字的位数超过2 ERR4: MOV IFERROR,1 MOV DX,OFFSET ERROR4 CALL PRINT JMP RETURN ;判断输入数的个数是否为零 EXIT1: MOV BL,COUNT CMP BL,0 JE ERR3 ;输入数目为0则输出错误并退出 AND BL,01H ;判断数字是否为两位数 CMP BL,0 JNE ERR2 ;输入数字个数为奇数则出错 RETURN: POP DI POP DX POP CX POP BX POP AX RET ;恢复现场 GETNUM ENDP ;-------------------------------------------------------- ;子程序2:显示字符串。入口参数为DX。 PRINT PROC PUSH AX MOV AH,09H INT 21H POP AX RET ;显示字符串 PRINT ENDP ;-------------------------------------------------------- ;子程序3:找到最小的数字。入口参数:INPUT[DI]和COUNT ,出口参数MINNUM。 FINDMIN PROC PUSH DI PUSH AX PUSH CX MOV DI,-2 ;数组下标初始为-2 ;把数组长度移入CX ;保护现场 MOV CL,COUNT MOV AL,CL CBW ;字节扩展 MOV CX,AX ADD DI,2 ;此时数组为0,然后依次加2循环 CMP CX,DI JE EXIT2 ;是否已经比较完毕 ;如果比较完毕所有数则退出 LOP: MOV AH,INPUT[DI] ;否则接着比较,移入十位数的ASCII码 MOV AL,INPUT[DI+1];移入个位数的ASCII码 CMP AH,MINNUM ;比较十位数,MINNUM低地址为十位 JA LOP ;如果MINNUM小,则直接比较下一个数 JE LOWNUM ;如果十位数字相等,则跳转比较个位,如果都不是则顺序执行将 该最小数存储到MINNUM中 STOMIN: MOV MINNUM,AH MOV MINNUM[1],AL JMP LOP LOWNUM: CMP AL,MINNUM[1] ;比较个位数 JNB LOP ;个位数比MINNUM大则跳到下一个数 ;否则将当前数写入为MINNUM ;恢复现场 JMP STOMIN EXIT2: POP CX POP AX POP DI RET FINDMIN ENDP ;-------------------------------------------------------- CODE ENDS END MAIN
六、程序运行结果
程序运行结果如下图所示:
说明:两个十进制数之间的分隔符为空格,输入结束标志为回车。 关键设计思路:
1、用户输入的数字以ASCII码的形式存储和比较,不进行字符ASCII码到数字的转换,且每个输入的两位数用MINNUM[1]数组的方式进行存储。
2、采用子程序结构:获取输入GETNUM、屏幕显示PRINT、查找最小数FINMIN,然后通过主调函数依次调用完成实验任务。
3、比较数字大小时,采用分别比较高位ASCII码和低位ASCII码的方式,不采用输入时将字符存储为数字,输出时进行逆转换的方法,是程序效率大为提高。 4、进行严格的检错,防止非法字符的输入,4种错误如下:
? 用户输入的字符不是0~9之间的数字,提示:ERROR:Character is not valid! ? 用户没有输入数字,提示:ERROR:Number is empty! ? 输入数字的位数超过2,提示:ERROR:Need separate char!
? 检测到回车后总的数字个数为奇数,提示:ERROR:Number must contain two
digits!
判断输入错误的设计思路:对输入的数字进行了位数和范围的严格限制,即必须是两位数字,如果数字小于10,需要高位补齐0,;数字之间以空格为分割,空格的个数不限制,以回车为输入结束标志符。
七、实验总结
本次实验是微原软件的第二个实验,设计的框架为主程序调用子程序模块:获取输入GETNUM、屏幕显示PRINT、查找最小数FINMIN。在查找最小数的子程序中,排序方式比较多,最开始也是计划进行使用冒泡排序的排序方式,然而在实验过程中改变了原来的思路,汇编语言输入值的存储方式为ASCII码而非数字的大小,且个位十位分别进行存储,这使我想到可以直接采用ASCII码进行比较:先与设定的最小值(39H,39H)比较十位,再比较个位,整个过程都不用将ASCII码转换为数字,排序后再将数字转为ASCII码来显示,简化了编程的复杂度。本次程序的设计中,由于最开始分块的时候只选择了3个主要模块,而检错功能放在了获取输入GETNUM模块中,导致该模块的程序比较大,跳转比较多,调试程序的时候也很吃力,如果将检错功能单独建立一个模块,会使实验代码更加规范。
所以在编程过程中最重要的是确定明确的思路和最优的编码方式,这此实验教会了我在实验过程中要保持清醒的头脑,面对问题要善于思考。