-- File: PtEcho.mesa, Last Edit: HGM November 19, 1979 12:00 PM

DIRECTORY
IODefs: FROM "IODefs" USING [CR, WriteChar],
ProcessDefs: FROM "ProcessDefs" USING [Detach, Yield, Abort, Aborted],
StatsDefs: FROM "StatsDefs" USING [StatBump, StatIncr],
PupDefs: FROM "PupDefs" USING [
GetFreePupBuffer, ReturnFreePupBuffer, PupBuffer,
PupSocket, PupSocketDestroy, PupSocketMake, PrintErrorPup,
SecondsToTocks, SetPupContentsBytes, GetPupContentsBytes, veryLongWait],
PupTypes: FROM "PupTypes" USING [
PupAddress, fillInPupAddress, fillInSocketID, echoSoc],
PtDefs: FROM "PtDefs" USING [
PtLookerReady, Done, WaitUntilDone,
CursorBits, echoIndex, lostIndex, recvIndex, sendIndex,
PtIn
terface, Random];

PtE
cho: PROGRAM [pt: PtDefs.PtInterface]
IMPORTS IODefs, ProcessDefs, StatsDefs, PupDefs, P
tDefs
EXPORTS PtDefs = BEGIN
OPEN pt, IODefs, StatsDefs, PupDefs, PupTypes, PtDefs;

PtEchoer: PUBLIC PROCEDURE =
BEGIN
PtLookerReady[];
ProcessDefs.Detach[FORK PtEchoit[]];
WaitUntilDone[1];
END;

PtEchoit: PROCEDURE =
BEGIN
l, k: CARDINAL;
b: PupBuffer;
r: CARDINAL ← 0;
cycle: CARDINAL;
where: PupAddress ← [pushHim.net,pushHim.host,echoSoc];
packetNumber: CARDINAL ← 0;
mySoc: PupSocket ← PupSocketMake[fillInSocketID,where,SecondsToTocks[2]];
SELECT length FROM -- can’t use zero
short => l←1;
cyclic => l←1; -- start at short
long, ignore => l←dataWordsPerPup;
random => NULL;
ENDCASE => ERROR;
UNTIL stopFlag DO
cycle ← 0;
UNTIL stopFlag DO
b ← GetFreePupBuffer[];
SELECT length FROM
short, long, ignore => NULL;
cyclic => l←cycle;
random => l←((r←Random[r]) MOD dataWordsPerPup*2)+1;
ENDCASE => ERROR;
SELECT data FROM
ignore => NULL;
cyclic => FOR k IN [0..l) DO b.pupBytes[k]←k+cycle; ENDLOOP;
ones => FOR k IN [0..l) DO b.pupBytes[k]←377B; ENDLOOP;
zeros => FOR k IN [0..l) DO b.pupBytes[k]←0; ENDLOOP;
alternating => FOR k IN [0..l) DO b.pupBytes[k]←252B; ENDLOOP;
random => FOR k IN [0..l) DO b.pupBytes[k]←(r←Random[r]); ENDLOOP;
ENDCASE => ERROR;
b.pupID.a←b.pupID.b ← (packetNumber←packetNumber+1);
b.pupType ← echoMe;
SetPupContentsBytes[b,l];
StatIncr[statDataPacketsSent];
StatBump[statDataBytesSent,l];
mySoc.put[b];
CursorBits[sendIndex] ← CursorBit
s[sendIndex]+1;
UNTIL (b←mySoc.get[])=NIL DO
SELECT TRUE FROM
(b.pupType=error) =>
BEGIN
PrintErrorPup[b];
END;
((b.pupType#iAmEcho)
OR (b.pupID.a#packetNumber)
OR (b.pupID.b#packetNumber)
OR (l#GetPupContentsBytes[b])) => WriteChar[’#];
ENDCASE =>
BEGIN
SELECT data FROM
ignore => NULL;
cyclic =>
FOR k IN [0..l) DO
IF b.pupBytes[k]#((k+cycle) MOD 400B) THEN WriteChar[’~];
ENDLOOP;
ones =>
FOR k IN [0..l) DO
IF b.pupBytes[k]#377B THEN WriteChar[’~];
ENDLOOP;
zeros =>
FOR k IN [0..l) DO
IF b.pupBytes[k]#0 THEN WriteChar[’~];
ENDLOOP;
alternating =>
FOR k IN [0..l) DO
IF b.pupBytes[k]#252B THEN WriteChar[’~];
ENDLOOP;
random =>
FOR k IN [0..l) DO
IF b.pupBytes[k]#((r←Random[r]) MOD 400B) THEN WriteChar[’~];
ENDLOOP;
ENDCASE => ERROR;
IF info THEN WriteChar[’!];
StatIncr[statDataPacketsReceived];
Sta
tBump[statDataBytesReceived,l];
CursorBits[recvIndex] ← CursorBits[recvIndex]+1;
EXIT;

END;
ReturnFreePupBuffer[b];
END
LOOP;
IF b#NIL THEN ReturnFreePupBuffer[b]
EL
SE
BEGIN
CursorBits[lostIndex] ← CursorBits[lostIndex]+1;
WriteChar[’?];
LOOP; -- Try same packet again, thats why the funny control structure
END;
cycle ← cycle+1;
IF cycle>dataWordsPerPup*2 THEN EXIT;
ENDLOOP;
WriteChar[CR];
ENDLOOP;
PupSocketDestroy[mySoc];
Done[];
END;

-- Echo Server

echoFork: PROCESS;
echoSocket: PupSocket ← NIL;
echoStop: BOOLEAN;

PtEchoOn: PUBLIC PROCEDURE =
BEGIN
echoStop ← FALSE;
echoSocket ← PupSocketMake[
echoSoc,
fillInPupAddress,
veryLongWait];
echoFork ← FORK PtEchoServer[];
ProcessDefs.Yield[]; -- let it get started to avoid Abort BUG
END;

PtEchoOff: PUBLIC PROCEDURE =
BEGIN
echoStop ← TRUE;
ProcessDefs.Abort[echoFork];
JOIN echoFork[];
PupSocketDestroy[echoSocket];
echoSocket ← NIL;
END;

PtEchoServer: PROCEDURE =
BEGIN
b: PupBuffer;
UNTIL echoStop DO
IF (b←echoSocket.get[ ! ProcessDefs.Aborted => EXIT])#NIL THEN
BEGIN
IF b.pupType#echoMe THEN
BEGIN
WriteChar[’↑];
ReturnFreePupBuffer[b];
LOOP;
END;
IF info THEN WriteChar[’$];
echoSocket.setRemoteAddress[b.source];
b.pupType ← iAmEcho;
echoSocket.put[b];
CursorBits[echoIndex] ← CursorBits[echoIndex]+1;
END;
ENDLOOP;
END;

-- initialization
END.