微机作业——“学生成绩统计”
1. 题目
从键盘输入某计算机应用班35人的《计组》成绩(百分制),按成绩从高到低排序。并计算全班总成绩、平均成绩、统计低于平均成绩的学生人数,并将结果在屏幕上显示。
2. 实现功能
- 从键盘输入35个百分制成绩。成绩用压缩BCD码存储。应只接受合法数字输入。
- 从高到低排序
- 计算全班总成绩
- 计算全班平均成绩
- 统计低于平均成绩的学生人数
- 在屏幕上显示以上结果。结果用压缩BCD码表示。
3. 流程图及解题分析
3.1 主程序
-
主程序:
程序结构上可分为6部分,首先是主程序main,主程序调用5个实现不同功能的子程序。子程序可分为输入成绩功能的子程序,按成绩降序排序功能的子程序,统计成绩总和功能的子程序,统计平均分功能的子程序,统计低于平均分人数功能的子程序。
-
流程图:
3.2 Input子程序
-
Input子程序:
Input子程序用于成绩的输入,以压缩BCD码的形式将成绩输入到score变量中进行储存,其中需要输入35名学生的成绩。
-
流程图:
3.3 Sort子程序
-
Sort子程序:
输入的成绩保存到score变量之后,对score中保存的35名学生成绩进行降序排序,本次采用选择排序算法。
-
流程图:
3.4 Output子程序
-
Output子程序:
将压缩的BCD码成绩输出到屏幕中,当输出完35名学生成绩后退出循环。
-
流程图:
3.5 Sum1子程序
-
Sum1子程序:
Sum1子程序用于计算全班总成绩,对score变量中保存的成绩进行遍历,将35名学生成绩进行相加,求得的结果再通过调整加指令转换成压缩BCD码格式存储到sum变量中,因为35名学生成绩总和最大为3500,所以sum变量为字类型(dw),求得的总和sum再通过02功能号分别将sum千百十个位的输出到屏幕中。
-
流程图:
3.6 Average子程序
-
Average子程序:
Average子程序用于统计全班平均成绩,将sum从压缩的BCD码格式转换成二进制形式,然后以sum作为被除数,35作为除数进行除法操作。
-
压缩BCD码转换二进制:
假设ax存的是sum压缩BCD码,A、A′分别表示AH高4位、AL高4位,B、B′分别表示AH的低4位、AL的低四位。可视为AX <==> ABA′B′(压缩BCD),下面公式中0ah为十进制的权值10。
转换公式:
( ( A * 0ah + B) * 0ah + A′) * 0ah + B′
-
流程图:
3.7 Nopass子程序
-
NoPass子程序:
NoPass子程序用于统计低于平均成绩的学生人数,对score变量中的成绩进行遍历,小于平均分的人数用count变量进行统计,最后将count转换成非压缩BCD格式分别用02号功能进行输出操作。
-
流程图:
4. 汇编源码
stack segment stack
d1 dw 128 dup(?)
top equ length d1
stack endsdata segment
score db 35 dup(0);store 35 scores of stduents
slength db 23h;store 35 as length of stduents
CRLF db 0dh,0ah,'$';newline
print_menu db '********************STUDENT_SCORE_SYSTEM********************',0dh,0ah,'$'
print_end db '****************************END*****************************',0dh,0ah,'$'
input_stduent db 'Please input 35 students score:',0dh,0ah,0dh,0ah,'$'
print_student db 'Student$'
print_colons db ': $'
check db 'OK!',0dh,0ah,'$';check for input success
print_output db 'Here are your input(Descending Order):',0dh,0ah,'$'
printsum db ' Sum of students scores: $'
printavg db ' Average of total scores: $'
print_fails db ' The count of no pass: $'
stu db 0;student[stu]:
temp db 0;temp in Procedure of Sort
sum dw 0;sum in Procedure of Sum1
avg dw 0;avg in Procedure of Average
count db 0;count of no pass (uncompressed BCD)
data endscode segment
assume cs:code, ds:data, ss:stack
main proc farpush dsxor ax,axpush ax
start:mov ax,datamov ds,axmov ax,stackmov ss,axmov sp,topxor cx,cxmov cl,slength;将计数35次用于输入成绩次数lea si,score;将si取到保存成绩的偏移地址xor ax,ax;********************STUDENT_SCORE_SYSTEM********************lea dx,print_menu;字符串入口为dxmov ah,09h;9号功能输出字符串int 21h;软中断;please input 35 students score:lea dx,input_stduentmov ah,09hint 21h
aa1: ;Studentlea dx,print_studentint 21h;[stu]inc stu;设置stu的目的:输出显示屏Student[stu]中的stu,用来计数 范围[1,35]push ax;入栈保护push bx;同上push cx;同上xor ax,axxor bx,bxmov al,stu;1---------------------------mov bl,0ah;2 这3行将stu从16进制转10进制div bl ;3---------------------------;---打印Student[]中的stu---mov bx,axmov ah,02hmov dl,bladd dl,30hint 21h mov dl,bhadd dl,30hint 21h;-------------------------pop cx;恢复现场pop bx;同上pop ax;同上;:lea dx,print_colonsint 21hcall far ptr Input ;tabpush dxmov dl,09h;09h对应的字符为tabpush axmov ah,02hint 21hpop axpop dxloop aa1;如果计数器CX为0,则退出循环,表示输入完35名学生成绩;-----输出显示屏OK----lea dx,checkmov ah,09hint 21h;--------------------call far ptr Sort;调用选择排序算法;newlinelea dx,CRLFmov ah,09hint 21h;Here are your input:lea dx,print_outputmov ah,09hint 21hcall far ptr Output;输出学生成绩子函数(排序后);newlinelea dx,CRLFmov ah,09hint 21hcall far ptr Sum1;统计成绩总和子函数;newlinelea dx,CRLFmov ah,09hint 21h;Average of total scores:lea dx,printavgint 21hcall far ptr Average;计算平均分子函数;newlinelea dx,CRLFmov ah,09hint 21h;The count of no pass:lea dx,print_failsint 21hcall far ptr NoPass;低于平均分人数统计子函数,将低于平均分结果以非压缩BCD码保存到变量count中;newlinelea dx,CRLFmov ah,09hint 21hint 21h;just for beauty;****************************END*****************************lea dx,print_endint 21hmov ah,4chint 21hret
main endp;procedure name:Input
;function:input 35 students'score
;entry:store BCD score by al
;exit:noneInput proc farpush axpush bxpush cxmov ah,01hint 21hmov cl,04hshl al,clmov bh,alint 21hand al,0fhadd al,bhmov [si],alinc sipop cxpop bxpop axret
Input endp;procedure name:Output
;function:output 35 students'score
;entry:none
;exit:
Output proc farpush axpush bxpush cxxor cx,cxmov cl,slength;count 23h-->35dlea si,score;db
aa2: mov al,[si]mov bl,al;copypush cxmov cl,04hshr al,clpop cxmov bh,30hadd al,bhmov dl,almov ah,02hint 21hand bl,0fhadd bl,30hmov dl,blint 21hinc simov dl,00hmov ah,02hint 21hint 21hloop aa2pop cxpop bxpop axret
Output endpSort proc farpush axpush bxpush dxxor cx,cxxor bx,bxxor dx,dxmov ax,0000hmov si,axxor ax,axmov cl,slengthdec clrotate2:mov dx,0000hmov al,[si]mov temp,al
rotate1:inc dlmov bx,dxmov bl,[si+bx]cmp al,bl;al >= bl, sort_nextjnc sort_next;;has carrymov al,blmov bl,tempmov temp,alpush axpush simov ax,siadd ax,dxmov si,axmov [si],blpop sipop ax
sort_next:cmp dl,cl;dl < slength, rotate1jnc for2;dl >= slength, outjmp rotate1
for2:push axpush bxmov ax,sipush silea si,scoremov bx,sipop sisub ax,bxpush cxmov cl,slengthdec cldec clcmp al,cl;i < slength - 0pop cxpop bxpop axmov dl,tempmov [si],dljnc lastinc sidec cxjmp rotate2
last:pop dxpop bxpop axret
Sort endp;caculate sum of score
;entry:score
;exit:none
Sum1 proc farpush axxor cx,cxxor ax,axxor bx,bxmov cl,slengthdec cl;34 add timelea ax,scoremov si,axmov al,[si]xor ah,ah
rotate3:inc simov bl,[si]add al,bldaaadc ah,00hloop rotate3mov temp,al;mov al,ahxor ah,ahmov bl,0ahdiv blpush cxmov cl,04hshl al,clpop cxadd ah,almov al,temp;newlinelea dx,CRLFpush axmov ah,09hint 21hpop axmov sum,axmov ah,09hmov dx,offset printsumint 21h;mov ah,02hmov ax,sumcmp ah,1;judge ah has carry?jnc ah_carry
sum_next:mov ax,summov cl,04hshr al,cladd al,30hmov dl,almov ah,02hint 21hmov ax,sumand al,0fhadd al,30hmov dl,almov ah,02hint 21hpop axret
ah_carry:mov dl,ahpush cxmov cl,04hshr dl,clpop cxadd dl,30hmov ah,02hint 21hmov ax,summov dl,ahand dl,0fhadd dl,30hmov ah,02hint 21hjmp sum_next
Sum1 endpAverage proc far
push axpush dxpush cxxor cx,cxmov ax,summov al,ahxor ah,ahmov bl,almov cl,04hshr al,cland bl,0fhmov dl,0ahmul dladd al,blmov bx,sumshr bl,clxor bh,bhmov dx,000ahmul dxadd ax,bxmov bx,sumxor bh,bhand bl,0fhmov dx,000ahmul dxadd ax,bxmov bx,0023hdiv bxaammov avg,ax; uncompressed BCDmov dl,ahadd dl,30hmov ah,02hint 21hmov ax,avgmov dl,almov ah,02hadd dl,30hint 21hpop cxpop dxpop axret
Average endpNoPass proc farpush axpush dxxor cx,cxmov cl,slengthlea si,scoremov bx,avg;0605hpush cxmov cl,04hshl bh,cl;6005hadd bl,bh; bl= 65h compressionpop cx
rotate4:mov al,[si]cmp al,bl;score[i] < avg?jnc rotate5inc count
rotate5:inc siloop rotate4mov cl,04hxor ax,axmov al,count;15hmov bl,0ahdiv bl;0102hmov dl,almov bl,ah;temp storeadd dl,30hmov ah,02hint 21hmov dl,bladd dl,30hint 21hpop dxpop axret
NoPass endpcode ends
end start
5.运行效果
6.后续「debug神器」
在本次调试过程中,由于代码量过于庞大,debug工具调试不为方便,只能通过单步T命令进行调试,尤其是再调试排序子程序的时候,理论上需要单步调试接近n平方次数(DosBox环境调试选择排序算法),所以为了跳过与问题无相干的代码,我往常会缩小代码调试的范围,将认为与问题相关的程序代码单独放到另一个汇编文件里进行调试,从而减少调试的次数。而在这次调试排序子程序过程中,由于边界上的问题,排序中偏后的数据发生错误的顺序,这就意味着从进入该排序子程序开始,就要遍历将近n的平方次,即使将该子程序单独放到另一个文件中进行调试,也要进行这样的单步调试过程。为了快速的匹配到关键问题所在,我将代码移至到VScode编辑器中进行调试,VScode编辑器中配置插件masm/tasm,本质上也是启动DosBox,就可以利用Vscode的打断点方式,来快速匹配到错误的具体范围,创建监视点数据查看寄存器中数据的变化。
还需要改进的地方
- 要求输入百分制成绩,目前只实现了[0,99]
- 输入成绩的合法性