-- File: PupTimeServerHot.mesa,  Last Edit: HGM  September 24, 1980  1:51 PM

DIRECTORY
  Inline USING [LongDivMod, DIVMOD, LowHalf, HighHalf],
  InlineDefs USING [MesaToBcplLongNumber],
  Process USING [Detach],
  System USING [GetGreenwichMeanTime],
  Time USING [AppendCurrent],

  Clock USING [TimeParameters, GetTimeParms, TimeIsKnown],
  TimeServerDefs USING [
    PupTimeFormat, TimeStatsEntry, timeStatsRequest, timeStatsReply, timeVersion,
    resetTimeRequest, resetTimeReply, lockTimeRequest, lockTimeReply, statText,
    statTenex, statAlto, ResetTime],

  StatsDefs USING [StatCounterIndex, StatIncr, StatGetCounter],
  PupDefs USING [
    PupBuffer, MoveStringBodyToPupBuffer, ReturnPup, ReturnFreePupBuffer],
  PupTypes USING [PupAddress, fillInPupAddress, dateTextIs, dateTextRequest];

PupTimeServerHot: PROGRAM
  IMPORTS
    Inline, InlineDefs, Process, System, Time, Clock, TimeServerDefs, StatsDefs,
    PupDefs
  EXPORTS TimeServerDefs =
  BEGIN OPEN StatsDefs, PupDefs, TimeServerDefs;

  locked, parmsOk: PUBLIC BOOLEAN ← FALSE;
  correction: PUBLIC INTEGER ← 0;
  resetAddress: PUBLIC PupTypes.PupAddress ← PupTypes.fillInPupAddress;

  PupTimeServer: PUBLIC PROCEDURE [b: PupBuffer] =
    BEGIN
    IF (~Clock.TimeIsKnown[] OR locked OR ~parmsOk) AND
      (b.pupType = PupTypes.dateTextRequest OR b.pupType = dateTenexRequest OR
	b.pupType = dateAltoRequest) THEN
      BEGIN ReturnFreePupBuffer[b]; RETURN; END;
    SELECT b.pupType FROM
      PupTypes.dateTextRequest =>
	BEGIN
	s: STRING = [30];
	Time.AppendCurrent[s, TRUE];
	MoveStringBodyToPupBuffer[b, s];
	ReturnPup[b, PupTypes.dateTextIs, s.length];
	StatIncr[statText];
	END;
      dateTenexRequest =>
	BEGIN OPEN Inline;
	now: LONG CARDINAL ← System.GetGreenwichMeanTime[];
	days, seconds: LONG CARDINAL;
	q1, q2, r1, r2: CARDINAL;
	-- 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
	[q1, r1] ← LongDivMod[now, 43200];
	[q2, r2] ← DIVMOD[q1, 2];
	days ← LOOPHOLE[LONG[q2] + LONG[15385]];
	seconds ← LOOPHOLE[LONG[r1] + LONG[r2*43200]];
	-- Yetch, I think this is what MAXC wants
	b.pupWords[0] ← HighHalf[days]*256 + LowHalf[days]/256;
	b.pupWords[1] ← LowHalf[days]*256 + HighHalf[seconds];
	b.pupWords[2] ← LowHalf[seconds];
	ReturnPup[b, dateTenexIs, 6];
	StatIncr[statTenex];
	END;
      dateAltoRequest =>
	BEGIN
	parms: Clock.TimeParameters ← Clock.GetTimeParms[];
	LOOPHOLE[@b.pupWords, LONG POINTER TO PupTimeFormat]↑ ←
	  [time: InlineDefs.MesaToBcplLongNumber[System.GetGreenwichMeanTime[]],
	    zoneS: IF parms.zone > 0 THEN west ELSE east, zoneH: ABS[parms.zone],
	    zoneM: parms.minutes, beginDST: parms.beginDst, endDST: parms.endDst];
	ReturnPup[b, dateAltoIs, 2*SIZE[PupTimeFormat]];
	StatIncr[statAlto];
	END;
      timeStatsRequest =>
	BEGIN OPEN TimeServerDefs;
	tse: LONG POINTER TO TimeStatsEntry ← LOOPHOLE[@b.pupWords];
	tse↑ ←
	  [version: timeVersion,
	    tenexRequests: InlineDefs.MesaToBcplLongNumber[
	    StatGetCounter[statTenex]],
	    stringRequests: InlineDefs.MesaToBcplLongNumber[
	    StatGetCounter[statText]],
	    altoRequests: InlineDefs.MesaToBcplLongNumber[
	    StatGetCounter[statAlto]], correction: correction,
	    resetAddress: resetAddress];
	ReturnPup[b, timeStatsReply, 2*SIZE[TimeStatsEntry]];
	END;
      lockTimeRequest => BEGIN locked ← TRUE; ReturnPup[b, lockTimeReply, 0]; END;
      resetTimeRequest =>
	BEGIN
	locked ← FALSE;
	Process.Detach[FORK ResetTime[b.address]];
	ReturnPup[b, resetTimeReply, 0];
	END;
      ENDCASE => BEGIN ReturnFreePupBuffer[b]; END;
    END;

  END.