*----------------------------------------------------------------------------
Title[AltoEther.Mc...December 4, 1980 9:53 AM...Taft];
* Main I/O task-level Dorado Ethernet microcode (Alto emulation)
*----------------------------------------------------------------------------


*----------------------------------------------------------------------------
* Note on instruction placement:
*----------------------------------------------------------------------------
* Emulator and input task code are in separate pages (though neither
* one completely fills up its page, so could share with other code).
* Output task and subroutine code fit together on one page.

* This means that the emulator and input tasks cannot use FF in
* instructions that call common subroutines. Also, calls to EResI and
* EResO must leave FF free.


*----------------------------------------------------------------------------
* Initialization code for Output Task
*----------------------------------------------------------------------------
Set[XTask, IP[EOT]];

Subroutine;
EOTInitPC: T← EOT, CoReturn;
TopLevel;
RBase← RBase[EORegs];
T← 33000C;* Init random number generator
RConst← T+(31C);* Constant (33031B = 13849)
T← 1400C;* Minimum inter-packet spacing
MinPktSpc← T+(110C), Branch[ExInit]; * 1510B = 840 => ~500 usec

*----------------------------------------------------------------------------
* Initialization code for Input Task
*----------------------------------------------------------------------------
Set[XTask, IP[EIT]];

Subroutine;
EITInitPC: T← EIT, CoReturn;
TopLevel;
MemBase← ECBR;
T← 300C;
T← T+T, RBase← RBase[AEmRegs];
BRHi← EmuBrHiReg;
BRLo← T;* ECBR← {EmuBrHiVal,,600}
ExInit:
T← EControl;
TIOA← T, Block;* full TIOA for initialization
Branch[.], Breakpoint;* Should never get here

*----------------------------------------------------------------------------
* Input Task
*----------------------------------------------------------------------------

Set[XTask, IP[EIT]];
* Non-emulator mode
KnowRBase[EIRegs];

* Subroutine called to reset input hardware and task PC to idle state.
* Call at EResI. Assumes TIOA selects Ethernet control register.
* Tasking must be turned off by caller.
EResI1:
LdTPC← EIT;* Load input TPC from Link
EResI2:
Link← T, Branch[EIRetn];* Restore caller’s PC and return

Subroutine;
EResI:
T← TurnOffRx;* Turn off input hardware
Output← T;
TopLevel;
T← Link, Call[EResI1];* Link← EIIdle, save caller’s PC


* Idle state of the Ethernet input task.
* Wake up here when first word of a new packet arrives.
EIIdle:
MemBase← ECBR;* Control block base register
RBase← RBase[EIRegs];
TIOA[EData];* Select data I/O address
EITemp2← Input, Branch[EIPLZ, IOAtten]; * Save first word of packet
TIOA[EControl];* Select control register

* Address filtering.
Fetch← EHLoc, T← A0;* Fetch local host address
T← RCY[T, EITemp2, 10], Call[EAdrCk]; * T← destination host, test for zero
pd← T-MD, Call[EAdrCk];* Test for destination host = me
pd← MD, Call[EAdrCk];* Test for me = 0 (promiscuous)

* Packet not accepted by filter.
* Tell hardware to ignore the rest of this packet.
T← WaitForBOP, Branch[EILast];* Wakeup at start of next packet

* Subroutine called by address filtering logic.
* Returns if ALU#0 but falls through (i.e., accepts the packet) if ALU=0.
Subroutine;
EAdrCk:
Branch[.+2, ALU=0];
EIRetn:
Return;
TopLevel;

* Packet accepted by filter.
* Reset output task if it is on (probably in retransmission wait).
TaskingOff;
Call[EResO];* Must leave FF free
TaskingOn;

* Set up pointer and count.
T← EIPLoc;* Set up EIPtr, return pointer
Call[EGPCnt];* Must leave FF free
MemBase← EIBR, Branch[EIWCZ, ALU=0]; * Test count for zero, select BR
BRLo← T, T← EITemp2, Block;* Load BR, recover data word, await next

* Input task (cont’d)

* Input main loop.
* Each iteration stores the word previously input from the interface
* and inputs the next word. This facilitates end-of-packet handling.
* EIPtr has negative of number of words remaining in the buffer.
* IOAtten branches if the data word being read is the end-of-packet status.
EIPtr← (Store← EIPtr)+1, DBuf← T, Branch[EIEnd, IOAtten];
T← Input, Block, Branch[.-1, ALU#0];

* Get here when buffer is exactly full (EIPtr=0).
* We know that the word in T is not EOP status since we would have taken
* the IOAtten branch if it were. If IOAtten is now true, then the word in T
* is the CRC (which we must discard) and the next input is the status word.
* If IOAtten is false, the buffer has overflowed.
T← Input, Branch[EIEnd1, IOAtten]; * Branch if end-of-packet
T← InBufOverflow, Branch[EIPost]; * Input buffer full post code

* Normal end-of-packet exit from main loop.
* One extra word (the CRC) has been stored in the buffer and must not be
* included in the count. The next input is the status word.
EIEnd:
EIPtr← (1S)-(EIPtr);* Make ending count positive, add 1
T← Input;* Read status word
EIEnd1:
T← T AND (EISMask);* Mask out uninteresting status bits
T← T XOR (InDone);* Post input done status

* Turn off interface and go to idle state (awaiting next SIO).
EIPost:
Call[EPost];* Post status in T (must not use FF)
T← TurnOffRx;
EILast:
Output← T, Block, Branch[EIIdle];

* Word count zero when interface started, post error status
EIWCZ:
T← CountZero, Branch[EIPost];* Word count zero post code

* Here if packet length zero, i.e., first word input was end-of-packet status.
* This should happen only if receiver-detected collision reporting is turned on,
* since runt packets are ordinarily filtered out by the phase decoder.
EIPLZ:
Fetch← EICLoc, T← EITemp2;* Fetch input word count
EIPtr← MD, Branch[EIEnd1];* Use as ending count, go report status

*----------------------------------------------------------------------------
* Output Task
*----------------------------------------------------------------------------

Set[XTask, IP[EOT]];
KnowRBase[EORegs];

* Subroutine called to reset output hardware and task PC to idle state.
* Call at EResO. Assumes TIOA selects Ethernet control register.
* Tasking must be turned off by caller.
EResO1:
LdTPC← EOT;* Load output TPC from Link
Branch[EResI2];* Restore Link from T and Return

Subroutine;
EResO:
T← TurnOffTx;* Turn off output hardware
Output← T;
TopLevel;
T← Link, Call[EResO1];* Link← EOIdle, save caller’s PC


* Idle state of the Ethernet output task
EOIdle:
MemBase← ECBR;* Set up MemBase, TIOA
RBase← RBase[EORegs];
TIOA[EControl];

* Test and update load
EOTemp1← (Fetch← ELLoc);* Fetch current load
T← MD+MD+1;* Load lsh 1 +1
Store← EOTemp1, DBuf← T, EOTemp1← MD, * Store new load, save current
Call[Random];* T← random number
T← RSH[T, 10];* Use leftmost 8 bits

* Test for load overflow (old load has sign bit set).
* Then mask countdown with old load and test for nonzero result.
* Note that the countdown clock ticks every 16 us, but we want to count in
* units of 32 us, so we double the count before using it.
EOTemp1← ((EOTemp1) AND T) LSH 1, Branch[ELodOv, R<0];
Branch[EWait, ALU#0];* Branch if wait required

* On first transmission attempt, enforce minimum inter-packet spacing.
* Note: RTClock counts 38-usec intervals in bits [0:9].
T← EOTime, RBase← RBase[RTClock]; * Time last transmission ended
T← T-(RTClock), RBase← RBase[EORegs]; * -(inter-packet spacing)
T← T+(MinPktSpc);* Compare with min permitted
T← RSH[T, 5], Branch[EOGo, Carry’]; * Start now if exceeded min
* Note: RTClock time is in units of 38 usec, but we pretend it is 32.
* Round up to ensure wait interval is nonzero.
EOTemp1← T+1;* Wait for remainder of interval

* Must wait before retransmitting. EOTemp1 has wait time in 16-usec units.
* If the input word count is nonzero, enable the receiver while waiting.
EWait:
Fetch← EICLoc;* Fetch input word count
PD← MD;
T← TurnOnRx, Branch[.+2, ALU=0]; * Branch if no count set up
Output← T;* Enable input wakeups

* Retransmission countdown wait loop.
* CountDown turns off data wakeups and requests a wakeup at next
* tick of countdown clock.
T← CountDown;
EOTemp1← (EOTemp1)-1, Output← T;
Block, Branch[.-1, ALU#0];

* Done waiting. Shut off the receiver if it was turned on.
TaskingOff;
Call[EResI];* Must leave FF free
TaskingOn;

* Output task (cont’d)

* Set up count and pointer
EOGo:
T← EOPLoc, Call[EGPCnt];* Set up EOPtr, return pointer
MemBase← EOBR, Branch[EOWCZ, ALU=0]; * Test count for zero, select BR
BRLo← T;* Load low BR

* Select data I/O address, then pre-fetch the first word.
EOPtr← (Fetch← EOPtr)+1;
Branch[EOEnd, ALU=0];

* Output main loop.
* Data transfer is pipelined: each iteration outputs a data word
* fetched in the previous iteration and starts the fetch of the data for
* the next iteration. This way, cache misses generally occur while our
* task is blocked, and lower-priority tasks are permitted to run while
* the cache is being loaded.
* EOPtr is the negative of the number of words remaining to be fetched.
* IOAtten branches if a collision, data late, or Fifo PE has aborted output.
EOPtr← (Fetch← EOPtr)+1, T← MD, Branch[EOAbrt, IOAtten];
Output← T, Block, Branch[.-1, ALU#0];

* Next-to-last word has been output and last word has been fetched
EOEnd:
Output← MD;* Output last word
TIOA[EControl];* Select control register
T← SendEOP;* Declare end of packet
Output← T, Block;

* Wake up here only when packet completely sent or collision has occurred.
* Note that EOPtr=0, the proper value to post for normal ending word count.
EOTemp1← Input, Branch[EOAbr1, IOAtten]; * Branch if aborted, get status
RBase← RBase[RTClock];* Remember current time
T← RTClock, RBase← RBase[EORegs];
EOTime← T;
EODone:
T← (EOTemp1) AND (EOSMask);* Mask out uninteresting status bits
T← T XOR (OutDone);* Post output done status

* Turn off interface and go to idle state (awaiting next SIO).
EOPost:
Call[EPost];* Post status in T
T← TurnOffTx;
EOLast:
Output← T, Block, Branch[EOIdle];

* Here if collision or other error occurred.
* If a collision, just try again. If not, post output done.
EOAbrt:
EOPtr← (1S)-(EOPtr);* Make count positive, add 1
TIOA[EControl];
EOTemp1← Input;* Read status

* If not a collision, post output done with whatever status bits we got.
* The status bits posted are not actually defined in the Alto, but the software
* will notice that the hardware status is not good and will therefore retry.
EOAbr1:
PD← (EOTemp1) AND (TxCollision); * Collision?
T← TurnOffTx, Branch[EODone, ALU=0]; * Branch if not
Output← T;* Turn off to reset collision bit
T← TurnOnTx, Branch[EOLast];* Turn on and try again

* Word count zero when interface started, post error status
EOWCZ:
T← CountZero, Branch[EOPost];* Word count zero post code

* Load overflow, post error status
ELodOv:
T← LoadOverflow, Branch[EOPost]; * Load overflow post code

*----------------------------------------------------------------------------
* Task-independent Subroutines
*----------------------------------------------------------------------------

Subroutine;

*----------------------------------------------------------------------------
* Post command completion.
* Expects post code and status in T and ending word count in EIPtr or EOPtr.
* Assumes RBase is set up but makes no assumption about MemBase or TIOA.
* Returns with standard MemBase and TIOA set up.
* Clobbers T and RBase.
*----------------------------------------------------------------------------

EPost:
MemBase← ECBR;* Select BR for page 1 I/O locations
T← (Store← EPLoc)+1, DBuf← T;* Store ending status in EPLoc
T← (Fetch← T)+1;* Fetch bit mask (know EBLoc=EPLoc+1)
Store← T, DBuf← ExPtr, T← MD;* Store word count (know EELoc=EBLoc+1)
RBase← RBase[NWW];* T has interrupt bit mask
NWW← (NWW) OR T, TIOA[EControl]; * Or bit(s) into NWW
Reschedule, Return;* Force emulator to notice


*----------------------------------------------------------------------------
* Get input or output pointer and count.
* Expects EIPLoc or EOPLoc in T (knows that EICLoc=EIPLoc-1, EOCLoc=EOPLoc-1).
* Sets up ExPtr (EIPtr or EOPtr) and returns memory base in T (for BRLo←).
* Upon return, ALU branches are conditioned by -count (for zero test).
* Also sets TIOA← EData.
*----------------------------------------------------------------------------

EGPCnt:
T← (Fetch← T)-1;* Fetch pointer
T← MD, Fetch← T, ExPtr← A0;* T← pointer, fetch count

* Want to set base register such that adding 2↑16-count (treating -count
* as an unsigned number) will address the first word of the packet.
* Note that (pointer-(2↑16-count)) mod 2↑16 = pointer+count
T← T+MD;* T← (pointer-(2↑16-count)) mod 2↑16
ExPtr← (ExPtr)-MD, TIOA[EData], Return; * ExPtr← -count


*----------------------------------------------------------------------------
* Generate random number and return it in T.
* Must have RBase[RNum] (=EORegs).
* Uses algorithm:
*
R← (2↑11 + 2↑2 + 2↑0)* R + 13849
*----------------------------------------------------------------------------

KnowRBase[RNum];

Random:
T← LSH[RNum, 11];* T← 2↑9 * R
T← T+(RNum);* (2↑9 + 2↑0)* R
T← LSH[T, 2];* (2↑11 + 2↑2)* R
T← T+(RNum);* (2↑11 + 2↑2 + 2↑0)* R
T← RNum← T+(RConst), Return;* +13849

TopLevel;