*----------------------------------------------------------------------------
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;