Text File

.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 <mbg@world.std.com>. .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 0 .EQU TM 1 .EQU TM 2 .EQU TM 3 .EQU TM 4 .EQU TM 5 .EQU TM 6

;;; .EQU TM TM ;For 1900-based epoch ;;; .EQU TM TM ;For 1970-based epoch .EQU TM TM ;For 1972-based epoch .SBTTL Miscellaneous Definitions

; Define the globals

.GLOBL .GLOBL .GLOBL

; 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

.ENABL LSB

ERROR: MOV #,R0 SEC RETURN

LOCLTM:: MOV (R5)+,R4 ;R4 = Argument count

CALL ;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 ;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>,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>,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-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,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 ;Set days since Jan 1 for Apr 1 MOV #<31.+28.+31.+30.+31.+30.+31.+31.+30.+31.-1>,IL ;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 ; ... INC IL ; ... 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,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 ;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,R0 ;R0 = Weekday for Jan 1 of this year ADD IL,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 ;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,R0 ;R0 = Weekday for Jan 1 of this year ADD IL,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 ;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 ;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 ;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

; "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: .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

; The following is for determining if daylight savings time is in effect

IL: .BLKW ;Day of week for Jan 1 IL: .BLKW ;Offset to first sunday in April IL: .BLKW ;Offset to last sunday in October

.END