; DDTapeReadTask.mu  -- Tape Microcode for the Alto Double Density Tape Controller
; Copyright Xerox Corporation 1980

; Body of Tape Controller microcode -- requires definitions in DDTapeDefs.mu
;
; Last modified by Tim Diebert, March 4, 1981  10:28 AM
; 
; Branch Conditionals
!1, 2, Tape2, CommandDcd;
!1, 2, DriveNotRdy, DriveRdy;
!1, 2, BadCmd, DRNoOp;
!1, 2, ReadLoop, NoBufLoop;
!3, 4, BytesLeft, , BytesGoneEven, ;
!3, 4, BytesLeft1, BytesLeftBufGone, BytesGoneOdd, BytesGoneOddBufGone;
!3, 4, BytesLeftBufGoneLoop, , AdjustByteCnt, ;
!1, 2, OpEnd, HardWareError;
!1, 2, ReadDataFwd1, HardWareError1;
!1, 2, ReadDataRev1, HardWareError2;
!1, 2, ReadRevLoop, ReadRevNoBufLoop;
!3, 4, ReadRevBytesLeft, , ReadRevBytesGoneEven, ;
!3, 4, ReadRevBytesLeft1, ReadRevBufGoneBytesLeft, ReadRevBytesGoneOdd, ReadRevBytesGoneOddBufGone;
!3, 4, ReadRevBufGoneBytesLeftLoop, , ReadRevAdjustByteCnt, ;
!1, 2, WriteError3, WriteEof1;
!1, 2, WriteError, WriteOp1;
!1, 2, ReadDataFwd2, NoReadAfter;
!1, 2, WriteError1, Erase1;
!1, 2, WriteError2, EraseVar1;
!1, 2, WriteOp2, HardWareError5;
!1, 2, EraseVar2, HardWareError4;
!1, 2, OpEnd3, OpEnd4;
!1, 2, Tape3, Tape4;
!1, 2, BytesLeftBufGoneLoop1, BailOut;

!37, 40, ReadFwd, FwdSpaceRec, BadCmd02, BadCmd03, BadCmd04, FwdSpaceFile,
 BadCmd06, BadCmd07, WriteFwd, EraseVar, WriteEdit, BadCmd13, WriteEof, Erase,
 BadCmd16, BadCmd17, ReadRev, BackSpaceRec, ReadRevEdit, BadCmd23, BadCmd24,
 BackSpaceFile, BadCmd26, BadCmd27, BadCmd30, BadCmd31, BadCmd32, BadCmd33,
 BadCmd34, Rewind, Unload, NoOp;


;
; Top of loop
;


Tape:
	NOP;
	Reset;			Reset the tape controller hardware.
	T← 2;
	WriteWordCnt← T;
	WriteByteCnt← T;
	GoCmd← SetResetWriteTask;	Wakeup the write task to start reset.
	TASK;
	NOP;

	T← 20;			Set up to read TStart.
	MAR← 602 OR T;
	NOP;
	L← MD;
	TCBBase← L;		Save the Pointer to the TCB.

Tape3:
	T← ResetWriteTask;	Wait for write task to reset.
	L← TStatus AND T;
	TASK, SH=0;
	:Tape3;		[Tape3, Tape4]

Tape4:
	Reset;			Reset the tape controller hardware.
	T← ForcedReset;		Check to see if we got here by a forced reset.
	L← TStatus AND T;
	L← StatusMask, SH=0;
	T← StatusOffset, :Tape2;	[Tape2, CommandDcd]

Tape2:
	GoCmd← ResetFMTEnbl;	Disable the formatter.
	NOP;
	TASK;
	GoCmd← SetFMTEnbl, :OpEnd1;	Enable the formatter.

CommandDcd:
	MAR← TCBBase + 1;	Get the command word.
	NOP;
	L← FmtCmd← MD;		Load up the command so we get the unit adressed.
	Command← L;

	T← ReadOffset;		Build read stuff for later.
	MAR← TCBBase + T;
	NOP;
	L← MD - 1;
	T← MD;
	ReadPtr← L, L← T, TASK;
	ReadBufSize← L;

	T← WriteOffset;		Build write stuff for later.
	MAR← TCBBase + T;
	NOP;
	L← MD - 1;
	T← MD;
	WritePtr← L, L← T, TASK;
	WriteByteCnt← L;

	T← RdyOnl;		Check for online and ready and save for later.
	T← TStatus . T;
	L← RdyOnl XOR T;
	SH=0;			
	T← NoOpCmd, :DriveNotRdy;		[DriveNotRdy, DriveRdy]

DriveNotRdy:
	T← Command . T;
	L← NoOpCmd XOR T;
	SH=0;
	:BadCmd;		[BadCmd, DRNoOp]

DriveRdy:
	T← 37;			Prepare for dispatch.
	L← Command AND T;
	SINK← M, BUS, TASK;	Dispatch.
	:ReadFwd;



;
; Bad or unkown command processing
;
BadCmd02:
	:BadCmd;

BadCmd03:
	:BadCmd;

BadCmd04:
	:BadCmd;

BadCmd06:
	:BadCmd;

BadCmd07:
	:BadCmd;

BadCmd13:
	:BadCmd;

BadCmd16:
	:BadCmd;

BadCmd17:
	:BadCmd;

BadCmd23:
	:BadCmd;

BadCmd24:
	:BadCmd;

BadCmd26:
	:BadCmd;

BadCmd27:
	:BadCmd;

BadCmd30:
	:BadCmd;

BadCmd31:
	:BadCmd;

BadCmd32:
	:BadCmd;

BadCmd33:
	:BadCmd;

BadCmd34:
	:BadCmd;

BadCmd:
	T← StatusMask;		Build Status.
	L← TStatus  . T;
	T← StatusOffset;
	MAR← TCBBase + T;
	T← M, BLOCK;
	L← 1 OR T, TASK;		Set the CmdErr bit
	MD← M, :Tape;

;
; Direct to drive commands
;
Rewind:
	GoCmd← RewindCmd, :WaitABit;

Unload:
	GoCmd← UnloadCmd, :WaitABit;

WaitABit:
	NOP;
	NOP;
	NOP;
	NOP;
	NOP;
	NOP, TASK;
	GoCmd← ZeroCmd, :NoFmtGoOpEnd;

DRNoOp:
	:NoFmtGoOpEnd;
NoOp:
	:NoFmtGoOpEnd;

;
; End of operation stuff
;
OpEnd:
	TASK;
	NOP;

OpEnd1:
	T← 14;	Set up to see if the formatter busy and data busy have gone
	L← TStatus . T;
	SH=0;
	:OpEnd3;	[OpEnd3, OpEnd4]

OpEnd3:
	BLOCK;		This is to remove initail wakeup or Data Busy wakeup.
	TASK;
	NOP;

OpEnd4:
	L← StatusMask;		Build Status.
	T← StatusOffset;

	MAR← TCBBase + T;
	T← M;
	L← TStatus  . T, TASK;
	MD← M;	

	SIO9Reset, BLOCK;		This is for the wait for FormatterBusy to go away.
	TASK;
	:Tape;

;
; End of operation where formatter busy will not give a final wakeup.
;
NoFmtGoOpEnd:
	L← StatusMask;		Build Status.
	T← StatusOffset;
	MAR← TCBBase + T;
	T← M, BLOCK;		This is to remove initail wakeup.
	L← TStatus  . T, TASK;
	MD← M;	

	SIO9Reset;		
	TASK;
	:Tape;

;
; Skip type operations
;
FwdSpaceRec:
	BLOCK, :NoDataOP;

FwdSpaceFile:
	BLOCK, :NoDataOP;

BackSpaceRec:
	BLOCK, :NoDataOP;

BackSpaceFile:
	BLOCK, :NoDataOP;

NoDataOP:
	GoCmd← GO;	Set the go flag bit and wait for at least 1 microsecond
	NOP;		to turn it off.
	NOP;
	NOP;
	NOP;
	NOP;
	NOP;
	GoCmd← ZeroCmd;

	NOP;		Check for formatter busy.
	T← FmtBusy;
	L← TStatus AND T;
	SH=0;
	:OpEnd;	[OpEnd, HardWareError]

;
; Read operations
;
;
; Read assumes that ReadPtr contains the starting address of the read buffer - 1
; and that RdBufSize contains the number of WORDS in the read buffer.
;
; The hardware provides a force of NEXT8 when a ←TReadF1 or ←TReadF2 encounters
;  no more data to be transfered. 
;  Thus every instruction that executes a ←TReadF1 or ←TReadF2 must have a branch
;  in the instruction following that goes to the end of data routine.

ReadFwd:
	GoCmd← GO; 			 start the ball rolling.
	NOP;
	NOP;
	NOP;
	NOP;
	NOP;
	GoCmd← ZeroCmd;			Remove IGO after about 1 microsecond.
	NOP;		Check for formatter busy.
	T← FmtBusy;
	L← TStatus AND T;
	SH=0;
	TASK, :ReadDataFwd1;	[ReadDataFwd1, HardWareError1]

ReadDataFwd2:
	EnRead, TASK;
	:ReadDataFwd3;				This NOP is to make the tasking work 
;				 	 for read after write.
ReadDataFwd1:
	EnRead;
	BLOCK;
	TASK;
	:ReadDataFwd3; 

ReadDataFwd3:
	L← T← ReadBufSize;		Build ReadBufBytes, & EndReadBuf values.
	ByteCnt← LLSH1;
	L← ReadPtr + T;
	EndReadBuf← L;
	L← ReadPtr;
	ReadBufStart← L;

	SINK← ByteCnt, BUS=0;		Check for no read buffer.
	:ReadLoop;			[ReadLoop, NoBufLoop]

ReadLoop:
	L←TReadF2, TASK;		Read the first byte of this word
	ReadTapeData← LLCY8, :BytesLeft;	 and move it to the top half of the word.
;					[BytesLeft, , BytesGoneEven, ]


BytesLeft:
	T← MAR← ReadPtr + 1;		Start memory operation at ReadPtr+1.
	L← EndReadBuf - T;		Check to see if this is the last word
;					 we can put in core.
	L← ReadTapeData, TReadF1, SH=0;	Read the data & AND it with previous byte.
	MD← M, L← T, TASK, :BytesLeft1;	Put it in memeory.
;				[BytesLeft1, BytesLeftBufGone, BytesGoneOdd, BytesGoneOddBufGone]
BytesLeft1:
	ReadPtr← L, :ReadLoop;		Save the new read pointer.

BytesLeftBufGone:
	ReadPtr← L, :NoBufLoop;	Save the pointer for last byte computation.

NoBufLoop:
	NOP;

BytesLeftBufGoneLoop:
	T← 4;	Set up to see if data busy has gone
	L← TStatus . T;
	T← ByteCnt + 1, SH=0;		Increment the byte count.
	SINK← TReadF2, L← T, TASK, :BytesLeftBufGoneLoop1;	[BytesLeftBufGoneLoop1, BailOut]

BytesLeftBufGoneLoop1:
	ByteCnt← L, :BytesLeftBufGoneLoop
;					[BytesLeftBufGoneLoop, , AdjustByteCnt, ]


BytesGoneEven:
	T← ReadBufStart;		Calculate number of bytes transfered.
	L← ReadPtr - T;
	ByteCnt← LLSH1, :ReadDone;

BytesGoneOddBufGone:
	ReadPtr← L, :BytesGoneOdd1;

BytesGoneOdd:
	ReadPtr← L, :BytesGoneOdd1;

BytesGoneOdd1:
	T← ReadBufStart;		Calculate number of bytes transfered.
	L← ReadPtr - T;
	ByteCnt← LLSH1, :AdjustByteCnt;

BailOut:
	ByteCnt← L;

AdjustByteCnt:
	L← ByteCnt - 1;
	ByteCnt← L, :ReadDone;

ReadDone:
	T← ByteCountOffset;	Prepare to store the byte count.
	MAR← TCBBase + T;
	TASK;
	MD← ByteCnt, :OpEnd;




;
; Read Reverse Data operation.
;
;
; ReadRev assumes that ReadPtr contains the starting address of the read buffer - 1
; and that RdBufSize contains the number of WORDS in the read buffer.
;
; The hardware provides a force of NEXT8 when a ←TReadF1 or ←TReadF2 encounters
;  no more data to be transfered. 
;  Thus every instruction that executes a ←TReadF1 or ←TReadF2 must have a branch
;  in the instruction following that goes to the end of data routine.

ReadRev:
	:BadCmd;
ReadRevEdit:
	:BadCmd;



;
; Write type Operations
;
WriteEof:
	T← FileProtectBit;
	L← TStatus AND T;
	SH=0;
	:WriteError3;	[WriteError3, WriteEof1]

WriteEof1:
	BLOCK, :NoDataOP;
	
WriteFwd:
	:WriteOp;

WriteEdit:
	:WriteOp;

WriteOp:
	T← FileProtectBit;
	L← TStatus AND T;
	SH=0;
	:WriteError;	[WriteError, WriteOp1]

WriteOp1:
	EnWrite, TASK;
	NOP;
	GoCmd← GO;
	NOP;
	NOP;
	NOP;
	NOP;
	NOP;
	GoCmd← ZeroCmd;
	NOP;		Check for formatter busy.
	T← FmtBusy;
	L← TStatus AND T;
	SH=0;
	:WriteOp2;	[WriteOp2, HardWareError5]

WriteOp2:
	T← ReadAfterWriteBit, BLOCK;	Check for read after write.
	L← Command AND T;
	SH=0;
	:ReadDataFwd2;	[ReadDataFwd2, NoReadAfter]

NoReadAfter:
	TASK;
	:OpEnd;

Erase:
	T← FileProtectBit;
	L← TStatus AND T;
	SH=0;
	:WriteError1;	[WriteError1, Erase1]

Erase1:
	BLOCK, :NoDataOP;

EraseVar:
	T← FileProtectBit;
	L← TStatus AND T;
	SH=0;
	:WriteError2;	[WriteError2, EraseVar1]

EraseVar1:
	GoCmd← GO;
	NOP;
	NOP;
	NOP;
	NOP;
	NOP;
	GoCmd← ZeroCmd;
	NOP;		Check for formatter busy.
	T← FmtBusy;
	L← TStatus AND T;
	SH=0;
	:EraseVar2;	[EraseVar2, HardWareError4]
EraseVar2:
	BLOCK;
	EnWrite, TASK;
	:OpEnd;

WriteError:
	:WriteError2;

WriteError1:
	:WriteError2;

WriteError3:
	:WriteError2;

WriteError2:
	T← StatusMask;		Build Status.
	L← TStatus  . T;
	T← StatusOffset;
	MAR← TCBBase + T;
	T← M, BLOCK;
	L← 2 OR T, TASK;		Set the Write Error bit
	MD← M, :Tape;

;
; Errors
;
HardWareError:
	:HardWareError2;
HardWareError1:
	:HardWareError2;
HardWareError4:
	:HardWareError2;
HardWareError5:
	:HardWareError2;
HardWareError2:
	T← StatusMask;		Build Status.
	L← TStatus  . T;
	T← StatusOffset;
	MAR← TCBBase + T;
	T← M, BLOCK;
	L← 4 OR T;		Set the Hardware Error bit
	MD← M;
	TASK;
	:Tape;