微机作业——“学生成绩统计“

微机作业——“学生成绩统计”

1. 题目

从键盘输入某计算机应用班35人的《计组》成绩(百分制),按成绩从高到低排序。并计算全班总成绩、平均成绩、统计低于平均成绩的学生人数,并将结果在屏幕上显示。

2. 实现功能

  1. 从键盘输入35个百分制成绩。成绩用压缩BCD码存储。应只接受合法数字输入。
  2. 从高到低排序
  3. 计算全班总成绩
  4. 计算全班平均成绩
  5. 统计低于平均成绩的学生人数
  6. 在屏幕上显示以上结果。结果用压缩BCD码表示。

3. 流程图及解题分析

3.1 主程序
  • 主程序:

    程序结构上可分为6部分,首先是主程序main,主程序调用5个实现不同功能的子程序。子程序可分为输入成绩功能的子程序,按成绩降序排序功能的子程序,统计成绩总和功能的子程序,统计平均分功能的子程序,统计低于平均分人数功能的子程序。

  • 流程图:

    微机作业——“学生成绩统计“_第1张图片

3.2 Input子程序
  • Input子程序:

    Input子程序用于成绩的输入,以压缩BCD码的形式将成绩输入到score变量中进行储存,其中需要输入35名学生的成绩。

  • 流程图:

    微机作业——“学生成绩统计“_第2张图片

3.3 Sort子程序
  • Sort子程序:

    输入的成绩保存到score变量之后,对score中保存的35名学生成绩进行降序排序,本次采用选择排序算法。

  • 流程图:

    微机作业——“学生成绩统计“_第3张图片

3.4 Output子程序
  • Output子程序:

    将压缩的BCD码成绩输出到屏幕中,当输出完35名学生成绩后退出循环。

  • 流程图:

    微机作业——“学生成绩统计“_第4张图片

3.5 Sum1子程序
  • Sum1子程序:

    Sum1子程序用于计算全班总成绩,对score变量中保存的成绩进行遍历,将35名学生成绩进行相加,求得的结果再通过调整加指令转换成压缩BCD码格式存储到sum变量中,因为35名学生成绩总和最大为3500,所以sum变量为字类型(dw),求得的总和sum再通过02功能号分别将sum千百十个位的输出到屏幕中。

  • 流程图:

    微机作业——“学生成绩统计“_第5张图片

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′

  • 流程图:

    微机作业——“学生成绩统计“_第6张图片

3.7 Nopass子程序
  • NoPass子程序:

    NoPass子程序用于统计低于平均成绩的学生人数,对score变量中的成绩进行遍历,小于平均分的人数用count变量进行统计,最后将count转换成非压缩BCD格式分别用02号功能进行输出操作。

  • 流程图: 微机作业——“学生成绩统计“_第7张图片

4. 汇编源码

stack segment stack
d1 dw 128 dup(?)
top equ length d1
stack ends

data 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 ends

code segment
assume cs:code, ds:data, ss:stack
main    proc far
        push ds
        xor ax,ax
        push ax
start:
        mov ax,data
        mov ds,ax
        mov ax,stack
        mov ss,ax
        mov sp,top
        xor cx,cx
        mov cl,slength;将计数35次用于输入成绩次数
        lea si,score;将si取到保存成绩的偏移地址
        xor ax,ax
		;********************STUDENT_SCORE_SYSTEM********************
		lea dx,print_menu;字符串入口为dx
		mov ah,09h;9号功能输出字符串
		int 21h;软中断
		;please input 35 students score:
		lea dx,input_stduent
		mov ah,09h
		int 21h
aa1:   		
		;Student
		lea dx,print_student
		int 21h
		;[stu]
		inc stu;设置stu的目的:输出显示屏Student[stu]中的stu,用来计数 范围[1,35]
		push ax;入栈保护
		push bx;同上
		push cx;同上
		xor ax,ax
		xor bx,bx
		mov al,stu;1---------------------------
		mov bl,0ah;2 这3行将stu从16进制转10进制
		div bl	  ;3---------------------------
		
		;---打印Student[]中的stu---
		mov bx,ax
		mov ah,02h
		mov dl,bl
		add dl,30h
		int 21h	  
		mov dl,bh
		add dl,30h
		int 21h
		;-------------------------
		pop cx;恢复现场
		pop bx;同上
		pop ax;同上
		;:
		lea dx,print_colons
		int 21h
		call far ptr Input		
		;tab
		push dx
        mov dl,09h;09h对应的字符为tab
		push ax
		mov ah,02h
		int 21h
		pop ax
		pop dx
        loop aa1;如果计数器CX为0,则退出循环,表示输入完35名学生成绩

		;-----输出显示屏OK----
        lea dx,check
        mov ah,09h
        int 21h
		;--------------------
	
		call far ptr Sort;调用选择排序算法
		;newline
		lea dx,CRLF
        mov ah,09h
        int 21h

		;Here are your input:
		lea dx,print_output
		mov ah,09h
		int 21h
		
        call far ptr Output;输出学生成绩子函数(排序后)

		;newline
		lea dx,CRLF
        mov ah,09h
        int 21h
        call far ptr Sum1;统计成绩总和子函数
		
		;newline
		lea dx,CRLF
        mov ah,09h
        int 21h
		
		;Average of total scores:
		lea dx,printavg
		int 21h
        call far ptr Average;计算平均分子函数
		
		;newline
		lea dx,CRLF
        mov ah,09h
        int 21h
		
		;The count of no pass:
		lea dx,print_fails
		int 21h
		call far ptr NoPass;低于平均分人数统计子函数,将低于平均分结果以非压缩BCD码保存到变量count中
		
		
		;newline
		lea dx,CRLF
		mov ah,09h
		int 21h
		int 21h;just for beauty

		;****************************END*****************************
		lea dx,print_end
		int 21h

        mov ah,4ch
        int 21h
        ret
main endp



;procedure name:Input
;function:input 35 students'score
;entry:store BCD score by al
;exit:none

Input   proc far
		push ax
        push bx
        push cx
        mov ah,01h
        int 21h
        mov cl,04h
        shl al,cl
        mov bh,al
        int 21h
        and al,0fh
        add al,bh
        mov [si],al
        inc si
        pop cx
        pop bx
		pop ax
        ret
Input   endp

;procedure name:Output
;function:output 35 students'score
;entry:none
;exit:
Output  proc far
		push ax
        push bx
        push cx
        xor cx,cx
        mov cl,slength;count 23h-->35d
        lea si,score;db
aa2:    mov al,[si]
        mov bl,al;copy
        push cx
        mov cl,04h
        shr al,cl
        pop cx
        mov bh,30h
        add al,bh
        mov dl,al
        mov ah,02h
        int 21h
        and bl,0fh
        add bl,30h
        mov dl,bl
        int 21h
        inc si
        mov dl,00h
        mov ah,02h
        int 21h
		int 21h
        loop aa2
        pop cx
        pop bx
		pop ax
        ret
Output  endp

Sort    proc far
		push ax
        push bx
		push dx
		xor cx,cx
		xor bx,bx
		xor dx,dx

		mov ax,0000h
		mov si,ax
		xor ax,ax
		mov cl,slength
		dec cl
		
rotate2:mov dx,0000h
		mov al,[si]
		mov temp,al
rotate1:inc dl
		mov bx,dx
		mov bl,[si+bx]
		cmp al,bl;al >= bl, sort_next
		jnc sort_next;
		;has carry
		mov al,bl
		mov bl,temp
		mov temp,al
		push ax
		push si
		mov ax,si
		add ax,dx
		mov si,ax
		mov [si],bl
		pop si
		pop ax
sort_next:
		cmp dl,cl;dl < slength, rotate1
		jnc for2;dl >= slength, out
		jmp rotate1
for2:
		push ax
		push bx
		mov ax,si
		push si
		lea si,score
		mov bx,si
		pop si
		sub ax,bx
		push cx
		mov cl,slength
		dec cl
		dec cl
		cmp al,cl;i < slength - 0

		pop cx
		pop bx
		pop ax
		
		mov dl,temp
		mov [si],dl
		jnc last
		inc si
		dec cx
		jmp rotate2	
last:
		
		pop dx
        pop bx
		pop ax
        ret
Sort    endp


;caculate sum of score
;entry:score
;exit:none
Sum1    proc far
		push ax
        xor cx,cx
        xor ax,ax
        xor bx,bx
        mov cl,slength
        dec cl;34 add time
        lea ax,score
        mov si,ax
        mov al,[si]
        xor ah,ah
rotate3:inc si
        mov bl,[si]
        add al,bl
        daa
        adc ah,00h
        loop rotate3
		
		mov temp,al;
		mov al,ah
		xor ah,ah
		mov bl,0ah
		div bl
		push cx
		mov cl,04h
		shl al,cl
		pop cx
		add ah,al
		mov al,temp
		
		;newline
		lea dx,CRLF
		push ax
		mov ah,09h
		int 21h
		pop ax
		
		
        mov sum,ax
		mov ah,09h
        mov dx,offset printsum
        int 21h
        ;mov ah,02h
		mov ax,sum
        cmp ah,1;judge ah has carry?
        jnc ah_carry
sum_next:
        mov ax,sum
        mov cl,04h
        shr al,cl
        add al,30h
        mov dl,al
        mov ah,02h
        int 21h
        mov ax,sum
        and al,0fh
        add al,30h
        mov dl,al
        mov ah,02h
        int 21h
		pop ax
        ret
ah_carry:
	mov dl,ah
	push cx
	mov cl,04h
	shr dl,cl
	pop cx
	add dl,30h
	mov ah,02h
	int 21h
	
	mov ax,sum
	mov dl,ah
	and dl,0fh
    add dl,30h
	mov ah,02h
    int 21h
	jmp sum_next
Sum1    endp

Average proc far
push ax
	push dx
	push cx
	xor cx,cx
	
	mov ax,sum
	mov al,ah
	xor ah,ah
	mov bl,al
	mov cl,04h
	shr al,cl
	and bl,0fh
	mov dl,0ah
	mul dl
	add al,bl
	
	mov bx,sum
	shr bl,cl
	xor bh,bh
	mov dx,000ah
	mul dx
	add ax,bx
	
	mov bx,sum
	xor bh,bh
	and bl,0fh
	mov dx,000ah
	mul dx
	add ax,bx
	
	mov bx,0023h
	div bx
	aam
	mov avg,ax; uncompressed BCD
	
	mov dl,ah
	add dl,30h
	mov ah,02h
	int 21h
	
	mov ax,avg
	mov dl,al
	mov ah,02h
	add dl,30h
	int 21h
	
	pop cx
	pop dx
	pop ax
    ret		
Average endp


NoPass proc far
	push ax
	push dx
	xor cx,cx
	mov cl,slength
	lea si,score
	mov bx,avg;0605h
	push cx
	mov cl,04h
	shl bh,cl;6005h
	add bl,bh; bl= 65h compression
	pop cx
rotate4:
	mov al,[si]
	cmp al,bl;score[i] < avg?
	jnc rotate5
	inc count
rotate5:
	inc si
	loop rotate4
	mov cl,04h
	xor ax,ax
	mov al,count;15h
	mov bl,0ah
	div bl;0102h
	mov dl,al
	mov bl,ah;temp store
	add dl,30h
	mov ah,02h
	int 21h
	mov dl,bl
	add dl,30h
	int 21h
	pop dx
	pop ax
	ret
NoPass endp
	
code ends
end start

5.运行效果

微机作业——“学生成绩统计“_第8张图片

6.后续「debug神器」

在本次调试过程中,由于代码量过于庞大,debug工具调试不为方便,只能通过单步T命令进行调试,尤其是再调试排序子程序的时候,理论上需要单步调试接近n平方次数(DosBox环境调试选择排序算法),所以为了跳过与问题无相干的代码,我往常会缩小代码调试的范围,将认为与问题相关的程序代码单独放到另一个汇编文件里进行调试,从而减少调试的次数。而在这次调试排序子程序过程中,由于边界上的问题,排序中偏后的数据发生错误的顺序,这就意味着从进入该排序子程序开始,就要遍历将近n的平方次,即使将该子程序单独放到另一个文件中进行调试,也要进行这样的单步调试过程。为了快速的匹配到关键问题所在,我将代码移至到VScode编辑器中进行调试,VScode编辑器中配置插件masm/tasm,本质上也是启动DosBox,就可以利用Vscode的打断点方式,来快速匹配到错误的具体范围,创建监视点数据查看寄存器中数据的变化。

还需要改进的地方

  • 要求输入百分制成绩,目前只实现了[0,99]
  • 输入成绩的合法性

你可能感兴趣的:(汇编,编程语言)