{
 File name: <CoPilot>DLion>NewDiskDLionB.mc
 Description: Dandelion Disk Controller microcode, Version 2.0
 Last Edited: bj, 16-Mar-86  4:27:05
}
{
	Copyright (C) 1979, 1980, 1981 by Xerox Corporation.  All rights reserved.
}
{
	THIS CODE WILL NOT RUN PROPERLY UNLESS
	DiskDlionInit HAS INITIALIZED
	REGISTERS U0C00, UMaxSectTst, UStatusMsk and USyncAddrMk!!
}
{
	First commands and main body of the TransferRun command in DiskDLionA.mc.
}
{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Subroutine used to process a field of a sector
	
	This subroutine is used to read, write or verify a field in a sector.
	Its arguments are:
	  1) Word Count ( ~= length of field, definition may change depending on the operation) in UWdCount
	  2) Control word - the word sent to control the hardware when processing the CRC wd, in RAdr and
	  3) Buffer address - the location in physical memory of the first word of the buffer for the field to be transferred, in RHRCnt and RCnt.

	The least significant two bits of the control word define the operation.
	These bits are WakeupControl.1
	and WriteEnable for the controller,
	WriteEnable is the LSB.
	  00 => Read operation,
	  10 => Verify operation,
	  11 => Write operation.
	
	The initialization section of the code
	primes the data word with 0 in case this is a write op,
	saves the control word used to process the CRC residue
	and creates the transfer control word (the WriteCRC bit is off).
	After this, two branches are taken, one for write,
	the other for read and verify.
	The write code starts writing the PLL synchronization pattern (zeros) immediately,
	followed by the synchronization word (all ones)
	or address mark and the data.
	A sync word of all ones is used for an SA4000 drive.
	The address mark 50A1 is used for the SA1000 Header field,
	50A3 is used for the Label and Data fields.
	The read and verify code waits until
	the PLL synchronization pattern is under the read head,
	then issues the transfer control word to start things up.
	The verify operation requires that the first word in the buffer
	be sent to the hardware before starting so this is done.
	The verfiy command uses the inner loop of the write code
	to write data to the controller to be verified.
	The read command has its own inner loop.
	When all data has been processed in all three commands,
	the wait command is sent to cause the CRC word to be processed,
	then the Freeze command is sent to stop the hardware
	and the next command is fetched from the IOCB.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

TransferField:	RAdr ← RAdr or UFreezeCmd					,c1; {create the real command word}
		UWaitCmd ← RAdr							,c2; {save control word used for CRC residue}
		RAdr ← RAdr and ~CWrCRC						,c3; {create the transfer command}

		UXferCmd ← RAdr, YDisp						,c1; {save transfer command, is this a write op?}
		KCtl ← UFreezeCmd, DISP4[SetupRdVer,0E]				,c2; {either start PLO sync pattern or prime for verify. make sure microcode wakeup=FirmwareEnable so we can clear the error flags (if Wakeup = SectorFound, ClrKFlags wil abort the Wakeup!). Clear the error flags so a delayed verify error flag from the CRC word of a previously verified field will not give a spurious error indication here}
{
	Setup the read and verify operations.
	The data separator on the SA4000 requires
	that the read head be enabled after
	the beginning of the synchronization pattern.
	The controller data separator for the SA1000
	does not care when it is enabled.
	The Read and Write microcode
	process in the same amount of time,
	so we may derive the position of the read head
	relative to the sector mark
	using the current location in the code.
	After the branch above,
	the write routine immediately begins writing
	the synchronization pattern and determining
	the address mark or sync word to be written.
	One might think delaying one click here
	would ensure the read head was over the synchronization pattern.
	Unfortunately, the position of the read head relative
	to the executing code is only known to within one click,
	so a delay of one click gives a real delay of zero to two clicks.
	By delaying two clicks here,
	we guarantee at least a one click delay.
	During the delay, the address of the data buffer
	and the word count are loaded.
	Since the verify operation needs to have the first data word
	ready as soon as the hardware finds the sync word,
	it is sent to the hardware here.
	This action has no effect if this is a read operation.
	Finally, the operation is chosen and starts.
	The read code adjusts the buffer pointer
	to point to the first word in the buffer
	so the first data word will be written properly.
	Note the value of word count depends of the operation performed.
	For the verify operation, it equals the number of words
	in the field minus one.  For the Read operation,
	it equals the number of words in the field.
}

SetupRdVer:	ClrKFlags							,c3, at[0E,10,SetupWrt]; {clear error flags (see above)}

{
	Wait for the read head to be in position
	while fetching the word count and
	adjusting the address so we can subtract
	in the MAR← below so Cin=0 so we can read a U register.
}
		Noop								,c1;
		RAdr ← UWdCount							,c2; {get word count for read or verify}
		RCnt ← RCnt+1							,c3; {adjust the address now so it will be set up after priming the controller for the verify op, yet allows us to subtract below.}
{
	Now have waited two clicks,
	decide what this operation is (read or verify) and
	prime the controller with the first data word if it's a verify.
	Note the start command is sent before the first data word.
	This is necessary because the the controller's
	WriteData register is cleared as long as TransferEnable is lo.
}
		MAR ← [RHRCnt, RCnt-1], Xbus ← UXferCmd, XDisp			,c1; {start pre-load and see whether this is a read (X.14=0) or verify (X.14=1).  Note the only legal values for RCnt here are xx15, xx17 and xx01.  RCnt will not generate a PgCross for any of these.}
		KCtl ← UXferCmd, DISP4[StartRd, 0D]				,c2; {start the operation and chose the operation}
{
	Start the verify operation.
	Since the first word hs already been sent,
	send the control word
	and test to see if there is any thing to verify
}

StartVer:	KOData ← MD, GOTO[WrtVerLp]					,c3, at[0F,10, StartRd]; {prime controller with first word to be verified AFTER WriteData clear input has been released above.  Start verifying}

{
	Start the Read operation.
	We sent the control word in this click
	and will start the next click with a memory reference
	using the restored buffer pointer.
	The controller is frozen with the status
	of the CRC word after the buffer is filled.
}

StartRd:	RAdr ← RAdr-1							,c3, at[0D,10,StartVer]; {do not incr word count to account for sync word read}

{
	start the ref to bring in the first data word
}

StartRdC1:	MAR ← [RHRCnt, RCnt-1], RCnt ← RCnt-1, GOTO[ReadLpC2]		,c1, at[1,2]; {start read into 1st loc in buffer point to first loc so first data word is written over the sync word or addr mk.}

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Read Command Inner Loop 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
{
	During each click, a word is read from the disk and written into main memory.
	The word count is decremented and if greater than zero, reading continues.
	When it reaches zero, we jump to freeze the controller with the final status.
	The last MAR← is not used as it is begun in response to the CRC word being read in.
}

ReadLp:		MAR ← [RHRCnt, RCnt], RCnt ← RCnt+1, BRANCH[ReadLpC2,FinRead]	,c1; {start mem write cycle}
ReadLpC2:	MDR ← KIData, WriteOK, CANCELBR[ReadLpC3, 2]			,c2, at[0,2,FinRead]; {write disk data to mem}
ReadLpC3:	RAdr ← RAdr-1, ZeroBr, GOTO[ReadLp]				,c3; {dcr word cnt, go start memory cycle,  note the WriteOK allows the MDR← to proceed even though the assembler fears a PgCross might be generated.  In fact, this cannot happen because we jump out of the loop before the final increment that would cause the PgCross}

{
	Here the controller has the second to last word ready.
	The address now points to the last word,
	so there could not have been a PageCross on the last MAR←
	so this MDR← will work.
	This is all incredibly ugly.
}

FinRead:	MDR ← KIData, WriteOK, CANCELBR[FinReadC3, 2]			,c2, at[1,2,ReadLpC2]; {store the second to last word in the field}
FinReadC3:	RAdr ← 1, ZeroBr						,c3; {set up for branch below}

{
	Store the last word using a special MAR←
	that does not generate a page carry.
	This is done so the MDR← will not be cancelled.
	This is also used to freeze the controller after the last word.
}

FinReadLp:	MAR ← [RHRCnt, RCnt+0], BRANCH[RdLastWd, FreezeRead]		,c1; {start store of last word}
RdLastWd:	MDR ← KIData							,c2, at[0,2,FreezeRead]; {store field's last word}
		RAdr ← RAdr xor RAdr, ZeroBr, GOTO[FinReadLp]			,c3; {set up ZeroBr to write last word}

{
	Now the controller has just read and checked the CRCword.
	Freeze the controller now.
}

FreezeRead:	KCtl ← UFreezeCmd, pRet4, GOTO[SndFreeze]			,c2, at[1,2,RdLastWd]; {stop the controller with the proper status and return}

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Set up Write Command
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
{
	Each field in a sector begins with
	6 words of zeros used by the disk's data separator
	to lock its PLL,
	a word of ones or an address mark to define the word boundry,
	the field's data and a CRC residue word.
	the first thing this routine does is begin writing the PLL sync pattern.
	The controller automatically begins writing a word of zeros.
	Because of the controller design,
	the second word of zeros will actually be being written
	when the first service request arrives.
	Thus to write 6 words of zeros,
	we send sync words 4 times.
	While sending the Sync words,
	we calculate the address mark or synchronization word
	to be sent to define the word boundry.
	The SA4000 requires a synchronization word of all ones.
	The Header field of an SA1000 disk requires an address mark of 50A1 hex.
	The Label and data fields of the SA1000 drive need an address mark of 50A3 hex.
	After writing the proper sync word or addres mark,
	we load the word count and start the inner loop to transfer data.
	At the end of the transfer, the Wait Command is sent out,
	causing the CRC word to be written,
	we wait one click for it to be written, then freeze the hardware.
	Finally, the IOCB pointer is reloaded
	and execution proceeds to fetch the next command.
}

SetupWrt:	KCtl ← UXferCmd							,c3, at[0F,10,SetupRdVer]; {send control wd}

		ClrKFlags, RAdr ← 4, GOTO[SyncLpC2]				,c1;  {Clear error flags left over from previous fields (see comment just after TransferField).}
{
	loop to write initial word of synchronization pattern, all zeros.
	Note the branch cannot be in C1 as that would mean the test would be in C3,
	but that conflicts with the KOData← there.
}

SyncLp:	Noop									,c1, at[0,2,FinSync];
SyncLpC2:	RAdr ← RAdr-1, ZeroBr						,c2; {dcr sync count}
		KOData ← 0, BRANCH[SyncLp,FinSync]				,c3; {send sync word, done yet?}
{
	Now calculate the sync word or addres mark in the time remaining.
	A sync word is used for SA4000 drives and is composed of all 1s.
	An address mark is used on SA1000 drives.
	The Header field address mark is 51A1'X.
	An address mark for the Label or Data field is 50A3'X.
	USyncAdrMk contains either FFFF or 50A1,
	depending on the drive type.
	If the field is Label or Data, this is ORed with a "2".
	The result is sent to the controller at the right time,
	the Word Count is loaded and the inner loop entered.
}

FinSync:	RAdr ← UField							,c1, at[1,2,SyncLp]; {UField=40 => Header =80 => Label, =C0 => Data}
		[] ← RAdr+RAdr, PgCarryBr, SuppressTimingWarning		,c2; {carry => Label or Data field being written, so turn on the tag bit}
		KOData←0, BRANCH[SetupHd, SetupLabDat]				,c3; {continue sending sync pattern and decide about tag bit}
{
	Sending Header Field,
	so don't insert the "Label or Data" tag
}

SetupHd:	RAdr ← 0, GOTO[MakeSyncAdrMk]					,c1, at[0,2,SetupLabDat];

{
	Sending Label or Data field,
	do insert the "Label or Data" tag bit
}

SetupLabDat:	RAdr ← CLabDatTag						,c1, at[1,2,SetupHd];

{
	Form the Sync word or Address mark by ORing the tag bit in.
	Obviously, this has no effect on the Sync word,
	only on the address mark
}

MakeSyncAdrMk:	RAdr ← RAdr or USyncAdrMk					,c2; {compose the sycn wd or addr mark}
		KOData ← RAdr LRot0						,c3; {Lo and behold, now its time to send it, so do so}
{
	Start the first memory reference
	and load the word count
	before jumping into the inner loop
}
		MAR ← [RHRCnt, RCnt], RCnt ← RCnt+1				,c1; {start read of first word from memory}
		RAdr ← UWdCount, CANCELBR[InitWrtC3, 2]				,c2; {get word count}
InitWrtC3:	KOData ← MD, RAdr ← RAdr-1					,c3; {decrement it for the word just sent to the disk and enter the inner loop}

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	inner loop for write and verify operations
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
{
	In each cycle,
	read a word from the buffer
	and write it to the disk.
}

WrtVerLp:	MAR ← [RHRCnt, RCnt], RCnt ← RCnt+1				,c1, at[0,2,FinWrtVer]; {start mem read}
WrtVerLpC2:	RAdr ← RAdr-1, ZeroBr, CANCELBR[WrtVerLpC3, 2]			,c2;  {dcr word ct, done yet?}
WrtVerLpC3:	KOData ← MD, BRANCH[WrtVerLp, FinWrtVer]			,c3; {send data to controller, quit if done}

{
	Done writing or verifying data, send wait command.
	Either 2 or 3 repetitions of the Wait command are to be sent.
	Two repetitions are sufficient if the SA4000 drive is connected
	or a verify operation is in progress.
	The controller has hardware to hold the write enable signal
	while the SA4000 data separator grinds out the CRC word.
	It does not seem to be effective when using the SA1000 drive.
	Unfortuately, there is no time to wait for the extra word when using the SA4000 drive,
	so the cases cannot be consistent.
	One should not wait when executing a verify operation
	as the CRC word would pass by and its status would be lost.
	Hence, we test to see if both this is a write operations
	and we are connected to the SA1000 disk before delaying an extra click.
}

FinWrtVer:	Xbus ← UWaitCmd, XDisp						,c1, at[1,2,WrtVerLp]; { Is this a write operation?}
		RAdr ← RAdr+1, Xbus ← KStatus, XwdDisp, BRANCH[FinVerify, FinWrite, 0E]	,c2; {loop ctr ← 1, is the SA1000 connected?}
FinVerify:	KCtl ← UWaitCmd, CANCELBR[SndWait, 0F]				,c3, at[0, 2, FinWrite]; {Verify op, process the CRC word and start waiting}
FinWrite:	KCtl ← UWaitCmd, BRANCH[SA1Wait, SA4Wait, 2]			,c3, at[1,2, FinVerify]; {Write op, process the CRC word and decide whether to wait an extra click}

SA1Wait:	GOTO[SndWaitC2]							,c1, at[0,2, SA4Wait]; {connected to SA1000, wait an extra clock by not decrementing loop ctr}
SA4Wait:	RAdr ← RAdr-1, NegBr, GOTO[SndWaitC2]				,c1, at[1,2, SA1Wait]; {connected to SA4000, loop ctr  ← 0 so executes SndWaitC3 only once.}
SndWait:	RAdr ← RAdr-1, NegBr						,c1, at[0F,10]; {have enough clicks passed yet?}
SndWaitC2:	BRANCH[SndWaitC3, SndFreeze], pRet4				,c2; {set up return in case and decide whether to stop controller now}
SndWaitC3:	KCtl ← UWaitCmd, CANCELBR[SndWait, 0F]				,c3, at[2,4,SndFreeze]; {have controller verify or write CRC word or wait until it is done.}

{
	Now stop the hardware and return.
	The Read routine joins the flow here with a CANCELBR[SndFreeze,2]
}

SndFreeze:	KCtl ← UFreezeCmd, RET[HeaderRet]				,c3, at[3,4,SndWaitC3]; {stop the hardware and return.   Note that during a write operation the last 4 bits of the CRC may still be in the SA4000's data separator.  They will not be lost because the WriteEnable seen by the disk is delayed by 5 bit times.}

{
	Finish IOCB
	This command is used to finish up an IOCB.
	It posts the final status,
	interrupts a Mesa process
	and either goes to the dormant state
	or continues processing with the next IOCB.
	The command word is followed by the 16 bit
	virtual address link to the next IOCB in the chain
	(the link is 0 if this is the last IOCB),
	a 16 bit mask used to interrupt a Mesa process,
	the command used to turn off the hardware
	if IOCB processing is to stop now
	and the address (lo 16 bits thereof)
	of the status location in the IOCB.
	The first action taken is to fetch
	and store the arguments to the command.
	Then we replace the HeadSelected field
	in the status received from the hardware
	with the Field number now in UField.
	This allows the Mesa processing to determine
	the number of the last field processed.
	The error bits from the hardware are then replaced
	with the error bits left from the last Test Status command
	or those for the last Transfer Run of Pages command,
	whichever came last.
	The Sector Found bit is left from the original device status
	if the controller is connected to an SA4000.
	If connected to an SA1000, this bit is masked off
	as it would be set only if a Header field was sought but not found.
	It is assumed the SA4000HeadDLion head
	will have preceeded the Finish IOCB command
	with either a Test Status, Transfer or Initializer Registers command
	so as to eliminate irrelevant error flags.
	Next, three conditions are tested.
	If either there was a memory error during the IOCB,
	any errors were detected or the link to the next IOCB is nil,
	the FirmwareBusy flag is cleared in the final status.
	If none of these conditions are true, is is set there.
	This is used by the Device Head
	to decide whether the microcode will continue or stop.
	If a new IOCB was added to the chain,
	the Device Head can determine whether the microcode saw it
	and if not, the Head can restart the microcode.
	After the final IOCB status has been posted,
	the interrupt mask is used to set the interrupt flags,
	causing the Head to receive an interrupt from the emulator.
	Finally, based on whether the FirmwareBusy flag was set or not,
	the microcode either issues the stop control word
	found as the second to last argument to this command
	and goes to the dormant state
	or it goes to map the virtual IOCB address given
	to a real IOCB address and continues with the next IOCB.
}
{
	fetch the Mesa interrupt mask from the argument list
}

FinishIOCB:	MAR ← [RHRAdr, RAdr], RAdr ← RAdr+1				,c1, at[0E,10,Inr]; {start fet and point to stop command}
		UNxtIOCBLnk ← RCnt, CANCELBR[$, 2]				,c2; {save link to next IOCB, was fetched as part of command fetch.}
		RCnt ← MD							,c3; {get mesa interrupt mask}
{
	fetch the stop command from the argument list
}
		MAR ← [RHRAdr, RAdr], RAdr ← RAdr+1				,c1; {Start fet and point to status addr}
		UInterruptMsk ← RCnt, CANCELBR[$, 2]				,c2; {save Mesa interrupt mask}
		RCnt ← MD							,c3; {get stop command}
{
	finally, fetch the address of the IOCB status word
}
		MAR ← [RHRAdr, RAdr+0]						,c1; {start fet of last word in IOCB}
		UFreezeCmd ← RCnt						,c2; {Save stop command}
		RAdr ← MD							,c3; {get phys addr of status, hold in RAdr}
{
	Decide whether to store the final sector count.
	this should be done only if there was a transfer operation in the IOCB.
	If not, trust the Head to put an "Initialize" op in the IOCB.
	Hence, UField will be CIOCBDone <=> There was no transfer.
}
		RCnt ← CIOCBDone						,c1; {get valuse used to test}
		[] ← RCnt xor UField, ZeroBr					,c2; {did a transfer? Zero => no}
		RCnt ← USectorCntAddr, BRANCH[StoSectCnt, ComposeStat]		,c3; {get addr of sector count in case and decide.}
{
	Store the final count of sectors left to process.
	This will be zero if there were no errors.
}

StoSectCnt:	MAR ← [RHRAdr, RCnt+0]						,c1, at[0,2,ComposeStat]; {Start store of final sector count, note it's on same page as final status since all in same IOCB.}
		MDR ← USectorCount						,c2; {store it}
		Noop								,c3;
{
	clear spurious error flags from the current status
	and combine it with the masked status.
	This will make up a major portion of the final status word.
}

ComposeStat:	RCnt ← ~UStatusMsk						,c1, at[1,2,StoSectCnt]; { get raw hardware status}
		RCnt ← RCnt and ~KStatus					,c2; {clear the HeadSelected field so the number of the current field may be inserted there. also clear the FirmwareBusy bit and the SA1000/SA4000 bit.  If this is an SA1000, clear the HeaderTag bit.}
		RCnt ← RCnt or UStatus						,c3; {combine hardware and masked status to form most of proper status word.}
{
	now fetch the current Field number so it can be OR'ed in.
	It contains the number of the current field being processed in the sector.
}
		RCnt ← RCnt LRot8						,c1; {swap bytes to get at HeadSelected field}
		RCnt ← RCnt or UField						,c2; {insert field number in to status}
		RCnt ← RCnt LRot8						,c3; {Re-align the hardware status}
{
	There are three tests that must be passed
	for FirmwareBusy to be set in the status word,
	there must have been no memory error,
	no processing errors
	and the Next IOCB link may not be null.
}
		Xbus←MStatus, XDisp						,c1; {test for memory error}
		Ybus ←  UStatus , NZeroBr, DISP4[MemOK,7]			,c2; {Test status word, was there a memory error?}
MemError:	RCnt ← RCnt or CMemError, CANCELBR[LastIOCB, 1]			,c3, at[0F, 10, MemOK]; {mem failed, set flag, make sure Branch at AnotherICOB goes to  ResetFirmwareBusy and quit}
MemOk:		Ybus ← UNxtIOCBLnk, ZeroBr,BRANCH[AnotherIOCB, LastIOCB]	,c3, at[7,10,MemError]; { test the next ICOB link and decide whether ther were processing errors}

{
	There was either a memory or processing error
	so just start the memory cycle for the write of the status,
	leave FirmwareBusy cleared.
}

LastIOCB:	MAR ← [RHRAdr, RAdr+0], CANCELBR[ResetFirmwareBusy,1]		,c1, at[1,2,AnotherIOCB]; {start store of final Status, no more IOCBs to be executed.}

{
	Had no memory or processing errors,
	may not be another ICOB though.
	Start the memory write cycle to set the IOCB status
	decide whether to set FirmwareBusy or not.
}

AnotherIOCB:	MAR ← [RHRAdr, RAdr+0], BRANCH[SetFirmwareBusy, ResetFirmwareBusy]	,c1, at[0,2,LastIOCB];
ResetFirmwareBusy:
		MDR ← RCnt, RHRAdr ← 0, GOTO[GetIntrMask]			,c2, at[1,2,SetFirmwareBusy]; {this is the Last IOCB to be done now, update final status andsave totest later}
SetFirmwareBusy:
		MDR ← RCnt +0FF+1, RHRAdr ← 0FF				,c2, at[0,2,ResetFirmwareBusy]; {Continue on after this ICOB with the next one -  set Firmware Busy in the saved status and in a bit to be tested below.}
{
	In either case, write the final status to the IOCB
	and save it so we can test it after sending the Mesa Interrupt
}

GetIntrMask:	RCnt ← UInterruptMsk					,c3; {get mesa interrupt mask}

{
	In one atomic operation,
	set the interrupt mask
}
		RAdr← uWP						,c1; {get the emulator's mask}
		RAdr ← RAdr or RCnt					,c2; {or in the new interrupt flags}
		uWP ← RAdr, MesaIntRq					,c3; {Update the mask for the emulator and notify emulator its been changed}
{
	Will another IOCB be processed or do we quit now?
	If FirmwareBusy (now in RHRAdr.7) is 0, quit.
	Otherwise proceed.
	Quitting is done by writing the control word
	following the Mesa interrupt mask
	to the controller and jumping to the Dormant state.
	Proceeding is done by loading the virtual link address
	into RAdr and going to map it to a real address.
}
		[] ← RHRAdr, XDisp					,c1; {Test FirmwareBusy}
		RHRAdr ← 0, DISP4[QuitNow,0E]				,c2; {IOCB virt addr has high bits=0}
{
	Quit after this IOCB
}

QuitNow:	KCtl ← UFreezeCmd, GOTO[GetCSB]				,c3, at[0E,10,QuitNow]; {stop hardware and go dormant}

{
	Proceed with next IOCB in the chain
}

GetNextIOCB: RAdr ← UNxtIOCBLnk, GOTO[NewIOCB]				,c3, at[0F,10,QuitNow]; {get virtual addr of new IOCB and go map it}

{
	this is a command used to set the UStatus register with the current status
	and the UField register to a known value (CIOCBDone).
	This command is used to ensure a valid, non-zero status
	when a tranfer operation was not performed.
}

InitRegs:	RCnt ← RCnt and ~KStatus				,c1, at[0F,10, Inr];
		UStatus ← RCnt						,c2;
		RAdr ← CIOCBDone					,c3;

		UField ← RAdr, GOTO[FinLdReg]				,c1; {This also load CIOCBDone into UHeaderQuitMsk, but that should be ok.}
{eof...}