最近一段时间,在做一个题目,主要实现汇编语言下面的电子钟功能.提供主要的设置时间,设置闹铃,显示时间等功能。本来想从网上找个参考,也好让自己更快的完成任务,也更快的入手.没想到,这么大一个Internet竟然没有让我发现一个很好的参考,说的准确一点,其实是根本没有发现任何一个参考. 只有一个是没有完成的,只有大致轮廓,不过我觉得不可行,索性自己,查阅IBM-PC汇编语言程序设计,最后写了个出来,拿出来供大家学习参考.同时如有不妥善指出,希望大家批评指正..主要目的是为了促进提高大家的学习.
下面是程序,已经在MASM5下编译运行通过..但有个缺点,也是明显的,就是CPU占用率太多.日后有时间再更正好了.
--------------------------------------------------------------------------------------------------------------------------------------------------
(请尊重原创,如果引用,请注明出处.)
;*************************************************************************** ;Author: ;Date : 2005/12/24 ;College: Zhejiang University of Science and Technology ;Subject:A simple Colck program writen by Asmble language. ;function: Display Time ; Set Time ; Set Clock Ring ; Set Display Style ;*************************************************************************** ;程序简介: ; 采用驻留中断程序的方式,将系统定时器提供给用户的ICH中断进行重新设计,加载新的 ; 处理程序,使得每秒得到中断。主程序首先注册新的中断处理程序,从系统取得系统时间 ; 然后无限等待,直到在中断程序中检测到有按键事件的发生,然后,主程序取消中断处理程序, ; 开始判断输入的键,然后根据相应的按键,提供相应的设计功能。 ; 中断处理程序每次得到系统定时器的中断时,判断是否到达1秒(约18.2次为1秒)。如果到达 ; 则,更新时间,存入数据区,然后更新数据的显示。当有按键事件发生时,设置数据区的按键 ; 标记为'真'。当主程序检查到该标记为真时,则取消中断程序的驻留。然后处理相应的事件。 ; ;主要步骤: ; 1.定义显示界面 ; 2.调用系统时间,并将得到的二进制数值转换成ASCII码,将数据存入内存 ; 3.定时的将存在内存区的时间数用显示字符串的形式显示出来(采用ICH中断) ; 4.获取键盘按键值,判断键值决定是否退出系统或执行其他功能 ; 5.时间设定功能,通过键盘设定时,分,秒(时:分:秒) ; 6.闹钟功能,定时报声(时:分) ; ;存在的缺点: ; 1.对输入时间的格式没有严格控制,比如输入11:11:11 同11'11'11,或 11,11,11 ; 同样的效果; ; 2.当程序运行时,由于主程序无限循环,然后等待中断刷新时间,因为无限循环,因此 ; 占用CPU使用时间比较多。以后扩展可采用一些方式比如,WAIT,HLT等功能实现资源的 ; 节省。 ; 3.有些函数可以进一步进行抽象和整合。从而使得代码数量减少。 ; ;参考书籍:IBM-PC汇编语言程序设计(第2版),沈美明,温冬婵,清华大学出版社 ; 296页,中断向量的设置与恢复; ; 301页,定时器中断程序 ; 389页,通用发声程序 ;*************************************************************************** .model small ;------------------------------------------------------------------------- .stack ;------------------------------------------------------------------------- .data
HOUR db ? MIN db ? SEC db ? MSEC db ?
NHOUR db 0 ;设置时间使用 NMIN db 0 ;设置时间使用 NSEC db 0 ;设置时间使用
RHOUR db 1 ;设置闹铃时间使用 RMIN db 1 ;设置闹铃时间使用 RSEC db 1 ;设置闹铃时间使用
FUNKEY db ? ;功能设置使用
keySw db 00h ;是否按键开关 RingSw db 00h ;闹铃开关
SWITCH db 0FFh ;刷新开关,真,则不断在定时器中断函数中刷新,否则不刷新
n db 0dh,0ah,'$' count dw 1 sMsg db '**** welcome to Simple clock ****',0dh,0ah,'$' qMsg db '**** Please Input q to quit ****',0dh,0ah db ' ','**** Input s to set new Time ****',0dh,0ah db ' ','**** Input r to set ringTime ****',0dh,0ah,'$' setTMsg db 'Please Input new Time(HH:MM:SS)',0dh,0ah,'$' setRMsg db 'Please Input Ring Time(HH:MM)',0dh,0ah,'$' authorMsg db '---------------------------------------',0dh,0ah db 'Author:ChengZengcun',0dh,0ah db 'E-mail:bohemia1985@163.com',0dh,0ah db 'Zhejiang University of Science and Technology',0dh,0ah db 'Version 1.0',0dh,0ah db 'Date:2005/12/23',0dh,0ah db '---------------------------------------',0dh,0ah,'$' ;------------------------------------------------------------------------- .code
PUSHA MACRO PUSH DS PUSH AX PUSH BX PUSH CX PUSH DX ENDM
POPA MACRO POP DX POP CX POP BX POP AX POP DS ENDM
;---------------------------------------------------------------------------- ;Main program
main proc far start: mov ax,@data ;allot data segment mov ds,ax push ds ;store the ds
;get the System time call GETTIME ;取得系统时间 call initUI ;初始化界面
;save old interrupt vector mov al,1ch ;al<=vector number mov ah,35h ;to get interrupt vector int 21h ;call DOS
push es ;save registers for restore(old base address) push bx ;save offset of interrupt 1CH push ds
;set new interrupt vector mov dx,offset tUpdate ;dx<=offset of procedure ring mov ax,seg tUpdate ;ax<=segment of procedure ring mov ds,ax ;ds<=ax mov al,1ch ;al<=vector# mov ah,25h ;to set interrupt vector int 21h ;call DOS
pop ds ;restore ds in al, 21h ;set interrupt mask bits and al, 11111100b out 21h,al sti
delay: PUSHA cmp ds:[keySw],0FFh ;check if any key is pressed jz exitdelay ;exit delay if any key is pressed
ringP: ;闹铃判断 ;mov dl,ds:[MIN] mov dl,ds:[RHOUR] ;判断闹铃时间(小时) cmp dl,ds:[HOUR] jne noRing mov bl,ds:[RMIN] ;判断闹铃时间(分钟) cmp bl,ds:[MIN] jne noRing
mov dh,ds:[RingSw] ;check if the Ring Switch is On.(只要调用SETRTIME就会打开闹铃开关) cmp dh,00h jz noRing call GENSOUND ;call GENSOUND to make clock ring noRing: POPA ;wait jmp delay
exitdelay: POPA
;restore old interrupt vector pop dx ;restore registers(restore the old offset) pop ds ;restore old base of interrupt mov al, 1ch ;al<=vector# mov ah, 25h ;to restore interrupt int 21h ;call DOS
pop ds ;restore ds
;读敲入的按键 mov ah,07h int 21h ;mov al,ds:[FUNKEY] cmp al,'s' jne next1
;判断读入字符然后进行相应处理,此时中断函数已经取消,只是单线程
setT: ; mov ah,07h ;读字符 ; int 21h mov ds:[FUNKEY],al call SETTIME ;调用子函数设置时间 ; mov ah,01h ; int 21h mov ds:[keySw],00h jmp start next1: cmp al,'r' jnz next2 mov ds:[FUNKEY],al
call SETRING;调用字函数设置闹铃时间 mov ds:[keySw],00h
jmp start
next2: cmp al,'q' je endmain mov ds:[keySw],00h jmp start endmain: mov ax,4c00h ;exit int 21h main endp
;------------------------------------------------------------------------ ;定时中断程序() ; ;Introduction: ;在系统定时器(中断类型为8)的中断处理程序中,有一条中断指令INT 1CH, ;时钟中断每发生一次(约每秒发生18.2次,即55ms发生一次)都要嵌套调用 ;一次中断类型1CH的处理程序。在ROM BIOS例程中,1CH的处理程序只有一条IRET, ;实际上它并没有作任何工作,只是为用户提供了一个中断类型号。如果用户有某种 ;定时周期性的工作需要完成,就可以利用系统定时器的中断间隔,用自己设计的 ;处理程序来代替原有的1CH的中断程序。 ; 本程序就是将原有1CH中断程序进行替换,实现了定时更新时间。采用DOS ;中断驻留中断程序。 ;
tUpdate proc near push ds ;save the working registers push ax push bx push cx push dx mov ax, @data ;allot data segment mov ds, ax sti
;judge if it is time for cUpdate dec count ;count for timer interval(约18.2次为1秒) jnz exit ;exit if not for 1 second mov count,18 ;control ring interval delay(10s)
;call the CALTIME call CALTIME ;更新时间
;检测刷新开关是否打开 mov bl,ds:[SWITCH] cmp bl,00 jz exit
;检测是否有按键按下,取适当的值进行退出控制 PUSHA MOV AH, 0BH ;检测是否输入消息(按键消息) INT 21H INC AL JNZ nokey mov ds:[keySw],0FFh ;设置已经被按键 POPA jmp exit nokey: POPA
call DISPLYH ;显示小时 call DISPLYM ;显示分钟 call DISPLYS ;显示秒 exit: cli mov al,20h ;set EOI out 20h,al pop dx ;restore the reg. pop cx pop bx pop ax pop ds iret ;interrupt return tUpdate endp
;-------------------------------------------------------------------- ;关闭定时刷新开关 closeSw proc near PUSHA mov bl,0h mov ds:[SWITCH],bl ;关闭刷新开关 POPA ret closeSw endp ;---------------------------------------------------------- ;打开定时刷新开关 openSw proc near PUSHA mov bl,0FFh mov ds:[SWITCH],bl ;打开刷新开关 POPA ret openSw endp ;--------------------------------------------------------------------------- ;初始化显示界面,可以在此处添加额外的设置选项 initUI proc near PUSHA ;设置显示方式(40×25 黑白文本,16级灰度) mov ah,0h mov al,00h int 10h ;显示头标题 mov dh,00h mov dl,02h call MOVCUR mov dx,offset sMsg ;dx<=offset of sMsg mov ah,09h ;to display sMsg int 21h ;call DOS ;显示结尾标题 mov dh,05h mov dl,02h call MOVCUR mov dx,offset qMsg ;dx<=offset of qMsg mov ah,09h ;to display qMsg int 21h ;call DOS
;显示作者信息 mov dh,09h mov dl,00h call MOVCUR mov dx,offset authorMsg ;dx<=offset of authorMsg mov ah,09h ;to display authorMsg int 21h ;call DOS
POPA ret initUI endp ;-------------------------------------------------------------------------- ;移动光标(dh:row,dl:col) ; dh ;参数设置行 ; dl ;参数设置列 MOVCUR proc near PUSHA ;设置光标位置 mov ah,2h mov bh,0 int 10h POPA ret MOVCUR endp ;------------------------------------------------------------------------- ;显示小时 DISPLYH PROC NEAR
PUSHA ; 设置光标位置 mov dh,2h ;set Row No. mov dl,10d ;set Column NO. call MOVCUR mov dh,0 mov dl,ds:[HOUR] call output ;调用output函数输出小时 mov dl,':' mov ah,02h int 21h POPA RET DISPLYH ENDP
;---------------------------------------------------------- ;显示分钟
DISPLYM PROC NEAR PUSHA ;设置光标位置 mov dh,2h ;set Row No. mov dl,13d ;set Column NO. call MOVCUR mov dh,0 mov dl,ds:[MIN] call output mov dl,':' mov ah,02h int 21h
POPA RET DISPLYM ENDP
;---------------------------------------------------------- ;显示秒 DISPLYS PROC NEAR PUSHA ;设置光标位置 mov dh,2h ;set Row No. mov dl,16d ;set Column NO. call MOVCUR mov dh,0 mov dl,ds:[SEC] call output POPA ret DISPLYS endp ;---------------------------------------------------------- ;调用DOS中断取得系统时间 GETTIME PROC NEAR PUSHA mov ah,2ch ;get the System time,CH:CL=(H:M),DH:DL=(s:1/100s) int 21h mov ds:[HOUR],CH ;取得小时 mov ds:[MIN],CL ;取得分钟 ; dec DH ;延迟1秒 mov ds:[SEC],DH ;取得秒 mov ds:[MSEC],DL
POPA ret GETTIME endp
;---------------------------------------------------------- ;调整时间,累加秒,分,时
CALTIME proc near PUSHA ;调整秒 inc ds:[SEC] cmp ds:[SEC],60d jb endc mov ds:[SEC],0h setM: ;调整分钟 inc ds:[MIN] cmp ds:[MIN],60d jb endc mov ds:[MIN],0h setH: ;调整小时 inc ds:[HOUR] cmp ds:[HOUR],24d jb endc mov ds:[HOUR],0h endc: POPA ret CALTIME endp ;--------------------------------------------------------------------------- ;设置时间 SETTIME proc near PUSHA call closeSw ;关闭定时刷新 mov dh,11h mov dl,00h call MOVCUR ;移动光标 mov dx,00h mov ah,09h ;输出提示信息,提示输出设置时间 mov dx,offset setTMsg int 21h call READNT ;读取时间(HH:MM:SS) ;设置时间 mov ch,ds:[NHOUR] ;HOUR mov cl,ds:[NMIN] ;MIN mov dh,ds:[NSEC] ;SECOND mov dl,0h ;1/100 SECOND mov ah,2Dh int 21h
cmp al,00h je SetSuccess mov ah,02h ;设置时间中断调用失败处理 mov dl,'!' int 21h SetSuccess: call openSw ;打开刷新开关 POPA ret SETTIME endp ;--------------------------------------------------------------------- ;设置闹铃时间: SETRING proc near PUSHA call closeSw ;关闭定时刷新 mov dh,11h mov dl,00h call MOVCUR ;移动光标 mov dx,00h mov ah,09h ;输出提示信息,提示输出设置时间 mov dx,offset setRMsg int 21h call READRINGT ;读取闹铃时间(HH:MM) mov ds:[RingSw],0FFH ;设置闹铃开关为 '开' rSetSuccess: call openSw ;打开刷新开关 POPA ret SETRING endp ;------------------------------------------------------------------ ;读取时间(从ASCII码到16进制数值的转换)(HH:MM:SS) READNT proc near PUSHA mov ah,01h int 21h mov bx,0h ;bx存放得到的数值(16进制) loop1: cmp al,'0' ;读取小时 jl endloop1 cmp al,'9' jg endloop1 sub al,30h ;transform from char to number cbw xchg ax,bx mov cx,0ah ;put 10d into cx mul cx xchg ax,bx add bx,ax mov ah,01h ;put the read interrupt type int 21h ;call the DOS interrupt jmp loop1 ;存取小时 endloop1: mov ds:[NHOUR],bl; mov bx,0h; loop2: mov ah,01h int 21h ;bx存放得到的数值(16进制) cmp al,'0' ;读取分钟 jl endloop2 cmp al,'9' jg endloop2 sub al,30h ;transform from char to number cbw xchg ax,bx mov cx,0ah ;put 10d into cx mul cx xchg ax,bx add bx,ax jmp loop2 ;存取分钟 endloop2: mov ds:[NMIN],bl mov bx,0h loop3: mov ah,01h int 21h ;bx存放得到的数值(16进制) cmp al,'0' ;读取秒 jl rlast cmp al,'9' jg rlast sub al,30h ;transform from char to number cbw xchg ax,bx mov cx,0ah ;put 10d into cx mul cx xchg ax,bx add bx,ax jmp loop3
rlast: ;存取秒 mov ds:[NSEC],bl POPA ret READNT endp ;---------------------------------------------------------------------------- ;读取闹铃时间 READRINGT proc near PUSHA mov ah,01h int 21h mov bx,0h ;bx存放得到的数值(16进制) rloop1: cmp al,'0' ;读取小时 jl rendloop1 cmp al,'9' jg rendloop1 sub al,30h ;transform from char to number cbw xchg ax,bx mov cx,0ah ;put 10d into cx mul cx xchg ax,bx add bx,ax mov ah,01h ;put the read interrupt type int 21h ;call the DOS interrupt jmp rloop1 ;存取小时 rendloop1: mov ds:[RHOUR],bl; mov bx,0h; rloop2: mov ah,01h int 21h ;bx存放得到的数值(16进制) cmp al,'0' ;读取分钟 jl rendloop2 cmp al,'9' jg rendloop2 sub al,30h ;transform from char to number cbw xchg ax,bx mov cx,0ah ;put 10d into cx mul cx xchg ax,bx add bx,ax jmp rloop2 ;存取分钟 rendloop2: mov ds:[RMIN],bl
POPA ret READRINGT endp ;----------------------------------------------------------------------------- ;闹铃发声程序 ; ;DI,保存发声频率,CX,BX指定发声程序的持续时间 GENSOUND proc near PUSHA push DI
;set the frequency mov di,020h mov bx,0FFh
mov al,0b6h ;write timer mode reg. out 43h,al mov dx,12h ;timer divisor mov ax,348ch ;1193100HZ/freq div di out 42h,al ;write timer2 count low byte mov al,ah out 42h,al ;write timer2 count high byte in al,61h ;get current port setting mov ah,al ;and save it in ah or al,03h ;turn speaker on out 61h,al waitsound: mov cx,500h ;wait for specified intevral delaySound: loop delaySound dec bx jnz waitsound mov al,ah ;recover value of port out 61h,al pop DI POPA ret GENSOUND endp ;--------------------------------------------------------------------------- ;输出字符(dx中存放十进制数值) output proc near PUSHA mov ax,dx ;put the final num into ax mov dx,00h mov cx,0h trans: mov bx,000ah ;this loop put the char into memory div bx ;divide 10d add dl,30h ;transform to character mov dh,0h push dx inc cx mov dx,00h cmp ax,00h jg trans cmp cx,01h jg print
addZero: ;增加前导0,,比如:02 mov dh,0 mov dl,'0' push dx inc cx print: ;this loop output the char cmp cx,0h jle endout pop dx mov dh,0 dec cx mov ah,02h int 21h ;call DOS output ability jmp print endout: POPA ret output endp
;---------------------------------------------------------- end start ;end assemble
.end