<> <> <> <> DIRECTORY Basics USING [LongDiv, LongDivMod, LongMult, LongNumber], BasicTime USING [DayOfWeek, MonthOfYear, Pulses, Unpacked, unspecifiedZone, Zone, ZoneAndDST], BasicTimeExtra USING[DST], 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, BasicTimeExtra, PhysicalVolume = { CARD: TYPE = LONG CARDINAL; <> <> <> <> <> knownTP: BasicTime.ZoneAndDST; knownTPValid: BOOL _ FALSE; <> <> OutOfRange: PUBLIC ERROR = CODE; TimeNotKnown: PUBLIC ERROR = CODE; TimeParametersNotKnown: PUBLIC ERROR = CODE; <> PulsesToMicroseconds: PUBLIC PROC [p: BasicTime.Pulses] RETURNS [ CARD ] = { RETURN [MultThenDiv[p, ProcessorFace.microsecondsPerHundredPulses, 100]]; }; PulsesToSeconds: PUBLIC PROC [p: BasicTime.Pulses] RETURNS [s: REAL ] = { <> RETURN [p*(ProcessorFace.microsecondsPerHundredPulses*1.0e-8)]; }; MicrosecondsToPulses: PUBLIC PROC [m: CARD] RETURNS [BasicTime.Pulses ] = { RETURN [MultThenDiv[m, 100, ProcessorFace.microsecondsPerHundredPulses]]; }; MultThenDiv: PROC [m1: CARD, m2: CARDINAL, dv: CARDINAL] RETURNS [result: CARD] = TRUSTED { OPEN mm1: LOOPHOLE[m1, num Basics.LongNumber]; t: MACHINE DEPENDENT RECORD [ SELECT OVERLAID * FROM separate => [low, mid, high: CARDINAL], lower => [lowlong: CARD, junk: CARDINAL], higher => [junk: CARDINAL, highlong: CARD], ENDCASE]; t.lowlong _ Basics.LongMult[mm1.lowbits, m2]; IF mm1.highbits # 0 THEN { t.highlong _ Basics.LongMult[mm1.highbits, m2] + t.mid; IF t.high # 0 THEN { 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 }; }; <> RETURN [t.lowlong/LONG[dv]] }; <> GMT: PUBLIC TYPE = INT; gmtBaseYear: CARDINAL = 1968; gmtOrigin: GMT = 0; -- representation for midnight, January 1st 1968 GMT alto1968: CARD = -- our time origin in Alto/Pup format -- LONG[ (gmtBaseYear-1901) * 365 + (gmtBaseYear-1901)/4 ] * 24 * 60 * 60; altoExpiry: CARD = LAST[CARD] - 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 PROC RETURNS [GMT] = TRUSTED { prTime: CARD _ LockedGetTime[]; IF prTime = ProcessorFace.gmtEpoch THEN { <> ok: BOOL; [ok, prTime] _ SetTimeAndTP[]; IF NOT ok THEN ERROR TimeNotKnown; }; RETURN [prTime-alto1968+gmtOrigin ] }; Period: PUBLIC PROC [from, to: GMT] RETURNS [INT] = { RETURN [to-from]; }; Update: PUBLIC PROC [base: GMT, period: INT] RETURNS [GMT] = { <> IF period IN [gmtOrigin-base .. gmtExpiry-base] THEN RETURN [base+period] ELSE ERROR OutOfRange; }; ToPupTime: PUBLIC PROC [g: GMT] RETURNS [ CARD ] = { <> RETURN [ alto1968+(g-gmtOrigin) ]; }; ToNSTime: PUBLIC PROC [g: GMT] RETURNS [ CARD ] = { <> RETURN [alto1968+(g-gmtOrigin)]; }; FromPupTime: PUBLIC PROC [p: CARD] RETURNS [ GMT ] = { <> IF p >= alto1968 THEN RETURN [(p-alto1968)+gmtOrigin] ELSE ERROR OutOfRange; }; FromNSTime: PUBLIC PROC [p: CARD] 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 ControlledUnpack: PUBLIC PROC [time: GMT, zone: BasicTime.Zone, dst: BasicTimeExtra.DST _ unspecified, beginDST: [0..366] _ 0, endDST: [0..366] _ 0] RETURNS [unpacked: BasicTime.Unpacked] = { secs, mins, hrs: CARD; IF time < earliestGMT OR time > latestGMT THEN ERROR OutOfRange[]; secs _ time - gmtOrigin; mins _ secs / 60 - (IF zone = BasicTime.unspecifiedZone THEN 0 ELSE zone); hrs _ mins / 60; unpacked.second _ secs MOD 60; unpacked.minute _ mins MOD 60; unpacked.zone _ (IF zone = BasicTime.unspecifiedZone THEN 0 ELSE zone); unpacked.dst _ no; DO <> 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; { 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; }; IF unpacked.dst = yes OR zone = BasicTime.unspecifiedZone OR dst = no OR (dst = unspecified AND NOT CheckDateGE[unpacked, beginDST, 2]) OR (dst = unspecified AND CheckDateGE[unpacked, endDST, 1]) THEN EXIT; hrs _ hrs+1; unpacked.dst _ yes; ENDLOOP; }; Unpack: PUBLIC PROC [time: GMT] RETURNS [ unpacked: BasicTime.Unpacked ] = { tp: BasicTime.ZoneAndDST = GetZoneAndDST[]; RETURN[ControlledUnpack[time, tp.zone, unspecified, tp.beginDST, tp.endDST]]; }; Pack: PUBLIC PROC [unpacked: BasicTime.Unpacked] RETURNS [time: GMT] = { <> 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 => { 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; }; yes => zone _ zone - 60; no => NULL; ENDCASE => ERROR; RETURN [gmtOrigin + ((LONG[daysSinceBase] * 24 + unpacked.hour) * 60 + unpacked.minute + zone) * 60 + unpacked.second] }; CheckDateGE: PROC [unpacked: BasicTime.Unpacked, dstDay, dstHour: CARDINAL] RETURNS [BOOL] = { 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 => { 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 } }; <> --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] ]; SetTime: PUBLIC PROC = TRUSTED { [] _ SetTimeAndTP[]; }; SetZoneAndDST: PUBLIC ENTRY PROC [tp: BasicTime.ZoneAndDST] = { 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]; }; GetZoneAndDST: PUBLIC PROC RETURNS [tp: BasicTime.ZoneAndDST] = { ENABLE UNWIND => NULL; IF knownTPValid OR SetTimeAndTP[].ok OR TPFromDisk[] THEN RETURN [knownTP]; RETURN [ [zone: BasicTime.unspecifiedZone, beginDST: 366, endDST: 366] ]; }; SetTimeAndTP: PROC RETURNS [ok: BOOL _ FALSE, secs: CARD _ ProcessorFace.gmtEpoch --NS/Pilot--] = { netTP: TimeParameters; 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; LockedSetTime[netTP, secs]; }; LockedGetTime: ENTRY PROC RETURNS [CARD] = TRUSTED { <> RETURN [ProcessorFace.GetGreenwichMeanTime[]]; }; LockedSetTime: ENTRY PROC [netTP: TimeParameters, secs: CARD--NS/Pilot--] = { <> IF NOT knownTPValid THEN { sysPhys: PhysicalVolume.Physical = GetSysPhys[]; IF sysPhys # NIL THEN [] _ PhysicalVolume.WriteTimeParameters[sysPhys, TRUE, netTP]; ConvertPhysTP[netTP]; }; TRUSTED {ProcessorFace.SetGreenwichMeanTime[secs]}; }; TPFromDisk: ENTRY PROC RETURNS [BOOL] = { sysPhys: PhysicalVolume.Physical = GetSysPhys[]; sysPhysStatus: PhysicalVolume.PhysicalRC; physTP: TimeParameters; physTPOK: BOOL; IF sysPhys = NIL OR knownTPValid 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? }; ConvertPhysTP: INTERNAL PROC [physTP: TimeParameters] = { knownTP _ [ zone: physTP.zone*60 + physTP.zoneMinutes, beginDST: physTP.beginDST, endDST: physTP.endDST]; IF physTP.direction # west THEN knownTP.zone _ -knownTP.zone; knownTPValid _ TRUE; }; GetSysPhys: PROC RETURNS [PhysicalVolume.Physical] = { sys: File.Volume = File.SystemVolume[]; RETURN [ IF sys = NIL THEN NIL ELSE PhysicalVolume.GetPhysical[sys] ]; }; TimeFromEthernet: PROC RETURNS [physTPOK: BOOL, physTP: TimeParameters, time: CARD] = { physTPOK _ FALSE; }; TimeFromEthernetOne: PROC RETURNS [physTPOK: BOOL, physTP: TimeParameters, time: CARD] = TRUSTED { 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) DO { <> 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 { 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 } ELSE PupDefs.ReturnFreePupBuffer[recv]; ENDLOOP; } REPEAT done => physTPOK _ TRUE; FINISHED => physTPOK _ FALSE ENDLOOP; PupDefs.PupSocketDestroy[soc]; }; TRUSTED { Process.Detach [FORK SetTime[]]; }; }.