home *** CD-ROM | disk | FTP | other *** search
- page 60,132
- title 'CLOCKON'
-
- ;mode equ 0 ;mode = 0 for hh:mm only
- mode equ -1 ;mode = -1 for hh:mm:ss
-
- if mode eq 0
- bytes equ 5 ;bytes in hh:mm time display
- ticks equ 36 ;ticks between clock updates
- else
- bytes equ 8 ;bytes in hh:mm:ss time display
- ticks equ 18 ;ticks between clock updates
- endif
- ;
- bel equ 07H
- tab equ 09H
- lf equ 0aH
- cr equ 0dH
- ;beta equ 0e1H
- beta equ ' '
- ;
- ;****************************************************************
- ;
- ;++++ START OF RESIDENT CODE
- ;
- ;****************************************************************
- ;
- code segment
-
- assume DS:code, SS:code ,CS:code ,ES:code
-
- org 0080H
-
- dbuff equ $
-
- org 0100H
-
- stack equ $
- start:
- jmp initialize
- ;
- inst_msg db 'Clockon.HJH' ;identifier message (leave it here!)
- msg_size equ $ - offset start
- ;
- colour db 70h ;predefined clock colours
- ; ;lo nybble=F/G hi nybble=B/G
- freq dw 00600H ;beep frequency
- beep_length dw 40H ;beep length (modified by CLOCKON x)
- ;
- max_alarms equ 9 ;never more than 9 - may be less
- alarm_times dw max_alarms dup (0ffffH) ;alarm times
- ;
- beep_count equ 10 ;predefined beep count
- beeps db beep_count ;set beep counter
- ;
- page_no db 0 ;current page number
- no_display_flag db '+' ;'+' to enable, '-' to inhibit
- last_hour dw 0 ;last hour - used to detect hour update
- old_cursor dw 0 ;old cursor position
- ;
- ;
- line_col equ 0050H - bytes ;starting line and column for display
-
- clk_count dw ticks ;counts left to next update
- hours dw 0 ;time ASCII store
- db ':'
- minutes dw 0
- db ':'
- seconds dw 0
- ;
- old_SP dw 0 ;called SP
- old_SS dw 0 ;called SS
- oldint08 dd 0 ;original INT 08 vector
- oldint10 dd 0 ;original INT 10 vector
- bios_flag db 0 ;bios active flag
-
- ;=============================================================================
- ; VIDINT intercepts and handles the video interrupt 10H.
- ;=============================================================================
-
- ASSUME CS:code, DS:nothing
-
- vidint proc near
- pushf ;simulate INT
- inc CS:bios_flag ;bump flag
- call CS:oldint10 ;call original function
- dec CS:bios_flag ;decrement flag
- iret
- vidint endp
- ;
- ;----------------------------------------------------------------
- ;++++ Replaced INT 08 - used to detect when clock update needed
- ;
- ASSUME CS:code, DS:code
-
- newint08 proc near
- push ax ;save ax
- push DS ;and DS
- pushf ;and flags
- push CS ;set up DS to point
- pop DS ;to CS
- mov ax,clk_count ;get clock count
- dec ax ;decrement by 1
- jz int1 ;skip if reaches zero
- mov clk_count,ax ;new value saved
- jmp int8 ;quick exit
- int1:
- cli ;stop interrupts
- mov ax,SS ;save SS and SP
- mov old_SS,ax
- mov old_SP,sp
- mov ax,DS ;set SS to DS
- mov SS,ax
- mov sp,offset stack ;and create a new stack
- sti ;restart interrupts
- push bx ;save other regs
- push cx
- push dx
- push ES
- push si
- push di
- push bp
- mov ax,ticks ;reset clock count
- mov clk_count,ax
- ;
- ;++++ Check for the time
- ;
- mov ah,0 ;read clock count
- int 1AH ;returns clock count in CX:DX
- ;
- ;++++ The following code takes the clock count returned by INT 1Ah
- ; and:-
- ; (1) Multiplies by 16 by using repetitive shifts
- ; (ignores overflow at the high end and inserts zeros
- ; at the low end of DX:AX)
- ; (2) Divides by 17478 to convert to minutes
- ; (17478 = 60 * 18.2065 * 16)
- ;
- mov ax,dx ;low time count to ax
- mov dx,cx ;hi time count to dx
- shl ax,1 ;multiply by 2
- rcl dx,1 ;multiply by 2, add carry
- shl ax,1 ;multiply by 2
- rcl dx,1 ;multiply by 2, add carry
- shl ax,1 ;multiply by 2
- rcl dx,1 ;multiply by 2, add carry
- shl ax,1 ;multiply by 2
- rcl dx,1 ;multiply by 2, add carry
- mov bx,04446h
- div bx ;divide it by 17478
- ;AX=minutes DX=remainder
- mov cx,max_alarms ;# alarms to test
- xor bx,bx ;start at first alarm
- alarm_loop:
- call alarm_test
- jz int3 ;quick exit if alarm was sounded
- add bx,2
- loop alarm_loop
- int3:
- call display_check ;see if display wanted
- jc int4 ;continue if carry set
- jmp int7 ;otherwise quick exit
- int4:
- mov seconds,dx ;set seconds to dx
- mov bx,60 ;divisor = 60
- xor dx,dx ;clear dx
- div bx
- mov minutes,dx ;set minutes to dx
- cmp ax,0 ;test ax
- aam ;adjust
- cmp ax,last_hour ;see if hour has changed
- jz int5
- mov last_hour,ax ;update hour store
- call beep ;and beep once
- int5:
- add ax,'00' ;make hours into ASCII
- xchg ah,al
- mov hours,ax ;save bytes
- mov ax,minutes ;get minutes
- aam ;adjust
- add ax,'00'
- xchg ah,al
- mov minutes,ax ;and store
- mov ax,seconds ;get seconds
- xor dx,dx
- mov bx,60
- mul bx
- mov bx,04446h
- div bx
- aam ;adjust
- add ax,'00'
- xchg ah,al
- mov seconds,ax ;and store
- mov ah,3
- mov bh,page_no ;get page number
- int 10H ;read cursor position
- mov old_cursor,dx
- mov ah,2
- mov bh,page_no ;get page number
- mov dx,line_col ;starting line/column
- int 10H ;set cursor position
- mov si,offset hours ;point to ASCII store
- mov bl,colour ;colour select
- mov cx,bytes ;bytes to send
- int6:
- push cx ;save count
- mov al,[si] ;get character
- inc si ;bump to next address
- push si ;save current pointer
- mov cx,1
- mov ah,9
- int 10H ;write char with attribute
- mov ah,3
- int 10H ;read cursor position
- inc dl ;bump by 1 column
- mov ah,2
- int 10H ;set cursor position
- pop si ;recover current pointer
- pop cx ;recover count
- loop int6
- mov dx,old_cursor
- mov bh,page_no
- mov ah,2
- int 10H ;reset old cursor position
- int7:
- pop bp ;restore registers
- pop di
- pop si
- pop ES
- pop dx
- pop cx
- pop bx
- cli ;restore SP and SS
- mov ax,old_SS
- mov SS,ax
- mov sp,old_SP
- sti
- int8:
- popf ;recover flags
- pop DS ;and regs
- pop ax
- jmp dword ptr CS:oldint08
- newint08 endp
- ;
- ;----------------------------------------------------------------
- ;++++ Test to see if display of clock wanted
- ; Returns CARRY flag set if wanted
- ;
- display_check proc near
- push ax
- push bx
- cmp no_display_flag,'-' ;test display flag
- je dc_off
- cmp bios_flag,0 ;in bios?
- jne dc_off ;not allowed if it is
- disp_1:
- mov ah,15 ;get video mode
- int 10H
- mov page_no,bh ;save current page
- cmp al,2 ;see if mode 2
- je dc_on
- cmp al,3 ;or mode 3
- je dc_on
- cmp al,7 ;or mode 7
- je dc_on
- dc_off:
- pop bx
- pop ax
- clc
- ret
- dc_on:
- pop bx
- pop ax
- stc
- ret
- display_check endp
- ;
- ;----------------------------------------------------------------
- ;++++ Test for alarm pointed by bx
- ;
- alarm_test proc near
- cmp ax,alarm_times[bx] ;compare result with alarm time
- jne alarm_test_2
- push ax ;save alarm time
- call dbl_beep ;alarm double beep
- mov al,beeps ;load beeps
- dec al ;decrement
- mov beeps,al ;and save
- cmp al,0 ;see if zero yet
- jnz alarm_test_1 ;skip if not
- mov beeps,beep_count
- mov ax,0FFFFh ;reset alarm time to max
- mov alarm_times[bx],ax ;to prevent retrigger
- alarm_test_1:
- xor ax,ax ;set zero flag
- pop ax ;recover current minutes
- alarm_test_2:
- ret
- alarm_test endp
- ;
- ;----------------------------------------------------------------
- ;++++ Produces double beep for alarm
- ;
- dbl_beep proc near
- call beep ;first beep
- push cx
- mov cx,beep_length ;timeout loop
- dbl1:
- dec al
- jnz dbl1 ;extra delay
- loop dbl1 ;inter-beep delay
- pop cx
- call beep ;second beep
- ret
- dbl_beep endp
- ;
- ;----------------------------------------------------------------
- ;++++ Beep generator
- ; Directly accesses sound generator timer in 8253
- ;
- beep proc near
- push ax
- push cx
- mov al,0B6H ;select timer #2
- out 43H,al
- mov ax,freq ;pick up timer count
- out 42H,al ;low byte
- mov al,ah
- out 42H,al ;high byte
- in al,61H ;get control word
- push ax
- or al,3 ;start timer
- out 61H,al
- mov cx,beep_length ;beep length constant
- beep1:
- dec al
- jnz beep1
- loop beep1 ;time delay for ON
- pop ax
- out 61H,al ;restore timer OFF
- pop cx
- pop ax
- ret
- beep endp
- ;
- ;----------------------------------------------------------------
- ;
- end_resident equ $
- ;
- ;****************************************************************
- ;
- ;++++ END OF RESIDENT CODE
- ;
- ;****************************************************************
- page
- ;****************************************************************
- ;++++ Initialization code
- ; Tests for already loaded code.
- ; If not present, installs code first.
- ; Then proceeds to decode command line.
- ;
- initialize proc near
- not word ptr start
- xor bx,bx
- mov ax,CS
- next_segment:
- inc bx
- cmp ax,bx
- mov ES,bx
- jz not_installed
- mov si,offset start
- mov di,si
- mov cx,msg_size
- repz cmpsb
- or cx,cx
- jnz next_segment
- call scan ;scan command line
- mov al,err_code ;get error code
- mov ah,4cH
- int 21H ;exit program
- ;
- not_installed:
- mov ax,3510H ;save old interrupt 10H vector
- int 21H
- mov word ptr oldint10,bx
- mov word ptr oldint10[2],es
- mov ax,2510H ;then set the new 10H vector
- mov dx,offset vidint
- int 21H
- mov ax,3508H ;save old interrupt 08H vector
- int 21H
- mov word ptr oldint08,bx
- mov word ptr oldint08[2],es
- mov ax,2508H ;then set the new 08H vector
- mov dx,offset newint08
- int 21H
- push CS ;make ES=CS
- pop ES
- call scan ;scan command line
- mov dx,offset msg7
- call print ;send message to advise now resident
- mov dx,offset end_resident ;last address to save
- mov cl,4
- shr dx,cl ;make into pages
- inc dx ;plus 1
- mov al,err_code ;get error code
- mov ah,31H
- int 21H ;terminate but stay resident
- initialize endp
- ;
- ;----------------------------------------------------------------
- ;++++ Scan command line starting at 0080h
- ; Looks for control bytes and/or time code of form 'hh:mm'
- ;
- scan proc near
- mov alarms,0 ;alarms done count
- xor ax,ax ;AX=current minutes store
- xor bx,bx
- xor dx,dx ;DH=':' field counter
- mov di,0FFFFh ;DI=minutes store
- mov ES:beeps,beep_count ;preset beep counter
- mov si,offset dbuff ;point to start of string area
- mov bl,[si] ;get string length byte
- inc si
- mov byte ptr [si+bx],al ;end of string set to zero
- ;
- ;++++ Character scanner loop
- ;
- scan1:
- call getchar ;get a character
- jnz scan1a
- jmp all_done ;exit if all done
- scan1a:
- cmp bl,' ' ;test for space
- jb scan1 ;skip over control characters
- je space_found ;space found
- cmp bl,'?' ;test for '?' option
- jne scan1b
- ;
- ;++++ help message requested - ignore all other functions
- ;
- mov dx,offset helpmsg
- call print
- ret
- ;
- ;++++ Test now for '-'
- ;
- scan1b:
- cmp bl,'-' ;test for '-' option
- jne scan1c ;skip if not
- mov ES:no_display_flag,bl ;set up flag
- jmp short scan1
- ;
- ;++++ Test now for '+'
- ;
- scan1c:
- cmp bl,'+' ;test for '+' option
- jne scan1d ;skip if not
- mov ES:no_display_flag,bl ;set up flag
- jmp short scan1
- ;
- ;++++ Test now for 'x'
- ;
- scan1d:
- cmp bl,'X' ;test for 'x' option
- jne scan1e ;skip if not
- push ax
- mov ax,ES:beep_length ;get beep length
- shr ax,1 ;halve it
- add ES:beep_length,ax ;beep length time increased 50%
- pop ax
- jmp short scan1
- ;
- ;++++ Test now for '*'
- ;
- scan1e:
- cmp bl,'*' ;test for '*' option
- jne scan2 ;skip if not
- mov alarms,0 ;alarms done count
- xor ax,ax ;AX=current minutes store
- xor bx,bx
- xor dx,dx ;DH=':' field counter
- mov di,0FFFFh ;DI=minutes store
- mov cx,max_alarms
- clear_1:
- mov ES:alarm_times[bx],di ;clear alarm minutes store
- add bx,2 ;next store
- loop clear_1
- jmp short scan1
- ;
- ;++++ Test now for ':' time delimiter
- ;
- scan2:
- cmp bl,':' ;test for separator
- jnz scan3 ;skip if not
- inc dh ;bump field count
- cmp dh,2 ;see if 2 ':' chars received
- jae time_1 ;error if found
- push dx
- mov cx,60 ;multiply hours by 60
- mul cx
- pop dx ;recover regs
- mov di,ax ;save hours*60
- mov ax,0 ;clear counter again
- jmp short scan4
- space_found:
- cmp di,0ffffH ;see if anything done yet
- je time_5 ;skip if not
- cmp dh,1 ;see if timer was being processed
- jne time_1
- add ax,di ;add minutes to count
- cmp ax,1440 ;see if overflow
- jb time_2
- time_1:
- mov dx,offset msg1 ;error message
- mov err_code,2
- jmp time_msg
- time_2:
- mov bx,alarms ;get current alarms
- cmp bx,max_alarms ;see if too many
- jb time_3
- mov dx,offset msg4 ;too many alarms
- mov err_code,1
- jmp time_msg
- time_3:
- add bx,bx ;make count into pointer
- cmp ES:alarm_times[bx],0FFFFh ;see if free
- je time_4
- inc alarms ;bump alarms count
- jmp time_2
- time_4:
- mov ES:alarm_times[bx],ax ;save alarm minutes count
- inc alarms ;update alarms counter
- time_5:
- xor ax,ax ;clear running sum
- mov di,0ffffH ;initialize minutes counter
- xor dx,dx ;clear ':' conter
- jmp scan1 ;try next count
- ;
- ;++++ Must be 0-9 or else will be ignored
- ;
- scan3:
- cmp bl,'0' ;test for ASCII '0'-'9'
- jb bad_char
- cmp bl,'9'
- ja bad_char
- sub bl,'0' ;make into binary digit
- push dx
- mov cx,10
- mul cx ;multiply running sum by 10
- add ax,bx ;add in new digit
- pop dx
- cmp ax,59 ;see if overflow
- ja time_1 ;out of range
- scan4:
- jmp scan1 ;loop for next char
- bad_char:
- mov dx,offset msg5
- mov err_code,3
- jmp time_msg
- ;
- ;++++ scan complete
- ;
- all_done:
- cmp di,0ffffH ;see if new time specified
- je scan7 ;skip if not
- dec si ;back up over EOT byte
- jmp space_found ;process data as though delimiter
- ;
- time_msg:
- call print ;print error message
- scan7:
- mov dx,offset msg6 ;assume display is set
- cmp ES:no_display_flag,'-' ;test for no display
- jne scan8
- mov dx,offset msg3 ;print message
- scan8:
- call print
- call show_times ;show times
- ret ;return
- scan endp
- ;
- ;----------------------------------------------------------------
- ;++++ Get character from input buffer
- ;++++ Convert to upper case if necessary
- ; Place in BL and set zero flag if BL=0
- ;
- getchar proc near
- mov bl,[si] ;get character
- inc si ;bump string pointer
- cmp bl,'a' ;below 'a' test
- jb getchar_1
- cmp bl,'z' ;above 'z' test
- ja getchar_1
- sub bl,20h ;make Upper Case
- getchar_1:
- or bl,bl ;test for zero
- ret
- getchar endp
- ;
- ;----------------------------------------------------------------
- ;++++ send character to CON:
- ;
- charout proc near
- push ax ;save ax
- mov ah,2 ;set up command
- int 21H ;send character in DL
- pop ax ;recover ax
- ret
- charout endp
- ;
- ;----------------------------------------------------------------
- ;++++ Print string to user
- ;
- print proc near
- push ax ;save ax
- mov ah,9 ;set up command
- int 21H ;print string ending with '$'
- pop ax ;recover ax
- ret
- print endp
- ;
- ;----------------------------------------------------------------
- ;
- show_times proc near
- mov cx,max_alarms ;maximum alarms allowed
- mov bx,0 ;start at first alarm
- mov msg2,'0'
- show_time_1:
- mov ax,ES:alarm_times[bx] ;get alarm time
- cmp ax,0FFFFh ;see if cleared
- je show_time_5
- inc msg2 ;bump alarm counter
- mov dx,0 ;hi word zero
- div div60 ;convert to hh:mm
- push dx ;save remainder (minutes)
- call show_number ;show hours
- mov dl,':'
- call charout
- pop ax
- call show_number ;show minutes
- mov dl,' '
- call charout ;couple of spaces
- call charout
- show_time_5:
- add bx,2 ;bump pointer
- loop show_time_1
- call crlf
- mov dx,offset msg2 ;total alarms set
- call print
- ret
- div60 dw 60
- show_times endp
- ;
- ;----------------------------------------------------------------
- ;++++ display number in AL as two decimal digits
- ;
- show_number proc near
- xor ah,ah ;clear hi byte
- div div10 ;divide by 10
- add ax,'00' ;make ASCII
- push ax ;save result
- mov dl,al
- call charout ;print 10s digit
- pop ax
- mov dl,ah
- call charout ;print 1s digit
- ret
- div10 db 10
- show_number endp
- ;
- ;----------------------------------------------------------------
- ;++++ Send CRLF to console
- ;
- crlf proc near
- push dx
- mov dl,CR
- call charout
- mov dl,LF
- call charout
- pop dx
- ret
- crlf endp
- ;
- ;----------------------------------------------------------------
- ;
- err_code db 0 ;error code for DOS return
- alarms dw 0 ;current alarm counter
- ;
- helpmsg db CR,LF,'******* CLOCKON V2.01e',BETA,'*******',CR,LF
- db 'Command format is CLOCKON [+|-] [x] [*] [hh:mm .... ]',CR,LF
- db TAB,'where + enables clock display',CR,LF
- db TAB,' - inhibits clock display',CR,LF
- db TAB,' x extends alarm sounds',CR,LF
- db TAB,' * clears all current alarms',CR,LF
- db TAB,' hh:mm sets alarm times (up to 9)',CR,LF,'$'
- msg1 db bel,'ERROR: Invalid time. Range is 00:00 to 23:59',CR,LF,'$'
- msg2 db '0 alarm(s) set.',CR,LF,'$'
- msg3 db 'Clock display is inhibited.',CR,LF,'$'
- msg6 db 'Clock display is enabled.',CR,LF,'$'
- msg4 db bel,'WARNING: Only 9 alarms allowed.',CR,LF,'$'
- msg5 db bel,'ERROR: Illegal character in command.',CR,LF,'$'
- msg7 db 'CLOCKON is now resident.',CR,LF,'$'
- code ends
- ;
- end start
-