; AltoEIA2.mu -- Microcode for controlling the Alto EIA interface.
;		This file contains the character-at-a-time and uninterpreted
;		block software interfaces and the interval timer handler.

;	Last modified September 23, 1978  4:16 PM


; **** Character-at-a-time Receiver ****

; LCB format:
; word	0 to 3	standard.  ***Next LCB link must point to itself***
;	4	begin pointer (first word of buffer)
;	5	end pointer (begin+length -- first word beyond end)
;	6	read pointer (next word to read)
;	7	write pointer (next word to write)
; Ring buffer conventions:
;	Empty buffer is represented by write pointer = read pointer.
;	Full buffer is represented by write pointer +1 = read pointer
;	(modulo wraparound).
; These conventions are the ones used in the Bcpl Ring Buffer package
; (word version).
; In each word, the data byte is stored in the right half and the
; rightmost 8 bits of hardware status in the left half.

!1, 2, ~RWrap, RWrap;
!1, 2, ~RBOvf, RBOvf;

RCAAT:	T← EIASTS;			Get hardware status
	L← 377 AND T;			Keep just right byte
	MTEMP← L LCY 8;			Swap to left half
	T← LCB;
	MAR← 6+T;			Reference LCB word 6
	T← EIADAT;			Merge status with data byte
	L← MTEMP OR T;
	EIADAT← L;
	L← MD;				L← read ptr (word 6)
	T← MD;				T← write ptr (word 7)
	EIATMP← L, MAR← T;		Reference word pointed to by write ptr
	L← 1+T;				L← write ptr +1
	MD← EIADAT, TASK;		Store status,,data in ring buffer
	EIADAT← L;			Save write ptr across TASK

	T← LCB;
	MAR← 5+T;			Reference LCB word 5
	L← 7+T;				Compute LCB+7
	MTEMP← L;
	T← EIADAT;			The saved write ptr +1
	L← MD-T;			Compare with end ptr (word 5)
	L← MD, SH=0;			L← begin ptr (word 4)
	T← EIATMP, :~RWrap;		[~RWrap, RWrap] T← read ptr
RWrap:	EIADAT← L;			Wrap write ptr around to beginning
~RWrap:	L← EIADAT-T;			Compare new write ptr with read ptr
	MAR← MTEMP, SH=0;		Reference LCB word 7
	TASK, :~RBOvf;			[~RBOvf, RBOvf]
~RBOvf:	MD← EIADAT, :ESRet2;		Store write ptr, go generate interrupt

; Here if ring buffer is full.  Post an overflow error.
RBOvf:	NOP;				TASK pending

	L← T← 2, :EIAPost;		Post code = 2, return index = 2
EPRet2:	:SameState;

; **** Character-at-a-time Transmitter ****

; LCB format:
; word	0 to 3	standard
;	4	begin pointer (first word of buffer)
;	5	end pointer (begin+length -- first word beyond end)
;	6	read pointer (next word to read)
;	7	write pointer (next word to write)
; Ring buffer conventions:
;	Empty buffer is represented by write pointer = read pointer.
;	Full buffer is represented by write pointer +1 = read pointer
;	(modulo wraparound).
; These conventions are the ones used in the Bcpl Ring Buffer package
; (word version).

!1, 2, ~TRBE, TRBE;
!1, 2, ~TWrap, TWrap;

TCAAT:	T← LCB;
	MAR← L← 6+T;			Reference LCB word 6
	EIATMP← L;
	T← MD;				T← read ptr (word 6)
	L← MD-T;			Compare with write ptr (word 7)
	L← T, SH=0, TASK;
	EIADAT← L, :~TRBE;		[~TRBE, TRBE] EIADAT← read ptr

; Transmit ring buffer not empty.  Update read pointer.
~TRBE:	MAR← EIATMP-1;			Reference LCB word 5
	T← EIADAT+1;			The saved read ptr +1
	L← MD-T;			Compare with end ptr (word 5)
	L← MD, SH=0;			L← begin ptr (word 4)
	MAR← EIATMP, :~TWrap;		[~TWrap, TWrap] Reference LCB word 6
~TWrap:	L← T;				Not wraparound, use old read ptr +1
TWrap:	TASK;				If at end, use begin ptr
	MD← M;				Update read ptr

; Transmit the data byte
	MAR← EIADAT;			Fetch word pointed to by old read ptr
	T← 2;				EIASend return index 2
	L← MD, :EIASend;		L← data byte, go send it

; Now generate interrupt if desired by software.
ESRet2:	MAR← LCB+1;			Reference LCB word 1
	T← NWW;				Current wakeups waiting
	L← MD OR T, TASK;		Merge in word 1 (interrupt mask)
	NWW← L, :SameState;		Stay in same state

; If ring buffer is empty, do nothing.
TRBE:	:SameState;

; **** Uninterpreted Block Transmitter ****

; LCB format:
; word	0 to 3	standard
;	4	bit 0:  0 if left byte is next, 1 if right
;		bits 1-15:  remaining byte count
;	5	address of word containing next byte
;	6	control command to be issued at end of block
; Bytes are fetched two per word, left byte first.

!1, 2, OkLCB5, NoLCB5;
!1, 2, TUMore, TUDone;
!1, 2, URight, ULeft;


; "Idle" state.
; When awoken, see if there is an LCB, and if so start sending data.

TUIdl:	T← LCB, BUS=0;			Is there an LCB?
	MAR← 2+T, :OkLCB5;		[OkLCB5, NoLCB5] Reference LCB word 2

; Here when there is no LCB.  Just ignore the wakeup.
NoLCB5:	:SameState;

; There is an LCB.  Zero the microcode status, which may have had an
; error posted due to Fill Character Transmitted hardware status.
OkLCB5:	L← TUBlk#;			L← new state to enter immediately
	MD← 0, :ChangeStateGo;		Store zero in LCB word 2

; ChangeStateGo immediately returns control to TUBlk to transmit first byte.

; "Transmitting uninterpreted block" state.
; Transmit next data byte if there is one.
TUBlk:	T← 4;
	MAR← L← LCB+T;			Reference LCB word 4
	L← M+1;				L← LCB+5
	MTEMP← L;
	T← 77777;
	L← MD AND T, T← MD;		T← LCB word 4 (count), test count=0
	L← MD, SH=0;			L← LCB word 5 (address)
	EIATMP← L, :TUMore;		[TUMore, TUDone] EIATMP← address

; Count wasn't zero, send another data byte.
TUMore:	L← 77777+T;			Decrement count and flip bit 0
	MAR← MTEMP-1;			Reference LCB word 4
	T← EIATMP+1, SH<0;		T← address+1, which byte?
	MD← M, L← T, TASK, :URight;	[URight, ULeft] Word 4 ← updated count

; Left byte is next.
ULeft:	MD← EIATMP;			Word 5 ← address (unchanged)

	MAR← EIATMP;			Reference data word in buffer
	T← 177400;			Extract left byte
	L← MD AND T;
	MTEMP← L LCY 8;			Swap to right
	L← MTEMP, :UData1;		L← data byte, go send

; Right byte is next.
URight:	MD← M;				Word 5 ← address+1

	MAR← EIATMP;			Reference data word in buffer
	T← 377;				Extract right byte
	L← MD AND T;

; Now send data byte.  L = data byte.
UData1:	T← 3, :EIASend;			EIASend return index 3
ESRet3:	:SameState;			Remain in same state

; Here when entire block has been transmitted.
; Issue control command specified in LCB, if any.
TUDone:	MAR← MTEMP+1;			Reference LCB word 6
	NOP;
	L← MD;				L← control command
	MAR← 177700;			Reference control register
	TASK;
	MD← M;				Issue command

; Post completion and chain to next block, if any.
	L← 0;				L← post code 0
	T← 3, :EIAPost;			T← return index 3
EPRet3:	L← TUIdl#, :ChangeStateGo;	Enter idle state immediately