; ChainEther*.mu -- Chained input microcode for the Alto Ethernet

; Last modified HGM December 14, 1978  6:24 PM
; typing in fix for 4,,371 posting again
; Last modified HGM October 18, 1978  8:57 PM
; trying to shrink it a few words
; Last modified HGM October 9, 1978  11:27 PM
;  fix for 377B,,0 posting

; Last modified September 23, 1978  5:19 PM

;4-way branches using NEXT6 and NEXT7
!17,20,2EIFB00,2EODOK,2EO2EOK,2ENOCMD,2EIFB01,2EODPST,2EOEPST,2ERESTO,2EIFB10,2EODCOL,2EOECOL,2ERESTI,2EIFB11,2EODUGH,2EOEUGH,2ERBRES;

;2-way branches using NEXT7
;2EOCDW1, 2EOCDWX, and 2EIGO are all related.  Be careful!
!7,10,,2EIFOK,,2EOCDW1,,2EIFBAD,2EOCDWX,2EIGO;

;Miscellaneous address constraints
!7,10,,2EOCDW0,2EODATA,2EIDFUL,2EIDZ4,2EOCDRS,2EIDATA,2EIPOST;
!7,10,,2EIDOK,,,2EIDMOR,2EIDPST,,2EAPOST;
!1,1,2EIFB1;
!1,1,2EIFRST;
!1,1,2ELOOK4;
!1,1,2EODCOR;

;2-way branches using NEXT9
!1,2,2EOREST,2ELOOK1;
!1,2,2ELOOK3,2ELOOK2;
!1,2,2EODDCB,2EIREST;
!1,2,2EPNTOK,2EPNTZ;
!1,2,2EOINPR,2EOINPN;
!1,2,2EODMOR,2EODEND;
!1,2,2EOLDOK,2EOLDBD;
!1,2,2EIFCHK,2EIFPRM;
!1,2,2EOCDWT,2EOCDGO;
!1,2,2ECNTOK,2ECNTZR;
!1,2,2EIFIGN,2EISET;
!1,2,2EIFNBC,2EIFBC;

;Ethernet microcode Status codes

$ESIDON	$377;	Input Done
$ESODON	$777;	Output Done
$ESIFUL	$1377;	Input Buffer full - words lost from tail of packet
$ESLOAD	$1777;	Load location overflowed
$ESCZER	$2377;	Zero word count for input or output command,
		; or odd input chain pointer, or zero input pointer
$ESABRT	$2777;	Abort - usually caused by reset command
$ESNEVR	$3377;	Never Happen - Very bad if it does

;Function Definitions

$EIDFCT	$L000000,014004,000100;	BS = 4,	 Input data
$EILFCT	$L016013,070013,000100;	F1 = 13, Input Look
$EPFCT	$L016014,070014,000100;	F1 = 14, Post
$EWFCT	$L016015,000000,000000;	F1 = 15, Wake-Up

$EODFCT	$L026010,000000,124000;	F2 = 10, Output data
$EOSFCT	$L024011,000000,000000;	F2 = 11, Start output
$ERBFCT	$L024012,000000,000000;	F2 = 12, Rest branch
$EEFCT	$L024013,000000,000000;	F2 = 13, End of output
$EBFCT	$L024014,000000,000000;	F2 = 14, Branch
$ECBFCT	$L024015,000000,000000;	F2 = 15, Countdown branch
$EISFCT	$L024016,000000,000000;	F2 = 16, Start input

;Main memory locations in page 1 reserved for Ethernet.
;There aren't constants for these locations for the second+third
;Ethernet boards, so we manufacture them.  To avoid extensive editing,
;the code for the normal ethernet also manufactures them.
;Odd addresses for double word references are not needed, thus "---".
;Constants 630, 631, and 642 do exist.

; R11 is normally used by MRT
; R21 and R26 are normaly used by the display and cursor tasks

;ELOC		600, 630, 642		Base location of Main Control block
;ECNTR		R12, R11, R42		Number of words left to transfer
;EPNTR		R13, R14, R21		Points before word to transfer


;EPLOC		600, 630, 642 = ELOC	Post location
;EBLOC		601, 631, 643 = ---	Interrupt bit mask

;EELOC		602, 632, 644 = ELOC+2	Ending count location
;ELLOC		603, 633, 645 = ELOC+3	Load location

;EICLOC		604, 634, 646 = ELOC+4	Input buffer Count
;EIPLOC		605, 635, 647 = ---	Input buffer Pointer

;EOCLOC		606, 636, 650 = ELOC+6	Output buffer Count
;EOPLOC		607, 637, 651 = ---	Output buffer Pointer

;EHLOC		610, 640, 652 = ELOC+10	Host Address
;ECHLOC		611, 641, 653 = ELOC+11	Chain Pointer for Input Control Block

; This section of code is designed to be included several times in one
; mu file.  Thats why all the tags have a 0 in front of them.  To make
; a version for another board, let Bravo change all 2E to 2E for you.

; For each instance of this microcode that you wish to include in
; your RAM, you will have to have something like the following:
;	$*ECNTR	$R12;
;	$*EPNTR	$R13;
;	$*ELOC	$600;
;	#ChainEther*.mu;

;  Don't forget to include *EREST in your PREDEF for the Boot Vector

; - Whenever a label has a pending branch, the list of possible
;   destination addresses is shown in brackets in the comment field.
; - Special functions are explained in a comment near their first use.
; - To avoid naming conflicts, all labels and special functions
;   have "E" as the first letter.

;Top of Ethernet Task loop

;Ether Rest Branch Function - ERBFCT
;merge ICMD and OCMD Flip Flops into NEXT6 and NEXT7
;ICMD and OCMD are set from AC0 [14:15] by the SIO instruction
;	00  neither 
;	01  OCMD - Start output
;	10  ICMD - Start input
;	11  Both - Reset interface

;  *** Ethernet task is idle -- waiting for StartIO from emulator
2EREST:	ERBFCT;		What's happening ?
	:2ENOCMD;	[2ENOCMD,2ERESTO,2ERESTI,2ERBRES]

2ENOCMD:	L← ESNEVR,:2EAPOST;	Shouldn't happen
2ERESTO:	:2ELOOK;		Output when no input buffers
2ERESTI:	:2ELOOK;		Probably new input buffer
2ERBRES:	L← ESABRT,:2EAPOST;	Reset Command

;Post status and halt.  Microcode status in L.
;Put microstatus,,hardstatus in EPLOC, merge c(EBLOC) into NWW.
;Note that we write EPLOC and read EBLOC in one operation


;Ether Post Function - EPFCT.  Gate the hardware status
;(LOW TRUE) to Bus [10:15], reset interface.

; Get here if we are Aborting
; Next7 must be a one to shake pending branch from EOCRDS
2EAPOST:	2EPNTR← L,TASK;	Save microcode status in 2EPNTR
	NOP;

	MAR← 2ELOC;	(EPLOC) double word reference
	T← NWW;
	MD← 2EPNTR,EPFCT;		BUS AND 2EPNTR with Status
	L← MD OR T, TASK;	NWW OR c(EBLOC)
	NWW← L, :2EREST;

; Non-Abort Post from Input routine
; L has microcode status, something is waking us up
2EIPOST:	T← 11;
	MAR← 2ELOC+T;	(ECHLOC)
	2EPNTR← L;	Save microcode status in 2EPNTR
	T← MD;
	MAR← 2+T;
	L← T;
	MD← 2ECNTR, TASK;	Save ending count
	2ECNTR← L;		2ECNTR Points to Current Input Control Block

; Advance Chain Pointer to next Input Control Block
	T← 11;
	MAR← 2ECNTR+T;	(ECHLOC) of Current Input Control Block
	L← MD;
	MAR← 2ELOC+T;	(ECHLOC)
	MTEMP← L, TASK;		MTEMP ← new Input Control Block
	MD← MTEMP;

	MAR← 2ECNTR, :2EWAKE;	(EPLOC) double word reference


; Non-Abort Post from Output routine
; L has microcode status, something is waking us up
2EOPOST:	T← 7;
	MAR← 2ELOC+T;	(EOPLOC) Zero output pointer so we won't send it again
	2EPNTR← L, TASK;
	MD← 0;

; don't bother to save ending count on output

	MAR← 2ELOC;	(EPLOC) double word reference
; Store status and generate interrupt(s)
2EWAKE:	T← NWW;
	MD← 2EPNTR,EPFCT;	BUS AND 2EPNTR with Status
	L← MD OR T;	NWW OR c(EBLOC)
	NWW← L,TASK;
	EOSFCT, :2ELOOK; 	Generate more Wakeups

; Look for something to do, Something must be generating Wakeups
2ELOOK:	T← 7;
	MAR← 2ELOC+T;	(EOPLOC)  Look to see if there is output ready
	NOP;
	SINK← MD,BUS=0;
	T← 11, :2EOREST;	[2EOREST,2ELOOK1]  Check for Input buffer ready
2ELOOK1:	MAR← 2ELOC+T;	(ECHLOC)
	T← 1;
	L← MD AND T, T← MD, BUS=0;
	EPFCT, SH=0, :2ELOOK3;  [2ELOOK3,2ELOOK2]

2ELOOK2:	TASK, :2ELOOK4;	[2ELOOK4] Nothing to do
2ELOOK4:	:2EREST;

2ELOOK3:	2EPNTR← L, :2EODDCB;		[2EODDCB,2EIREST]

; Interface has been reset, use EOSFCT to generate another wakeup
2EODDCB:	EOSFCT,:2ECNTZR;	Odd Control Block Pointer


;This is a subroutine called from both input and output (2EOCDGO
;and 2EISET).  The return address is determined by testing ECBFCT,
;which will branch if the buffer has any words in it, which can
;only happen during input.

2ESETUP:	NOP;
	L← MD,BUS=0;	check for zero length
	T← MD-1,:2ECNTOK;	[2ECNTOK,2ECNTZR] start-1

2ECNTZR:	L← ESCZER,:2EAPOST;	Zero word count.  Abort

;Ether Countdown Branch Function - ECBFCT.
;NEXT7 = Interface buffer not empty.

2ECNTOK:	2ECNTR← L, L← 0+T+1;
	SH=0, L← T;
	ECBFCT, 2EPNTR← L, :2EPNTOK;		[2EPNTOK,2EPNTZ]

2EPNTZ:	L← ESCZER, :2EAPOST;	[2EAPOST] Empty Pointer

2EPNTOK:		:2EODATA;		[2EODATA,2EIDATA]

;Ethernet Input

;It turns out that starting the receiver for the first time and
;restarting it after ignoring a packet do the same things.

2EIREST:	:2EIFIGN;		2EPNTR is 0 to flag input mode

;Address filtering code.

;When the first word of a packet is available in the interface
;buffer, a wakeup request is generated.  The microcode then
;decides whether to accept the packet.  Decision must be reached
;before the buffer overflows, within about 14*5.44 usec.
;if EHLOC is zero, machine is 'promiscuous' - accept all packets
;if destination byte is zero, it is a 'broadcast' packet, accept.
;if destination byte equals EHLOC, packet is for us, accept.

;2EIFRST is really a subroutine that can be called from 2EIREST
;or from 2EIGO, output countdown wait.  If a packet is ignored
;and 2EPNTR is zero, 2EIFRST loops back and waits for more
;packets, else it returns to the countdown code.

;Ether Branch Function - EBFCT
;NEXT7 = IDL % OCMD % ICMD % OUTGONE % INGONE (also known as POST)
;NEXT6 = COLLision - Can't happen during input

;  *** Ethernet task may wait for a packet to arrive
;      if called from EIGO via EOCDWT, a packet is already arriving
;      if called from EIGO via 2EIFIGN, it waits here
2EIFRST:	T← 10;
	MAR← 2ELOC+T;	(EHLOC) Get Ethernet address
	T← 377,EBFCT;	What's happening?
	L← MD AND T,BUS=0,:2EIFOK;[2EIFOK,2EIFBAD] promiscuous?

2EIFOK:	MTEMP← LLCY8,:2EIFCHK;  [2EIFCHK,2EIFPRM] Data wakeup

2EIFBAD:	ERBFCT,TASK,:2EIFB1;	[2EIFB1] POST wakeup; xCMD FF set?
2EIFB1:	:2EIFB00;		[2EIFB00,2EIFB01,2EIFB10,2EIFB11]

2EIFB00:	:2EIFIGN;		IDL or INGONE, restart rcvr
2EIFB01:	:2ELOOK;	Probably Output to do
2EIFB10:	:2ELOOK;	Other way to look for Output
2EIFB11:	L← ESABRT,:2EAPOST;	ICMD and OCMD, abort

2EIFPRM:	TASK,:2EIFBC;	Promiscuous. Accept

;Ether Look Function - EILFCT.  Gate the first word of the 
;data buffer to the bus, but do not increment the read pointer.

2EIFCHK:	L← T← 177400,EILFCT;	Mask off src addr byte (BUS AND)
	L← MTEMP-T,SH=0;	Broadcast?
	SH=0,TASK,:2EIFNBC;	[2EIFNBC,2EIFBC] Our Address?

2EIFNBC:	:2EIFIGN;		[2EIFIGN,2EISET]

2EIFBC:	:2EISET;		[2EISET] Enter input main loop

;Ether Input Start Function - EISFCT.  Start receiver.  Interface
;will generate a data wakeup when the first word of the next
;packet arrives, ignoring any packet currently passing.

2EIFIGN:	SINK← 2EPNTR,BUS=0,EPFCT;Reset; Called from output?
	EISFCT,TASK,:2EOCDWX;  [2EOCDWX,2EIGO] Restart rcvr

2EOCDWX:	EWFCT,:2EOCDWT;	Return to countdown wait loop

2EISET:	T← 11;		Get Pointer and Count out of Current Input Control Block
	MAR← 2ELOC+T;	(ECHLOC)
	NOP;
	T← MD;
	MAR← 4+T,:2ESETUP;	(EICLOC) Double word reference

;Input Main Loop

;Ether Input Data Function - EIDFCT.  Gate a word of data to
;the bus from the interface data buffer, increment the read ptr.
;		* * * * * W A R N I N G * * * * *
;The delay from decoding EIDFCT to gating data to the bus is
;marginal.  Some logic in the interface detects the situation
;(which only happens occasionally) and stops SysClk for one cycle.
;Since memory data must be available during cycle 4, and SysClk
;may stop for one cycle, this means that the MD← EIDFCT must
;happen in cycle 3.  There is a bug in this logic which occasionally
;stops the clock in the instruction following the EIDFCT, so
;the EIDFCT instruction should not be the last one of the task,
;or it may screw up someone else (such as RDRAM).

;2EIDOK, 2EIDMOR, and 2EIDPST must have address bits in the pattern:
;xxx1   xxx4        xxx5
;ECBFCT is used to force an unconditional branch on NEXT7

;  *** Ethernet task may wait for next input word
2EIDATA:	T← 2ECNTR-1, BUS=0;
	MAR← L← 2EPNTR+1, EBFCT;	[2EIDMOR,2EIDPST] What's happening
2EIDMOR:	2EPNTR← L, L← T, ECBFCT;	[2EIDOK,2EIDPST] Guaranteed to branch
2EIDOK:	MD← EIDFCT, TASK;	[2EIDZ4] Read a word from the interface
2EIDZ4:	2ECNTR← L, :2EIDATA;

; We get to 2EIDPST for one of two reasons:
; (1) The buffer is full.  In this case, an EBFCT (NEXT[7]) is pending.
;     We want to post "full" if this is a normal data wakeup (no branch)
;     but just "input done" if hardware input terminated (branch).
; (2) Hardware input terminated while the buffer was not full.
;     In this case, an unconditional branch on NEXT[7] is pending, so
;     we always terminate with "input done".
2EIDPST:	L← ESIDON, :2EIDFUL;	[2EIDFUL,2EIPOST] Presumed to be INGONE
2EIDFUL:	L← ESIFUL, :2EIPOST;	Input buffer overrun

;Ethernet output

;It is possible to get here due to a collision.  If a collision
;happened, the interface was reset (EPFCT) to shut off the
;transmitter.  EOSFCT is issued to guarantee more wakeups while
;generating the countdown.  When this is done, the interface is
;again reset, without really doing an output.

2EOREST:	T← 3;
	MAR← 2ELOC+T;		(ELLOC) Get load
	L← R37;			Use clock as random # gen
	2EPNTR← LRSH1;		Use bits [6:13]
	L← MD,EOSFCT;		L← current load
	SH<0,2ECNTR← L;		Overflowed?
	MTEMP← LLSH1,:2EOLDOK;	[2EOLDOK,2EOLDBD]

2EOLDBD:	L← ESLOAD,:2EOPOST;	Load overlow

2EOLDOK:	L← MTEMP+1;		Write updated load
	MAR← 2ELOC+T;		(ELLOC);
	MTEMP← L,TASK;
	MD← MTEMP,:2EORST1;	New load = (old lshift 1) + 1

2EORST1:	L← 2EPNTR;			Continue making random #
	2EPNTR← LRSH1;
	T← 377;
	L← 2EPNTR AND T,TASK;
	2EPNTR← L,:2EORST2;

;At this point, 2EPNTR has 0,,random number, ENCTR has old load.

2EORST2:	T← 11;
	MAR← 2ELOC+T;		(ECHLOC) Has an input buffer been set up?
	T← 2ECNTR;
	L← 2EPNTR AND T;		L← Random & Load
	SINK← MD,BUS=0;
	2ECNTR← L,SH=0,EPFCT,:2EOINPR;[2EOINPR,2EOINPN] 

2EOINPR:	EISFCT,:2EOCDWT;		[2EOCDWT,2EOCDGO] Enable in under out

2EOINPN:	:2EOCDWT;			[2EOCDWT,2EOCDGO] No input.

;Countdown wait loop.  MRT will generate a wakeup every
;37 usec which will decrement 2ECNTR.  When it is zero, start
;the transmitter.

;Ether Wake Function - EWFCT.  Sets a flip flop which will cause
;a wakeup to this task the next time MRT wakes up (every 37 usec).
;Wakeup is cleared when Ether task next runs.  EWFCT must be
;issued in the instruction AFTER a task.

;  *** Ethernet task is waiting for first input word or retransmission clock
2EOCDWT:	L← 177400,EBFCT;	What's happening?
	2EPNTR← L,ECBFCT,:2EOCDW0;[2EOCDW0,2EOCDRS] Packet coming in?
2EOCDW0:	L← 2ECNTR-1,BUS=0,TASK,:2EOCDW1; [2EOCDW1,2EIGO]
2EOCDW1:	2ECNTR← L,EWFCT,:2EOCDWT;	[2EOCDWT,2EOCDGO]

; We have probably been reset
2EOCDRS:	L← ESABRT,:2EAPOST;		[2EAPOST] POST event

2EIGO:	:2EIFRST;			[2EIFRST] Input under output

;Output main loop setup

2EOCDGO:	T← 6;
	MAR← 2ELOC+T;		(EOCLOC) Double word reference
	EPFCT;			Reset interface
	EOSFCT,:2ESETUP;		Start Transmitter

;Ether Output Start Function - EOSFCT.  The interface will generate
;a burst of data requests until the interface buffer is full or the
;memory buffer is empty, wait for silence on the Ether, and begin
;transmitting.  Thereafter it will request a word every 5.44 us.

;Ether Output Data Function - EODFCT.  Copy the bus into the
;interface data buffer, increment the write pointer, clears wakeup
;request if the buffer is now nearly full (one slot available).

;Output main loop

;  *** Ethernet task may wait to send next output word
2EODATA:	L← MAR← 2EPNTR+1,EBFCT;	What's happening?
	T← 2ECNTR-1,BUS=0,:2EODOK;	[2EODOK,2EODPST,2EODCOL,2EODUGH]
2EODOK:	2EPNTR← L,L← T,:2EODMOR;	[2EODMOR,2EODEND]
2EODMOR:	2ECNTR← L,TASK;
	EODFCT← MD,:2EODATA;	Output word to transmitter

2EODPST:	L← ESABRT,:2EAPOST;	[2EAPOST] POST event

2EODCOL:	EPFCT,:2EODCOR;		[2EODCOR] Collision
2EODCOR:	:2EOREST;			Wait for Outgone

2EODUGH:	L← ESABRT,:2EAPOST;	[2EAPOST] POST + Collision

;Ether 2EOT Function - EEFCT.  Stop generating output data wakeups,
;the interface has all of the packet.  When the data buffer runs
;dry, the interface will append the CRC and then generate an
;OUTGONE post wakeup.

2EODEND:	EEFCT;			Disable data wakeups
	TASK;			Wait for EEFCT to take
	:2EO2EOT;			Wait for Outgone

;Output completion.  We are waiting for the interface buffer to
;empty, and the interface to generate an OUTGONE Post wakeup.

;  *** Ethernet task may wait for output buffer to empty
2EO2EOT:	EBFCT;			What's happening?
	:2EO2EOK;			[2EO2EOK,2EOEPST,2EOECOL,2EOEUGH]

2EO2EOK:	L← ESNEVR,:2EAPOST;	Runaway Transmitter. Never Never.

2EOEPST:	L← ESODON,:2EOPOST;	POST event.  Output done

2EOECOL:	EPFCT,:2EOREST;		Collision

2EOEUGH:	L← ESABRT,:2EAPOST;	POST + Collision