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

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

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

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

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

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

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

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

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


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

; don't bother to save ending count on output

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1EO1EOK:	L← ESNEVR,:1EAPOST;	Runaway Transmitter. Never Never.

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

1EOECOL:	EPFCT,:1EOREST;		Collision

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