Time,Date,and Chime TSR



; chime.asm Time and date TSR Com file TASM code
;
;
; Works thru Win XP in a CMD box
;
; NEEDS UNINSTALL CODE ADDED !!
;
; RESIDENT TIME CLOCK and DATE DISPLAY with optional chime
;
; Original author unidentified
;
; Revised by Thomas A. Lundin
;
; usage: chime [/c]
; /c to toggle chiming. No chimes by default.
;
; Clock display can be toggled on and off by repeating 'chime'.
;
; Chimes should be toggled off when using heavy interrupt-driven
; software such as communications to avoid losing characters or
; hanging the system.
;
; 9/4/87 note:
; This version uses the clock tick to regulate the duration of
the
; chimes, meaning that the chimes should be the same length from
; one system to another, no matter what the CPU speed.
; Also modified start-up routines to automatically set the time
; display background to color or monochrome.
;
; 9/21/87 note:
; This version alterates a date display with the time display,
every
; 1.5 seconds. The date display is static, i.e., it is not
updated
; at midnight (it would be inefficient to check every hour for
an
; event which occurs once every 24 hours).
;
; Turning the chime off and on (typing chime, twice) will reset
the date.
;
; 1/05/88 note:
; This revision sets the clock display in the lower right corner
; instead of the upper right.
;
; 1/22/91 note:
; This revision causes the clock to automatically position itself
; according to the number of columns in the display. The
; program did this before, but only when it was initially loaded.
; Thus if you switched between an 80 and 132 column mode, the
clock would
; no longer be displayed in the upper right hand corner or
wherever.
; The program now reads the number of columns every time the clock
is
; displayed.
;
; 2/1/91 The chime has now been replaced by a ®cuckoo¯. The clock
will now
; cuckoo for the appropriate hourly figure and cuckoo once on
the
; quarter hour.
;
;v1.1 Toad Hall Tweak, 17 Apr 93
; - Retabified, reformatted for consistent upper, lower case.
; - Minor tweaks, tightening.
; David Kirschbaum, Toad Hall

TICKS EQU 27 ; number of ticks between updates (1.5
sec)
;old value -- 27
BEEPS1 EQU 1 ; duration of first beep in clock
ticks
BEEPS2 EQU 3 ; duration of second beep in clock
ticks
BEEPS3 EQU 1 ; pause between cuckoos
TONE1 EQU 5e7h ; first chime tone 5f7h
TONE2 EQU 74ch ; second chime tone

CR EQU 0DH ;v1.1
LF EQU 0AH ;v1.1

INTERRUPTS segment at 0h
org 1ch*4 ; This is to use INT 1Ch
timer_int label dword ; which is the timer interupt
INTERRUPTS ends

SCREEN SEGMENT AT 0B000H ; A dummy segment to use
SCREEN ENDS ; as the Extra Segment

CSEG SEGMENT
ASSUME CS:CSEG
ORG 100h ; org = 100 to make this into
; a .COM file
First: jmp Load_Clock

old_time_int dd ? ; The address INT 1Ch normally
uses
count500 dw TICKS ; Used to update clock every nnn
counts
beepticks dw 0 ; number of ticks for a BEEP
beepsleft db 0 ; number of beeps to output
cuckoo dw 0 ; number of cuckoos to output
cursor dw 0 ; Location of the cursor on
the screen
beept db 0 ; have-we-beeped flag
inbeep db 0 ; beep-in-progress flag
flash db 1 ; flashing colon flag
spkrstat db 0 ; old speaker status
video_port dw ? ; Video status port - check for
scanning
numcol db ? ; number of columns

hh dw 0 ; hours
mm dw 0 ; minutes
sc dw 0 ; seconds
hh1 dw 0 ; 12-hour value for cuckoo
hhdiv dw 32771 ; hours divisor (65543/2)
mmdiv dw 546 ; minutes divisor (1092/2)
ssdiv dw 9 ; second divisor (18/2)

displayit dw (7020h) ; leading space
dw 5 dup(703ah) ; Initial value for the clock
dw 2 dup(7020h) ; Add 2 ' 's for am/pm.

day_of_wk dw 0 ; day of the week
month dw 0 ; month
day dw 0 ; day
chimon dw 1 ; flag for chime in use or not
clokon dw 1 ; flag for chime in use or not

Clock PROC NEAR ; The timer INT will now come
here

cmp CS:clokon,1 ; is this interrupt silent ?
jz NewInt ; no, go execute it
jmp dword ptr old_time_int ; silent, just execute old int
NewInt:
pushf ; First call old time
interrupt to
call dword ptr old_time_int ; update count

call NeedBeep ; need to continue beep ?
dec CS:count500 ; should recalculate the
time ?
jnz Dont_Recalculate

push ax ; Save the registers - good
form
push cx
push di
push si
push ES

xor CS:flash,1 ; toggle the flashing colon
call Calc ; Recalculate the time
mov CS:count500,TICKS ; Reset Count500
;
; Dont_Recalculate:
; This is the routine for recalculating clock position when you switch
in and
; out of different column modes

mov ax,0 ; move memory location for column
numbers
mov ES,ax ; into ES:di
mov ax,044ah ;
mov di,ax
mov ah,ES:[di] ; move number of columns into ah
sub ah,8 ; Move to eight places before edge
shl ah,1 ; Mult by two (char and attribute
bytes)

; end of clock position routine

assume ES:SCREEN ; Set up screen as the Extra Segment
mov cx,SCREEN
mov ES,cx
mov dx,video_port ; This is the screen status
port
mov byte ptr cursor,ah ; Move cursor to it's memory
location

mov di,cursor ; Set up cursor on screen as
destination
mov si,offset displayit
mov cx,16 ; To move char and attributes

Scan_Low:
mov ah,CS:[si] ; Move byte to be written into AH
mov ES:[di],ah ; Move to screen one byte at a time.
inc di ; Position to attribute byte
inc si ; on screen.
loop Scan_Low ; Go back foe next byte

pop ES ; Here are required pops to exit
pop si
pop di
pop cx
pop ax

Dont_Recalculate:
iret ; An interrupt needs an IRET

Clock ENDP

Calc PROC NEAR ; Here we recalculate the time and
store it
push ax ; Pushes to save everything that
push bx ; gets destroyed
push cx
push dx

cmp CS:flash,1 ; do date or time?
jz Dtime ; TIME
mov bx,offset displayit
mov ax,CS:month
mov CS:[bx+0],ah ; Move first month digit into
display
mov CS:[bx+2],al ; Then the second digit
mov byte ptr CS:[bx+4],'-' ; a hyphen
mov ax,CS:day ; get day
mov CS:[bx+6],ah ; and move them into the display in
memory
mov CS:[bx+8],al
mov byte ptr CS:[bx+10],' ' ; move space into display
mov ax,CS:day_of_wk
mov CS:[bx+12],ah ; move day of the week into
display
mov CS:[bx+14],al
jmp Restore

Dtime:

; note: Peter Norton p.223 explains that the time formula is more
precisely
; shown as:
; hh = clkcount / 65543
; mm = hh.remainder / 1092
; ss = mm.remainder / 18
;
; trouble is, the 65543 value won't work as a single-word divisor,
; so our trick is to divide the clock count and divisor values in
half,
; which should have no appreciable affect on the accuracy of the time.

xor ax,ax ; Set up for clock read.
INT 1Ah ; Read the clock.
mov bx,dx ; Save low(minutes) portion.
mov dx,cx ; Move high(hours) portion to AX.
mov ax,bx ; dx:ax = clock count

clc
rcr dx,1 ; div count by 2 so we can use a
rcr ax,1 ; single precision dividend

div CS:hhdiv ; compute hours
mov CS:hh,ax ; save it
mov ax,dx ; prepare remainder for minutes
xor dx,dx
div CS:mmdiv ; compute minutes

cmp ax,60 ; 60 minutes shows up sometimes
jl Mm_Ok ; mostly it doesn't
xor ax,ax ; but if it does, zero out the minutes
inc CS:hh ; and bump up the hour

Mm_Ok:
mov CS:mm,ax ; save it
mov bx,offset displayit ;v1.1
mov byte ptr CS:[bx],' ' ; blank out first and last
positions
mov byte ptr CS:[bx+14],' '
mov ax,CS:hh
cmp ax,12 ; is it am or pm?
jl Am ; am
;Pm:
mov byte ptr CS:[bx+12],'p' ; Otherwise move 'P' into the
display.
sub ax,12 ; pm, subtract 12
jmp short Chek12 ; Continue.

Am: mov byte ptr CS:[bx+12],'a' ; Move an 'A' into the
display.

Chek12:
or ax,ax ; Make zero hour...
jnz Am_Pm_Done
mov ax,12 ; ...a twelve

Am_Pm_Done:
mov CS:hh1,ax ; hour value for cuckoo
aam ; Convert AX to BCD - a nice
command
add ax,3030h ; Add '0' to both bytes in AX
to make ascii
cmp ah,'0' ; Is the 1st digit '0'?
jne Dont_Edit ; Then don't blank the
character.
mov ah,' ' ; Otherwise, put a space in
AH.

Dont_Edit:
mov CS:[bx+2],ah ; Move first hours digit into
display
mov CS:[bx+4],al ; Then the second digit
;----------------------------------
mov byte ptr CS:[bx+6],':' ; in which case use a colon
mov ax,CS:mm ; get minutes
aam ; Again convert AX to Binary Coded
Decimal
add ax,3030h ; Add to make two ASCII characters
mov CS:[bx+8],ah ; and move them into the display in
memory
mov CS:[bx+10],al

;---------routine for alarm chime goes
here------------------------------------

cmp CS:chimon,0 ; chimes off?
jz Restore ; yes, don't beep
cmp CS:inbeep,1 ; already in a beep loop?
jz Restore ; yes, don't be redundant

cmp ax,3030h ; on the hour (full cuckoo
routine)
jz Alarm3
cmp ax,3135h ; on the 1/4 hour (single
cuckoo)
jz Alarm2
cmp ax,3330h ; on the 1/2 hour (single
cuckoo)
jz Alarm2
cmp ax,3435h ; on the 3/4 hour (single
cuckoo)
jz Alarm2
mov CS:beept,0 ; we have not beeped

Restore: ; Restore registers
pop dx
pop cx
ImRet2: pop bx
ImRet1: pop ax

ImRet: ret

NeedBeep:
cmp CS:inbeep,1 ; are we beeping right now ?
jnz ImRet ; no, immediate return
dec CS:beepticks ; yes, done beeping?
jnz ImRet ; no, immediate return
push ax
mov al,CS:spkrstat ; yes, shut off speaker
out 61h,al
dec CS:beepsleft ; any more beeps waiting?
jz NoBeeps ; no, go home
cmp CS:beepsleft,1 ; how many await?
jnz Onward
call Pause
jmp ImRet1

Onward:
push bx
mov bx,TONE2 ; second tone in cuckoo
mov CS:beepticks,BEEPS2 ; second tone is longer than
first tone
call Tone_a ; start it beeping
jmp ImRet2 ; go home
NoBeeps:
dec CS:cuckoo ; are there any more cuckoos?
jz No_Cuckoo ; no more cuckoos
push bx
mov bx,TONE1 ; beginning of next cuckoo
mov CS:beepsleft,3
call Tone ;start it beeping
jmp ImRet2
;------------------------------------------------------------------------------
Alarm3:
push ax ;save the register
mov ax,hh1 ;move hour value into ax
mov CS:cuckoo,ax ; hourly cuckoo - repeat for number
of hours
pop
ax
mov bx,TONE1 ; send tone 1
mov CS:beepsleft,3
call Tone
jmp Restore

Alarm2:
mov CS:cuckoo,1 ; only a single cuckoo for
quarter hour
; intervals
mov bx,TONE1 ; send tone 2
mov CS:beepsleft,2
call Tone
jmp Restore
;---------------------------------------
No_Cuckoo:
mov CS:beept,1 ; we have beeped
mov CS:inbeep,0 ; and we're not in one any
more
jmp ImRet1
;---------------------------------------
Tone:
cmp CS:beept,1 ; do nothing if chime has been
beeped
jz NoTone ; earlier in this clock update
mov CS:beepticks,BEEPS1 ; this long on beeps
Tone_a:
MOV AL,0B6H ; else condition the timer
OUT 43H,AL
MOV AX,BX ; this is the freq
OUT 42H,AL
MOV AL,AH
OUT 42H,AL ; out you all go
IN AL,61H ; read spkr port
MOV CS:spkrstat,AL
OR AL,3
OUT 61H,AL ; send a beep
mov CS:inbeep,1
NoTone: ret
;--------------------------------------------------------
Pause:
mov CS:beepticks,BEEPS3
ret
;------------------------------------------------------------------------------
Calc ENDP


Load_Clock PROC NEAR ; This procedure initializes
everything
assume DS:INTERRUPTS ; The Data Segment will be the
interrupt area
; mov ax,INTERRUPTS
xor ax,ax
mov DS,ax

MOV SI,0081H ; addr of command line arguments
Next:
MOV AL,CS:[SI] ; get command line char
CMP AL,0DH ; Return ends it.
JZ Again
CMP AL,'/' ; switch char
JZ GetSwitch ; see what it is
Next1:
INC SI ; else point to next char
JMP Next ; and loop

GetSwitch:
inc si
mov al,CS:[si]
cmp al,'c' ; chime toggle switch
jnz Next1 ; wrong
switch
mov CS:togchim,1 ; toggle them chimes
jmp Next1 ; get next switch if there is one

Again:
mov ax,CS:sig_vector ; get signature vector
cmp ax,5fh ; if less than 0x60
jg Vok
jmp NoLoad ; forget it

Vok:
add ax,3500h
int 21h
mov ax,ES
cmp ax,434ch ; are we already loaded ?
jnz NoSig ; no
cmp bx,4f4bh
jnz NoSig ; and no
mov bx,word ptr timer_int ; yes
mov ES,word ptr timer_int+2
call Gdate ; get the system date
cmp CS:togchim,1
jnz No1 ; no toggle chimes
call ToglChim ; go toggle chimes
jmp short Exit

No1:
mov ax,ES:[bx-2]
xor ax,1 ; toggle activation mode
mov word ptr ES:[bx-2],ax
Exit: mov ax,4c00h ; return to DOS
int 21h

;---------------------------------------
ToglChim:
mov ax,ES:[bx-4] ; get the chime flag
xor ax,1 ; toggle it
mov word ptr ES:[bx-4],ax
push DS
mov di,CS
mov DS,di
mov dx,offset chimonmsg ; chimes on
cmp ax,1 ; beep if it's been turned on
jz BeepMsg
mov dx,offset chimoffmsg ; chimes off
BeepMsg:
mov ah,9
int 21h
pop DS
ret
;---------------------------------------
NoSig:
mov ax,ES ; no current signature...
or ax,bx ; ...but is it safe to load
one?
jz SetSig ; yes
mov ax,CS:sig_vector ; no, try another slot
dec ax
mov CS:sig_vector,ax
jmp Again
;---------------------------------------
Gdate:
push bx
mov ah,2ah ; call DOS GetDate command
int 21h
xor ah,ah ; make an offset out of the day of the
week
clc
rcl ax,1
mov si,ax
lea bp,days
mov ax,[bp+si] ; get the ASCII day of the week
pop bx
mov word ptr ES:[bx-10],ax ; save it
mov al,dh ; month
xor ah,ah
aam ; Convert AX to BCD - a nice command
add ax,3030h ; Add '0' to both bytes in AX to make
ASCII
cmp ah,'0' ; Is the 1st digit '0'?
jne Gd1 ; Then don't blank the character.
mov ah,' ' ; Otherwise, put a space in AH.
Gd1:
mov word ptr ES:[bx-8],ax
mov al,dl ; day
xor ah,ah
aam ; Convert AX to BCD - a nice command
add ax,3030h ; Add '0' to both bytes in AX to make
ASCII
mov word ptr ES:[bx-6],ax
ret ; go home
;---------------------------------------
NoLoad label near
mov ax,4c01h ; abort with error
int 21h

SetSig:
cli
push DS
mov ax,CS:sig_vector
add ax,2500h ; signature reads:
mov dx,434ch ; 'CL'
mov DS,dx
mov dx,4f4bh ; 'OK'
int 21h
pop DS

mov ax,word ptr timer_int ; Get the old interrupt
service routine
mov word ptr old_time_int,ax ; address and put it into our
location
mov ax,word ptr timer_int+2 ; OLD_TIME_INT so we can still
call it
mov word ptr old_time_int+2,ax

mov word ptr timer_int,offset Clock ; Now load the address
of our clock
mov word ptr timer_int+2,CS ; routine into TIMER_INT so
the timer
; interrupt will call CLOCK
sti

mov ah,15 ; Ask for service 15 of INT 10H
int 10h ; This tells us how the display is set
up
sub ah,8 ; Move to eight places before edge
shl ah,1 ; Mult by two (char and attribute
bytes)
mov byte ptr cursor,ah ; Move cursor to it's memory
location
mov video_port,03bah ; Assume this is a monochrome
display
test al,4 ; Is it?
jnz Get_Time ; Yes - jump out
add cursor,8000h ; No - set up for graphics
display
mov video_port,03dah
mov cx,8
mov ax,4f20h
mov di,offset displayit ;v1.1
Color:
mov CS:[di],ax
inc di
inc di
loop Color

Get_Time:
mov bx,offset Clock ; yes ; get addr of chime
mov ax,CS
mov ES,ax
cmp CS:togchim,1 ; need to toggle ?
jnz Noct ; no
call ToglChim
Noct: call Gdate

mov di,CS
mov DS,di
mov dx,offset hello ; chimes on
mov ah,9
int 21h
call Calc ; This is to avoid showing
; 00:00 for first 500 counts
mov dx,offset Load_Clock ; Set up for everything but
Load_Clock
int 27h ; to stay attached to DOS

sig_vector dw 67h ; signature vector
togchim db 0 ; to toggle chimes
days db 'uSoMuTeWhTrFaS'
chimonmsg db 'Chime ON',CR,LF,'$'
chimoffmsg db 'Chime OFF',CR,LF,'$'

hello db CR,LF,'CHIME INSTALLED',CR,LF
db CR,LF,'USAGE: ',CR,LF,CR,LF
db ' chime [/c] (/c: toggle chimes)',CR,LF
db ' chime by itself toggles
display',CR,LF,'$'


Load_Clock ENDP

CSEG ENDS

END First

.



Relevant Pages

  • Re: Time,Date,and Chime TSR
    ... Clock display can be toggled on and off by repeating 'chime'. ... Turning the chime off and on ... mov CS:count500,TICKS; Reset Count500 ...
    (alt.lang.asm)
  • Re: Display file name
    ... You appear to be trying to display the first FCB. ... mov bl,16h; for offset of T1 ... If you just type "myprog", you wn't have anything in the first FCB, so you won't see anything. ...
    (comp.lang.asm.x86)
  • Re: Popping Stack and then Displaying value
    ... How can I display what is in AX, after the instruction "add ax,bx"? ... You can use Int 10h AH.9 to write an ascii character and its attribute ... mov ax,cx ... ;;outer loop for 16 lines. ...
    (comp.lang.asm.x86)
  • serial communication between 8051 and BOB-3
    ... I am using BOB-3 (basic ... overlay board) from decade engineering as the overlay module. ... I see some garbage characters displayed on the display ... MOV TMOD, #20H ...
    (comp.arch.embedded)
  • Re: Display file name
    ... You appear to be trying to display the first FCB. ... mov bl,16h; for offset of T1 ...
    (comp.lang.asm.x86)

Loading