MCALL .MODULE
MODULE LOCLTM,RELEASE=Y01,VERSION=11,COMMENT=<LOCLTM/MBGLIB>,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 <
[email protected]>.
.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) <future>
; TM[10] Seconds east of Greenwich (hi-order word) <future>
; (negative indicates west of Greenwich)
; TM[11] Time Zone code <future>
;
; 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 <DTM.TM+TIM.HI>(R4),R2 ;R2 = Hi-order time
MOV <DTM.TM+TIM.LO>(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<DA.DAY>,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 #-<DA$DAY>,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<DA.MON>,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 #-<DA$MON>,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<DA.AGE>,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<DA.YR>,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