问答题 某班有32名同学,现需通过键盘输入某门课程成绩,求出最高分、最低分、平均分,并统计各分数段的人数:90~100分数段、80~89分数段、70~79分数段、60~69分数段、不及格分数段。把结果送显示器输出,并以文本文件(ASCⅡ码)的形式存储在当前目录下。要求采用模块化结构进行程序设计,编写键盘输入子程序,检查输入数据的合理性,并有完善的提示和处理;编写将两位BCD码转换成ASCⅡ码的子程序;编写屏幕显示输出子程序;编写文件处理等子程序。
【正确答案】DSEG    SEGMENT
   COUNT   EQU  32
   SCORE1 DW   32 DUP(0)    ;存放32名学生成绩(BCD码)
   SCORE2 DB   32 DUP(0)    ;存放32名学生成绩(二进制)
   TEMP1  DB    4 DUP(0)    ;暂时存放某学生成绩
   TEMP2  DB    3 DUP(0)
   ZGF1   DB    0   ;最高分(二进制)
   ZDF1   DB    0FFH    ;最低分(二进制)
   PJF1   DB    0   ;平均分(二进制)
   ZGF2   DW    0   ;最高分(压缩的BCD码)
   ZDF2   DW    0   ;最低分(压缩的BCD码)
   PJF2   DW    0   ;平均分(压缩的BCD码)
   S90_100DW    0   ;90~100分数段人数
   S80_89 DW    0   ;80~89分数段人数
   S70_79 DW    0   ;70~79分数段人数
   S60_69 DW    0   ;60~69分数段人数
   S0_59  DW    0   ;不及格分数段人数
   FILE    DB    'SCORE.txt',0   ;定义输出文件名字符串
   HANDLE  DW    ?    ;保存输出文件句柄
   BUF     DB    1024DUP(0)    ;文件缓冲区
   COUNT2 DW    ?    ;读入缓冲区BUF的字节数
   STR1   DB    0DH,0AH,'请输入学生成绩(按回车结束): $'
   STR2   DB    0DH,0AH,'学生学号: $'    ;输入学号提示
   STR3   DB    0DH,0AH,'注意: 输入数据非法(非字符数字或大于100).请重新输入: $,
             ;输入非法,重新输入提示
   STR4   DB    0DH,0AH,'最高分: $'    ;最高分提示
   STR5   DB    0DH,0AH,'最低分: $'    ;最低分提示
   STR6   DB    0DH,0AH,'平均分: $'    ;平均分提示
   STR7   DB    0DH,0AH,'分数在90~100之间的人数: $';分数在90~100之间的人数提示
   STR8   DB    0DH,0AH,'分数在80~89之间的人数: $'  ;分数在80~89之间的人数提示
   STR9   DB    0DH,0AH,'分数在70~79之间的人数: $'  ;分数在70~79之间的人数提示
   STR10  DB    0DH,0AH,'分数在60~69之问的人数: $'  ;分数在60~69之间的人数提示
   STR11  DB    0DH,0AH,'分数在0~59之间的人数: $'  ;不及格人数提示
   DSEG   ENDS
   SSEG   SEGMENT
   STACK  DB    100DUP(0)
   SSEG   ENDS
   CODE   SEGMENT
     ASSUME CS: CODE,DS: DATA;SS: SSEG
   MAIN    PROC FAR
   SCTJJG  MACRO X,Y    ;输出对分数进行的某项统计值宏定义
   MOV     AX,X    ;取对分数进行的某项统计值(压缩的BCD码)
   CALL,  BCDTASC    ;调用将某项统计值(压缩的BCD码)转换为ASCⅡ码子程序
   LEA     DX,Y    ;取某项统计值偏移地址
   CALL    PMXSSC    ;调用输出某项统计值子程序
   ENDM
   START: MOV    AX,DSEG    ;段地址装填
          MOV    DS,AX
          MOV    AX,SSEG    ;堆栈段地址装填
          MOV    SS,AX
          MOV    SP,LENGTH STACK
          CALL   JPSR    ;调用键盘输入子程序,输入学生成绩
          CALL   FSTJ    ;调用对分数进行各项统计子程序
          CALL   PMCSH    ;调用屏幕初始化子程序,清屏、光标定位等
          SCTJJG ZGF2,STR4   ;宏调用,输出最高分
          SCTJJG ZDF2,STR5   ;宏调用,输出最低分
          SCTJJG PJF2,STR6   ;宏调用.输出平均分
          SCTJJG S90~100,STR7   ;宏调用,输出90~100分数段人数
          SCTJJG S80~89,STR8   ;宏调用,输出80~89分数段人数
          SCTJJG S70~79,STR9   ;宏调用,输出70~79分数段人数
          SCTJJG S60~69,STR10   ;宏调用,输出60~69分数段人数
          SCTJJG S0~59,STR11   ;宏调用,输出0~59分数段人数
          CALL   WJCL    ;调用文件处理子程序。将屏幕显示内容送入SCORE.txt
          MOV    AX,4COOH
          INT    21H    ;键盘输入学生成绩子程序
   JPSR   PROC
          MOV    DX,OFFSET STR1 ;输入学生成绩提示
          MOV    AH,09H
          INT    21H
          LEA    DI,SCORE1
          LEA    SI.SCORE2
          MOV    CX,COUNT
          MOV    BH,30H    ;BH用于处理学号十位数字
          MOV    BL,31H    ;BL用于处理学号个位数字
                    ;外循环处理32个成绩
   LOP1:  MOV   STR2+11,BH    ;将学号值嵌入“学生学号: ”提示中
          MOV   STR2+12,BL
          MOV   DX,OFFSET STR2;输入学生学号提示
          MOV   AH,09H
          INT   21H
   LOP2:  PUSH  AX    ;内循环处理每一个成绩
          PUSH  BX
          PUSH  CX
          PUSH  DX
   LOP7:  XOR   CX,CX
          LEA   BX,TEMP1   ;TEMP1临时存放分数
   LOP3:  MOV   AH,01
          INT   21H
          CMP   AL,0DH    ;按回车键表示一个分数输入结束
          JE    LOP4
          CMP   AL,'0'H    ;检查输入数据的合理性: '0'~'9,合理
          JB    LOP6   ;输入字符小于'0',转
          CMP   AL,'9'
          JA    LOP6   ;输入字符大于'9',转
          SUB   AL,30H
          MOV   [BX],AL
          INC   CH    ;记录输入分数的位置
          CMP   CH,3
          JA    LOP6   ;输入分数位数大于3(输入分数大于100),转
          INC   BX
          JMP   LOP3
   LOP6:  MOV   DX,OFFSET STR3 ;输入非法,重新输入提示
          MOV   AH,09H
          INT   21H
          JMP   LOP7;转,重新输入一个分数
   LOP4:  CALL  BCDTBIN    ;将TEMP1中的一个分数(非压缩BCD码)转换为二进制数,出
                            口参数在AL中
          MOV  [SI],AL    ;分数的二进制形式送入SCORE2中
          CMP  AL,100
          JA   LOP6   ;输入分数大于100,转
          CALL FYSTYSB    ;调用非压缩BCD码转换成压缩BCD码的子程序。入口参数在
                           TEMP1和DI、CH中
          POP  DX
          POP  CX
          POP  BX
          POP  AX
          INC  BL    ;用于处理学号提示
          CMP  BL,'9'
          JLE  LOP8   ;输入字符大于'9',转
          AND  BL,0F0H
          INC  BH
   LOP8:  INC  DI
          INC  DI
          INC  SI
          DEC  CX
          JNZ  LOP1
          RET
          JPSR ENDP
        ;对分数进行各项统计子程序
   FSTJ    PROC
           PUSH  AX
           PUSH  BX
           PUSH  CX
           PUSH  DX
           PUSH  SI
           PUSH  DI
           MOV   SI,OFFSET SCORE1
           MOV   DI,OFFSET SCORE2
           MOV   CX,COUNT
           XOR   AX,AX    ;AX用于累加全部分数
    FLOP1: MOV   DL,ZGF1   ;求最高分
           MOV   DH,[DI]    ;取分数的二进制形式数据送DH
           CMP   DH,DL
           JBE   FLOP2
           MOV   ZGF1,DH    ;获得最高分
           PUSH  AX
           MOV   AX,[SI]
           MOV   ZGF2,AX
           POP   AX
   FLOP2:  MOV   DL,ZDF1   ;求最低分
           CMP   DH,DL
           JAE   FLOP3
           MOV   ZDF1,DH    ;获得最低分
           PUSH  AX
           MOV   AX,[SI]
           MOV   ZDF2,AX
           POP   AX
   FLOP3:  ADD   AL,DH    ;累加全部分数
           ADC   AH,0
           CMP   DH,90   ;统计90~100分数段的人数
           JB    FLOP4
           INC   S90_100
           JMP   FLOP8
   FLOP4:  CMP   DH,80;统计80~89分数段的人数
           JB    FLOP5
           INC   S80_89
           JMP   FLOP8
   FLOP5:  CMP   DH,70   ;统计70~79分数段的人数
           JB    FLOP6
           INC   S70_79
           JMP   FLOP8
   FLOP6:  CMP   DH,60   ;统计60~69分数段的人数
           JB    FLOP7
           INC   S60_69
           JMP   FLOP8
   FLOP7:  INC   S0_59   ;统计不及格的人数
   FLOP8:  INC   DI
           INC   SI
           INC   SI
           LOOP  FLOP1
           MOV   BL,COUNT
           DIV   BL   ;累加的全部分数÷32=平均分,送入AL
           MOV   PJF1,AL
           CALL  BINTBCD    ;将PJF1(二进制数)转换为压缩的BCD码,并送入PJF2。入口
                 参数在AL中,出口参数在AX中
           MOV   PJF2,AX
           MOV   AX,S90_100
           CALL  BINTBCD    ;将S90~100(二进制数)转换为压缩的BCD码,再返还S90~100。入口参数在AL中,出口参数在AX中
           MOV   S90_100,AX
           MOV   AX,S80_89
           CALL  BINTBCD    ;将S80~89(二进制数)转换为压缩的BCD码,再返还S80~89。入口参数在AL中,出口参数在AX中
           MOV   S80_89,AX
           MOV   AX,S70_79
           CALL  BINTBCD    ;将S70~79(二进制数)转换为压缩的BCD码,再返还S70~79。
                          入口参数在AL中,出口参数在AX中
           MOV   S70_79,AX
           MOV   AX,S60_69
           CALL  BINTBCD    ;将S60~69(二进制数)转换为压缩的BCD码,再返还S60~69。入口参数在AL中,出口参数在AX中
           MOV   S60_69,AX
           MOV   AX,S0_59
           CALL  BINTBCD    ;将S0~59(二进制数)转换为压缩的BCD码,再返还S0~59。入口参数在AL中,出口参数在AX中
           MOV  S0_59,AX
           POP  DI
           POP  SI
           POP  DX
           POP  CX
           POP  BX
           POP  AX
           RET
           FSTJ ENDP
             ;压缩BCD码转换成为非压缩BCD码的子程序。入口参数在AL中出口参数在AX中
   YSBTFYS    PROC
          PUSH CX
          MOV  AH,AL
          MOV  CL,4
          SHR  AH,CL
          AND  AL,0FH
          POP  CX
          RET
          YSBTFYS    ENDP
                ;压缩BCD码转换成为ASC码的子程序。入口参数在AL中,出口参数在TEMP1中
   BCDTASC    PROC
            PUSH  BX
            PUSH  CX
            PUSH  DX
            PUSH  SI
            PUSH  DI
            MOV   TEMP1,''    ;将存放百位数字的内存单元预置空格,以避免出现73为073的情况
            MOV   TEMPI+1,''    ;将存放十位数字的内存单元预置空格
            MOV   TEMP1+2,''    ;将存放个位数字的内存单元预置空格
            CMP   AH,0
            JZ    BCLOP1
            ADD   AH,30H    ;处理百位数
            MOV   TEMP1,AH
   BCLOP1:  CALL  YSBTFYS
            CMP   TEMP1,''
            JNZ   BCLOP2   ;TEMP1不等于空格,转
            CMP   AH,0
            JZ    BCLOP3   ;十位数字等于0,且TEMP1等于空格,转
   BCLOP2:  ADD   AH,30H    ;处理十位数
            MOV   TEMP1+1,AH
   BCLOP3:  ADD   AL,30H    ;处理个位数
   MOV  TEMP1+2,AL
   MOV  TEMP1+3,'$,
   POP  DI
   POP  SI
   POP  DX
   POP  CX
   POP  BX
   RET
   BCDTASC    ENDP
                 ;非压缩BCD码转换成为压缩BCD码的子程序。入口参数在TEMP1和DI和CH中
   FYSTYSB  PROC
         PUSH AX
         PUSH BX
         PUSH DX
         PUSH SI
         LEA  BX,TEMP1   ;一个合法分数TEMP1(非压缩BCD码)转换为压缩
                     ;BCD码后送入SCORE1
         MOV  AL,CH
         XOR  AH,AH
         ADD  BX,AX
         DEC  BX    ;从输入分数的个位开始的压缩BCD码调整
         XOR  AX,AX
         MOV  AL,[BX]    ;取输入分数的个位数
         DEC  CH
         JZ   FIOP    ;取完输入分数的数字位,转
         DEC  BX
         MOV  AH,[BX]    ;取输入分数的十位数
         MOV  CL,4
         ROR  AH,CL
         OR   AL,AH    ;合并输入分数的十位数和个位数到AL中
         XOR  AH,AH
         DEC  CH
         JZ   FLOP    ;取完输入分数的数字位,转
         DEC  BX
         MOV  AH,[BX]    ;取输入分数的百位数
   FLOP: MOV  [DI],AX    ;将处理后的分数送SCORE1
         POP  SI
         POP  DX
         POP  BX
         POP  AX
         RET
     FYSTYSB  ENDP
     ;屏幕初始化子程序,清屏、光标定位等
     PMCSH  PROC
     W_TOP=0   ;窗口左上角行号
     W LEFT=0   窗口左上角列号
     W BOTTOM=24   ;窗口右下角行号
     W BOTTOM=79   ;窗口右下角列号
     W PROPERTY=7   ;属性值,黑底白字
     PAGE N0=0     显示页号
     MOV   AL,PAGE NO
     MOV   AH,5
     INT   10H    ;设置当前页,AL=页号
     MOV   CH,W_TOP
     MOV   CH,W_LEFT
     MOV   DH,W_BOTTOM
     MOV   DL,W_RIGHT
     MOV   BH,PROPERTY
     MOV   AL,0
     MOV   AH,6
     INT   10H    ;上卷指定窗口
     MOV   BH,PAGE_NO    ;光标定位到窗口左上角
     MOV   DH,W_TOP
     MOV   DL,W_LEFT
     MOV   AH,2
     INT   10H
     RET
   PMCSH    ENDP
       ;屏幕显示输出子程序。入口参数在TEMP1和DX中
   PMXSSC PROC
       PUSH  AX
       PUSH  DX
       MOV   AH,09H
       INT   21H
       LEA   DX,TEMP1
       INT   21H
       POP   DX
       POP   AX
       RET
     PMXSSC    ENDP
       ;文件处理子程序
      WJCL  PROC
      MOV    AH,3CH    ;建立文件
      MOV    CX,20H    ;正常文件属性
      LEA    DX,FILE
      INT    21H
      MOV    HANDLE,AX    ;保存输出文件句柄
      MOV    SI,160   ;本段程序将显示器显示的内容复制到数据缓冲区
              ;BUF,从显示器的第二行开始
      LEA    DI,BUF
      MOV    CH,8   ;共复制显示器8行内容
      XOR    DX,DX    ;读入缓冲区BUF的显示器文件的字节数
      WLOP1: PUSH SI
      MOV    CL,80   ;显示器每行显示80个字符
      WLOP2: MOV  AX,08800H    ;彩显卡段地址
      MOV    DS,AX
      MOV    BX,[SI]
      MOV    AX,SEG BUF    ;数据缓冲区段地址
      MOV    DS,AX
      MOV    [D1],BL    ;将读入显示器字符的ASCⅡ码送入BUF
      INC    DX
      INC    DI
      INC    SI
      INC    SI
      DEC    CL
      JNZ    WLOP2
               ;以下6行代码用来将刚读入BUF的一行显示器的内容从尾部开始.删除连续空格,直到遇到
          一个非空格字符,BUF指针DI同步减少
   WLOP4: DEC  DI
          MOV  BL,[DI]
          CMP  BL,20H
          JNZ  WLOP3
          DEC  DX
          JMP  WLOP4
        ;以下5行代码用来在从BUF送入的显示器每一行字符最后添加回车、换行符号.以使BUF输出到文本文件的内容能自动换行
   WLOP3: INC    DI
          MOV    BYTE PTR[DI],0DH;
          INC    DI
          INC    DX
          MOV    BYTE PTR[DI],0AH
          INC    DI
          INC    DX
          POP    SI
          ADD    SI,160   ;为扫描显示器下一行内容做准备
          DEC    CH
          JNZ    WLOP1
          MOV    COUNT2,DX
          MOV    AX,DSEG
          MOV    DS,AX
          LEA    DX,BUF    ;数据缓冲区偏移地址
          MOV    CX,COUNT2   ;取出已读入文件字节数
          MOV    BX,HANDLE    ;取输出文件句柄
          MOV    AH,40H    ;顺序写
          INT    21H    ;关闭输出文件
          MOV    AH,3EH
          MOV    BX,HANDLE
          INT    21H
          RET
   WJCL   ENDP
      ;非压缩BCD码转换为二进制数
   BCDTBlN    PROC
          PUSH  BX
          PUSH  CX
          PUSH  DX
          PUSH  SI
          XOR  AL,AL
          LEA  SI,TEMP1
   LOOP1: MOV  DL,[SI]
          MOV  BL,AL    ;出口参数由AL带出
          SHL  AL,1
          SHL  AL,1
          ADD  AL,BL
          SHL  AL,1
          ADD  AL,DL
          DEC  CH
          JZ   LOOP2   ;十进制BCD码数位转换完,转
          INC  SI
          JMP  LOOP1
   LOOP2:  POP    SI
           POP    DX
           POP    CX
           POP    BX
           RET
   BCDTBIN    ENDP
   ;SCORE1(BCD码)转换为SCORE2(二进制)
        SCOTSCO2 PROC
        PUSH  AX
        PUSH  BX
        PUSH  CX
        PUSH  DX
        PUSH  SI
        PUSH  DI
        LEA   SI,SCORE1
        LEA   DI,SCORE2
        MOV   DX,COUNT
   SLOP1: MOV  BX,[SI]
          MOV  AX,BX
          SHL  AH,1
          SHL  AH,1
          ADD  AH,BH
          SHL  AH,1
          MOV  CL,4
          SHR  BL,CL
          ADD  AH,BL
          MOV  BH,AH
          SHL  AH,1
          SHL  AH,1
          ADD  AH,BH
          SHL  AH,1
          AND  AL,0FH
          ADD  AH,AL
          MOV  [DI],AH
          INC  SI
          INC  SI
          INC  DI
          DEC  DX
          JNZ  SLOP1
          POP  DI
          POP  SI
          POP  DX
          POP  CX
          POP  BX
          POP  AX
          RET
   SCOTSCO2 ENDP
           ;二进制数转换为压缩BCD码。入口参数在AL中,出口参数在AX中
     BINTBCD    PROC
         PUSH   BX
         PUSH   CX
         PUSH   DX
         PUSH   SI
         PUSH   DI
         MOV    TEMP2,0
         MOV    TEMP2+1.0
         MOV    TEMP2+2.0
         LEA    DI,TEMP2
         MOV    BL,10
  BLOP1: CMP    AL,0
         JZ     BLOP2
         XOR    AH,AH
         DIV    BL
         MOV    [DI],AH
         INC    D1
         JMP    BLOP1
  BLOP2: MOV    AH,TEMP2+1   ;取十位数字
         MOV    CL,4
         SHL    AH,CL
         MOV    AL,TEMP2   ;取个位数字
         OR     AL,AH    ;十位和个位数字合并
         MOV    AH,TEMP2+2   ;取百位数字
         POP    DI
         POP    SI
         POP    DX
         POP    CX
         POP    BX
         RET
   BINTBCD    ENDP
   MAIN  ENDP
   CSEG    ENDS
   END    START
【答案解析】本练习是对一个班级的学生进行成绩统计管理分析,是综合性比较强的题目。目的是通过该题目规范学生的编程思路。该题目比较完整,不仅有键盘输入,还有数据合理性验证,将录入数据经格式化处理后存放在要求的位置,同时还包含文件管理和屏幕输出显示部分。