电子钟-汇编程序

电子钟-汇编程序

        最近一段时间,在做一个题目,主要实现汇编语言下面的电子钟功能.提供主要的设置时间,设置闹铃,显示时间等功能。本来想从网上找个参考,也好让自己更快的完成任务,也更快的入手.没想到,这么大一个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

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页