学生课程设计(论文)
题目:打字计时练习
学生姓名:学号:
所在院(系): m 专业:计算机科学与技术
班级:
指导教师:职称:
2008年12 月26 日
攀枝花学院本科学生课程设计任务书
注:任务书由指导教师填写。
摘要
在IBM-PC系统中,驻留的ROM中有8KB的BIOS。它实现对系统中种种常用设备的输入输出操作的管理。其中的16H中断调用即为键盘输入输出中断调用,实现从键盘输入字符到AL寄存器、读键盘缓冲区字符到AL寄存器、读取特殊功能键的状态等功能。本程序是一个在键盘上练习打字并能统计时间的程序,在缓冲区中预放了一些字母,运行时,可按照屏幕上显示的字母输入练习,每输入完一行按回车键后,可显示出练习输入的时间。通过它练习英文打字练习,并能够统计时间。程序运行时,使用自编的中断向量处理程序。程序退出时,恢复中断向量。
关键词打字计时,寄存器,缓冲区,中断向量
1、课程设计题目描述和要求
1.1 问题提出及功能要求
设计一个在键盘上练习打字并能统计时间的程序。通过它练习英文打字练习,并能够统计时间。屏幕上显示出提示信息和练习例句,按照例句从键盘上输入字母即可,每输入完一行后按回车键,可显示出练习输入的时间和下一行例句。按Esc键可退出程序。
1.2 运行环境
Windows XP/98/2000操作系统、文本编辑器使用EDIT或NOTEPAD,汇编程序使用MASM.EXE,连接程序使用LINK.EXE,调试程序使用DEBUG.EXE。
1.3 本课题涉及的知识点
定义数据段,设置主要参数:设置9号中断向量地址,9号中断向量首址,1C 向量地址;定义时间参数为秒、分、小时。在代码段,调用中断8259、键盘中断、时钟中断、并设置打字计时文件程序顺序。整个程序使用序分化各子功能。
2、算法设计
2.1设计思路
在键盘上练习的打字例句及其地址表、计时单元等预先放在DATA数据段中,自编的中断程序kbint检测按键时产生的中断,并把按键的扫描码转换为ASCII 码存入缓存区buffer,这个工作和ROM09类型的键盘中断例行程序所完成的工作大致相同,只是在解释各个键时作了简化,即只解释了可显示字符,如英文字母、数字及一些符号;其他特殊键,如组合键、双态键、功能键等都未作解释,只作为字符码0来处理。在主程序部分保存原09类型的中断向量,设置自编例行程序的中断向量,设置中断屏蔽位,然后开中断。当键盘中断处理后,主程序中安排指令读取缓冲区中的字符并显示在屏幕上。在主程序的末尾,利用DOS功能恢复原09类型的中断向量,然后返回DOS。在显示键入的字符时,还应判断字符是否为回车(0DH)。如为回车符,说明一个句子输入完毕,此时调用显示时间的子程序显示出打字时间,然后顺序显示下一个例句。如果所存放的例句都已显示完,则又从第一个例句开始显示,直至键入某一功能键(如Esc键、Shift键等)退出程序,结束打字练习。打字时间统计利用自定时器中断处理程序clint来完成。每输完一例句,计时一次。在此中断例行程序中,定时器中断的次数记录在计数单元count中,当count计数值为18时,sec计数单元加1 ,当sec计数值达到60时,min计数单元加1 。因为输入完一个句子无论如何也不会达到小时级,所以对hours计数值的调整判断,实际上只起到退出定时器中断的作用。
显示时间的子程序disptime分别将各遍野单元的二进制数转换为十进制数,并以min:sec:msec的形式显示出来。其中msec是由count记数值转换成的:count×1秒/18.2=count×55ms
2.2程序流程图
3、代码设计
3.1功能模块说明
3.1.1定义数据段,设置主要参数:
缓冲区:BUFFER, BUFPT1 ,BUFPT2
3.1.2代码段
调用中断8259,键盘中断,时钟中断,设置打字计时文件程序顺序
3.1.3使用子程序分化各子功能
DISPTIME PROC NEAR
BINDEC PROC NEAR ;将二进制转化为十进制显示时间
DECDIV PROC NEAR ;转化成字符显示
DISPCHAR PROC NEAR
KBGET PROC NEAR ;获取键盘键入的信息
KBINT PROC NEAR ;查表查到相应的字符
RESET09 PROC ;重新设置09号中断
RESET1C PROC ;重新设置1c功能
RESET PROC ;回复1C与09
3.2源程序
STACK SEGMENT PARA STACK 'STACK'
DB 256 DUP(0)
TOP LABEL WORD
STACK ENDS
DATA SEGMENT PARA PUBLIC 'DATA'
BUFFER DB 16H DUP(0) ;缓冲区大小
BUFPT1 DW 0 ;缓冲区1
BUFPT2 DW 0 ;缓冲区2
KBFLAG DB 0 ;参数,80H表示允许,00表示不允许PROMPT DB ' *PLEASE PRACTISE TYPING*',0DH,0AH,'$' ;定义提示语句SCANTAB DB 0,0,'1234567890-=',8,0 ;定义键盘
DB 'QWERTYUIOP[]',0DH,0 ;定义键盘第一行英文字母及符号DB 'ASDFGHJKL;',0,0,0,0 ;键盘第二行英文字母及符号
DB 'ZXCVBNM,./',0,0,0 ;键盘第三行英文字母及符号
DB ' ',0,0,0,0,0,0,0,0,0,0,0,0,0
DB '789-456+1230.'
******设置一些存储地址便于理解******
EVEN
OLDCS9 DW ? ;9号中断向量地址
OLDIP9 DW ? ;9号中断向量首地址
******预设置打字练习的句子******
STR1 DB 'ABCD EFGH IJKL MNOP QRST UVWX YZ.' ;句子1 DB 0DH,0AH,'$'
STR2 DB 'THERE ARE SOME NEWSPAPERS ON THE TABLE.'
DB 0DH,0AH,'$'
STR3 DB 'THERE ARE SOME CLOUDS IN THE SKY.'
DB 0DH,0AH,'$'
STR4 DB 'SHE ALWAYS EATS HER LUNCH AT NOON.'
DB 0DH,0AH,'$'
STR5 DB 'I DO NOT LIKE AUTUMN AND WINTER.'
CRLF DB 0DH,0AH,'$'
COLON DB ':','$'
EVEN
SADDR DW STR1,STR2,STR3,STR4,STR5
******定义时间参数******
COUNT DW 0 ;记时器记数
SEC DW 0 ;秒
MIN DW 0 ;分
HOURS DW 0 ;小时
SAVE_LC DW 2 DUP(?) ;1C向量地址
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,ES:DATA,SS:STACK
MAIN PROC FAR
START:
MOV AX,STACK
MOV SS,AX
MOV SP,OFFSET TOP
PUSH DS ;现场保护
SUB AX,AX
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV ES,AX
;*******将原中断向量09H保存在自设的单元中******
MOV AH,0
MOV AL,4
INT 21H
MOV AH,0BH
MOV BH,0
MOV BL,4
INT 11H
MOV AH,35H ;取中断向量的功能调用
MOV AL,09H
INT 21H
MOV OLDCS9,ES
MOV OLDIP9,BX
;******设置自编处理程序的中断向量******
PUSH DS
MOV DX,SEG KBINT ;自编中断类型程序段地址放入DS MOV DS,DX
MOV DX,OFFSET KBINT ;自编中断类型程序偏移地址放入DX MOV AL,09H
MOV AH,25H ;设置中断向量功能调用
INT 21H
POP DS
;******将原中断向量1CH保存在自设的单元中******
MOV AH,35H
MOV AL,1CH
INT 21H
MOV SAVE_LC,BX
MOV SAVE_LC+2,ES
;******设置自编处理程序的中断向量******
PUSH DS
MOV DX,SEG CLINT
MOV DS,DX
MOV DX,OFFSET CLINT
MOV AL,1CH
MOV AH,25H
INT 21H
POP DS
;******允许键盘和定时器中断******
IN AL,21H ;允许键盘和定时器中断
AND AL,11111100B
OUT 21H,AL
;******设置显示方式******
FIRST: MOV AH,0
MOV AL,3
INT 10H
;******显示提示信息******
MOV DX,OFFSET PROMPT ;显示提示信息MOV AH,9
INT 21H
;******在屏幕上显示例句******
MOV SI,0
NEXT: MOV DX,SADDR[SI] ;显示例句
MOV AH,09H
INT 21H
;******初始化变量******
MOV COUNT,0 ;设置初值
MOV SEC,0
MOV MIN,0
MOV HOURS,0
;******允许更高一级的中断******
STI
FOREVER:
CALL KBGET
TEST KBFLAG,80H
JNZ ENDINT
PUSH AX
CALL DISPCHAR
POP AX
CMP AL,0DH
JNZ FOREVER
MOV AL,0AH
CALL DISPCHAR
;******显示时间******
CALL DISPTIME
LEA DX,CRLF
MOV AH,09H
INT 21H
;******显示例句结束否?******
ADD SI,2
CMP SI,5*2
JNE NEXT
JMP FIRST
;******原来的中断向量******
ENDINT: CLI
PUSH DS
MOV DX,SAVE_LC
MOV AX,SAVE_LC+2
MOV DS,AX
MOV AL,1CH ;恢复中断向量1CH
MOV AH,25H
INT 21H
POP DS
PUSH DS
MOV DX,OLDIP9
MOV AX,OLDCS9
MOV DS,AX
MOV AL,09H
MOV AH,25H
INT 21H
POP DS
STI
RET
MAIN ENDP
;******定时器中断处理程序******
CLINT PROC NEAR
PUSH DS
MOV BX,DATA
MOV DS,BX
LEA BX,COUNT
INC WORD PTR[BX]
CMP WORD PTR[BX],18 ;初始化记数JNE RETURN
CALL INCT
ADJ:
CMP HOURS,12 ;是否满12小时JLE RETURN
SUB HOURS,12 ;满足减去12 RETURN:
POP DS
STI
IRET
CLINT ENDP
INCT PROC NEAR
MOV WORD PTR[BX],0
ADD BX,2
INC WORD PTR[BX] ;BX内容自增1 CMP WORD PTR[BX],60 ;和60比较
JNE EXIT
CALL INCT
EXIT: RET
INCT ENDP
;******显示打字练习时间******
DISPTIME PROC NEAR ;显示时间主段MOV AX,MIN ;分移动到AX寄存器CALL BINDEC ;显示数字
MOV BX,0
MOV AL,':'
MOV AH,0EH ;显示冒号
INT 10H
MOV AX,SEC ;秒移动到AX
CALL BINDEC
MOV BX,0
MOV AL,':'
MOV AH,0EH
INT 10H
MOV BX,COUNT
MOV AL,55D ;百分秒
MUL BL
CALL BINDEC
RET
DISPTIME ENDP
******将二进转化为十进显示时间******
BINDEC PROC NEAR
MOV CX,100D ;显示百位
CALL DECDIV
MOV CX,10D ;显示十位
CALL DECDIV
MOV CX,1 ;显示个位
CALL DECDIV
RET
BINDEC ENDP
DECDIV PROC NEAR ;转化成字符显示
MOV DX,0
DIV CX
MOV BX,0
ADD AL,30H ;转换成ASCII
MOV AH,0EH
INT 10H ;显示一个字符
MOV AX,DX
RET
DECDIV ENDP
******获取键盘键入的信息******
KBGET PROC NEAR
PUSH BX
CLI
MOV BX,BUFPT1 ;标志缓冲区的大小
CMP BX,BUFPT2
JNZ KBGET2
CMP KBFLAG,0 ;!=00H的时候,一直获取键盘JNZ KBGET3
STI
POP BX
JMP KBGET
KBGET2:
MOV AL,[BUFFER+BX]
INC BX
CMP BX,16H
JC KBGET3
MOV BX,0
KBGET3:
MOV BUFPT1,BX
POP BX
RET
KBGET ENDP
;******键盘中断处理程序******
KBINT PROC NEAR
PUSH BX
PUSH AX
IN AL,60H ;键盘输入寄存器端口地址PUSH AX
IN AL,61H ;控制商品寄存器地址
OR AL,80H
OUT 61H,AL ;读数据
AND AL,7FH ;第八位清0,ASCII只有7位OUT 61H,AL
POP AX
TEST AL,80H
JNZ KBINT2
MOV BX,OFFSET SCANTAB ;查表输出字符
XLAT SCANTAB ;查表
CMP AL,0
JNZ KBINT4
MOV KBFLAG,80H
JMP KBINT2
KBINT4:
MOV BX,BUFPT2
MOV [BUFFER+BX],AL
INC BX
CMP BX,16H
JC KBINT3
MOV BX,0
KBINT3:
CMP BX,BUFPT1
JZ KBINT2
MOV BUFPT2,BX
KBINT2: CLI
MOV AL,20H
OUT 20H,AL ;送825920H端口POP AX
POP BX
STI
IRET
KBINT ENDP
DISPCHAR PROC NEAR ;显示字符
PUSH BX
MOV BX,0
MOV AH,0EH ;显示一个字符在AL里INT 10H ;显示一个字条符
POP BX
RET
DISPCHAR ENDP
CODE ENDS
END START
4、调试与运行
4.1实验步骤:
1)打开windows命令窗口
2)进入masm程序的安装目录,汇编源文件,如图:
图4.1.2 汇编程序
3)连接程序,并查看生成可执行文件weijia.exe。
图4.1.3 查看生成可执行文件
4)关闭对话框,打开可执行文件weijia.exe,开始打字计时练习。
4.2运行程序:
1)设置主程序段显示提示信息,程序功能应该实现下图所示:
图4.2.1 程序调试
2)调用DISPCHAR PROC NEAR 显示用户字条,调用DISPTIME PROC NEAR 显示时间,然后输出下一句提示程序应该实现下图所示:
图4.2.2 程序调试
3)如此循环多次,当第五个例句输入完成后,再返回第一句继续打字计时。
图4.2.3 程序调试
4.3调试中出现的问题
没有注意到要操作的目标数据(内存中的数据)的大小和使用的操作容器(寄存器)的大小没有一致。另外,对于处理数据的ds:[bx]方式,如mov ds:[bx],dx add bx,2在循环中应注意bx的递增或递减大小,如果处理字节,比如用dl,循环中就加或减1;如果处理字,比如用dx,就加或减2。在以后应该留意这个一致性。在8086上,ss和sp都是16位的,栈所处理数据的最小单位也是16位的,
即最小是一个字,栈一次读和一次写,都至少是一个字那么大。
5、总结
课程设计对我们多数初学汇编的学生来说,确实是个一座高峰。翻越它是要付出诸多努力的。我也是摸索了好久才弄明白它的。在此做个小结,也许对自己今后的学习有一些借鉴的作用。为了避免寄存器冲突的问题,在设计子程序时,应把所有用到的寄存器都入栈保存,但用来传递参数和结果的寄存器则要注意,不必入栈的就不入栈了,以免得不到预期的结果。可以把寄存器当作变量来看待,有时候它存储的是数据变量(如把BP当作字符个数的记录器),而有时候它存储的是地址变量(如DI、SI);内存中的数据,按它们的结构关系,则可以把它们看待成数组,我们对数组,可以通过首地址来访问它们。传递参数和结果的,一般可以用寄存器,也可以用一段内存空间来传递,特别是需要传递的数据较多的时候。在设计子程序时,往往涉及到许多的循环,这时候就要注意把不该循环的变量要放在循环体外。一周的汇编语言课程设计结束了,我的收获是很大的。通过课程设计的编程和对编程过程中遇到的问题的思考,有效的提高了我对汇编的认识和理解,切实的提高了我的编程能力,补全了我的不足。通过这次课程设计,我巩固所了学过的知识,能用汇编语言熟练地运用顺序、选择、循环三大结构,进一步掌握了各个指令的功能,特别是转向指令,掌握了子程序的定义及调用,以及正确地运用DEBUG指令。在整个程序的编写中,调试占了很大部分的时间。要想写好汇编语言的程序,必须认真对待代码的每一个细节,还必须熟练的掌握debug命令,这对程序的调试是非常重要的。通过这次实际动手能力的培养,我进一步熟悉了汇编语言的结构和使用方法,基本达到了能独立阅读、编制和调试一定规模的汇编语言程序的水平另外,我觉得,学习汇编有一个比较大的好处,就是可以了解到在电脑中每一个字节数据的来龙去脉,我觉得学习汇编可以发挥一下自己的空间想像力,特别是对内存中数据的组织、存储关系的想像。当然,学习汇编还有另一个重要的收获,就是如何处理数据的问题。