.MCALL .MODULE .MODULE LOCLTM,RELEASE=Y01,VERSION=11,COMMENT=,IDENT=NO,LIB=YES ; Copyright (c) 2000 ; Megan Gentry ; Framingham, Massachusetts ; All Rights Reserved ; Commercial Distribution Prohibited ; ; This software may be freely copied and used in its entirety for any ; purpose so long as the above copyright notice and these comments are ; preserved in the source form of this software, and the binary ; copyright is preserved in any image built from it. ; ; The author has used best efforts in the research, design, development ; and testing of this software. The author makes no warranty of any ; kind, expressed or implied, with regard to this software and its ; suitability for a given application. The author shall not be liable ; in any event for incidental or consequential damages in connection ; with, or arising out of, the use or performance of this software. Use ; of this software constitutes acceptance of these terms. ; ; The author is committed to making a best effort at fixing any errors ; found in the software and would welcome any reports of problems, ; comments or suggestions regarding the software. Please send email ; to . .SBTTL Edit History ;+ ; ; Edit History: ; ; X00 (00) 17-Nov-1999 Megan Gentry ; Initial coding. ; ; X00 (01) 19-Nov-1999 Megan Gentry ; o Coding change to use a register in modulo calculation ; to make it faster. ; o Convert days per month table to a word table to make ; calculation of days since Jan 1 faster by reducing ; number of instructions ; o Added conditional for EIS ; ; X00 (02) 22-Nov-1999 Megan Gentry ; Converted name to ILCTIM, to make it closer to the ; U*x style localtime() routine. ; ; X00 (03) 24-Nov-1999 Megan Gentry ; Adding code to determine when daylight savings time ; is in effect so as to return the flag. Current code ; will be based on the generic US rule. ; ; X00 (04) 29-Nov-1999 Megan Gentry ; To be consistent with localtime() call on U*x, this ; routine should return years since 1900. ; ; X00 (05) 29-Nov-1999 Megan Gentry ; Check for patently invalid date needs to be split to ; check month and day separately. ; ; X00 (06) 20-Jan-2000 Megan Gentry ; Updated Copyright ; ; X00 (07) 28-Jan-2000 Megan Gentry ; Removed .ASECT use for defining structure offsets, converted ; to using .DSECT/.DS/.EQU ; ; X00 (08) 10-Feb-2000 Megan Gentry ; o Changed routine name to LOCLTM to bring it even closer to the ; U*x name for the routine. ; o Added more EIS conditional code ; ; Y01 (09) 11-Feb-2000 Megan Gentry ; o Ready for initial release ; ; Y01 (10) 11-Feb-2000 Megan Gentry ; o Development of mktime library routine resulted in discovery ; that there was insufficient information in the TM Array for ; mktime to be a complete complement of locltm -- the ticks ; information was lost. This change added a new TM Array ; entry for the tick information. ; ; Y01 (11) 14-Feb-2000 Megan Gentry ; o Month information is supposed to be returned in range 0-11, ; not 1-12, per U*x library routine localtime() ; o Corrected TM Array names to be consistent with U*x ; library routine localtime() ; ;- .SBTTL Definitions ; RT-11 System Macros to be used .MCALL .DATE .GTIM .MCALL .ASSUM ; Declare the system definition library .LIBRARY "SRC:SYSTEM.MLB" ; RT-11 System structure definitions to be used .MCALL .DATDF .DTMDF .TIMDF .DATDF .DTMDF .TIMDF .MCALL .DSECT .DS .EQU .SBTTL Local definitions ; Define local structure of TM Array .DSECT .DS TM.SEC ; : Seconds after the minute (0 - 59) .DS TM.MIN ; : Minutes after the hour (0 - 59) .DS TM.HOUR ; : Hours since midnight (0 - 23) .DS TM.MDAY ; : Day of month (1 - 31) .DS TM.MON ; : Month of year (0 - 11) .DS TM.YEAR ; : Years since 1900 (72 - 199) .DS TM.WDAY ; : Days since Sunday (0 - 6) .DS TM.YDAY ; : Days since January 1st (0 - 365) .DS TM.ISDST ; : Daylight Savings Time flag ; Note: Other than TM.TICKS, the following are not supported yet .DS TM.GMT 2 ; : Seconds east of Greenwich .DS TM.TZ ; : Time zone code .DS TM.TICKS ; : Ticks into a second (0 - 59) ; Define Days since Sunday values .EQU TM$SUN 0 .EQU TM$MON 1 .EQU TM$TUE 2 .EQU TM$WED 3 .EQU TM$THU 4 .EQU TM$FRI 5 .EQU TM$SAT 6 ;;; .EQU TM$EWD TM$MON ;For 1900-based epoch ;;; .EQU TM$EWD TM$THU ;For 1970-based epoch .EQU TM$EWD TM$SAT ;For 1972-based epoch .SBTTL Miscellaneous Definitions ; Define the globals .GLOBL $SYSLB .GLOBL $ARGER .GLOBL $NXADR ; Define any conditionals .IIF NDF FT.EIS FT.EIS = 0 ;By default, don't use EIS .SBTTL Revision and Copyright string for images .PSECT .COPY. RO,D .NLCSI TYPE=I .ASCII /Copyright (c) 2000 Megan Gentry/<15><12> .SBTTL LOCLTM - Convert RT-11 date/time to an array of values ;+ ; ; LOCLTM ; This fortran-callable subroutine is modelled after the U*x ; call localtime(), and is designed to take a date and time ; specified in the RT-11 date word and timeblock format (or ; take the current date and time if unspecified) and fill a ; data structure with the converted date and time information. ; ; Call: ; ; CALL LOCLTM ( TM [, DATMBK] ) ; ; where: ; TM is an INTEGER*2 array containing 13. entries which will ; be filled in with individual date/time information as ; documented below. ; ; DATMBK is an optional INTEGER*2 array containing the RT-11 date ; and time in the following format: ; ; +----------+ ; | date |[0] DTM.DT (returned from .DATE) ; +----------+ ; | time, hi |[2] DTM.TM+TIM.HI (as returned from .GTIM) ; + + ; | time, lo |[4] DTM.TM+TIM.HI ; +----------+ ; ; If this argument is not provided, the current date and ; time will be used to fill in the TM Array. ; ; Return: ; If there was no error, the TM array has been filled in with ; the following information: ; ; TM[0] Seconds after the minute [0-59] ; TM[1] Minutes after the hour [0-59] ; TM[2] Hours since midnight [0-23] ; TM[3] Day of the month [1-31] ; TM[4] Month of the year [1-12] ; TM[5] Years since 1900 [0-199] ; TM[6] Days since Sunday [0-6] ; TM[7] Days since January 1 [0-365] ; TM[8] Daylight Savings Time flag ; ; The next three entries are not currently returned. If/When ; additional time zones are supported, this information may ; be returned. ; ; TM[9] Seconds east of Greenwich (lo-order word) ; TM[10] Seconds east of Greenwich (hi-order word) ; (negative indicates west of Greenwich) ; TM[11] Time Zone code ; ; The following is RT-specific since RT stores the date and ; time information down to clock ticks. ; ; TM[12] Ticks into the second [0-59] ; ; Errors: ; o If there is no TM Array specified in the call ; o If the date or time information is invalid ; ; Notes: ; o This module was designed from the standpoint of taking as ; input (or using) a date/time value specified in whatever ; form is appropriate for the system and using it to fill the ; TM Array. This is why we don't use the U*x style 32-bit ; integer containing seconds since Jan 1, 1900. ; ; o The base year of the RT-11 Epoch is 1972, but we still return ; a count of years since 1900 (since that is what the U*x ; localtime routine does). ; ; o If a date/time block is not specified in the call, this routine ; allocates space from the stack in order to perform the programmed ; requests to obtain the current date and time. ; ; o The daylight savings time flag is set or reset based on the ; generic US rule. ; ;- .GLOBL LOCLTM .PSECT SYS$I,I .ENABL LSB ERROR: MOV #$ARGER,R0 SEC RETURN LOCLTM:: MOV (R5)+,R4 ;R4 = Argument count CALL $NXADR ;Get first argument (-> TM Array) BCS ERROR ;None specified! MOV R0,R1 ;R1 -> TM Array SUB #<5*2>,SP ;Make room for argument blocks ; (2 words for emt area, ; 3 words for date/time block) CALL $NXADR ;Try for pointer to date/time block BCC 10$ ;We got it... MOV SP,R4 ;R4 -> date/time block ADD #<3*2>,R4 ; (at the hi-order time word) MOV SP,R3 ;R3 -> EMT Area block .GTIM R3,R4 ;Get the current time .DATE ;Get the current date MOV R0,-(R4) ; and save it BR 20$ 10$: MOV R0,R4 ;R2 -> Date/time block 20$: MOV (R4),R2 ;R2 = Hi-order time MOV (R4),R3 ;R3 = Lo-order time ; Here we validate the time. The time is patently invalid if the ; hi-order portion is greater than 79. If it exactly equals 79, ; then the low order can be no larger than 6655. CMP R2,#79. ;Is time invalid? BHI ERROR ;Clearly yes... BLO 30$ ;Clearly no... CMP R3,#6655. ;Maybe... Check lo-order limit... BHI ERROR ;Clearly yes... 30$: ; Here we parse the date word. ; ; RT extended date word (16 bits) = EEMMMMDDDDDYYYYY ; E = epoch bits ; M = month bits ; D = day bits ; Y = year bits ; ; The date is patently invalid if the day and/or month field is zero. ; Further checking *could* be done to ensure that the month field is ; also not greater than 12. And if we wanted to get really pedantic, ; we could have the code verify that the day field is valid for a ; given month, taking into account leap years. .Assume DTM.DT EQ 0 MOV @R4,R0 ;R0 = Date word BIC #^C,R0 ;Isolate the day field .IF EQ FT.EIS ASL R0 ; and right-justify in word ASL R0 ; ... ASL R0 ; ... SWAB R0 ; ... .IFF ;EQ FT.EIS ASH #-,R0 ; and right-justify in word .ENDC ;EQ FT.EIS MOV R0,TM.MDAY(R1) ;Save 'Day of month' in TM Array BEQ ERROR ;If zero, date is patently invalid... .Assume DTM.DT EQ 0 MOV @R4,R0 ;R0 = Date word BIC #^C,R0 ;Isolate the month field .IF EQ FT.EIS ASR R0 ; and right-justify in word ASR R0 ; ... SWAB R0 ; ... .IFF ;EQ FT.EIS ASH #-,R0 ; and right-justify in word .ENDC ;EQ FT.EIS BEQ ERROR ;If zero, date is patently invalid... CMP R0,#12. ;Nonzero, is it a valid month? BHI ERROR ;Nope... MOV R0,TM.MON(R1) ;Save 'Month of year' in TM Array ;;; bit #3,tm.year(r1) ;Leap year? ;;; bne 34$ ;Nope... ;;; cmp r0,#2 ;Yes, is this february? ;;; bne 34$ ;Nope... ;;; cmp tm.mday(r1),#29. ;Yes, is the day valid? ;;; bhi error ;Nope... ;;; br 36$ ;;; ;;;34$: cmp tm.mday(r1),il$dpm-2(r0) ;Valid day for this month? ;;; bhi error ;Nope... ;;;36$: ;;;- .Assume DTM.DT EQ 0 MOV @R4,R0 ;R0 = Date word BIC #^C,R0 ;Isolate the epoch bits SWAB R0 ; and shift to bits <06:05> ASR R0 ; ... MOV R0,-(SP) ;Save intermediate on stack .Assume DTM.DT EQ 0 MOV @R4,R0 ;R0 = Date word BIC #^C,R0 ;Isolate the year bits BIS (SP)+,R0 ;Merge epoch bits MOV R0,TM.YEAR(R1) ;Save 'Years since epoch' in TM Array ; Hour = time % 216000l; ; time = time / 216000l; ; We have to do double precision here since the largest value can ; be 5183999. (60 ticks * 60 seconds * 60 minutes * 24 hours - 1 tick) ; 5183999 = 79 (*65536) + 6655. ; ; The value 216000 is (60 ticks * 60 seconds * 60 minutes) ; 216000 = 3 (*65536) + 19392. .IF EQ FT.EIS CLR R0 ;Crude divide by 216000, which 40$: INC R0 ; yields hours since midnight SUB #19392.,R3 ; ... SBC R2 ; ... BCC 50$ ; ... ADD #19392.,R3 ; ... ADC R2 ; ... BR 60$ ; ... 50$: SUB #3,R2 ; ... BHIS 40$ ; ... ADD #19392.,R3 ;Correct the remainder ADC R2 ; ... ADD #3,R2 ; ... 60$: DEC R0 ;Correct the quotient MOV R0,TM.HOUR(R1) ;Save 'Hours Since Midnight' ; in TM Array .IFF ;EQ FT.EIS DIV #21600.,R2 ;Divide by 21600. MOV R3,-(SP) ;Save first stage remainder MOV R2,R3 ;And setup to divide CLR R2 ; by an additional 10. DIV #10.,R2 ; value/21600/10 == value/216000 MOV R2,TM.HOUR(R1) ;Save 'Hours Since Midnight' ; in TM Array MOV R3,R2 ;Setup to determine real remainder MUL #21600.,R2 ; which is remainder from second ADD (SP)+,R3 ; division, plus the remainder ADC R2 ; from the first, double precision .ENDC ;EQ FT.EIS ; Minute = time % 3600 ; time = time / 3600 ; We still have to do double precision here since the largest value can ; still be 215999. (60 ticks * 60 seconds * 60 minutes - 1 tick) .IF EQ FT.EIS CLR R0 ;Crude divide by 3600, which 70$: INC R0 ; yields minutes past the hour SUB #3600.,R3 ; ... BHIS 70$ ; ... SBC R2 ; ... BCC 70$ ; ... ADD #3600.,R3 ;Correct the remainder ADC R2 ; ... DEC R0 ;Correct the quotient MOV R0,TM.MIN(R1) ;Save 'Minutes past hour' in TM Array .IFF ;EQ FT.EIS DIV #3600.,R2 ;Divide by 3600. for minutes past hour MOV R2,TM.MIN(R1) ;Save 'Minutes past hour' in TM Array CLR R2 ;Clear high-order component .ENDC ;EQ FT.EIS ; second = time % 60 ; time = time / 60; ; At this point, we can stop doing double precision since the largest ; value is 3599. (60 ticks * 60 seconds - 1 tick) .IF EQ FT.EIS CLR R0 ;Crude divide by 60, which 80$: INC R0 ; yields second of minute SUB #60.,R3 ; and tick of second BHIS 80$ ; ... ADD #60.,R3 ;Correct the remainder DEC R0 ;Correct the quotient MOV R0,TM.SEC(R1) ;Save 'Seconds past minute' ; in TM Array .IFF ;EQ FT.EIS DIV #60.,R2 ;Divide by 60. MOV R2,TM.SEC(R1) ;Save 'Seconds past minute' ; in TM Array .ENDC ;EQ FT.EIS MOV R3,TM.TICKS(R1) ;Save 'Ticks into Second' ; in TM Array CLR R0 ;Reset counter of days since Jan 1 MOV #IL$DPM,R4 ;R3 -> Days per month table MOV TM.MON(R1),R3 ;R3 = Number of month BR 100$ 90$: ADD (R4)+,R0 ;Accumulate count of days 100$: .IF EQ FT.EIS DEC R3 ;More months to account for? BGT 90$ ;Yes... .IFF ;EQ FT.EIS SOB R3,90$ .ENDC ;EQ FT.EIS 110$: ADD TM.MDAY(R1),R0 ;Accumulate days for this month DEC R0 ;Adjust so Jan 1 = zero MOV #<31.+28.+31.-1>,IL$APR ;Set days since Jan 1 for Apr 1 MOV #<31.+28.+31.+30.+31.+30.+31.+31.+30.+31.-1>,IL$OCT ;Set days since Jan 1 for Nov 1 ; (we'll start with Nov and work ; backward to last sunday in Oct) ; Although it is understood that the algorithm for determining whether ; a given year is a leap year is more complex then simply checking the ; low-order two bits of the year, it has already been determined that ; this simple algorithm is sufficient to work properly for all years in ; the range of valid years for RT-11 (1972 - 2099). MOV TM.YEAR(R1),-(SP) ;Stack years since start of epoch ADD #DA.YR0,@SP ; and convert to actual year BIC #^B<11>,(SP)+ ;Is it a leap year? BNE 120$ ;Nope... INC R0 ;Yes, account for 29-day february INC IL$APR ; ... INC IL$OCT ; ... 120$: MOV R0,TM.YDAY(R1) ;Save 'Days since January 1st' ; in TM Array ; Here we determine the day of the week (sunday = 0). This code depends ; on the fact that the first year of the RT epoch (1972) was a leap year, ; and that every four years across the range of valid dates is also a ; leap year. If a different epoch is selected (I don't see why it would ; be), a more precise algorithm should probably be used. MOV #TM$EWD,R0 ;R0 = dow for first day of epoch MOV TM.YEAR(R1),-(SP) ;Stack the years since epoch BEQ 130$ ;Zero... INC R0 ;Account for first year of epoch ; being a leap year ADD @SP,R0 ;Starting day shifts by one per year DEC @SP ;We only count a leap year in this ; calculation when we're beyond it ASR @SP ;Simple divide by four to determine ASR @SP ; number of leap years since epoch 130$: ADD (SP)+,R0 ;Adjust dow by number of leap years MOV R0,IL$YWD ;Save weekday count of start of year ; (Note: not yet reduced by modulo) ADD TM.YDAY(R1),R0 ;Add Days since January 1st, this year ; Now we have to reduce the count via modulo 7 to a value 0 - 6. .IF EQ FT.EIS MOV #7.,R2 ;R2 = Modulo 140$: SUB R2,R0 ;Reduce count by modulo BHIS 140$ ;Hasn't gone negative, keep going... ADD R2,R0 ;Account for extra reduction... MOV R0,TM.WDAY(R1) ;Save 'Days since Sunday' in TM Array .IFF ;EQ FT.EIS CLR R2 ;Clear high order component MOV R0,R3 ;R3 = Value to reduce by modulo 7 DIV #7.,R2 ;Reduce value by modulo 7 MOV R3,TM.WDAY(R1) ;Save 'Days since Sunday' in TM Array .ENDC ;EQ FT.EIS ; In this code, we determine the number of days into the year for ; the first sunday in April, when daylight savings time begins... MOV IL$YWD,R0 ;R0 = Weekday for Jan 1 of this year ADD IL$APR,R0 ;R0 = Weekday for Apr 1 of this year .IF EQ FT.EIS MOV #7.,R2 ;R2 = Modulo 150$: SUB R2,R0 ;Reduce by modulo BHIS 150$ ;Hasn't gone negative, keep going... ADD R2,R0 ;Account for extra subtract .IFF ;EQ FT.EIS CLR R2 ;Clear high order component MOV R0,R3 ;R3 = Value to reduce by modulo 7 DIV #7.,R2 ;Reduce value by modulo 7 MOV R3,R0 ;R0 = Value modulo 7 (remainder) .ENDC ;EQ FT.EIS BEQ 160$ ;First sunday was Apr 1 NEG R0 ;R0 = Count of days to first Sunday ADD #7.,R0 ; in April ADD R0,IL$APR ;Adjust the count of days to Apr 1 160$: ; In this code, we determine the number of days into the year for ; the last sunday in October, when standard time begins... MOV IL$YWD,R0 ;R0 = Weekday for Jan 1 of this year ADD IL$OCT,R0 ;R0 = Weekday for Nov 1 of this year ; (We're starting with Nov 1 and ; working backward to last Sunday ; in Oct) .IF EQ FT.EIS MOV #7.,R2 ;R2 = Modulo 170$: SUB R2,R0 ;Reduce by modulo BHIS 170$ ;Hasn't gone negative, keep going... ADD R2,R0 ;Account for extra subtract .IFF ;EQ FT.EIS CLR R2 ;R2 = High order component (zero) MOV R0,R3 ;R3 = Value to reduce by modulo 7 DIV #7.,R2 ;Reduce value by modulo 7 MOV R3,R0 ;R0 = Value modulo 7 (remainder) .ENDC ;EQ FT.EIS BNE 180$ ;R0 = Count of days since Sunday ; (which was last sunday of October) .IF EQ FT.EIS MOV R2,R0 ;It was exactly one week ago... .IFF ;EQ FT.EIS MOV #7.,R0 ;It was exactly one week ago... .ENDC ;EQ FT.EIS 180$: SUB R0,IL$APR ;Adjust the count of days to first ; Sunday in October ; Here is the beginning of the code which attempts to figure out if ; daylight savings time is in affect. For the meantime, this routine ; uses only the algorithm defined for the mainland US, and only the ; generic one at that... CLR TM.ISDST(R1) ;Default to standard time CMP TM.YDAY(R1),IL$APR ;Before first sunday in April? BLO 210$ ;Yes, still standard time BHI 190$ ;It's after the first sunday in April CMP TM.HOUR(R1),#2 ;First sunday in April, after 2am? BLO 210$ ;Not yet, still in standard time... BR 200$ ;Yes, we're in daylight savings time 190$: CMP TM.YDAY(R1),IL$OCT ;After last sunday in October? BHI 210$ ;Yes, it is standard time BLO 200$ ;It's before last sunday of October CMP TM.HOUR(R1),#2 ;Last sunday in October, after 2am? BHI 210$ ;Yes, we're in standard time again... 200$: MOV #1,TM.ISDST(R1) ;Flag for daylight savings time 210$: ADD #<1972.-1900.>,TM.YEAR(R1) ;Now make it years since 1900 ; to match localtime() DEC TM.MON(R1) ;Also, month is returned in range 0-11 ADD #<5*2>,SP ;Restore stack CLC ;Return success RETURN .DSABL LSB .SBTTL Pure Data .PSECT SYS$D,D ; "Thirty days hath September, ; April, June and November; ; All the rest have thirty-one ; Excepting February alone; ; Which hath but twenty-eight, in fine, ; Till leap year gives it twenty-nine." IL$DPM: .WORD 31. , 28. , 31. , 30. , 31. , 30. .WORD 31. , 31. , 30. , 31. , 30. ; 31. (last entry not ever used) .SBTTL Impure Data .PSECT SYS$D,D ; The following is for determining if daylight savings time is in effect IL$YWD: .BLKW ;Day of week for Jan 1 IL$APR: .BLKW ;Offset to first sunday in April IL$OCT: .BLKW ;Offset to last sunday in October .END