汇编——学生成绩管理系统

网上有很多资源,这是其中的一个版本,先上代码:
;用notepad++查看此文档比较好~~
.model small				;small,程序只能有一个代码段和一个数据段
.stack 200h					;堆栈段名 stack,默认大小 1024 字节,这里是 200h 字节
.data						;数据段名_data
   student struc			;学生的数据结构,32 字节,2^5,便于计算地址
      xname db 14 dup('$')
      class db 14 dup('$')
      num dw 0
      score dw 0			;为了保存1位小数,以10倍值保存
   student ends
	stu_size equ 32			;32=2^5
   
	stu_db student 100 dup(<'x','y',6,500>)		;声明100 名学生的空间的结构体stu_db
	stu_num db 0								;已保存的学生人数
	stu_seq db 100 dup('$')						;排序信息,排序操作在序列中进行,排序结果体现在序列中

welcome db '-----Student Information Mannagement System-----',0dh,0ah ;打印菜单
		db '------------------------------------------------',0dh,0ah;
		db '0. Add Information.',0dh,0ah				;0. 录入学生成绩(十进制形式)
		db '------------------------------------------------',0dh,0ah;
		db '1. Sort by the numbers of students.',0dh,0ah;1.按学号排序显示
		db '------------------------------------------------',0dh,0ah;
		db '2. Sort by the scores of students.',0dh,0ah	;2.按成绩排序显示
		db '------------------------------------------------',0dh,0ah;
		db '3. Statistic the Average score',0dh,0ah		;3.统计平均成绩
		db '------------------------------------------------',0dh,0ah;
		db '4. Show Statistics of each kind.',0dh,0ah	;4.统计各分数段人数
		db '------------------------------------------------',0dh,0ah;
		db '5. Exit.',0dh,0ah							;5.退出
		db '------------------------------------------------',0dh,0ah;
	 	db 'please select one'
	 	db '$'	 	
   msg1 db 'name:','$'
   msg2 db 'class:','$'
   msg3 db 'number:','$'
   msg4 db 'score:','$'
m_str_l db 0dh,0ah,'please input your ','$'
m_num_l db 0dh,0ah,'please input your ','$'


table		dw CASE0,CASE1,CASE2,CASE3,CASE4,CASE5
buflen 		db 	100									;输入缓冲区大小 100
aclen		db 	? 									;实际输入长度
buf    		db	100 dup(0),'$'						;实际缓冲区
bufrear		equ offset buf+100						;缓冲区尾

ns6 db 0				;统计各分数段人数
n67 db 0
n78 db 0
n89 db 0
n91 db 0

ms6 db '(Not Pass)0 ~ 60:','$'	;显示各分数段人数
m67 db '(Pass)   60 ~ 70:','$'
m78 db '(Normal) 70 ~ 80:','$'
m89 db '(Good)   80 ~ 90:','$'
m91 db '(Great) 90 ~ 100:','$'

.CODE					;代码段名_text
START:

ps macro str 			;打印字符串,要求以'$'结尾
   push ax
   push dx
	lea dx,str
	mov ah,9
	int 21h
	
	pop dx	
	pop ax
	endm

pc macro ch				;打印字符
   push ax
   push dx
	mov dl,ch
	mov ah,2
	int 21h
	pop dx
	pop ax
	endm

endl macro 				;打印'\n'
	pc 0dh
	pc 0ah
	endm

scs macro 				    ;从键盘输入字符串,保存在buf中,串长aclen
	lea dx,buflen
	mov ah,10
	int 21h
	endm

scc macro 				    ;从键盘输入字符,保存在al
	mov ah,1
	int 21h
	endm

memcpy macro dest,src,len   ;经典的内存拷贝
	push ax
	push cx
	push si
	push di
	mov ax,ds
	mov es,ax 				;串操作di要用到附加段
	mov cl,len
	mov ch,0
	lea si,src
	lea di,dest
	cld
	rep movsb
	;mov [di],'
	pop di
	pop si
	pop cx
	pop ax
	endm
;使bx指向编号为ax的学生单元
;寄存器:ax,bx,cx
GET_STU macro
	push ax
	push cx
	lea bx,stu_db
	mov cl,5			
	shl ax,cl			;?????????
	add bx,ax 			;bx指向这个学生的存储单元
	pop cx
	pop ax
	endm
	
	mov ax,@data
	mov ds,ax
WELC:					;调用ps函数打印welcome数据段
	endl
	ps welcome
	pc ':'
	
	;输入选项
	scc 				;ascii码保存在al
	endl
	endl
	mov ah,0
	mov bx,ax
	sub bx,'0'			;?????????
	cmp bx,5
	jbe CASE0TO5		;bx寄存器不高于5则转移到CASE0T05
	jmp WELC
CASE0TO5:
	shl bx,1			;?????????
	jmp table[bx] 		;基址寻址
CASE0: 					;录入学生成绩(十进制形式)
	call  ins_stu
	jmp WELC
CASE1: 					;按学号排序显示
	call num_sort
	call print_seq
	jmp WELC
CASE2: 					;按成绩排序显示
	call score_sort
	call print_seq
	jmp WELC
CASE3: 					;统计平均成绩
	call get_average
	jmp WELC
CASE4: 					;统计各分数段人数
	call get_sat
	jmp WELC
CASE5:
	mov ah,4ch      	;结束
	int 21h
	
	
	
	;ins_stu:输入学生信息
	;入口:stu_num已有学生人数,即最新空白编号
	;出口:stu_db
	;存储单元:stu_db,stu_num
ins_stu proc near
	push ax
	push bx
	push cx
	push dx
	
	mov al,stu_num
	mov ah,0
	;确定存放在第几个位置
	lea bx,stu_db
	mov cl,5
	shl ax,cl
	add bx,ax 			;bx指向空白单元
input_name:
	ps  m_str_l			;m_str_l db 0dh,0ah,'(Length < 14 bytes) ','$'
	ps  msg1			;msg1 db 'name:','$'
	scs
	cmp aclen,14
	jae input_name		;大于等于跳转至重新输入name
	memcpy [bx].xname,buf,aclen;否则拷贝buf长度为aclen的字符串到stu_db结构体下的xname
	endl
input_class:
	ps  m_str_l
	ps  msg2
	scs
	cmp aclen,14
	jae input_class
	memcpy [bx].class,buf,aclen
	endl

	ps m_num_l
	ps msg3
	scs
	call str2num		;调用str2num:将数字由字符串转成数值
	mov [bx].num,ax
	endl

	ps m_num_l
	ps msg4
	scs
	call score_fmt		;调用score_fmt:将分数格式存储
	mov [bx].score,ax
	endl
	
	inc stu_num 		;增加学生记录
  	pop dx
  	pop cx
  	pop bx
  	pop ax
	ret
ins_stu endp
	;pnum:以十进制形式输出一个无符号数
	;入口:ax需要输出的正数
	;出口:
	;存储单元:
pnum proc near
	push ax
	push bx
	push cx
	push dx
	
	mov bx,bufrear
	dec bx	
	mov [bx],'$'
	
OUTLOOP:
	or ax,ax
	jz OUTLOOPFIN ;ax为零
	mov dx,0
	mov cx,10
	div cx
	add dl,'0'
	dec bx
	mov byte ptr [bx],dl
	jmp OUTLOOP
OUTLOOPFIN:
	cmp bx,offset bufrear-1 ;输出0
	jne PRINT_NUM
	dec bx
	mov BYTE ptr [bx],'0'
PRINT_NUM:
	mov dx,bx
	mov ah,9
	int 21h
	pop dx
	pop cx
	pop bx
	pop ax
	ret
	pnum endp
	
	;pscore:以十进制形式输出带一位小数的分数
	;入口:ax需要输出的正数
	;出口:
	;存储单元:
pscore proc near
	push ax
	push bx
	push cx
	push dx
	
	mov bx,bufrear
OUTLOOP2:
	or ax,ax
	jz OUTLOOPFIN2
	mov dx,0
	mov cx,10
	div cx
	add dl,'0'
	dec bx
	mov byte ptr [bx],dl
	jmp OUTLOOP2
OUTLOOPFIN2:
	cmp bx,offset bufrear-1 ;x要改成0x,以便小数格式输出
	je S0X
	cmp bx,offset bufrear ;空要改成00,以便小数格式输出
	jne PRINT_SCORE
S00:
	;==,为空
	dec bx
	mov BYTE ptr [bx],'0'
S0X:
	dec bx
	mov BYTE ptr [bx],'0'
PRINT_SCORE:
	mov dx,bx
	mov bx,bufrear
	dec bx
	mov cl,[bx] ;暂存小数
	mov [bx],'$'
	;mov dx,bx
	mov ah,9
	int 21h
	
	pc '.'
	pc cl
	
	pop dx
	pop cx
	pop bx
	pop ax
	ret
pscore endp

	;pstu:输出学生信息
	;入口:ax存储编号
	;出口:
	;存储单元:
pstu proc near
	push  ax
	push bx
	push cx
	GET_STU
	ps msg1
	ps [bx].xname;
	pc ' '
	ps msg2
	ps [bx].class
	pc ' '
	ps msg3
	mov ax,[bx].num
	call pnum 
	pc ' '
	ps msg4
	mov ax,[bx].score
	call pscore
	endl
	pop cx
	pop bx
	pop  ax
	ret
pstu endp

	;str2num:将数字由字符串转成数值
	;入口:buf
	;出口:ax
	;存储单元:buf缓冲区,aclen缓冲区内容长度
str2num proc
	push bx
	push cx
	push si
	mov  ax,0
	mov  cl,aclen
	mov  ch,0
	lea  si,buf
GET_NUM:
	;mov ax,dest ;add dest,dest*10 + [si]-'0'
	mov bl,10
	mul  bl ;以前的数*10,乘法指令必须用ax
	mov  bl,[si]
	mov  bh,0
	add  ax,bx
	sub  ax,'0'
	inc  si
	loop  GET_NUM
	
	pop si
	pop cx
	pop bx
	ret
str2num endp

	;score_fmt:将分数格式存储
	;入口:buf
	;出口:ax
	;存储单元:buf缓冲区,aclen缓冲区内容长度
score_fmt proc
	push bx
	push cx
	push dx
	push si
	
	mov ax,0
	mov cl,aclen
	mov ch,0
	mov dx,0
	lea si,buf
GET_SCORE:
	cmp BYTE ptr [si],'.' ;这个类型很重要
	jnz MUL_DEX ;不是小数点
	mov dx,1 ;发现小数点
	inc si
	loop GET_SCORE
	
MUL_DEX:
	mov bl,10
	mul bl
	mov bl,[si]
	mov bh,0
	add ax,bx
	sub ax,'0'
	
	inc si
	loop GET_SCORE
	
	cmp dx,0
	jnz END_SCORE
	mov bl,10
	mul bl
END_SCORE:
	pop si
	pop dx
	pop cx
	pop bx
	ret
score_fmt endp

	;get_average:算平均分并输出
	;入口:stu_db
	;出口
	;存储单元:stu_db,stu_num
get_average proc near
	push ax
	push bx
	push cx
	push dx
	
	mov cx,0 ;学生编号
	mov ax,0 ;暂存平均成绩低8位
	mov dx,0 ;暂存平均成绩高8位
REPERT:
	cmp cl,stu_num
	jae OUT_PUT ;总分已经加完了
	;找到编号为cx的学生
	push ax
	push cx
	mov ax,cx
	GET_STU
	pop cx
	pop ax
	
	add ax,[bx].score ;32位加法
	adc dx,0
	inc cl
	jmp REPERT
OUT_PUT:
	mov cl,stu_num
	mov ch,0
	or cl,0 ;假设学生人数不多于255人
	jnz END_AVE ;没有学生,不能除0
	inc cx
END_AVE:
	div cx ;扩展为16位除法,商保存在ax中
	endl
	call pscore
	endl
	
	pop dx
	pop cx
	pop bx
	pop ax
	ret
get_average endp

	;get_sat:统计各分数段人数并输出
	;入口:stu_db
	;出口
	;存储单元:ns6,n67,n78,n89,n91,stu_db,stu_num
get_sat proc near
	push ax
	push bx
	push cx
	
	mov ns6,0
	mov n67,0
	mov n78,0
	mov n89,0
	mov n91,0
	mov cx,0 ;学生编号(人数-1)
	
SAT_IN:
	cmp cl,stu_num
	jae SAT_OUT ;已经完了
	
	;找到编号为cx的学生
	push ax
	push cx
	mov ax,cx
	GET_STU
	pop cx
	pop ax
	
	inc cl
	
	mov ax,[bx].score
	cmp ax,600			;由于是以10倍值进行存储的所以是与600比较
	jb IS6				;小于跳转
	cmp ax,700
	jb I67
	cmp ax,800
	jb I78
	cmp ax,900
	jb I89
	;>=90
	inc n91
	jmp SAT_IN
IS6:
	inc ns6
	jmp SAT_IN
I67:
	inc n67
	jmp SAT_IN
I78:
	inc n78
	jmp SAT_IN
I89:
	inc n89
	jmp SAT_IN

SAT_OUT:
	mov ah,0
	
	ps ms6
	mov al,ns6
	call pnum
	endl
	
	ps m67
	mov al,n67
	call pnum
	endl

	ps m78
	mov al,n78
	call pnum
	endl

	ps m89
	mov al,n89
	call pnum
	endl

	ps m91
	mov al,n91
	call pnum
	endl

	pop cx
	pop bx
	pop ax
	ret
get_sat endp
	
	;print_seq:根据序列stu_seq输出列表
	;输入:stu_seq
	;输出:
	;存储单元:stu_seq
print_seq proc near
	push ax
	push bx
	;stu_seq保存了编号序列,以'$'结束
	lea bx,stu_seq
PRINT_SEQ_BEGIN:
	mov al,[bx]
	cmp al,'$'
	jz PRINT_SEQ_END
	mov ah,0
	call pstu
	inc bx
	jmp PRINT_SEQ_BEGIN
PRINT_SEQ_END:
	pop bx
	pop ax
	ret
print_seq endp

	;num_sort:按学号排序
	;输入:stu_seq
	;输出:stu_seq
	;存储单元:
num_sort proc near
	push bx
	push dx

	mov dl,0
	lea bx,stu_seq
SORT_BEGIN:
	cmp dl,stu_num
	jae SORT_END
	mov BYTE ptr [bx],dl
	inc bx
	inc dl
	jmp SORT_BEGIN
SORT_END:
	mov BYTE ptr [bx],'$'
	;stu_seq保存了按输入顺序的序列,以'$'结束

	cmp stu_num,1 ;学生数小于等于1,没必要排序
	jle NON_NUM

	mov cl,stu_num ;循环次数,内存量不能直接减
	dec cl
	mov ch,0
LOOP1:
	mov di,cx ;暂存外循环cx
	lea bx,stu_seq ;指向第一个序号
LOOP2:
	mov al,BYTE ptr [bx]
	mov ah,0
	
	push bx
GET_STU
	mov dx,[bx].num ;dx前一个序列号对应的学号
	pop bx

	mov al,BYTE ptr [bx+1]   
	push bx
	GET_STU
	cmp dx,[bx].num ;[bx].num后一个序列号对应的学号
	pop bx
jae CONTI
	mov al,BYTE ptr [bx] ;小于则交换两个序列号
	;call pnum
	;endl
	xchg al,BYTE ptr [bx+1]
	;call pnum
	;endl
	mov BYTE ptr [bx],al
CONTI:
	inc bx ;下一个序列号
	loop LOOP2
	mov cx,di ;恢复外循环
	loop LOOP1
	
NON_NUM:
	pop dx
	pop bx
	ret
num_sort endp

	;score_sort:按成绩排序
	;输入:stu_seq
	;输出:stu_seq
	;存储单元:
score_sort proc near
	push bx
	push dx
	
	mov dl,0
	lea bx,stu_seq
SCORE_BEGIN:		
	cmp dl,stu_num
	jae SCORE_END
	mov BYTE ptr [bx],dl
	inc bx
	inc dl
	jmp SCORE_BEGIN
SCORE_END:
	mov BYTE ptr [bx],'$'
	;stu_seq保存了按输入顺序的序列,以'$'结束
	
	cmp stu_num,1 ;学生数小于等于1,没必要排序
	jle NON_SCORE
	mov cl,stu_num ;循环次数,内存量不能直接减
	dec cl
	mov ch,0
SLOOP1:
	mov di,cx ;暂存外循环cx
	lea bx,stu_seq ;指向第一个序号
SLOOP2:
	mov al,BYTE ptr [bx]
	mov ah,0
	
	push bx
	GET_STU
	mov dx,[bx].score ;dx前一个序列号对应的学号
	pop bx
	
	mov al,BYTE ptr [bx+1]
	push bx
	GET_STU
	cmp dx,[bx].score ;[bx].num后一个序列号对应的学号
	pop bx
	
	jae SCONTI
	mov al,BYTE ptr [bx] ;小于则交换两个序列号
	;call pnum
	;endl
	xchg al,BYTE ptr [bx+1]
	;call pnum
	;endl
	mov BYTE ptr [bx],al
SCONTI:
	inc bx ;下一个序列号
	loop SLOOP2
	mov cx,di ;恢复外循环
	loop SLOOP1
NON_SCORE:
	pop dx
	pop bx
	ret
	score_sort endp
END START

加了很多的注释,看完有700行汇编实在头疼,另外有一些解释:

程序主要功能:0号功能 录入学生成绩

           1号功能 按学号排序

   2号功能 按成绩排序

   3号功能 统计平均成绩

   4号功能 统计个分数段人数

   5号功能 退出

 

一些重要函数的解释:

1.Marco宏定义部分:

 

Ps函数(参数:str):打印字符串

 

Pc函数(参数:ch):打印一个字符

 

Endl函数(无参数):打印“\n”

 

Scs函数(无参数):从键盘输入字符并保存在al寄存器中

 

Memcpy函数(参数:dest目标地址 参数:src原地址 参数:len长度):内存拷贝

 

GET_STU函数(无参数):先给xnameclass指定总共28个空间,为了便于计算所以需要32字节(mov cl,5  shl ax,cl),push ax和cx bx指向100个学生空间,ax左移5位(相当于乘以32,由于stu_db是结构体空间所以相当于将bx指向下一个学生空间)

 

注:Table数组存有CASE0~CASE5,分别代表6个功能:通过将bx寄存器输入的字符型数据转换成数值型比较然后传入基址寻址中找到对应的CASE进行相应的响应

 

2.子程序部分

 

这个部分也是对应有改动的部分,改动了关于成绩分数的保留一位小数部分,简单的部分直接说明下思想,比较繁琐的部分会粘贴一些代码进行解释。

 

 

Str2num函数:首先是子程序ins_stu输入学生信息这个子程序中input_class中调用的str2num子程序,这个函数的作用是将数字由字符串转成数值便于比较,具体方法是用bx寄存器低位部分bl表示一个乘数10,每次读入一个字符然后判断是否结束,不结束的话就将这个字符乘上bl再加到ax寄存器上,然后减去字符’0’转换成数值型,不停循环就可以变成数值型的班级号码。基本思想:add dest,dest*10 + [si]-'0'

 

 

Score_fmt函数:这个函数的作用是将输入的成绩分数乘以10倍存储,逐位比较判断是否输入的为小数点,如果不是的话就如同str2num函数一样乘10累加转换成数值型,若有小数点就直接将指针后移一位继续乘10累加。

Pnum函数:这个函数的作用以十进制形式输出一个无符号数,其实就是用dl存储除以10结果的余数,然后将数值数字型的转换回字符型直接从bufrear缓冲区尾,往前全部存回buf缓冲区。

 

 

Pscore函数:这个函数的作用是一十进制的形式输出带一位小数的分数,基本转换思想与p_num函数差不多,具体的转换过程是判断尾端是否为0,如果为0那么原数就是整数;如果尾端不是0,那么原数为小数(前提假设输入的时候不存在90.0这种情况)。

 

 

Pstu函数:这个函数的作用是输出学生信息(包含名字,班级,学号,成绩分数),每次通过GET_STU获取下一个学生空间,其中,学号是通过调用pnum函数输出字符型数,成绩分数通过调用pscore函数输出带一位小数的分数。

 

 

Get_sat函数:这个函数的作用是统计各分数段的人数并输出,首先就是通过GET_STU函数获取下一个学生空间同时用cl寄存器计数,将CASE处已经清零的bx寄存器里的成绩分数放入ax中,与10倍值进行比较分别跳转到不同的分数段然后输出。

 

 

Num_sort函数:核心函数,所用的是冒泡排序的方法,具体解释一下。

首先将dl寄存器清零,将bx寄存器指向stu_seq数组空间的首地址,代码如下:num_sort proc near

push bx

push dx

mov dl,0

lea bx,stu_seq

 

接下来是SORT_BEGIN循环:

SORT_BEGIN:

cmp dl,stu_num

jae SORT_END

mov BYTE ptr [bx],dl

inc bx

inc dl

jmp SORT_BEGIN

这里stu_num为已存储的学生人数,大于等于跳转,即小于的时候完成stu_seq[i] = i这样一个初始化;

 

SORT_END:

mov BYTE ptr [bx],'$' ;最后一位用结束符

;stu_seq保存了按输入顺序的序列,以'$'结束

 

cmp stu_num,1  ;学生数若小于等于1,则不需要排序

jle NON_NUM ;小于等于的话跳转

 

mov cl,stu_num  ;循环次数,内存量不能直接减

dec cl

mov ch,0

 

 

以下以第一个学生和第二个学生为例注释:

LOOP1:

mov di,cx  ;暂存外循环cx

lea bx,stu_seq  ;指向第一个序号

LOOP2:

mov al,BYTE ptr [bx] ;al低位为零

mov ah,0 ;ah高位清零

push bx ;bx此时为零

GET_STU

mov dx,[bx].num  ;把第一个学生的学号给dx寄存器

pop bx ;bx仍然为零

 

mov al,BYTE ptr [bx+1]   ;bx1里的内容为1赋给alal1

push bx ;bx仍然为零

GET_STU ;这里是关键,al作为1左移5位(相当于乘以32)此时bx指向下一个学生空间

cmp dx,[bx].num ;[bx].num ;dx里第一个学生的学号与第二个学生对比

pop bx ;bx依然为零

jae CONTI ;大于等于则跳转

mov al,BYTE ptr [bx]  ;把第一个学生的学号赋给al

;call pnum

;endl

xchg al,BYTE ptr [bx+1] ;bx+1空间里为第一个学生,al里是第二个学生

;call pnum

;endl

mov BYTE ptr [bx],al ;将第二个学生放到bx空间里实现交换

CONTI:

inc bx ;下一个序列号 ;指针指向第二个学生

loop LOOP2

mov cx,di ;恢复外循环

loop LOOP1

NON_NUM:

pop dx

pop bx

ret

num_sort endp

 

 

Score_sort函数:同上

 

 

总结:这个项目学习到了子程序嵌套设计的一些技巧,虽然项目细节部分尤其是排序这部分一开始理解起来有些困难,但是通过不停地查看前述的寄存器调用终于还是明白了。


  • 5
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值