-- File: TedLocked.mesa, Last Edit: HGM July 17, 1979 1:08 PM

-- Copyright Xerox Corporation 1979, 1980

DIRECTORY
CommUtilDefs: FROM "CommUtilDefs" USING [GetTicks],
AltoEthernetDefs: FROM "AltoEthernetDefs" USING [
StartIO, ShortenData, EthernetDeviceBlockHandle, NIL0,
EthernetPost, EthernetNotPosted, hardwareAOK,
inputDone, outputDone, inputBufferOverflow, outputLoadOverflow,
zeroLengthBuffer, hardwareReset, interfaceBroken],
TedDefs: FROM "TedDefs" USING [
lock, hardware, myNetwork, myDevice, ethernetEncapsulationOffset,
interruptBit, inputCommand, resetCommand, outputCommand],
DriverDefs: FROM "DriverDefs" USING [
Glitch, GetInputBuffer, PutOnGlobalDoneQueue, PutOnGlobalInputQueue],
BufferDefs: FROM "BufferDefs" USING [Buffer, QueueObject, Dequeue];

TedLocked: MONITOR LOCKS TedDefs.lock
IMPORTS CommUtilDefs, DriverDefs, BufferDefs, AltoEthernetDefs, TedDefs
EXPORTS TedDefs
SHARES BufferDefs =
BEGIN OPEN TedDefs;


tedPleaseStop: PUBLIC BOOLEAN;
nextBufferPointer: PUBLIC POINTER;
currentInputBuffer: PUBLIC BufferDefs.Buffer;
nextInputBuffer: PUBLIC BufferDefs.Buffer;
currentOutputBuffer: PUBLIC BufferDefs.Buffer;
timeSendStarted: PUBLIC CARDINAL;
outputQueue: PUBLIC BufferDefs.QueueObject;

ZeroLengthBuffer: PUBLIC ERROR = CODE;
HardwareBroken: PUBLIC ERROR = CODE;
Imposs
ibleMicrocodeStatus: PUBLIC ERROR = CODE;

Interrupt: PUBLIC ENTRY PROCEDURE =
BEGIN
myDevice: AltoEthernetDefs.EthernetDeviceBlockHandle ← TedDefs.myDevice;
b, temporaryBuffer: BufferDefs.Buffer;
savedWordsLeft: CARDINAL;
savedPostData: AltoEthernetDefs.EthernetPost;

UNTIL tedPleaseStop DO

UNTIL myDevice.postData#AltoEthernetDefs.EthernetNotPosted DO
WAIT hardware;
ENDLOOP;

savedPostData ← myDevice.postData;
savedWordsLeft ← myDevice.wordsLeft;

-- Turn on input as soon as we can, so we don’t drop as many packets.
myDevice.postData ← AltoEthernetDefs.EthernetNotPosted;
myDevice.inputBuffer ← [
nextInputBuffer.length-ethernetEncapsulationOffset,nextBufferPointer];
AltoEthernetDefs.StartIO[inputCommand];

SELECT savedPostData.microcodeStatus FROM
AltoEthernetDefs.inputDone =>
IF savedPostData.hardwareStatus=AltoEthernetDefs.hardwareAOK THEN
BEGIN
IF (temporaryBuffer←DriverDefs.GetInputBuffer[])#NIL THEN
BEGIN
temporaryBuffer.device ← ethernet;
b ← currentInputBuffer;
b.length ← (b.length-ethernetEncapsulationOffset)-savedWordsLeft;
b.network ← @myNetwork;
DriverDefs.PutOnGlobalInputQueue[b];
currentInputBuffer ← temporaryBuffer;
END;
END;

AltoEthernetDefs.outputDone, AltoEthernetDefs.outputLoadOverflow =>
FlushTheCurrentOutputBuffer[];

AltoEthernetDefs.hardwareReset =>
-- There are three reasons for getting here:
-- 1) getting back from the Debugger
-- 2) lost interrupt, and the Watcher poked us
-- 3) stuck output packet (transciever not plugged in), and the Watcher killed it
BEGIN
IF currentOutputBuffer#NIL THEN FlushTheCurrentOutputBuffer[];
END;

AltoEthernetDefs.inputBufferOverflow => NULL;

AltoEthernetDefs.zeroLengthBuffer => DriverDefs.Glitch[ZeroLengthBuffer];
AltoEthernetDefs.interfaceBroken => DriverDefs.Glitch[HardwareBroken];
ENDCASE => DriverDefs.Glitch[ImpossibleMicrocodeStatus];


-- There are two buffers used for input: the current one, and a hot standby.
-- Way up at the beginning of this loop, a read was started into the standby buffer.

-- At this point, currentInputBuffer has a buffer to be setup for use next time.
-- If we just finished a read then it is a new one left there by the inputDone processing,
-- (or the old one if we are recycling it because we couldn’t get a new one)
-- otherwise, we are reusing the previous one.

temporaryBuffer ← nextInputBuffer;
nextInputBuffer ← currentInputBuffer;
currentInputBuffer ← temporaryBuffer;
nextBufferPointer ← AltoEthernetDefs.ShortenData[
@nextInputBuffer.encapsulation+ethernetEncapsulationOffset];
nextBufferPointer↑ ← 0;

-- see if there is output to do, there will be some for several reasons:
-- 1 output finished, and another buffer was queued
-- 2 input finished, and leftover outputBuffer (in under out)
-- 3 input finished, and somebody didn’t kick us because a packet was pouring in
-- 4 (chained) we got some input first

BEGIN
IF currentOutputBuffer#NIL THEN GOTO SendThisOne;
IF outputQueue.length=0 THEN GOTO NothingToSend;
currentOutputBuffer ← BufferDefs.Dequeue[@outputQueue];
timeSendStarted ← CommUtilDefs.GetTicks[];
GOTO SendThisOne;
EXITS
SendThisOne =>
BEGIN -- start output if not already input coming in
IF myDevice.inputBuffer.pointer↑#0 THEN GOTO PouringIn;
myDevice.interruptBit ← 0; -- inhibit interrupts for reset
myDevice.postData ← AltoEthernetDefs.EthernetNotPosted;
-- beware of hardware/microcode screwup
-- it seems to hang while sending, after a collision, until a gateway packet arrives
UNTIL myDevice.postData#AltoEthernetDefs.EthernetNotPosted DO
AltoEthernetDefs.StartIO[resetCommand];
ENDLOOP;
myDevice.interruptBit ← interruptBit; --interrupts back on
myDevice.postData ← AltoEthernetDefs.EthernetNotPosted;
myDevice.retransmissionMask ← 0; -- zero the load for new packet
myDevice.outputBuffer.count ← currentOutputBuffer.length;
myDevice.outputBuffer.pointer ← AltoEthernetDefs.ShortenData[
@currentOutputBuffer.encapsulation+ethernetEncapsulationOffset];
AltoEthernetDefs.StartIO[outputCommand];
EXITS
PouringIn => NULL;
END;
NothingToSend => NULL;
END;

ENDLOOP;
END
; -- Interrupt

FlushTheCurrentOutputBuffer: PROCEDURE =
BEGIN
myDevice.outputBuffer ← [0,AltoEthernetDefs.NIL0];
DriverDefs.PutOnGlobalDoneQueue[currentOutputBuffer];
currentOutputBuffer ← NIL;
END;

END. -- TedLocked