电子钟-汇编程序
最近一段时间,在做一个题目,主要实现汇编语言下面的电子钟功能.提供主要的设置时间,设置闹铃,显示时间等功能。本来想从网上找个参考,也好让自己更快的完成任务,也更快的入手.没想到,这么大一个Internet竟然没有让我发现一个很好的参考,说的准确一点,其实是根本没有发现任何一个参考. 只有一个是没有完成的,只有大致轮廓,不过我觉得不可行,索性自己,查阅IBM-PC汇编语言程序设计,最后写了个出来,拿出来供大家学习参考.同时如有不妥善指出,希望大家批评指正..主要目的是为了促进提高大家的学习.
下面是程序,已经在MASM5下编译运行通过..但有个缺点,也是明显的,就是CPU占用率太多.日后有时间再更正好了.
--------------------------------------------------------------------------------------------------------------------------------------------------
(请尊重原创,如果引用,请注明出处.)
;***************************************************************************
;Author: ChengZengcun()
;Date : 2005/12/24
;Email : bohemia1985@163.com
;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
发表于 @ 2005年12月24日 16:09:00|评论(loading...)|编辑