// PupTimeServ.bcpl - Pup Time Server // Last modified March 19, 1980 12:07 AM by Boggs get "pup0.decl" get "pup1.decl" get "PupTimeServ.decl" external [ // outgoing procedures TimeServ; TimeServCtx; ResetTimeServ; GetTime // incoming procedures Dequeue; Enqueue; Unqueue MoveBlock; MultEq; Zero; NovaDiv SetTimer; TimerHasExpired; InitializeContext; Block; Dismiss AppendStringToPup; GetPBI; ReleasePBI; CompletePup ExchangePorts; OpenLevel1Socket; CloseLevel1Socket; SetPupDPort DoubleIncrement; DoubleAdd; DoubleSubtract // outgoing statics @ts // incoming statics lenPup; pbiTQ; @ms ] compileif nova then [ external [ RTC ] ] compileif stringFormat then [ external [ UNPACKDT; CONVUDT ] ] static @ts // TimeServCtx is the main process for this module. // It maintains the calendar clock, correcting when necessary. // ResetTimeServ causes the time server to forget what time it is // and probe for the time from another time server. // Until it has reset itself, it doesn't answer requests. // The time is reset by spawning a process, so the time probably wont // have been reset yet when ResetTimeServ returns. // TimeServ is logically part of the miscellaneous services process, // handling the Time protocol packet types. //---------------------------------------------------------------------------- let TimeServ(pbi) be //---------------------------------------------------------------------------- // Called from Misc Server, pbi contains a time protocol Pup [ ExchangePorts(pbi) switchon pbi>>PBI.pup.type into [ compileif stringFormat then [ case ptStringTimeRequest: [ if ts>>TS.dontKnowTime ne 0 % ts>>TS.doCorrect eq 0 endcase let dateVec = vec 6; UNPACKDT(lv ts>>TS.altoClk, dateVec) let buffer = vec 20; CONVUDT(buffer, dateVec) AppendStringToPup(pbi, 1, buffer) CompletePup(pbi, ptStringTimeReply) DoubleIncrement(lv ts>>TS.stats.stringReqs) return ] ] case ptTenexTimeRequest: [ if ts>>TS.dontKnowTime ne 0 % ts>>TS.doCorrect eq 0 endcase // Q1, R1 ← AltoTime/43200. Q1 = halfDays, R1 = seconds // Q2, R2 ← Q1/2, Q2 = days. R2 = halfDays (0 or 1) // Days ← Q2 + 15385 // Seconds ← R1 + R2*43200 let R1 = nil let Q1 = NovaDiv(lv ts>>TS.altoClk, #124300, lv R1) let R2 = Q1 & 1 let Q2 = Q1 rshift 1 let days = vec 1; days!0 = 0; days!1 = Q2 DoubleAdd(days, table [ 0; 15385 ]) let seconds = vec 1; seconds!0 = 0; seconds!1 = R1 if R2 ne 0 then DoubleAdd(seconds, table [ 0; #124300 ]) pbi>>PBI.pup.words↑1 = days!0 lshift 8 + days!1 rshift 8 pbi>>PBI.pup.words↑2 = days!1 lshift 8 + seconds!0 pbi>>PBI.pup.words↑3 = seconds!1 CompletePup(pbi, ptTenexTimeReply, pupOvBytes+6) DoubleIncrement(lv ts>>TS.stats.tenexReqs) return ] case ptAltoTimeRequest: [ if ts>>TS.dontKnowTime ne 0 % ts>>TS.doCorrect eq 0 endcase MoveBlock(lv pbi>>PBI.pup.words↑1, lv ts>>TS.altoClk, 2) MoveBlock(lv pbi>>PBI.pup.words↑3, lv ts>>TS.timeParams, 3) CompletePup(pbi, ptAltoTimeReply, pupOvBytes+10) DoubleIncrement(lv ts>>TS.stats.altoReqs) return ] case ptTimeStatsRequest: [ MoveBlock(lv pbi>>PBI.pup.words, lv ts>>TS.stats, size Stats/16) CompletePup(pbi, ptTimeStatsReply, pupOvBytes+size Stats/8) return ] case ptLockTimeRequest: [ unless Authenticate(pbi) endcase ts>>TS.dontKnowTime = true CompletePup(pbi, ptLockTimeReply, pupOvBytes) return ] case ptResetTimeRequest: [ unless Authenticate(pbi) endcase ResetTimeServ(lv pbi>>PBI.pup.words) CompletePup(pbi, ptResetTimeReply, pupOvBytes) return ] ] ReleasePBI(pbi) // Failure: discard the Pup. ] //---------------------------------------------------------------------------- and TimeServCtx() be // a context //---------------------------------------------------------------------------- [ let lvRTC = alto? 575b, lv RTC let myRTC = @lvRTC [ Block() repeatuntil @lvRTC-myRTC gr ticksPerSec [ //1 second has elapsed since last update myRTC = myRTC + ticksPerSec if ts>>TS.doCorrect then [ DoubleIncrement(lv ts>>TS.corrCount, -1) if MultEq(lv ts>>TS.corrCount, table [ 0; 0 ]) then [ MoveBlock(lv ts>>TS.corrCount, lv ts>>TS.correction, 2) test ts>>TS.corrSign ne 0 ifso myRTC = myRTC - ticksPerSec //gain 1 second ifnot myRTC = myRTC + ticksPerSec //lose 1 second ] ] DoubleIncrement(lv ts>>TS.altoClk) ] ] repeat ] //---------------------------------------------------------------------------- and GetTime(lvDest) = valof //---------------------------------------------------------------------------- [ unless ts>>TS.dontKnowTime do MoveBlock(lvDest, lv ts>>TS.altoClk, 2) resultis ts>>TS.dontKnowTime eq 0 ] //---------------------------------------------------------------------------- and Authenticate(pbi) = pbi>>PBI.pup.id↑1 eq 27182 //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- and ResetTimeServ(port; numargs na) be //---------------------------------------------------------------------------- // Call this to Reset clock from the internet. // The work is done asynchronously by a process spawned by this procedure // so time may not yet be reset when this procedure returns. // Port is who to send the request to. It defaults to all time servers // on all directly connected nets. // If ResetTimeCtx is active and we get another reset request, perhaps the // port was bad in the first request, so reach into the context and change // the port. [ if ts>>TS.resetCtx eq 0 then [ ts>>TS.resetCtx = InitializeContext(lv GetPBI(ms>>MS.soc)>>PBI.pup, lenPup, ResetTimeCtx) Enqueue(ms>>MS.ctxQ, ts>>TS.resetCtx) ] let resetPort = lv ts>>TS.stats.resetPort MoveBlock(resetPort, (na eq 0 ? table [ 0; 0; 0 ], port), lenPort) if MultEq(lv resetPort>>Port.socket, table [ 0; 0 ]) then resetPort>>Port.socket↑2 = psMiscServ ] //---------------------------------------------------------------------------- and ResetTimeCtx(ctx) be //---------------------------------------------------------------------------- // This context's stack is a PBI. The context destroys itself // when done by removing itself from its ctxQ and releasing the PBI. [ let resetPort = lv ts>>TS.stats.resetPort let soc = vec lenPupSoc; OpenLevel1Socket(soc, 0, 0) let gotTime = false [ let pbi = GetPBI(soc, true); if pbi ne 0 then [ SetPupDPort(pbi, resetPort) pbi>>PBI.allNets = resetPort>>Port.host eq 0 & resetPort>>Port.net eq 0 CompletePup(pbi, ptAltoTimeRequest, pupOvBytes) ] let timer = nil; SetTimer(lv timer, 1000) // 10 seconds [ Block() repeatuntil TimerHasExpired(lv timer) % soc>>PupSoc.iQ.head ne 0 pbi = Dequeue(lv soc>>PupSoc.iQ); if pbi eq 0 break if pbi>>PBI.pup.type eq ptAltoTimeReply then [ gotTime = true MoveBlock(lv ts>>TS.altoClk, lv pbi>>PBI.pup.bytes, 2) MoveBlock(resetPort, lv pbi>>PBI.pup.sPort, lenPort) ] ReleasePBI(pbi) ] repeatuntil gotTime ] repeatuntil gotTime CloseLevel1Socket(soc) ts>>TS.dontKnowTime = false ts>>TS.resetCtx = 0 Unqueue(ms>>MS.ctxQ, ctx) Enqueue(pbiTQ, ctx-offset PBI.pup/16) ]