home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 June
/
SIMTEL_0692.cdr
/
msdos
/
at
/
atnudg2.arc
/
ATCLOCK.ASM
< prev
next >
Wrap
Assembly Source File
|
1988-09-07
|
18KB
|
570 lines
page 64,130
Title E ATCLOCK - Daily housekeeping.F
;box
;╒═════════════════════════════════════════════════════════════╕
;│ Created 2 Jan. '88 │
;│ Date of last revision: 07-Sep-1988 │
;╞═════════════════════════════════════════════════════════════╡
;│ ATCLOCK.ASM │
;│ │
;│ Single segment '.COM' type program. │
;│ Description of function: Checks for 1st time run today │
;│ from 'TODAY.AT' file. Nudges clock if so. │
;│ Keeps standard/daylight time, changes 1 hour if needed. │
;│ Set nudge value with '/Nxxx', daylight-std with '/D','/S'. │
;│ │
;╘═════════════════════════════════════════════════════════════╛
;ebx
; 3 April '88 - Daylight savings time!
; 5 Sept. '88 - Major revision. Midnight rollover,
; multiple days, daylight/standard comands.
; Macros used for messages to stdout. Structure for date file.
; Uses .286 mode. Program is not re-entrant.
; Link with: ASMUTILS.LIB
;
.286
INCLUDE ASMUTILS.ASI
.SALL
CSEG SEGMENT PARA PUBLIC 'CODE' ;Segments at beginning,
CSEG ENDS
MSGSEG SEGMENT BYTE PUBLIC 'CODE' ;Message data at end.
MSGSEG ENDS
CGroup GROUP CSEG,MSGSEG ;All really the same segment.
; ; Flag bits for /Commands.
NewNudge EQU 1
DSCmdRcvd EQU 2
Daylite EQU 4
OneNudge EQU 8
;
DateInfo STRUC
PDATE DB ' '
DOW DB ?,?,?,? ; Today's date with month/day names.
DOM DB ?,?,? ; Like " Sun 08 Jan 1989 "
MONTH DB ?,?,?,?
CENTURY DB ?,?
YEAR DB ?,?,' '
StOrDl DB 'S' ; S-standard time, D-daylight.
BinYear DW ? ; Year in binary.
JulDate DW ? ; Julian date in binary.
PDATNUMS DB 6 DUP (?) ; Date in numbers. Like: "890108"
AscNudge DB 5 DUP (?) ; Nudge value. Like "-0182"
DateInfo ENDS
PAGE
SEGZERO SEGMENT AT 0
ORG 46CH ;Time info in BIOS' data area.
TimerLow DW ?
TimerHigh DW ?
SEGZERO ENDS
;
CSEG SEGMENT
ASSUME CS:CGroup,DS:CGroup
EXTRN Asc2Bin:NEAR, SkipSpac:NEAR, AX4Digs:NEAR, DispDOSMsg:NEAR,\
NextLine:NEAR, SimplMsg:NEAR, ProgName:NEAR
ORG 100H
START: JMP PROG
SCommands DB 0 ;Flags for '/' commands.
NudgeVal DW 0 ;# ticks to nudge clock.
DOSDate DateInfo <>
; ;Structure expansion edited out of listing.
TFDate DateInfo <>
MsgSize EQU OFFSET DOSDate.StOrDl - OFFSET DOSDate
TFSiz EQU TYPE DOSDate ; Size of our data file. <Pg. 187>
DaysPassed DW 0 ;#days to adjust for, calculated if needed.
CMOSCent DB 19H
CMOSYear DB ?
CMOSMon DB ?
CMOSDay DB ?
DaysPerMon DB 31
DaysInFeb DB 28,31,30,31,30,31,31,30,31,30
DAYS DB 'Sun Mon Tue Wed Thu Fri Sat '
MOS DB 'JanFebMarAprMayJunJulAugSepOctNovDec'
TimeDisp DB ' '
TimeData DB ?,?,':',?,?,':',?,?
TFName DB 60 DUP (?)
TodayAT DB 'TODAY.AT',0
CmdLtrs DB '1DNS'
CmdProcs DW OneCmd,DSCmds,NCmd,DSCmds
ULine DB 2,0 ;Buffer to get a 'y' or 'n'.
UResp DB 0,0
PAGE
; ; --- Begin main program ---
PROG PROC NEAR
CLD
; ; Check DOS Version
MOV AH,30H ; Check for DOS Version 3.0+.
INT 21H
CMP AL,3
JAE @F ; If so, continue.
DMessage <"ATCLOCK: Requires DOS 3 or above!"> \
AddLine+ErrorMsg+NoRet ;Macro for display error and abort.
; ; This error message won't even work under DOS 1.0!
;
; ; Check command line for /N /D /S or /1 commands.
@@: MOV SI,80H ;Read data from PSP 'till we get
LODSB ; '/' or end of line.
CMP AL,02 ;Must be 2 chars entered.
JB GetPath
CALL SkipSpac ;Skip spaces after '/'.
ReadCmds: LODSB
CMP AL,0DH
JE GetPath
CMP AL,'/'
JNE ReadCmds
LODSB
CMP AL,0DH
JE GetPath
AND AL,0DFH ;Force upper case.
MOV DI,OFFSET CmdLtrs ;Check for a match to one
MOV CX,CmdProcs - CmdLtrs ; of our commands.
REPNE SCASB
JNE ReadCmds ;Loop if no match.
SUB DI,OFFSET CmdLtrs + 1 ;DI = Command index.
SHL DI,1 ;DI = offset in table.
CALL CmdProcs[DI] ;Execute the command.
JMP ReadCmds
;
; ; Get the path to our directory. Generate date file's name.
GetPath: PUSH DS ;Save our segment.
CALL ProgName ;Get path name to this prog in DS:SI.
MOV DI,OFFSET TFName ;Move path into name field for
REP MOVSB ; our data file.
POP DS ;Get our segment back and move in
MOV SI,OFFSET TodayAT ; filename after path.
MOV CX,9
REP MOVSB
;
; ; Get the date and compare with the date file.
CALL DateData ; Convert DOS date to our formats.
CALL GetDateFile ; Try to read in the date file.
JC ReWrite ;Skip tests if no file.
;
; ; File read OK. Check dates.
MOV SI,OFFSET DOSDate.BinYear ;[SI]-DOS' numeric date.
MOV DI,OFFSET TFDate.BinYear ;[DI]-File's " "
MOV CX,05 ;2 words + 6 digits.
REPE CMPSW ;Compare.
JE CheckDS ;Branch if same day.
PAGE
; ; New day. Calculate the number that have passed.
TEST SCommands,OneNudge
JZ @F ;Set single day if /1 command.
MOV DaysPassed,1
JMP SHORT CheckDS
@@: CALL GetPassed
CMP DaysPassed,0 ;File error if no days passed.
JE ReWrite ;Ignore old file, rewrite.
;
; ; File data is good. Check Std/Daylight params.
CheckDS: MOV AL,TFDate.StOrDl ;Get file's param.
CMP AL,DOSDate.StOrDl ;Old = New ?
JE @F ;Branch if so.
TEST SCommands,DSCmdRcvd ;Did we get a command?
JNZ DSChange ;Branch if we did.
MOV DOSDate.StOrDl,AL ;No command, set new=old.
@@: CMP DaysPassed,0 ;Nudge if new day.
JNE NewDay
TEST SCommands,NewNudge ;Not new day. Rewrite file if
JZ SetCMOS ;a new nudge value was entered.
ReWrite: CALL WrDateFile
JMP SHORT SetCMOS ;Just set the clock if not.
;
; ; We have a command to exchange daylight <> standard.
DSChange: CALL TimeChange ;Ask for confirm, change if OK.
CMP DaysPassed,0 ; If no nudge required, write
JE WrtToday ; the date file and quit.
;
; ; New day - nudge the clock.
NewDay: TEST SCommands,NewNudge ;Did we set new nudge value?
JNZ @F ;Branch if yes.
MOV SI,OFFSET TFDate.AscNudge
MOV CX,04 ;Convert 4 digits to binary.
CALL Asc2Bin
MOV NudgeVal,AX ;Save as current nudge value.
@@: MOV AX,NudgeVal ;Get amount to adjust.
TEST AX,AX ;Skip this if it's zero.
JZ WrtToday
IMUL DaysPassed ;Ticks per day * Number of days.
CALL AdjustDOS ; Adjust the DOS clock first.
;
; ; Clock twiddling done if needed. Update file, set clock.
WrtToday: CALL WrDateFile ; Write new date file.
CALL DateData ; Recalculate the date params from DOS.
SetCMOS: CALL SetATClock ; Set the CMOS clock.
;
; ; Display current date, time.
MOV DX,OFFSET DOSDate ;Write date to screen.
MOV CX,MsgSize
MOV AH,AddLine
CALL SimplMsg
MOV DX,OFFSET TimeDisp ;Write time to screen.
MOV CX,9 ;9 characters, + CrLf.
MOV AH,AddLine
CALL SimplMsg
RET
PROG ENDP
; ; *** End main program ***
PAGE
; ; Command response functions.
; ; /1 command - Nudge clock by one day only.
OneCmd PROC NEAR ;Just set the flag.
OR SCommands,OneNudge
RET
OneCmd ENDP
;
; ; /N command - set new nudge value.
NCmd PROC NEAR
CALL SkipSpac ;Skip spaces after 'N'.
MOV CX,04 ;Get up to four digits.
CALL Asc2Bin
CMP CX,04 ;Did we get any digits?
JE @F ;Branch if no - leave it zero.
MOV NudgeVal,AX ;Set new value and flag.
OR SCommands,NewNudge ;(could be 0).
@@: RET
NCmd ENDP
;
; ; /S or /D commands - Standard or Daylight.
DSCmds PROC NEAR
OR SCommands,DSCmdRcvd ;Set command flag.
MOV DOSDate.StOrDl,AL ;Save the letter.
RET
DSCmds ENDP
;
; ; Subroutines for clock prog.
GetDateFile PROC NEAR ;Try to open the date file.
MOV DX,OFFSET TFName ;We will return carry set if fail.
MOV AX,3D02H ;Read/write access.
INT 21H
JC NoDF ;Return error if no file.
; ; The file is open. Try to read it.
MOV BX,AX ;Handle to BX.
MOV CX,TFSiz ;# bytes to CX.
MOV DX,OFFSET TFDate
MOV AH,3FH ;'Read from file or device'
INT 21H
JC ReadErr ;Must have no DOS error and #bytes
CMP AX,CX ; read = # requested or file NG.
JE ReadOK
ReadErr: MOV AH,3EH ;Error on read. Close file, forget.
INT 21H ;Ignore close errors.
STC ;Return fail.
JMP SHORT NoDF
ReadOK: MOV AH,3EH ;Close file so we can re-write it.
INT 21H ;Clears carry.
NoDF: RET
GetDateFile ENDP
PAGE
; ; Get date from DOS. Setup our data area with:
; ; 1. Full date in Ascii for display & date file.
; ; 2. Date in numbers for date file / compare.
; ; 3. Julian day, year in binary for days passed calculate.
; ; 4. Binary format needed by CMOS clocks.
DateData PROC NEAR
MOV DI,OFFSET DOSDate.PDATE
MOV AX,2A20H ;Put a space at beginning.
STOSB ;Revise date string from start.
INT 21H ;DOS Get date call.
MOV DOSDate.BinYear,CX ;Save year. AL has day-of-week.
; ;Day of week.
MOV SI,OFFSET DAYS
XOR AH,AH ;Multiply days x4 for offset
ADD SI,AX ; into table.
CALL SetDM ;Convert & store.
; ;Day of month in Ascii.
MOV AL,DL ;Day of month in binary.
CALL ConvDig ;Convert & store.
MOV CMOSDay,AL ;Save for setting clock.
MOV BYTE PTR [DI],' ' ;Add a space.
MOV AX,WORD PTR DosDate.DOM ;Put same value in numeric
MOV WORD PTR DOSDate.PDATNUMS + 4,AX ;date.
; ;Month.
MOV AL,DH ;Get month number, 1-12 (not 0-11)
MOV DI,OFFSET DOSDate.PDATNUMS + 2 ; for variety.
CALL ConvDig ;Put in numeric date field.
MOV CMOSMon,AL ;Save for setting clock.
MOV AL,DH ;Adjust and put month name in.
DEC AL
MOV DI,OFFSET DOSDate.MONTH
MOV SI,OFFSET MOS ;Months calculated like days.
CALL SetDM
; ;Julian date in binary.
MOV AX,DX ;Binary month/day to AH/AL.
MOV DX,DOSDate.BinYear ;Binary year to DX.
CALL JulianDate ;Get Julian date in AX.
MOV DOSDate.JulDate,AX ;Save for file.
; ;Year.
MOV AX,'91' ;Put in '19'.
SUB DX,1900 ;Get (Year-1900).
CMP DX,100
JB Twentieth
MOV CMOSCent,20H
MOV AX,'02' ;Change after 2000.
SUB DX,100
Twentieth: STOSW
MOV AL,DL ;Put year in AL.
CALL ConvDig
MOV CMOSYear,AL ;Save for setting clock.
MOV BYTE PTR [DI],' '
MOV AX,[DI-2] ;Put year in numeric date.
MOV WORD PTR DOSDate.PDATNUMS,AX
RET
DateData ENDP
PAGE
; ; Calculate number of days passed since last run.
GetPassed PROC NEAR
MOV AX,DOSDate.JulDate ;Today's Julian date, year.
MOV BX,DOSDate.BinYear
SUB BX,TFDate.BinYear ;Get # years gone by.
JZ @F ;Branch if none.
DEC BX ;Sanity check.
JNZ DFNoGood ;Branch if > 1 year.
ADD AX,365 ;Correct for a year. Check for
TEST BYTE PTR TFDate.BinYear,03 ; last year was a leap.
JNZ @F ;Branch if it wasn't.
INC AX ;One more day to correct.
@@: SUB AX,TFDate.JulDate
JC DFNoGood ;Branch if today < yesterday.
TEST AH,AH ;Check for difference > 255.
JZ @F ;Branch if not - file is OK.
DFNoGood: DMessage <"ATCLOCK: Date file invalid!"> \
AddLine+ErrorMsg+NoRet ; Return w/o updating DaysPassed.
@@: MOV DaysPassed,AX ;Save # days to adjust for.
RET
GetPassed ENDP
;
; ; Change daylight - standard after confirmation.
TimeChange PROC NEAR
@@: DMessage <"Do you want to set the clock ">
CMP DOSDate.StOrDl,'D' ;Ask for confirmation.
JE @F ;Branch if going to daylight.
DMessage <"back 1 hour for standard time? ">
JMP SHORT WaitConf
@@: DMessage <"ahead 1 hour for daylight time? ">
WaitConf: CALL GetYorN ;Wait for response.
JNE NoTC ;Branch if 'No'.
MOV DX,1 ;Set one hour's worth of ticks.
MOV AX,7
CMP DOSDate.StOrDl,'D' ;Are we going to daylight?
JE @F ;Branch if positive adjust.
NEG AX ;Add negative 1 hour.
NOT DX
@@: CALL AdjustDOS ;Enter correction to DOS clock only.
DMessage <"Time change set."> AddLine
NoTC: RET
TimeChange ENDP
;
; ; Adjust the DOS clock by DX:AX ticks (signed).
AdjustDOS PROC NEAR
XOR BX,BX ;Get access to data area.
MOV DS,BX
ASSUME DS:SEGZERO ;Inform MASM.
CLI ;Hold the clock off.
ADD AX,TimerLow ; Get the adjusted values in AX:DX.
ADC DX,TimerHigh
CMP DX,23 ;Check for over/underflow.
JA DayChange ;Branch if so.
JB @F ;Branch elsewhere if < 23Hrs.
CMP AX,7*24 ;Check for > 23:59.
JAE DayChange
@@: CALL TicsDays ;No overflow. Set clock, reload seg.
JMP SHORT AdjDone
;
DayChange: TEST DH,80H ;Date rolled.
JZ Manana ;Branch if forward.
ADD AX,7*24 ;It's yesterday.
ADC DX,24 ;Put 24 hrs. back into clock.
CALL TicsDays ;Set clock, int on, reload seg.
DEC AX ;Take the 1 day out of the calendar.
JNZ @F ;Branch if same year.
DEC DX ;Now NewYears' Eve.
MOV AX,365
TEST DX,03
JNZ @F
INC AX ;Now New Years' Eve of a leap year!
@@: JMP SHORT NewDate
;
Manana: SUB AX,7*24 ;Take the day back out of the clock.
SBB DX,24
CALL TicsDays ;Set clock, int on, reload seg.
ASSUME DS:CGroup ;Don't mislead Masm.
INC AX ;Put the day into the calendar.
MOV BX,365 ;Set BX to #days this year.
TEST DX,03
JNZ @F
INC BX ;It's a leap year.
@@: CMP AX,BX ;Check for year rolled.
JBE NewDate
INC DX
MOV AX,1 ;Happy New Year!
NewDate: CALL Jul2DOS ;Set the new date in DOS.
AdjDone: DMessage <"Clock adjusted."> AddLine
RET
AdjustDos ENDP
;
; ; Write new date file. Set nudge value from variable.
WrDateFile PROC NEAR
MOV AX,NudgeVal ;Convert nudge value to ascii in
MOV DI,OFFSET DOSDate.AscNudge ;date info.
CALL AX4Digs
MOV DX,OFFSET TFName ;Create or overwrite 'Today'.
XOR CX,CX ;Normal, visible file.
MOV AH,3CH
INT 21H
JC WritErr ;Bail out if error on create.
MOV BX,AX ;File handle to BX.
MOV CX,TFSiz ;# bytes to CX.
MOV DX,OFFSET DOSDate ;DX -> data to write.
MOV AH,40H ;'Write to file or device'.
INT 21H
JC WritErr ;Bail on error.
CMP AX,CX ;Also if disk full <gasp>.
JE WriteOK
WritErr: DMessage <"ATCLOCK: DOS error writing data file!"> \
AddLine+ErrorMsg
WriteOK: MOV AH,3EH ;Close the file.
INT 21H ;Ignore errors. Can't do anything.
RET
WrDateFile ENDP
PAGE
; ;Set the CMOS clock from the DOS clock.
SetATClock PROC NEAR
MOV AH,2CH ;Let DOS convert the raw ticks to
INT 21H ; hr:min:sec. We don't care if a
CMP DL,45 ; tick went by. Round off DOS'
JB @F ; ".01" seconds.
INC DH
; ;Change DOS time data to clock data format.
@@: MOV DI,OFFSET TimeData ;Point to time display data.
MOV AL,CH ;Convert hours for display and
CALL ConvDig ; to BCD for CMOS clock call.
INC DI ;Skip the colon.
MOV CH,AL
MOV AL,CL ;Same for minutes.
CALL ConvDig
INC DI ;Skip the colon.
MOV CL,AL
MOV AL,DH ;Same for seconds.
CALL ConvDig
INC DI ;Skip the colon.
MOV DH,AL
XOR DL,DL ;DL=0 for standard time,
CMP DOSDate.StOrDl,'D'
JNE @F ; 1 for daylight.
INC DL
@@: MOV AH,03
INT 1AH ;Set the CMOS clock.
; ;Set the date from our date from DOS.
MOV CH,CMOSCent ;Good for 112 more years...
MOV CL,CMOSYear
MOV DH,CMOSMon
MOV DL,CMOSDay
MOV AH,05
INT 1AH ;Set the date.
RET
SetATClock ENDP
;
; ; Julian date: From month/day in AH/AL,
JulianDate PROC NEAR ; Binary year in DX. Result in AX.
PUSH DX
MOV CL,28 ;Default 28 days in Feb.
TEST DX,03 ;Non-zero means non leap year.
JNZ @F ;Good for years between 1900 & 2100.
INC CL
@@: MOV DaysInFeb,CL ;Set February.
MOV SI,OFFSET DaysPerMon
XOR CX,CX
MOV DX,CX
XCHG CL,AH ;Add days per month oneata time.
MOV DL,AL ;Accumulate in DX.
DEC CX ;Months 0-11.
JZ @F ;No correction for January.
NxtMonth: LODSB
ADD DX,AX
LOOP NxtMonth
@@: MOV AX,DX ;Result in right place.
POP DX
RET
JulianDate ENDP
;
; ; Jul2DOS: Convert Year:Julian in DX:AX to DOS format
; ; and set the DOS clock.
Jul2DOS PROC NEAR
MOV CX,DX ;Year same format.
MOV SI,OFFSET DaysPerMon
MOV BL,28 ;Set correct #days in Feb.
TEST DX,03
JNZ @F
INC BL
@@: MOV DaysInFeb,BL
XOR DX,DX
MOV BX,DX ;Clear scratch regs.
IncMos: INC DH ;Add to months & subtract days.
TEST AH,AH ;Skip check if still > 255.
JNZ @F
CMP AL,[SI] ;Conversion done if
JBE Found ; AX < #days in month DH.
@@: MOV BL,[SI]
SUB AX,BX ;Subtract the month, loop.
INC SI
JMP IncMos
Found: MOV DL,AL ;Set proper regs for DOS.
MOV AH,2BH ;Set DOS date.
INT 21H
RET
Jul2DOS ENDP
;
; ; Get yes or no response from user.
GetYorN PROC NEAR
MOV DX,OFFSET ULine
MOV AH,0AH ;Via redirectable call.
INT 21H
XOR AH,AH ;No options for message.
CALL NextLine ;cr,lf...
MOV AL,UResp ;Get the letter.
AND AL,0DFH ;Force upper case.
CMP AL,'Y' ;It's Y or it's not!
RET
GetYorN ENDP
;
TicsDays PROC NEAR ;Set Bios clock, load date.
ASSUME DS:SEGZERO
MOV TimerLow,AX
MOV TimerHigh,DX
STI ;Int back on, get our segment.
MOV AX,CS
MOV DS,AX
ASSUME DS:CGroup
MOV AX,DOSDate.JulDate ;DX:AX = Year:Julian date.
MOV DX,DOSDate.BinYear
RET
TicsDays ENDP
PAGE
; ; Update day or month data.
SetDM PROC NEAR
XOR AH,AH ;Mult. AL x3 for data address
ADD SI,AX ;in list. Move data to o/p area.
SHL AL,1
ADD SI,AX
MOV CX,03
REP MOVSB
MOV AL,' '
STOSB ;Add space after data.
RET
SetDM ENDP
;
; ; Convert binary # in AL to 2-digit Ascii for display
; ; and packed BCD for CMOS clock set call. Uses BX.
ConvDig PROC NEAR
AAM ;AL to unpacked BCD in AH,AL.
MOV BX,AX ;Save for further convert.
OR AX,3030H ;Change to Ascii.
XCHG AL,AH ;Change to Intel.
STOSW ;Save for display.
MOV AL,BL ;Get LS nibble.
SHL BH,4 ;OR in MS nibble.
OR AL,BH
RET
ConvDig ENDP
CSEG ENDS
END START