; 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,0EIFB00,0EODOK,0EO0EOK,0ENOCMD,0EIFB01,0EODPST,0EOEPST,0ERESTO,0EIFB10,0EODCOL,0EOECOL,0ERESTI,0EIFB11,0EODUGH,0EOEUGH,0ERBRES;

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

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

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

;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 0E to 1E 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
0EREST:	ERBFCT;		What's happening ?
	:0ENOCMD;	[0ENOCMD,0ERESTO,0ERESTI,0ERBRES]

0ENOCMD:	L← ESNEVR,:0EAPOST;	Shouldn't happen
0ERESTO:	:0ELOOK;		Output when no input buffers
0ERESTI:	:0ELOOK;		Probably new input buffer
0ERBRES:	L← ESABRT,:0EAPOST;	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
0EAPOST:	0EPNTR← L,TASK;	Save microcode status in 0EPNTR
	NOP;

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

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

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


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

; don't bother to save ending count on output

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

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

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

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


;This is a subroutine called from both input and output (0EOCDGO
;and 0EISET).  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.

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

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

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

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

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

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

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

0EIREST:	:0EIFIGN;		0EPNTR 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.

;0EIFRST is really a subroutine that can be called from 0EIREST
;or from 0EIGO, output countdown wait.  If a packet is ignored
;and 0EPNTR is zero, 0EIFRST 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 0EIFIGN, it waits here
0EIFRST:	T← 10;
	MAR← 0ELOC+T;	(EHLOC) Get Ethernet address
	T← 377,EBFCT;	What's happening?
	L← MD AND T,BUS=0,:0EIFOK;[0EIFOK,0EIFBAD] promiscuous?

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

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

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

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

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

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

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

0EIFBC:	:0EISET;		[0EISET] 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.

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

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

0EISET:	T← 11;		Get Pointer and Count out of Current Input Control Block
	MAR← 0ELOC+T;	(ECHLOC)
	NOP;
	T← MD;
	MAR← 4+T,:0ESETUP;	(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).

;0EIDOK, 0EIDMOR, and 0EIDPST 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
0EIDATA:	T← 0ECNTR-1, BUS=0;
	MAR← L← 0EPNTR+1, EBFCT;	[0EIDMOR,0EIDPST] What's happening
0EIDMOR:	0EPNTR← L, L← T, ECBFCT;	[0EIDOK,0EIDPST] Guaranteed to branch
0EIDOK:	MD← EIDFCT, TASK;	[0EIDZ4] Read a word from the interface
0EIDZ4:	0ECNTR← L, :0EIDATA;

; We get to 0EIDPST 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".
0EIDPST:	L← ESIDON, :0EIDFUL;	[0EIDFUL,0EIPOST] Presumed to be INGONE
0EIDFUL:	L← ESIFUL, :0EIPOST;	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.

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

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

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

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

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

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

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

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

;Countdown wait loop.  MRT will generate a wakeup every
;37 usec which will decrement 0ECNTR.  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
0EOCDWT:	L← 177400,EBFCT;	What's happening?
	0EPNTR← L,ECBFCT,:0EOCDW0;[0EOCDW0,0EOCDRS] Packet coming in?
0EOCDW0:	L← 0ECNTR-1,BUS=0,TASK,:0EOCDW1; [0EOCDW1,0EIGO]
0EOCDW1:	0ECNTR← L,EWFCT,:0EOCDWT;	[0EOCDWT,0EOCDGO]

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

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

0EOCDGO:	T← 6;
	MAR← 0ELOC+T;		(EOCLOC) Double word reference
	EPFCT;			Reset interface
	EOSFCT,:0ESETUP;		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
0EODATA:	L← MAR← 0EPNTR+1,EBFCT;	What's happening?
	T← 0ECNTR-1,BUS=0,:0EODOK;	[0EODOK,0EODPST,0EODCOL,0EODUGH]
0EODOK:	0EPNTR← L,L← T,:0EODMOR;	[0EODMOR,0EODEND]
0EODMOR:	0ECNTR← L,TASK;
	EODFCT← MD,:0EODATA;	Output word to transmitter

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

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

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

;Ether 0EOT 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.

0EODEND:	EEFCT;			Disable data wakeups
	TASK;			Wait for EEFCT to take
	:0EO0EOT;			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
0EO0EOT:	EBFCT;			What's happening?
	:0EO0EOK;			[0EO0EOK,0EOEPST,0EOECOL,0EOEUGH]

0EO0EOK:	L← ESNEVR,:0EAPOST;	Runaway Transmitter. Never Never.

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

0EOECOL:	EPFCT,:0EOREST;		Collision

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