DIRECTORY Basics USING[ LongDiv, LongDivMod, LongMult, LongNumber ], BasicTime USING[ DayOfWeek, MonthOfYear, Pulses, Unpacked, unspecifiedZone, Zone, ZoneAndDST ], File USING[ SystemVolume, Volume ], GermSwap USING[ switches ], PhysicalVolume USING[ GetPhysical, Physical, PhysicalInfo, PhysicalRC, WriteTimeParameters ], Process USING[ Detach ], ProcessorFace USING[ GetClockPulses, GetGreenwichMeanTime, gmtEpoch, microsecondsPerHundredPulses, SetGreenwichMeanTime ], PupDefs USING[ GetFreePupBuffer, MsToTocks, PupBuffer, PupSocket, PupSocketDestroy, PupSocketMake, ReturnFreePupBuffer, SetPupContentsBytes], PupTypes USING[ allHosts, allNets, fillInSocketID, miscSrvSoc, Pair]; BasicTimeImpl: CEDAR MONITOR IMPORTS Basics, File, GermSwap, PhysicalVolume, Process, ProcessorFace, PupDefs EXPORTS BasicTime, PhysicalVolume--TimeParameters-- = BEGIN OutOfRange: PUBLIC ERROR = CODE; TimeNotKnown: PUBLIC ERROR = CODE; TimeParametersNotKnown: PUBLIC ERROR = CODE; PulsesToMicroseconds: PUBLIC PROC[p: BasicTime.Pulses] RETURNS[ LONG CARDINAL ] = { RETURN[MultThenDiv[p, ProcessorFace.microsecondsPerHundredPulses, 100]] }; PulsesToSeconds: PUBLIC PROC[p: BasicTime.Pulses] RETURNS[s: REAL ] = BEGIN s _ p; s _ ( s * ProcessorFace.microsecondsPerHundredPulses ) / 1e8; END; MicrosecondsToPulses: PUBLIC PROC[m: LONG CARDINAL] RETURNS[BasicTime.Pulses ] = { RETURN[MultThenDiv[m, 100, ProcessorFace.microsecondsPerHundredPulses]] }; MultThenDiv: PROC [m1: LONG CARDINAL, m2: CARDINAL, dv: CARDINAL] RETURNS [result: LONG CARDINAL] = TRUSTED BEGIN OPEN mm1: LOOPHOLE[m1, num Basics.LongNumber]; t: MACHINE DEPENDENT RECORD [ SELECT OVERLAID * FROM separate => [low, mid, high: CARDINAL], lower => [lowlong: LONG CARDINAL, junk: CARDINAL], higher => [junk: CARDINAL, highlong: LONG CARDINAL], ENDCASE]; t.lowlong _ Basics.LongMult[mm1.lowbits, m2]; IF mm1.highbits # 0 THEN BEGIN t.highlong _ Basics.LongMult[mm1.highbits, m2] + t.mid; IF t.high # 0 THEN BEGIN OPEN q: LOOPHOLE[result, num Basics.LongNumber]; IF t.high >= dv THEN t.high _ t.high MOD dv; -- overflow; lowbits will be right [quotient: q.highbits, remainder: t.mid] _ Basics.LongDivMod[t.highlong, dv]; q.lowbits _ Basics.LongDiv[t.lowlong, dv]; RETURN END; END; RETURN[t.lowlong/LONG[dv]] END; GMT: PUBLIC TYPE = INT; gmtBaseYear: CARDINAL = 1968; gmtOrigin: GMT = 0; -- representation for midnight, January 1st 1968 GMT alto1968: LONG CARDINAL = -- our time origin in Alto/Pup format -- LONG[ (gmtBaseYear-1901) * 365 + (gmtBaseYear-1901)/4 ] * 24 * 60 * 60; altoExpiry: LONG CARDINAL = LAST[LONG CARDINAL] - alto1968; gmtExpiry: GMT = -- intersection of Alto expiry and 31 bits, minus "nullGMT" representation -- gmtOrigin + MIN[altoExpiry, LAST[INT] - gmtOrigin - 1] - 1; earliestGMT: PUBLIC GMT _ gmtOrigin + LONG[LAST[BasicTime.Zone]] * 60; latestGMT: PUBLIC GMT _ gmtExpiry; Now: PUBLIC ENTRY PROC RETURNS [GMT] = -- N.B.: serialize access to ProcessorFace TRUSTED BEGIN ENABLE UNWIND => NULL; prTime: LONG CARDINAL _ ProcessorFace.GetGreenwichMeanTime[]; IF prTime = ProcessorFace.gmtEpoch -- means "unset clock" THEN BEGIN ok: BOOL; [ok, prTime] _ SetTimeAndTP[]; IF NOT ok THEN ERROR TimeNotKnown; END; RETURN[prTime-alto1968+gmtOrigin ] END; Period: PUBLIC PROC[from, to: GMT] RETURNS [INT] = { RETURN[to-from] }; Update: PUBLIC PROC[base: GMT, period: INT] RETURNS [GMT] = BEGIN IF period IN [gmtOrigin-base .. gmtExpiry-base] THEN RETURN[base+period] ELSE ERROR OutOfRange; END; ToPupTime: PUBLIC PROC[g: GMT] RETURNS[ LONG CARDINAL ] = { RETURN[ alto1968+(g-gmtOrigin) ] }; ToNSTime: PUBLIC PROC[g: GMT] RETURNS[ LONG CARDINAL ] = { RETURN[alto1968+(g-gmtOrigin)] }; FromPupTime: PUBLIC PROC[p: LONG CARDINAL] RETURNS[ GMT ] = { IF p >= alto1968 THEN RETURN[(p-alto1968)+gmtOrigin] ELSE ERROR OutOfRange }; FromNSTime: PUBLIC PROC[p: LONG CARDINAL] RETURNS[ GMT ] = { IF p >= alto1968 THEN RETURN[(p-alto1968)+gmtOrigin] ELSE ERROR OutOfRange }; monthTable: ARRAY BasicTime.MonthOfYear OF CARDINAL = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; startWeekday: BasicTime.DayOfWeek = Monday; -- January 1st 1968 was a Monday Unpack: PUBLIC PROC[time: GMT] RETURNS[ unpacked: BasicTime.Unpacked ] = BEGIN tp: BasicTime.ZoneAndDST = GetZoneAndDST[]; secs, mins, hrs: LONG CARDINAL; IF time < earliestGMT OR time > latestGMT THEN ERROR OutOfRange[]; secs _ time - gmtOrigin; mins _ secs / 60 - (IF tp.zone = BasicTime.unspecifiedZone THEN 0 ELSE tp.zone); hrs _ mins / 60; unpacked.second _ secs MOD 60; unpacked.minute _ mins MOD 60; unpacked.zone _ (IF tp.zone = BasicTime.unspecifiedZone THEN 0 ELSE tp.zone); unpacked.dst _ no; DO -- once if not dst, twice if dst daysSinceBase: CARDINAL = hrs / 24; daysInFourYears: CARDINAL = ( 3*365 + 366 ); fourYears: CARDINAL = daysSinceBase / daysInFourYears; daysBeyondFourYears: CARDINAL = daysSinceBase MOD daysInFourYears; pseudoDaysBeyond: CARDINAL = -- corrected as if every year had 366 days daysBeyondFourYears + (MAX[31+29,daysBeyondFourYears]-(31+29))/365; oddYears: [0..4) = pseudoDaysBeyond / 366; dayOfYear: CARDINAL = (pseudoDaysBeyond MOD 366)+1; -- one's origin, 366-day year unpacked.year _ gmtBaseYear + 4*fourYears + oddYears; unpacked.weekday _ startWeekday; THROUGH [0..daysSinceBase MOD 7) DO unpacked.weekday _ SUCC[unpacked.weekday]; IF unpacked.weekday = unspecified THEN unpacked.weekday _ FIRST[BasicTime.DayOfWeek]; ENDLOOP; FOR month: BasicTime.MonthOfYear IN BasicTime.MonthOfYear[January..December] DO IF dayOfYear <= monthTable[SUCC[month]] THEN {unpacked.month _ month; EXIT}; REPEAT FINISHED => ERROR ENDLOOP; unpacked.day _ dayOfYear-monthTable[unpacked.month]; unpacked.hour _ hrs MOD 24; BEGIN yearStart: CARDINAL = fourYears * daysInFourYears + (IF oddYears = 0 THEN 0 ELSE 366 + (oddYears-1)*365); unpacked.daysThisYear _ daysSinceBase - yearStart; unpacked.secondsThisYear _ secs - ((LONG[yearStart] * 24) * 60) * 60; END; IF unpacked.dst = yes OR tp.zone = BasicTime.unspecifiedZone OR NOT CheckDateGE[unpacked, tp.beginDST, 2] OR CheckDateGE[unpacked, tp.endDST, 1] THEN EXIT; hrs _ hrs+1; unpacked.dst _ yes; ENDLOOP; END; Pack: PUBLIC PROC[unpacked: BasicTime.Unpacked] RETURNS[ time: GMT ] = BEGIN tp: BasicTime.ZoneAndDST = GetZoneAndDST[]; daysInFourYears: CARDINAL = ( 3*365 + 366 ); yearsSinceBase: CARDINAL = IF unpacked.year < gmtBaseYear THEN ERROR OutOfRange ELSE unpacked.year - gmtBaseYear; oddYears: [0..4) = yearsSinceBase MOD 4; day: CARDINAL = -- day number in year -- monthTable[unpacked.month] + unpacked.day - 1--adjust to zero origin-- - -- monthTable assumes all February's have 29 days, so correct for that -- (IF oddYears#0 AND unpacked.month NOT IN [January..February] THEN 1 ELSE 0); daysSinceBase: CARDINAL = (yearsSinceBase/4) * daysInFourYears + oddYears * 365 + day + (IF oddYears # 0 THEN 1 ELSE 0) --leap day--; zone: [-720-60..+721] _ IF unpacked.zone # BasicTime.unspecifiedZone THEN unpacked.zone ELSE IF tp.zone # BasicTime.unspecifiedZone THEN tp.zone ELSE ERROR TimeParametersNotKnown[]; SELECT unpacked.dst FROM unspecified => BEGIN unpacked.weekday _ startWeekday; THROUGH [0..daysSinceBase MOD 7) DO unpacked.weekday _ SUCC[unpacked.weekday]; IF unpacked.weekday = unspecified THEN unpacked.weekday _ FIRST[BasicTime.DayOfWeek]; ENDLOOP; IF tp.zone = BasicTime.unspecifiedZone THEN ERROR TimeParametersNotKnown[]; IF CheckDateGE[unpacked,tp.beginDST,2] AND ~CheckDateGE[unpacked,tp.endDST,2] THEN zone _ zone - 60; END; yes => zone _ zone - 60; no => NULL; ENDCASE => ERROR; RETURN[ gmtOrigin + ((LONG[daysSinceBase] * 24 + unpacked.hour) * 60 + unpacked.minute + zone) * 60 + unpacked.second ] END; CheckDateGE: PROC[unpacked: BasicTime.Unpacked, dstDay, dstHour: CARDINAL] RETURNS[BOOL] = BEGIN day: CARDINAL _ monthTable[unpacked.month] + unpacked.day; SELECT TRUE FROM day < dstDay-6 => RETURN[FALSE]; -- before the interesting week day > dstDay => RETURN[TRUE]; -- after the interesting week unpacked.weekday = Sunday => RETURN[unpacked.hour >= dstHour]; -- critical Sunday ENDCASE => BEGIN THROUGH BasicTime.DayOfWeek[FIRST[BasicTime.DayOfWeek]..unpacked.weekday) DO day _ day-1 ENDLOOP; -- calculate day number of preceding Sunday RETURN[day > dstDay-6] -- before/after the critical Sunday END END; --VolumeFormat.--TimeParameters: PUBLIC TYPE = MACHINE DEPENDENT RECORD[ direction(0:0..0): { west(0), east(1) }, -- California is west zone(0:1..4): [0..12], zoneMinutes(1:0..6): [0..60), beginDST(0:5..15): [0..366], -- April 30 is 121 endDST(1:7..15): [0..366] ]; knownTP: BasicTime.ZoneAndDST; knownTPValid: BOOL _ FALSE; SetTime: PUBLIC ENTRY PROC = { ENABLE UNWIND => NULL; [] _ SetTimeAndTP[] }; SetZoneAndDST: PUBLIC ENTRY PROC[tp: BasicTime.ZoneAndDST] = BEGIN ENABLE UNWIND => NULL; clientTP: TimeParameters = [ direction: IF tp.zone > 0 THEN west ELSE east, zone: ABS[tp.zone] / 60, zoneMinutes: ABS[tp.zone] MOD 60, beginDST: tp.beginDST, endDST: tp.endDST]; sysPhys: PhysicalVolume.Physical = GetSysPhys[]; IF sysPhys # NIL THEN [] _ PhysicalVolume.WriteTimeParameters[sysPhys, TRUE, clientTP]; ConvertPhysTP[clientTP]; END; GetZoneAndDST: PUBLIC ENTRY PROC RETURNS[tp: BasicTime.ZoneAndDST] = BEGIN ENABLE UNWIND => NULL; IF knownTPValid OR SetTimeAndTP[].ok OR TPFromDisk[] THEN RETURN[knownTP]; RETURN[ [zone: BasicTime.unspecifiedZone, beginDST: 366, endDST: 366] ]; END; SetTimeAndTP: INTERNAL PROC RETURNS[ok: BOOL, secs: LONG CARDINAL--NS/Pilot--] = BEGIN sysPhys: PhysicalVolume.Physical = GetSysPhys[]; netTP: TimeParameters; ok _ FALSE; IF GermSwap.switches[c] THEN RETURN; -- => communication package isn't running [ok, netTP, secs] _ TimeFromEthernet[]; IF NOT ok THEN [ok, netTP, secs] _ TimeFromEthernetOne[]; IF NOT ok THEN RETURN; IF NOT knownTPValid THEN BEGIN IF sysPhys # NIL THEN [] _ PhysicalVolume.WriteTimeParameters[sysPhys, TRUE, netTP]; ConvertPhysTP[netTP]; END; TRUSTED{ProcessorFace.SetGreenwichMeanTime[secs]}; END; TPFromDisk: INTERNAL PROC RETURNS[BOOL] = BEGIN sysPhys: PhysicalVolume.Physical = GetSysPhys[]; sysPhysStatus: PhysicalVolume.PhysicalRC; physTP: TimeParameters; physTPOK: BOOL; IF sysPhys = NIL THEN RETURN[FALSE]; [rootStatus: sysPhysStatus, timeValid: physTPOK, time: physTP] _ PhysicalVolume.PhysicalInfo[sysPhys]; IF sysPhysStatus # ok OR NOT physTPOK THEN RETURN[FALSE]; ConvertPhysTP[physTP]; RETURN[TRUE] -- even although we haven't set the clock value? END; ConvertPhysTP: PROC[physTP: TimeParameters] = BEGIN knownTP _ [ zone: physTP.zone*60 + physTP.zoneMinutes, beginDST: physTP.beginDST, endDST: physTP.endDST]; IF physTP.direction # west THEN knownTP.zone _ -knownTP.zone; knownTPValid _ TRUE; END; GetSysPhys: PROC RETURNS[PhysicalVolume.Physical] = BEGIN sys: File.Volume = File.SystemVolume[]; RETURN[ IF sys = NIL THEN NIL ELSE PhysicalVolume.GetPhysical[sys] ] END; TimeFromEthernet: PROC RETURNS[physTPOK: BOOL, physTP: TimeParameters, time: LONG CARDINAL] = BEGIN physTPOK _ FALSE; END; TimeFromEthernetOne: PROC RETURNS[physTPOK: BOOL, physTP: TimeParameters, time: LONG CARDINAL] = TRUSTED BEGIN id: PupTypes.Pair = LOOPHOLE[ProcessorFace.GetClockPulses[]]; soc: PupDefs.PupSocket = PupDefs.PupSocketMake[ PupTypes.fillInSocketID, [net: PupTypes.allNets, host: PupTypes.allHosts, socket: PupTypes.miscSrvSoc], PupDefs.MsToTocks[500]]; THROUGH [0..10) -- how often to transmit request DO BEGIN ENABLE UNWIND => PupDefs.PupSocketDestroy[soc]; send: PupDefs.PupBuffer = PupDefs.GetFreePupBuffer[]; send.pupType _ dateAltoRequest; send.pupID _ id; PupDefs.SetPupContentsBytes[send, 0]; soc.put[send]; DO recv: PupDefs.PupBuffer = soc.get[]; IF recv = NIL THEN EXIT; IF recv.pupType = dateAltoIs AND recv.pupID = id THEN BEGIN PupTime: TYPE = MACHINE DEPENDENT RECORD [ timeHigh, timeLow: CARDINAL, direction: { west(0), east(1) }, zone: [0..127], zoneMinutes: [0..255], beginDST, endDST: CARDINAL ]; data: PupTime = LOOPHOLE[@recv.pupWords, LONG POINTER TO PupTime]^; PupDefs.ReturnFreePupBuffer[recv]; physTP _ [ direction: IF data.direction = west THEN west ELSE east, zone: data.zone, zoneMinutes: data.zoneMinutes, beginDST: data.beginDST, endDST: data.endDST]; time _ LOOPHOLE[ Basics.LongNumber[num[lowbits: data.timeLow, highbits: data.timeHigh]]]; GOTO done END ELSE PupDefs.ReturnFreePupBuffer[recv]; ENDLOOP; END REPEAT done => physTPOK _ TRUE; FINISHED => physTPOK _ FALSE ENDLOOP; PupDefs.PupSocketDestroy[soc]; END; TRUSTED{ Process.Detach[FORK SetTime[]] }; END. ÎCedar Nucleus: low-level time facilities BasicTimeImpl.mesa Andrew Birrell November 30, 1983 9:31 am Last Edited by: Levin, December 16, 1983 3:44 pm Errors Part I: Fine-grain timer Copied from Pilot's SystemImpl. Have to do triple divide t.high is 0, so let mesa do the work... Part II: Packed times Returns the time "period" seconds later than "base" (earlier iff "period" is negative). Raises "OutOfRange" if the result would be before 1968 or after 2036. Returns time according to the Alto/Pup time standard. No errors. Returns time according to the Xerox NS protocol time standard. No errors. Accepts time according to the Alto/Pup time standard. Raises "OutOfRange" for times earlier than 1968. Accepts time according to the Xerox NS protocol time standard. Raises "OutOfRange" for times beyond about 2036. Part III: Packing and unpacking Raises "OutOfRange" for times before 1968 or after 2036. Part IV: Finding and setting time and zone/dst information Ê¢˜Jšœ)™)Jšœ™Jšœ)™)J™0J˜šÏk ˜ Jšœœ.˜:Jšœ œP˜_Jšœœ˜#Jšœ œ ˜JšœœI˜]Jšœœ ˜Jšœœg˜zJšœœ€˜Jšœ œ7˜E—J˜šœœ˜JšœH˜OJšœÏcœ˜5—J˜Jš˜J˜Jšœ™˜Jšœ œœœ˜ J˜Jšœœœœ˜"J˜Jšœœœœ˜,J˜—J™˜š Ïnœœœœœœ˜QJšœœD˜L—J˜š Ÿœœœœœ˜EJš˜J˜Jšœ=˜=Jšœ˜—J˜š Ÿœœœœœœ˜PJšœœD˜L—J˜šŸ œœœœœœœ œœ˜kJš˜Jšœ™Jšœœ˜.š œœ œœœœ˜4Jšœœ˜'Jšœœœœ˜2Jšœœ œœ˜4Jšœ˜ —J˜-šœ˜Jš˜J˜7šœ ˜Jš˜Jšœœ ˜0Jšœ™Jšœœœž"˜OJ˜MJ˜*Jš˜Jšœ˜—Jšœ˜—Jšœ'™'Jšœ œ˜Jšœ˜—J˜—Jšœ™˜Jšœœœœ˜J˜Jšœ œ˜J˜Jšœ œž4˜HJ˜šœ œœž(˜BJšœC˜G—J˜Jš œ œœœœœ ˜;J˜šœ œžM˜^Jšœ œ œœ˜;—J˜Jš œ œœœœ˜FJ˜Jšœ œœ ˜"J˜š Ÿœœœœœœž*˜QJšœ˜ Jšœœœ˜Jšœœœ(˜=Jšœ!ž˜9šœ˜ Jšœœ˜ Jšœ˜Jšœœœœ˜"Jšœ˜—Jšœ˜"Jšœ˜—J˜š Ÿœœœ œœœ˜2Jšœœ ˜—J˜šŸœœœœ œœœ˜;Jš˜Jšœœ#˜/Jšœœ ˜Jšœœ ˜Jšœ˜Jšœž™ž—J˜šŸ œœœœœœœ˜9Jšœœ˜%JšœA™A—J˜šŸœœœœœœœ˜8Jšœœ˜#JšœJ™J—J˜šŸ œœœœœœœ˜;Jš œœœœœœ˜OJšœg™g—J˜šŸ œœœœœœœ˜:Jš œœœœœœ˜OJšœp™p—J˜—Jšœ™˜šœ œœœ˜5Jšœ=˜=—J˜Jšœ,ž ˜LJ˜š Ÿœœœœœ"˜HJš˜Jšœ+˜+Jšœœœ˜Jšœœœœ˜BJšœ˜šœ˜Jšœœ%œœ ˜=—Jšœ˜Jšœœ˜Jšœœ˜Jšœœ%œœ ˜MJšœ˜šœž ˜#Jšœœ ˜#Jšœœ˜,Jšœ œ#˜6Jšœœœ˜Bšœœž*˜GJšœœ)˜C—Jšœ*˜*Jšœ œœ ž˜QJšœ5˜5Jšœ ˜ Jšœœ˜ šœœ˜-Jšœ˜!Jšœœ˜3—Jšœ˜Jšœœ)˜Lšœœœ˜*Jšœœ˜$—Jšœœ˜Jšœ˜Jšœ4˜4Jšœœ˜š˜šœ œ ˜3Jšœœœœ˜5—Jšœ2˜2Jšœ$œ˜E—Jšœ˜Jšœ˜Jšœ$˜&Jšœœ&˜,Jšœ$˜&Jšœœ˜ J˜ Jšœ˜—Jšœ˜Jšœ˜—J˜š Ÿœœœœœ˜FJš˜Jšœ+˜+Jšœœ˜,šœœœ˜9Jšœœ ˜Jšœ˜!—Jšœ"œ˜(šœœž˜(Jšœ-ž˜FJšœžI˜KJš œœ œœœœœ˜L—šœœ˜Jšœ$˜$Jšœ˜Jš œœœœž œ˜/—šœ˜Jšœ*˜,Jšœ˜šœœ$˜+Jšœ˜ Jšœœ˜$——šœ˜˜Jš˜Jšœ ˜ Jšœœ˜ šœœ˜-Jšœ˜!Jšœœ˜3—Jšœ˜Jšœ%œœ˜KJšœ%œ#˜MJšœ˜Jšœ˜—Jšœ˜Jšœœ˜ —Jšœœ˜Jšœœ]˜wJšœ˜Jšœ8™8—J˜š Ÿ œœ0œœœ˜ZJš˜Jšœœ-˜:šœœ˜Jšœœœž˜?Jšœœœž˜;Jšœœž˜Q—šœ˜ Jš˜Jšœœ(˜IJšœ œž+˜CJšœž#˜:Jš˜—Jšœ˜—J˜—Jšœ:™:˜š žœœœœ œœ˜HJšœ)ž˜>J˜J˜Jšœž˜/J˜J˜—J˜Jšœ˜Jšœœœ˜J˜šŸœœœœ˜Jšœœœœ˜/—J˜šŸ œœœœ˜