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

DIRECTORY
PrincOpsUtils USING [LowHalf],
ProcessorFace 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 PrincOpsUtils, ProcessorFace, 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 ← ProcessorFace.GetClockPulses[];
  -- Alto Pulses are short
  lightning ← -INTEGER[PrincOpsUtils.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.