SCAU 汇编综合性实验——通过子程序调用实现数据处理

这篇博客探讨了一个使用汇编语言实现的冒泡排序算法,涉及输入数据处理、错误检查及输出显示。博主指出了程序中存在的两个问题:1) 输入数据为0时不被正确处理;2) 输入错误时错误信息有时无法正确显示。针对这些问题,博主提供了分析和可能的解决方案,同时展示了排序算法的逻辑和代码运行效果。
摘要由CSDN通过智能技术生成

题目

在这里插入图片描述
我选择了题目二。关于题目二的理解我放在了代码的注释里面。
在此基础上我额外实现了:
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 输入非法时错误信息有时候不能正确打印出来

在这里插入图片描述
分析:应该是输入模块出了问题。但是只是影响显示效果,不影响程序的正确性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值