学生成绩管理系统(汇编)

编程软件:Masm for Windows 集成实验环境 2015

宏和子程序部分已经被提取出来,原先程序已经详细注释了,只是稍作美化了一下,为了方便理解,建议读者先理解一下结构体,如何用地址的方法,准确定位到数据,虽然使用的是结构体存储数据,但是存取改数据,我个人还是使用的地址定位的方法,所以结构体后面的地址,是相对地址,只要理解地址如何取用数据,就会方便理解代码了;另外,本人编写过程中,传递的参数使用的极少,所以子程序调用的时候,可能有些寄存器内的数据已经在上一段程序中被定义好了,如果要理解程序,阅读起来可能有点乱,所以寄存器里面的数据如果能记住,读代码就会方便很多,主要需要记得是BX,DX,CX,SI,DI(BP只在查询学号并修改成绩中有用到) ,BX我主要是用来存结构体的首位地址,还有结构体中数据每一行的首地址,ADD BX,TYPE  students 就是换行的方式,DX主要保存一些数据,CX则是计数器,SI有时候也作为计数器,SI和DI也会当做相对定位的指针,准确定位到每一个字节区域,用来配合BX的找出结构体中的数据,结构体数据存储方式如下

BX(结构体首地址) DI(0) DI(1) DI(2) DI(3) DI(4) DI(5) DI(6) DI(7) DI(8) DI(9) DI(10) DI(11) DI(12) DI(13) ... ... ...
BX+TYPE students DI(0) ... ... ... ... ...                      
.....                                  
.....                                  
.....                                  

上面每一个小格就是一个字节单元,SNO:DI(0~8)为学号存储单元可存9个字节数据,NAME:DI(9~12)为名字存储单元可存4个字节数据,GRADE:DI(13)为成绩单元存入0~100的成绩,所以只需要一个字节单元。SORT:剩余的是多出来的部分,防止数据错位,以字型对齐。理解上述部分,数据存储方式,程序就通俗易懂了,汇编主要还是理解代码在机器内的运作方式,只要懂得机器内如何运作代码,汇编就容易上手了。。。改变代码内的N值,可控制输入学生信息的个数,以下N=5。按空格结束,所有结束方式均为按空格。。。

运行结果如下图:

数据输入:

数据排序输出:

按学号查找并修改成绩:

 

退出程序:

1.宏定义部分

  • ;字符输出宏定义

SHUCHU MACRO X

    MOV AH,2

    MOV DL,X

    INT 21H

ENDM

  • ;字符串输出宏定义

S_SHUCHU MACRO Y
    LEA DX,Y
    MOV AH,9
    INT 21H
ENDM

  • ;单字符输入宏定义

SHURU MACRO
    MOV AH,1
    INT 21H
ENDM

  • ;字符检测

CHECK MACRO X
    .IF X>'9' || X<'0' && X!=' '
        S_SHUCHU HINT3            ;提示输入错误
        SHURU                    ;让提示显示,避免被刷掉,按任意键返回主菜单
        JMP RESTORE                ;跳回主菜单
    .ENDIF

ENDM

  • ;成绩输出宏定义

GR_SHUCHU MACRO X
    MOV AL,X        ;接收成绩
    MOV AH,0        ;AX高位清0
    MOV CL,10        ;除数,等会用来取余数和商
    MOV DH,0        ;压栈前高位置0
    .WHILE AL != 0  ;如果商不为0,则继续除10
        DIV CL
        MOV DL,AH    ;余数置于DL
        ADD DL,30H  ;转换为ASCII码
        PUSH DX        ;压栈
        INC DH
    MOV AH,0        ;高位即余数清0
    .ENDW
    ;循环完成取数操作,即从右到左,从个位数开始取数,一直到最高位停止取数
    .IF DH == 0                ;判断输入成绩是否为0,若为0直接输出
        SHUCHU 30H
    .ELSE                    ;否则弹出栈中相应内容输出
            MOV CL,DH
        .WHILE CL
            POP DX
            SHUCHU DL
            DEC CL
        .ENDW
    .ENDIF            ;成绩输出结束
ENDM

 2.子程序定义部分

  • ;成绩输入子程序

GR_SHURU PROC
     MOV CX,0
     MOV DL,10
    .WHILE CX<=100
        SHURU            ;键盘接收一个字符
        CHECK AL            ;检测输入的是否为数字
    .IF AL == ' '
        MOV [BX+DI],CL        ;如果输入空格,则成绩接收结束,并存入对应储存单元,即结构体GRADE单元
        RET
    .ELSE
        SUB AL,30H        ;进行ASCII码转换为数字
        CBW                ;AL扩展为AX,高位清0
        XCHG CX,AX        ;交换数值
        MUL DL            ;AL×10放入AX中
        ADD CX,AX        ;将数字相加,得到新的数值,放入cl中
    .ENDIF
    .ENDW
    S_SHUCHU WARING        ;成绩输入不合格输出警告
    RET
GR_SHURU ENDP
    

  • ;菜单显示子程序

SHOW PROC NEAR
    MOV AX,3  ;清屏
    INT 10H
    S_SHUCHU LIST
    RET
SHOW ENDP

;菜单选项子程序
CHOICE PROC NEAR
    SHURU        ;调用宏输入一个字符
    .IF AL=='1'         ;调用输入功能
        CALL INPUT
    .ELSEIF AL =='2'    ;调用输出功能,且排序
        CALL RANK        ;调用排序,对数据按成绩进行排序,由高到低
        CALL OUTPUT
    .ELSEIF AL=='3'        ;调用查找功能
        CALL SEARCH
    .ELSEIF AL=='0'        ;退出程序    
        JMP EXIT
    .ELSE
        S_SHUCHU WARING        ;输出警告提示
    .ENDIF
    SHURU    ;接收一个字符,用于暂停当前界面,按任意键刷新菜单
    RET
CHOICE ENDP

  • ;输入子程序

INPUT PROC NEAR
    S_SHUCHU HINT1
    
    LEA BX,STU_ARRAY        ;结构体初始化指针,行定位
    MOV SI,0                ;统计输入的行数
    
    .WHILE SI<N                ;N为行数,控制输入几行
      
 MOV CX,9            ;循环,学号共定义9个字节空间
        MOV DI,0            ;相对指针,用于定位对应的结构体内的元素,列定位
    LPI:
        SHURU
        CHECK AL        ;检测输入字符是否为数字
        .IF AL == ' '
            JMP XHI            ;若输入空格,则跳到下一项输入
        .ELSE
            MOV [BX+DI],AL     ;将学号逐个输入到对应的位置,最大9位
            INC DI
        .ENDIF
        LOOP LPI
    XHI:
        SHUCHU '    '
        MOV CX,4        ;姓名输入,最大可输入4个字符
        MOV DI,9        ;结构体定义的NAME段位置定位
    LPI1:
        SHURU
        .IF AL == ' '    
            JMP XMI        ;输入空格表示输入下一项
        .ELSE
            MOV [BX+DI],AL  ;对应位置存入相应字符,最多输入4位
            INC DI
        .ENDIF
        LOOP LPI1
    XMI:
        SHUCHU '    '
        MOV DI,13        ;定位到GRADE区域,成绩输入
        CALL GR_SHURU        ;调用子程序,成绩输入
        
        ADD BX,TYPE students    ;跳到下一行,即结构体数字的下一行,加上相应的结构体大小
        INC SI                ;行数统计自增一次
        SHUCHU 13
        SHUCHU 10
    .ENDW    
    RET
INPUT ENDP

  • ;输出子程序

OUTPUT PROC NEAR    ;该部分可参照输入子程序,理解地址所对应数据,方便理解代码
    S_SHUCHU HINT2
    
    LEA BX,STU_ARRAY
    MOV SI,0
    
    .WHILE SI<N
        MOV CX,9
        MOV DI,0
    LPO:
        MOV DL,[BX+DI]
        SHUCHU DL
        INC DI
        LOOP LPO
        
        SHUCHU '    '
        MOV CX,4
    LPO1:
        MOV DL,[BX+DI]
        SHUCHU DL
        INC DI
        LOOP LPO1
        
        SHUCHU '    '
        MOV DL,[BX+DI]
        GR_SHUCHU DL
        
        ADD BX,TYPE students
        INC SI
        
        SHUCHU '    '
        MOV DX,SI        ;获取当前的行数
        GR_SHUCHU DL    ;将行数转换为对应的ASCII码,输出对应的行数
        
        SHUCHU 13
        SHUCHU 10
    .ENDW
    RET    
OUTPUT ENDP

  • ;降序排序

RANK PROC NEAR
    MOV CX,N-1            ;总的数据个数减一,即循环次数
.WHILE CX                ;冒泡排序法
    PUSH CX                ;外循环次数保护,压栈
    LEA BX,STU_ARRAY    ;回到结构体的第一行
    .WHILE CX
        MOV DI,13
        MOV DL,[BX+DI]        ;取到的第一个值,与它的后一位值比较,对应到结构体为GRADE部分
        MOV DH,[BX+DI+14]    ;取到的第二个值,与其前一位比较,同上
            .IF DL<DH        ;如果前一个小于后一个成绩,则进行数据交换
            MOV DI,0
                .WHILE DI<14                ;结构体实际长度为14
                    XCHG AL,[BX+DI]
                    XCHG [BX+DI+14],AL        ;.while内为前后数据交换部分
                    XCHG [BX+DI],AL
                    INC DI
                .ENDW
            .ENDIF
        ADD BX,TYPE students    ;跳到第二个位置与后一段数据比较,冒泡排序法
        DEC CX                    ;内循环自减
    .ENDW
    POP CX
    DEC CX                        ;外循环自减
.ENDW
    RET
RANK ENDP

  • ;查找并修改子程序

SEARCH PROC NEAR
    S_SHUCHU FIND
    
    LEA BX,STU_ARRAY    ;取存储数据的首地址,即结构体首地址
    LEA BP,SOURCE        ;存放查找的学号的首地址
    
    MOV CX,9            ;循环,学号共定义了9个字节空间
    MOV DI,0            ;相对指针,用于定位需要存入元素,列定位
LPF:
    SHURU
    CHECK AL        ;检测输入字符是否为数字
    .IF AL == ' '
        JMP FNXT            ;若输入空格,则跳到下一项输入
    .ELSE
        MOV [BP+DI],AL     ;将学号逐个输入到对应的位置,最大9位
        INC DI
    .ENDIF
    LOOP LPF
FNXT:
    MOV CX,N        ;查找对比的个数,即总共的行数(学生数)
LPF1:
    PUSH CX            ;保护外循环次数,压栈
        MOV CX,9    ;学号一共有9个存储区域
        MOV DI,0
    LPF2:                    
        MOV AL,[BX+DI]        ;结构体,已经存入的数据
        MOV AH,[BP+DI]        ;查找输入的学号
        CMP AL,AH            ;按位对比学号
        JNZ FNXT1            ;如果有一个没有相等则跳过该学生,该学生不是要查找的学生
        INC DI
        .IF DI == 9
            POP CX            ;弹出多余的CX,避免无法返回,即RET执行和栈有关,需先弹出栈中多余的数据,避免程序运行出错
            JMP FNXT2        ;如果查找到对应学号则跳出,执行修改成绩程序段
        .ENDIF
    LOOP LPF2                ;循环9次对比完一位的学号
        FNXT1:
        ADD BX,TYPE students    ;指向下一个学生,即下一行数据区域
        POP CX                ;外循环被保护的CX,出栈
LOOP LPF1                    ;一共对比N次,即和N个学生的学号比较
    S_SHUCHU NFIND    
    RET
FNXT2:
    S_SHUCHU HINT4
    MOV SI,13                ;指针指向GRADE对应段
    MOV DL,[BX+SI]            ;get到原始成绩
    GR_SHUCHU DL            ;输出旧的成绩
    S_SHUCHU HINT5            ;提示是否修改成绩
    SHURU                    ;接收字符,判断是否修改成绩
    .IF AL=='y'
        S_SHUCHU RESULT
        MOV DI,13
        CALL GR_SHURU            ;调用输入成绩子程序
    .ENDIF
    RET
SEARCH ENDP

源代码如下:

students STRUCT			 ;偏移量
	SNO DB 9 DUP(?)  	 ;0-8
	NAME DB 4 DUP(?)	 ;9-12
	GRADE DB ?			 ;13
	SORT DD ?   		 ;14
students ENDS   ;结构体共16个字节

DATAS SEGMENT
	N = 4			;数组大小定义
    STU_ARRAY students N DUP(<>)  ;结构体数组
    JIANGE DD 10 DUP (?)  ;数据溢出显示遮挡菜单
    LIST DB 13,10
    	DB '*~*~*~*~*~*~*~LIST*~*~*~*~*~*~*~*~*~*~*',13,10
    	DB '@         1.INPUT                     @',13,10
    	DB '@         2.OUTPUT(RANK)              @',13,10
    	DB '@         3.FIND(SNO)                 @',13,10
    	DB '@         0.QUIT                      @',13,10
    	DB '*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*',13,10
    	DB 'PLEASE INPUT YOUR CHOICE:$'
    WARING DB 'INPUT ERROR!!!$'
    HINT1 DB 13,10,'SNO		NAME	GRADE',13,10,'$'
    HINT2 DB 13,10,'SNO		NAME	GRADE	RANK',13,10,'$'
    HINT3 DB 13,10,'INPUT ERROR! PLEASE AFRESH.',13,10,'$'
    HINT4 DB ' OLD GRADE:$'
    HINT5 DB 13,10,'ARE THE SCORES MODIFIED?(y/n)',13,10,'$'
    FIND DB 13,10,'PLEASE ENTER THE (SNO) YOU WANT TO FIND,END BY SPACE:',13,10,'$'
    NFIND DB 13,10,'NOT FOUND!!',13,10,'$'
    RESULT DB 13,10,'PLEASE ENTER NEW GRADE:',13,10,'$'
    SOURCE DB 9 DUP(?)
DATAS ENDS

;字符输出宏定义
SHUCHU MACRO X
	MOV AH,2
	MOV DL,X
	INT 21H
ENDM

;字符串输出宏定义
S_SHUCHU MACRO Y
	LEA DX,Y
	MOV AH,9
	INT 21H
ENDM

;单字符输入宏定义
SHURU MACRO
	MOV AH,1
	INT 21H
ENDM

;字符检测
CHECK MACRO X
	.IF X>'9' || X<'0' && X!=' '
		S_SHUCHU HINT3			;提示输入错误
		SHURU					;让提示显示,避免被刷掉,按任意键返回主菜单
		JMP RESTORE				;跳回主菜单
	.ENDIF

ENDM

;成绩输出宏定义
GR_SHUCHU MACRO X
	MOV AL,X        ;接收成绩
	MOV AH,0		;AX高位清0
	MOV CL,10		;除数,等会用来取余数和商
	MOV DH,0		;压栈前高位置0
	.WHILE AL != 0  ;如果商不为0,则继续除10
		DIV CL
		MOV DL,AH	;余数置于DL
		ADD DL,30H  ;转换为ASCII码
		PUSH DX		;压栈
		INC DH
	MOV AH,0		;高位即余数清0
	.ENDW
	;循环完成取数操作,即从右到左,从个位数开始取数,一直到最高位停止取数
	.IF DH == 0				;判断输入成绩是否为0,若为0直接输出
		SHUCHU 30H
	.ELSE					;否则弹出栈中相应内容输出
			MOV CL,DH
		.WHILE CL
			POP DX
			SHUCHU DL
			DEC CL
		.ENDW
	.ENDIF			;成绩输出结束
ENDM

	;主程序代码段         MAIN
CODES SEGMENT
    ASSUME CS:CODES,DS:DATAS
START:
    MOV AX,DATAS
    MOV DS,AX
    
RESTORE:			;输入错误后,跳回的点
	.WHILE 1
	CALL SHOW   ;调用菜单
	CALL CHOICE ;接收选项并调用对应子程序
	.ENDW		;死循环

EXIT:    
	MOV AH,4CH
    INT 21H
	;主程序代码段结尾		MAIN	

;成绩输入子程序
GR_SHURU PROC
 	MOV CX,0
 	MOV DL,10
	.WHILE CX<=100
		SHURU			;键盘接收一个字符
		CHECK AL			;检测输入的是否为数字
	.IF AL == ' '
		MOV [BX+DI],CL		;如果输入空格,则成绩接收结束,并存入对应储存单元,即结构体GRADE单元
		RET
	.ELSE
		SUB AL,30H		;进行ASCII码转换为数字
		CBW				;AL扩展为AX,高位清0
		XCHG CX,AX		;交换数值
		MUL DL			;AL×10放入AX中
		ADD CX,AX		;将数字相加,得到新的数值,放入cl中
	.ENDIF
	.ENDW
	S_SHUCHU WARING		;成绩输入不合格输出警告
	RET
GR_SHURU ENDP
    
;菜单显示子程序
SHOW PROC NEAR
	MOV AX,3  ;清屏
    INT 10H
    S_SHUCHU LIST
    RET
SHOW ENDP

;菜单选项子程序
CHOICE PROC NEAR
	SHURU		;调用宏输入一个字符
	.IF AL=='1'		 ;调用输入功能
		CALL INPUT
	.ELSEIF AL =='2'	;调用输出功能,且排序
		CALL RANK		;调用排序,对数据按成绩进行排序,由高到低
		CALL OUTPUT
	.ELSEIF AL=='3'		;调用查找功能
		CALL SEARCH
	.ELSEIF AL=='0'		;退出程序	
		JMP EXIT
	.ELSE
		S_SHUCHU WARING		;输出警告提示
	.ENDIF
	SHURU	;接收一个字符,用于暂停当前界面,按任意键刷新菜单
	RET
CHOICE ENDP

;输入子程序
INPUT PROC NEAR
	S_SHUCHU HINT1
	
	LEA BX,STU_ARRAY		;结构体初始化指针,行定位
	MOV SI,0				;统计输入的行数
	
	.WHILE SI<N				;N为行数,控制输入几行
		MOV CX,9			;循环,学号共定义9个字节空间
		MOV DI,0			;相对指针,用于定位对应的结构体内的元素,列定位
	LPI:
		SHURU
		CHECK AL		;检测输入字符是否为数字
		.IF AL == ' '
			JMP XHI			;若输入空格,则跳到下一项输入
		.ELSE
			MOV [BX+DI],AL 	;将学号逐个输入到对应的位置,最大9位
			INC DI
		.ENDIF
		LOOP LPI
	XHI:
		SHUCHU '	'
		MOV CX,4		;姓名输入,最大可输入4个字符
		MOV DI,9		;结构体定义的NAME段位置定位
	LPI1:
		SHURU
		.IF AL == ' '	
			JMP XMI		;输入空格表示输入下一项
		.ELSE
			MOV [BX+DI],AL  ;对应位置存入相应字符,最多输入4位
			INC DI
		.ENDIF
		LOOP LPI1
	XMI:
		SHUCHU '	'
		MOV DI,13		;定位到GRADE区域,成绩输入
		CALL GR_SHURU		;调用子程序,成绩输入
		
		ADD BX,TYPE students	;跳到下一行,即结构体数字的下一行,加上相应的结构体大小
		INC SI				;行数统计自增一次
		SHUCHU 13
		SHUCHU 10
	.ENDW	
	RET
INPUT ENDP


;输出子程序
OUTPUT PROC NEAR	;该部分可参照输入子程序,理解地址所对应数据,方便理解代码
	S_SHUCHU HINT2
	
	LEA BX,STU_ARRAY
	MOV SI,0
	
	.WHILE SI<N
		MOV CX,9
		MOV DI,0
	LPO:
		MOV DL,[BX+DI]
		SHUCHU DL
		INC DI
		LOOP LPO
		
		SHUCHU '	'
		MOV CX,4
	LPO1:
		MOV DL,[BX+DI]
		SHUCHU DL
		INC DI
		LOOP LPO1
		
		SHUCHU '	'
		MOV DL,[BX+DI]
		GR_SHUCHU DL
		
		ADD BX,TYPE students
		INC SI
		
		SHUCHU '	'
		MOV DX,SI		;获取当前的行数
		GR_SHUCHU DL	;将行数转换为对应的ASCII码,输出对应的行数
		
		SHUCHU 13
		SHUCHU 10
	.ENDW
	RET	
OUTPUT ENDP

;降序排序
RANK PROC NEAR
	MOV CX,N-1			;总的数据个数减一,即循环次数
.WHILE CX				;冒泡排序法
	PUSH CX				;外循环次数保护,压栈
	LEA BX,STU_ARRAY	;回到结构体的第一行
	.WHILE CX
		MOV DI,13
		MOV DL,[BX+DI]		;取到的第一个值,与它的后一位值比较,对应到结构体为GRADE部分
		MOV DH,[BX+DI+14]	;取到的第二个值,与其前一位比较,同上
			.IF DL<DH		;如果前一个小于后一个成绩,则进行数据交换
			MOV DI,0
				.WHILE DI<14				;结构体实际长度为14
					XCHG AL,[BX+DI]
					XCHG [BX+DI+14],AL		;.while内为前后数据交换部分
					XCHG [BX+DI],AL
					INC DI
				.ENDW
			.ENDIF
		ADD BX,TYPE students	;跳到第二个位置与后一段数据比较,冒泡排序法
		DEC CX					;内循环自减
	.ENDW
	POP CX
	DEC CX						;外循环自减
.ENDW
	RET
RANK ENDP

;查找并修改子程序
SEARCH PROC NEAR
	S_SHUCHU FIND
	
	LEA BX,STU_ARRAY	;取存储数据的首地址,即结构体首地址
	LEA BP,SOURCE		;存放查找的学号的首地址
	
	MOV CX,9			;循环,学号共定义了9个字节空间
	MOV DI,0			;相对指针,用于定位需要存入元素,列定位
LPF:
	SHURU
	CHECK AL		;检测输入字符是否为数字
	.IF AL == ' '
		JMP FNXT			;若输入空格,则跳到下一项输入
	.ELSE
		MOV [BP+DI],AL 	;将学号逐个输入到对应的位置,最大9位
		INC DI
	.ENDIF
	LOOP LPF
FNXT:
	MOV CX,N		;查找对比的个数,即总共的行数(学生数)
LPF1:
	PUSH CX			;保护外循环次数,压栈
		MOV CX,9	;学号一共有9个存储区域
		MOV DI,0
	LPF2:					
		MOV AL,[BX+DI]		;结构体,已经存入的数据
		MOV AH,[BP+DI]		;查找输入的学号
		CMP AL,AH			;按位对比学号
		JNZ FNXT1			;如果有一个没有相等则跳过该学生,该学生不是要查找的学生
		INC DI
		.IF DI == 9
			POP CX			;弹出多余的CX,避免无法返回,即RET执行和栈有关,需先弹出栈中多余的数据,避免程序运行出错
			JMP FNXT2		;如果查找到对应学号则跳出,执行修改成绩程序段
		.ENDIF
	LOOP LPF2				;循环9次对比完一位的学号
		FNXT1:
		ADD BX,TYPE students	;指向下一个学生,即下一行数据区域
		POP CX				;外循环被保护的CX,出栈
LOOP LPF1					;一共对比N次,即和N个学生的学号比较
	S_SHUCHU NFIND	
	RET
FNXT2:
	S_SHUCHU HINT4
	MOV SI,13				;指针指向GRADE对应段
	MOV DL,[BX+SI]			;get到原始成绩
	GR_SHUCHU DL			;输出旧的成绩
	S_SHUCHU HINT5			;提示是否修改成绩
	SHURU					;接收字符,判断是否修改成绩
	.IF AL=='y'
		S_SHUCHU RESULT
		MOV DI,13
		CALL GR_SHURU			;调用输入成绩子程序
	.ENDIF
	RET
SEARCH ENDP
;结束

CODES ENDS
    END START

猜你喜欢

转载自blog.csdn.net/qq_42476927/article/details/85198820
今日推荐