-- File: PupRouterOut.mesa,  Last Edit: BLyon  January 16, 1981  4:08 PM

DIRECTORY
  Inline USING [LowHalf],
  System USING [GetClockPulses],
  StatsDefs USING [StatIncr],
  PupRouterDefs USING [
    routerLock, maxHop, OutThings, RoutingTableEntry, GetRoutingTableEntry,
    BeSurePupIsOn, SetPupChecksum],
  CommFlags USING [doDebug, doShow, doStats, doStorms],
  DriverDefs USING [GetDeviceChain, Glitch, Network, PutOnGlobalDoneQueue],
  PupDefs USING [outgoingPup, zappedOutgoingPup, PupBuffer],
  BufferDefs,
  PupTypes USING [allNets, allHosts, PupType, PupAddress, PupHostID, PupNetID],
  DriverTypes USING [bufferSeal];

PupRouterOut: MONITOR LOCKS PupRouterDefs.routerLock
  IMPORTS Inline, System, StatsDefs, PupRouterDefs, DriverDefs
  EXPORTS BufferDefs, PupRouterDefs, PupDefs
  SHARES BufferDefs, DriverTypes =
  BEGIN OPEN StatsDefs, PupRouterDefs, DriverDefs, PupDefs, PupTypes;

  -- EXPORTed TYPEs
  Network: PUBLIC TYPE = DriverDefs.Network;

  dataWordsPerPup: PUBLIC CARDINAL;

  outThings: PUBLIC OutThings ← [outStormy: FALSE, showOut: FALSE, outShower:];

  -- parameters for killing packets
  lightning: INTEGER ← 30;
  bolt: INTEGER ← 10;

  BufferSealBroken: PUBLIC ERROR = CODE;
  IllegalPupLength: PUBLIC ERROR = CODE;

  PupRouterSendThis: PUBLIC ENTRY PROCEDURE [b: PupBuffer] =
    BEGIN
    IF b.dest.net = allNets AND b.dest.host = allHosts THEN
      BEGIN PupRouterBroadcastThis[b]; RETURN; END;
    SendUnlocked[b];
    END;

  SneakySendUnlocked: PUBLIC PROCEDURE [b: PupBuffer] = JustSendUnlocked;
  JustSendUnlocked: INTERNAL PROCEDURE [b: PupBuffer] =
    BEGIN SendUnlocked[b]; END;

  SendUnlocked: INTERNAL PROCEDURE [b: PupBuffer] =
    BEGIN
    d: PupAddress ← b.dest;
    n: PupNetID ← d.net;
    rte: RoutingTableEntry;
    network: Network;
    h: PupHostID;
    IF CommFlags.doStats THEN StatIncr[statPupSent];




    IF CommFlags.doDebug THEN BeSurePupIsOn[];
    IF CommFlags.doDebug AND b.seal # DriverTypes.bufferSeal THEN
      Glitch[BufferSealBroken];




    IF CommFlags.doStorms AND outThings.outStormy -- for debugging only
       AND (lightning ← lightning + 1) > bolt OR lightning < 0 THEN
      BEGIN
      IF lightning > bolt THEN
	BEGIN
	IF bolt > 100 THEN
	  BEGIN
	  mumble: LONG CARDINAL ← System.GetClockPulses[];
	  -- Alto Pulses are short
	  lightning ← -INTEGER[Inline.LowHalf[mumble] MOD 20B];
	  bolt ← 10;
	  END
	ELSE BEGIN lightning ← 0; bolt ← bolt + 1; END;
	END;
      IF CommFlags.doShow AND outThings.showOut THEN
	outThings.outShower[zappedOutgoingPup, b];
      PutOnGlobalDoneQueue[b];
      IF CommFlags.doStats THEN StatIncr[statZappedP];
      RETURN;
      END;

    -- Maybe we should do something for the local case?

    b.pupTransportControl ← 0;
    rte ← GetRoutingTableEntry[n];
    IF rte = NIL OR rte.hop > maxHop OR (network ← rte.network) = NIL THEN
      BEGIN -- don't know where to send it
      IF CommFlags.doStats THEN StatIncr[statPupsSentNowhere];
      BEGIN -- Send him an error Pup, but beware of RequeueProc-- END;
      PutOnGlobalDoneQueue[b];
      RETURN; -- wait to see if there is an alternate path

      END;
    IF CommFlags.doShow AND outThings.showOut THEN
      outThings.outShower[outgoingPup, b];
    h ← rte.route;
    IF h = 0 THEN h ← b.dest.host; -- we are on the same net
    IF CommFlags.doDebug AND ((b.pupLength + 1)/2) >
      dataWordsPerPup + wordsPerPupHeader THEN
      DriverDefs.Glitch[IllegalPupLength];
    SetPupChecksum[b];
    network.encapsulatePup[b, h];
    network.sendBuffer[b];
    END;

  PupRouterBroadcastThis: PUBLIC PROCEDURE [b: PupBuffer] =
    BEGIN
    network: Network;
    IF CommFlags.doStats THEN StatIncr[statPupBroadcast];
    IF CommFlags.doDebug THEN BeSurePupIsOn[];
    IF CommFlags.doDebug AND b.seal # DriverTypes.bufferSeal THEN
      Glitch[BufferSealBroken];
    b.network ← network ← DriverDefs.GetDeviceChain[];
    IF network = NIL THEN {PutOnGlobalDoneQueue[b]; RETURN; };
    b.allNets ← TRUE; -- this is where it gets turned on
    b.dest.host ← allHosts;
    PupBroadcaster[b];
    END;

  -- b.network is already set up to the desired network

  PupBroadcaster: PUBLIC PROCEDURE [b: PupBuffer] =
    BEGIN
    network: Network ← b.network;
    IF ~network.alive OR (b.bypassZeroNet AND network.netNumber = [0, 0]) THEN
      BEGIN PutOnGlobalDoneQueue[b]; RETURN; END;
    -- goes (slowly) around in circles
    b.pupTransportControl ← 0;
    IF network.netNumber.b > 377B THEN b.dest.net ← b.source.net ← [0]
    ELSE b.dest.net ← b.source.net ← [network.netNumber.b];
    b.source.host ← [network.hostNumber];
    network.encapsulatePup[b, PupTypes.allHosts];
    SetPupChecksum[b];
    network.sendBuffer[b];
    END;

  -- Pup is assumed to have arrived from somewhere else.  We fixup defaults here.

  SwapPupSourceAndDest: PUBLIC PROCEDURE [b: PupBuffer] =
    BEGIN
    network: Network ← b.network;
    temp: PupAddress;
    IF CommFlags.doDebug AND b.seal # DriverTypes.bufferSeal THEN
      Glitch[BufferSealBroken];
    temp ← b.source;
    b.source ← b.dest;
    b.dest ← temp;
    -- in case we are returning a broadcast packet
    IF b.dest.net = 0 AND network.netNumber.b < 400B THEN
      b.dest.net ← [network.netNumber.b];
    IF b.source.net = 0 AND network.netNumber.b < 400B THEN
      b.source.net ← [network.netNumber.b];
    IF b.source.host = PupTypes.allHosts THEN
      b.source.host ← [network.hostNumber];
    END;

  ReturnPup: PUBLIC PROCEDURE [
    b: PupBuffer, type: PupTypes.PupType, bytes: CARDINAL] =
    BEGIN SwapPupSourceAndDest[b]; SendPup[b, type, bytes]; END;


  wordsPerPupHeader: CARDINAL = 11;
  bytesPerPupHeader: CARDINAL = wordsPerPupHeader*2;

  GetPupContentsBytes: PUBLIC PROCEDURE [b: PupBuffer] RETURNS [bytes: CARDINAL] =
    BEGIN RETURN[b.pupLength - bytesPerPupHeader]; END;

  SetPupContentsBytes: PUBLIC PROCEDURE [b: PupBuffer, bytes: CARDINAL] =
    BEGIN
    IF bytes > dataWordsPerPup*2 THEN Glitch[IllegalPupLength];
    b.pupLength ← bytes + bytesPerPupHeader;
    END;

  SetPupContentsWords: PUBLIC PROCEDURE [b: PupBuffer, words: CARDINAL] =
    BEGIN
    IF words > dataWordsPerPup THEN Glitch[IllegalPupLength];
    b.pupLength ← words*2 + bytesPerPupHeader;
    END;

  SendPup: PUBLIC PROCEDURE [b: PupBuffer, type: PupType, bytes: CARDINAL] =
    BEGIN
    b.pupType ← type;
    SetPupContentsBytes[b, bytes];
    PupRouterSendThis[b];
    END;


  END.