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

来源:互联网 发布:蜂群算法中的fit函数 编辑:程序博客网 时间:2024/05/17 04:53
网上有很多资源,这是其中的一个版本,先上代码:
;用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 endsstu_size equ 32;32=2^5   stu_db student 100 dup(<'x','y',6,500>);声明100 名学生的空间的结构体stu_dbstu_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 ','$'tabledw CASE0,CASE1,CASE2,CASE3,CASE4,CASE5buflen db 100;输入缓冲区大小 100aclendb ? ;实际输入长度buf    db100 dup(0),'$';实际缓冲区bufrearequ offset buf+100;缓冲区尾ns6 db 0;统计各分数段人数n67 db 0n78 db 0n89 db 0n91 db 0ms6 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;代码段名_textSTART:ps macro str ;打印字符串,要求以'$'结尾   push ax   push dxlea dx,strmov ah,9int 21hpop dxpop axendmpc macro ch;打印字符   push ax   push dxmov dl,chmov ah,2int 21hpop dxpop axendmendl macro ;打印'\n'pc 0dhpc 0ahendmscs macro     ;从键盘输入字符串,保存在buf中,串长aclenlea dx,buflenmov ah,10int 21hendmscc macro     ;从键盘输入字符,保存在almov ah,1int 21hendmmemcpy macro dest,src,len   ;经典的内存拷贝push axpush cxpush sipush dimov ax,dsmov es,ax ;串操作di要用到附加段mov cl,lenmov ch,0lea si,srclea di,destcldrep movsb;mov [di],'pop dipop sipop cxpop axendm;使bx指向编号为ax的学生单元;寄存器:ax,bx,cxGET_STU macropush axpush cxlea bx,stu_dbmov cl,5shl ax,cl;?????????add bx,ax ;bx指向这个学生的存储单元pop cxpop axendmmov ax,@datamov ds,axWELC:;调用ps函数打印welcome数据段endlps welcomepc ':';输入选项scc ;ascii码保存在alendlendlmov ah,0mov bx,axsub bx,'0';?????????cmp bx,5jbe CASE0TO5;bx寄存器不高于5则转移到CASE0T05jmp WELCCASE0TO5:shl bx,1;?????????jmp table[bx] ;基址寻址CASE0: ;录入学生成绩(十进制形式)call  ins_stujmp WELCCASE1: ;按学号排序显示call num_sortcall print_seqjmp WELCCASE2: ;按成绩排序显示call score_sortcall print_seqjmp WELCCASE3: ;统计平均成绩call get_averagejmp WELCCASE4: ;统计各分数段人数call get_satjmp WELCCASE5:mov ah,4ch      ;结束int 21h;ins_stu:输入学生信息;入口:stu_num已有学生人数,即最新空白编号;出口:stu_db;存储单元:stu_db,stu_numins_stu proc nearpush axpush bxpush cxpush dxmov al,stu_nummov ah,0;确定存放在第几个位置lea bx,stu_dbmov cl,5shl ax,cladd bx,ax ;bx指向空白单元input_name:ps  m_str_l;m_str_l db 0dh,0ah,'(Length < 14 bytes) ','$'ps  msg1;msg1 db 'name:','$'scscmp aclen,14jae input_name;大于等于跳转至重新输入namememcpy [bx].xname,buf,aclen;否则拷贝buf长度为aclen的字符串到stu_db结构体下的xnameendlinput_class:ps  m_str_lps  msg2scscmp aclen,14jae input_classmemcpy [bx].class,buf,aclenendlps m_num_lps msg3scscall str2num;调用str2num:将数字由字符串转成数值mov [bx].num,axendlps m_num_lps msg4scscall score_fmt;调用score_fmt:将分数格式存储mov [bx].score,axendlinc stu_num ;增加学生记录  pop dx  pop cx  pop bx  pop axretins_stu endp;pnum:以十进制形式输出一个无符号数;入口:ax需要输出的正数;出口:;存储单元:pnum proc nearpush axpush bxpush cxpush dxmov bx,bufreardec bxmov [bx],'$'OUTLOOP:or ax,axjz OUTLOOPFIN ;ax为零mov dx,0mov cx,10div cxadd dl,'0'dec bxmov byte ptr [bx],dljmp OUTLOOPOUTLOOPFIN:cmp bx,offset bufrear-1 ;输出0jne PRINT_NUMdec bxmov BYTE ptr [bx],'0'PRINT_NUM:mov dx,bxmov ah,9int 21hpop dxpop cxpop bxpop axretpnum endp;pscore:以十进制形式输出带一位小数的分数;入口:ax需要输出的正数;出口:;存储单元:pscore proc nearpush axpush bxpush cxpush dxmov bx,bufrearOUTLOOP2:or ax,axjz OUTLOOPFIN2mov dx,0mov cx,10div cxadd dl,'0'dec bxmov byte ptr [bx],dljmp OUTLOOP2OUTLOOPFIN2:cmp bx,offset bufrear-1 ;x要改成0x,以便小数格式输出je S0Xcmp bx,offset bufrear ;空要改成00,以便小数格式输出jne PRINT_SCORES00:;==,为空dec bxmov BYTE ptr [bx],'0'S0X:dec bxmov BYTE ptr [bx],'0'PRINT_SCORE:mov dx,bxmov bx,bufreardec bxmov cl,[bx] ;暂存小数mov [bx],'$';mov dx,bxmov ah,9int 21hpc '.'pc clpop dxpop cxpop bxpop axretpscore endp;pstu:输出学生信息;入口:ax存储编号;出口:;存储单元:pstu proc nearpush  axpush bxpush cxGET_STUps msg1ps [bx].xname;pc ' 'ps msg2ps [bx].classpc ' 'ps msg3mov ax,[bx].numcall pnum pc ' 'ps msg4mov ax,[bx].scorecall pscoreendlpop cxpop bxpop  axretpstu endp;str2num:将数字由字符串转成数值;入口:buf;出口:ax;存储单元:buf缓冲区,aclen缓冲区内容长度str2num procpush bxpush cxpush simov  ax,0mov  cl,aclenmov  ch,0lea  si,bufGET_NUM:;mov ax,dest ;add dest,dest*10 + [si]-'0'mov bl,10mul  bl ;以前的数*10,乘法指令必须用axmov  bl,[si]mov  bh,0add  ax,bxsub  ax,'0'inc  siloop  GET_NUMpop sipop cxpop bxretstr2num endp;score_fmt:将分数格式存储;入口:buf;出口:ax;存储单元:buf缓冲区,aclen缓冲区内容长度score_fmt procpush bxpush cxpush dxpush simov ax,0mov cl,aclenmov ch,0mov dx,0lea si,bufGET_SCORE:cmp BYTE ptr [si],'.' ;这个类型很重要jnz MUL_DEX ;不是小数点mov dx,1 ;发现小数点inc siloop GET_SCOREMUL_DEX:mov bl,10mul blmov bl,[si]mov bh,0add ax,bxsub ax,'0'inc siloop GET_SCOREcmp dx,0jnz END_SCOREmov bl,10mul blEND_SCORE:pop sipop dxpop cxpop bxretscore_fmt endp;get_average:算平均分并输出;入口:stu_db;出口;存储单元:stu_db,stu_numget_average proc nearpush axpush bxpush cxpush dxmov cx,0 ;学生编号mov ax,0 ;暂存平均成绩低8位mov dx,0 ;暂存平均成绩高8位REPERT:cmp cl,stu_numjae OUT_PUT ;总分已经加完了;找到编号为cx的学生push axpush cxmov ax,cxGET_STUpop cxpop axadd ax,[bx].score ;32位加法adc dx,0inc cljmp REPERTOUT_PUT:mov cl,stu_nummov ch,0or cl,0 ;假设学生人数不多于255人jnz END_AVE ;没有学生,不能除0inc cxEND_AVE:div cx ;扩展为16位除法,商保存在ax中endlcall pscoreendlpop dxpop cxpop bxpop axretget_average endp;get_sat:统计各分数段人数并输出;入口:stu_db;出口;存储单元:ns6,n67,n78,n89,n91,stu_db,stu_numget_sat proc nearpush axpush bxpush cxmov ns6,0mov n67,0mov n78,0mov n89,0mov n91,0mov cx,0 ;学生编号(人数-1)SAT_IN:cmp cl,stu_numjae SAT_OUT ;已经完了;找到编号为cx的学生push axpush cxmov ax,cxGET_STUpop cxpop axinc clmov ax,[bx].scorecmp ax,600;由于是以10倍值进行存储的所以是与600比较jb IS6;小于跳转cmp ax,700jb I67cmp ax,800jb I78cmp ax,900jb I89;>=90inc n91jmp SAT_INIS6:inc ns6jmp SAT_INI67:inc n67jmp SAT_INI78:inc n78jmp SAT_INI89:inc n89jmp SAT_INSAT_OUT:mov ah,0ps ms6mov al,ns6call pnumendlps m67mov al,n67call pnumendlps m78mov al,n78call pnumendlps m89mov al,n89call pnumendlps m91mov al,n91call pnumendlpop cxpop bxpop axretget_sat endp;print_seq:根据序列stu_seq输出列表;输入:stu_seq;输出:;存储单元:stu_seqprint_seq proc nearpush axpush bx;stu_seq保存了编号序列,以'$'结束lea bx,stu_seqPRINT_SEQ_BEGIN:mov al,[bx]cmp al,'$'jz PRINT_SEQ_ENDmov ah,0call pstuinc bxjmp PRINT_SEQ_BEGINPRINT_SEQ_END:pop bxpop axretprint_seq endp;num_sort:按学号排序;输入:stu_seq;输出:stu_seq;存储单元:num_sort proc nearpush bxpush dxmov dl,0lea bx,stu_seqSORT_BEGIN:cmp dl,stu_numjae SORT_ENDmov BYTE ptr [bx],dlinc bxinc dljmp SORT_BEGINSORT_END:mov BYTE ptr [bx],'$';stu_seq保存了按输入顺序的序列,以'$'结束cmp stu_num,1 ;学生数小于等于1,没必要排序jle NON_NUMmov cl,stu_num ;循环次数,内存量不能直接减dec clmov ch,0LOOP1:mov di,cx ;暂存外循环cxlea bx,stu_seq ;指向第一个序号LOOP2:mov al,BYTE ptr [bx]mov ah,0push bxGET_STUmov dx,[bx].num ;dx前一个序列号对应的学号pop bxmov al,BYTE ptr [bx+1]   push bxGET_STUcmp dx,[bx].num ;[bx].num后一个序列号对应的学号pop bxjae CONTImov al,BYTE ptr [bx] ;小于则交换两个序列号;call pnum;endlxchg al,BYTE ptr [bx+1];call pnum;endlmov BYTE ptr [bx],alCONTI:inc bx ;下一个序列号loop LOOP2mov cx,di ;恢复外循环loop LOOP1NON_NUM:pop dxpop bxretnum_sort endp;score_sort:按成绩排序;输入:stu_seq;输出:stu_seq;存储单元:score_sort proc nearpush bxpush dxmov dl,0lea bx,stu_seqSCORE_BEGIN:cmp dl,stu_numjae SCORE_ENDmov BYTE ptr [bx],dlinc bxinc dljmp SCORE_BEGINSCORE_END:mov BYTE ptr [bx],'$';stu_seq保存了按输入顺序的序列,以'$'结束cmp stu_num,1 ;学生数小于等于1,没必要排序jle NON_SCOREmov cl,stu_num ;循环次数,内存量不能直接减dec clmov ch,0SLOOP1:mov di,cx ;暂存外循环cxlea bx,stu_seq ;指向第一个序号SLOOP2:mov al,BYTE ptr [bx]mov ah,0push bxGET_STUmov dx,[bx].score ;dx前一个序列号对应的学号pop bxmov al,BYTE ptr [bx+1]push bxGET_STUcmp dx,[bx].score ;[bx].num后一个序列号对应的学号pop bxjae SCONTImov al,BYTE ptr [bx] ;小于则交换两个序列号;call pnum;endlxchg al,BYTE ptr [bx+1];call pnum;endlmov BYTE ptr [bx],alSCONTI:inc bx ;下一个序列号loop SLOOP2mov cx,di ;恢复外循环loop SLOOP1NON_SCORE:pop dxpop bxretscore_sort endpEND 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函数:同上

 

 

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


1 0