题目
我选择了题目二。关于题目二的理解我放在了代码的注释里面。
在此基础上我额外实现了:
1 用英文逗号充当数据间分隔符
2 两个数据间可以存在多个英文逗号
3 按回车键结束数据的输入
算法逻辑
输入:
输出:
代码
;以无符号十进制形式输入一组字数据(数据个数不超过100个),
;使用冒泡排序法将输入数据升序排序,分别显示排序前后的数据系列。
;输入、排序、输出都在子程序进行,除主程序外,所有子程序都
;不能直接访问变量。
DATAS SEGMENT
ARR DW 100 DUP(0) ;可以存储100个元素的字数组
LEN DW 0 ;数组ARR的实际元素个数
;以下字符串用于提示
INPUT1 DB "Now you can enter a total of less than 100 datas.",0DH,0AH,
"(Tips:1 Each data should be separated by a comma! ",0DH,0AH,
" 2 Enter a carriage return to finish your input! )",0DH,0AH,
"Enter: ",24H
INPUT2 DB 0DH,0AH,"retype please!",0DH,0AH,"Enter: ",24H
BFS DB 0DH,0AH,'Before sorting: ',24H
AFS DB 0DH,0AH,'After sorted: ',24H
DATAS ENDS
STACKS SEGMENT
DW 30H DUP(0)
TOP LABEL WORD
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS,SS:STACKS
MAIN PROC FAR
MOV AX,DATAS
MOV DS,AX
MOV AX,STACKS
MOV SS,AX
MOV SP,TOP
;输入
LEA DI,ARR
MOV SI,LEN
LEA DX,INPUT1
LEA CX,INPUT2;以上都是给输入子模块传递参数。主模块中寄存器都是用来给子模块传递参数的,所以就不必保存当前寄存器的状态了。
;;输入算法;
;;参数说明: 1 DI,第一个形参,负责接收字数组首地址ARR
;2 SI,第二个形参(也是局部变量),
;负责接收字数组长度LEN,并返回LEN的值
;3 DX,第三个形参,负责接收提示语句首地址INPUT1
;4 CX,第四个形参,接收提示错误语句首地址INPUT2
CALL INPUT
;;输入算法;
MOV LEN,SI ;!!!记得退出子模块后要把SI的值保存到LEN
;显示输入结果
LEA DI,ARR
MOV SI,LEN
LEA DX,BFS
;;输出算法;
;;参数说明: 1 DI,第一个形参,接收字数组首地址ARR
;2 SI,第二个形参,接收字数组的元素个数LEN
;3 DX,第三个形参,接收提示语句的首地址AFS或BFS
CALL PRINT
;;输出算法;
;升序排序
LEA DI,ARR
MOV SI,LEN
;;冒泡排序算法核心;
;;参数说明: 1 DI,第一个形参,接收字数组的首地址ARR
;2 SI,第二个形参,接收字数组的长度LEN
CALL SORT
;;冒泡排序算法核心;
;排序后再显示结果
LEA DI,ARR
MOV SI,LEN
LEA DX,AFS
CALL PRINT
MOV AH,4CH
INT 21H
MAIN ENDP
INPUT PROC NEAR
;;输入算法核心;
;;参数说明: 1 DI,第一个形参,负责接收字数组首地址ARR
;2 SI,第二个形参(也是局部变量),
;负责接收字数组长度LEN,并返回LEN的值
;3 DX,第三个形参,负责接收提示语句首地址INPUT1
;4 CX,第四个形参,接收提示错误语句首地址INPUT2
;5 BX,局部变量,访问数组ARR的指针
;6 BP,局部变量,存储该次输入的数值
MOV BP,0
MOV AH,9
INT 21H ;打印提示语句
JMP ENTER1
;输入有误(数据溢出或输入的数据个数超过100—越界),提示重新输入
AGAIN:
MOV SI,0 ;之前输入的数据全部丢弃,重置数组长度
MOV BX,DI ;重置访问指针
MOV BP,0 ;重置先前接收的数字
MOV DX,CX
MOV AH,9
INT 21H
;开始输入字符
ENTER1:
MOV AH,1
INT 21H
;单个数字合法性
CMP AL,0DH
JE EXIT ;结束全部输入
CMP AL,','
JNE CONTINUE1 ;即不是回车又不是分隔符
;是分隔符,接下来看看前一个字符是不是数字
CMP BP,0
JE ENTER1 ;当前分隔符前面不是数字
JMP NEXT1 ;当前分隔符前面是数字
CONTINUE1:
CMP AL,'0'
JB AGAIN
CMP AL,'9'
JA AGAIN
SUB AL,30H ;转数字
AND AX,0FH ;一个数字字符对应的数字用4位就足够表示了
XCHG AX,BP ;为乘法做准备(把这一轮已经输入的数值换入AX)
MOV DX,10
MUL DX
JC AGAIN
ADD BP,AX ;把这一次输入的数字加到之前输入的数上面
JC AGAIN
LOOP ENTER1
;结束当前这个数据的输入
NEXT1:
MOV [BX],BP
ADD BX,2
MOV BP,0 ;重置为0
ADD SI,1 ;数组中元素个数加1
JMP ENTER1
;结束全部数据的输入
EXIT:
CMP BP,1
JL FINISH1
MOV [BX],BP
ADD SI,1 ;保证程序在输入数字后立刻按回车的情况下不会错误
FINISH1:
RET
;;输入算法核心;
INPUT ENDP
SORT PROC NEAR
;;冒泡排序算法核心;
;;参数说明: 1 DI,第一个形参,接收字数组的首地址ARR
;2 SI,第二个形参,接收字数组的长度LEN
;3 CX,外循环局部变量,代表还需要比较的趟数
;4 CX,内循环局部变量,代表该趟需要比较的次数
;5 BP,局部变量,外循环指针i
;6 BX,局部变量,内循环指针j
CMP SI,1
JBE FINISH2 ;数组中元素个数小于或等于1,就没必要排序了
MOV CX,SI
SUB CX,1 ;外循环结束变量
MOV BP,0 ;外循环指针i初始化为0
LOOP1:
PUSH CX
;往下可省略
MOV CX,SI
SUB CX,1
SUB CX,BP ;内循环结束变量--这里可以优化!可以直接
;用新一轮外循环的CX的值充当内循环的结束变量!
;这样可以省略BP相关的两行代码和该注释往上三行代码
MOV BX,DI ;内循环指针j重置
LOOP2:
MOV AX,[BX+2]
CMP [BX],AX
JBE NEXT2
XCHG [BX],AX ;升序,前面的数大于后面的数则交换两数位置
MOV [BX+2],AX
NEXT2:
ADD BX,2
LOOP LOOP2
POP CX
ADD BP,1 ;易错!这个外循环指针每次只能加1!
LOOP LOOP1
FINISH2:
RET
;;冒泡排序算法核心;
SORT ENDP
PRINT PROC NEAR
;;输出算法核心;
;;参数说明: 1 DI,第一个形参,接收字数组首地址ARR
;2 SI,第二个形参,接收字数组的元素个数LEN
;3 DX,第三个形参,接收提示语句的首地址AFS或BFS
;4 BP,局部变量,代表数组ARR每个数字的位数
;5 BX,局部变量,代表遍历数组ARR的指针
CMP SI,0
JE FINISH3 ;数组为空则直接返回
MOV AH,9
INT 21H ;打印提示语句
MOV CX,SI
MOV BX,DI
TRAVEL2:
MOV DI,CX ;保存LEN的值,避免被破坏
MOV BP,0 ;每个数组元素(数字)都有自己的位数,需要重置
MOV AX,WORD PTR [BX]
;拆分数字
SPLIT:
XOR DX,DX ;重置准备工作
MOV SI,10
DIV SI ;除法指令不接受立即数!!
PUSH DX
INC BP
CMP AX,0 ;商为0则数字拆分结束
JNE SPLIT
;显示拆解的数字
XOR CX,CX
MOV CX,BP ;栈内有几个数字,就应循环弹出几次
DISP:
POP DX
ADD DL,30H ;转化成字符
MOV AH,2
INT 21H
LOOP DISP
;准备显示下一个数据
MOV DL,',' ;每个数字用逗号分隔
MOV AH,2
INT 21H
MOV CX,DI ;恢复CX中的数组长度
ADD BX,2
LOOP TRAVEL2
FINISH3:
RET
;;输出算法核心;
PRINT ENDP
CODES ENDS
END MAIN
运行效果
存在的bug
1 输入的数据不能为0
分析:这是输入模块出了问题。原因是这个模块占用了太多寄存器,寄存器不够用了。我用BP作为标志位,来判断前面输入的字符是否是数字字符。BP=0则说明前面输入的字符不是数字字符(这个判断逻辑在前面输入0时会出现问题)。当前面一个字符是 ‘0’,此时恰有BP=0,紧接着输入英文逗号,程序会认为前面输入的也是逗号,所以就不会把那个0存放进数组中。
解决方法:额外拿其他东西充当标志位就可以了。再改一改相关的代码逻辑。我懒得改了
2 输入非法时错误信息有时候不能正确打印出来
分析:应该是输入模块出了问题。但是只是影响显示效果,不影响程序的正确性。