{
 File name: DiskDLionA.mc
 Description: Dandelion Disk Controller microcode, Version 2.0
 Last Edited: bj,  2-Jul-86 12:13:32
 Fiala, 22-Jul-86 15:48:24 Change MCtl ← U0C00 to UMCtl (= 8C02).
}
{
	Copyright (C) 1979, 1980, 1981, 1982, 1983, 1986 by Xerox Corporation.  All rights reserved.
}
{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Start of Code
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
{
	THIS CODE WILL NOT RUN PROPERLY
	UNLESS InitDLion HAS INITIALIZED
	REGISTERS UMaxSectTst, UStatusMsk and USyncAddrMk!!
}
{
	Initially, the microcode is in the dormant state.
	Upon awakening, it constructs the CSB address
	and looks there for a pointer to an IOCB
}

SetTask[4], StartAddress[GetCSB];

GetCSB:		RAdr ← uIOPage					 		,c1;
		RHRAdr ← cedarIOPageHigh					,c2, at[0F,10];
		{Noop}								,c3;
{
	Get IOCB physical address from CSB
	
	Remember: Cedar thinks uIOPage = [page, bank], not:
		MAR ← [RHRAdr, RAdr + DiskCSBOffsetIOCB]
}
		MAR ← [RHRAdr, DiskCSBOffsetIOCB]				,c1; {fet IOCB Addr from CSB}
		RHRAdr ← 0, CANCELBR[GetCSB2C3,2]				,c2; {IOCB in 0th 64k block of virt mem}
GetCSB2C3:	RAdr ← MD							,c3; {get Lo 16 bits of IOCB address}

{
	Find the physical address of the IOCB
	from its virtual address
}

NewIOCB: 	Map ← [RHRAdr, RAdr+0], ZeroBr					,c1; {start map ref, is this a nil IOCB?}
		RCnt ← RAdr, BRANCH[GoodIOCB, NilIOCB]				,c2; {save lo 8 bits of IOCB address, decide about IOCB Addr}
GoodIOCB:	RAdr ← MD, RHRAdr ← MD, GOTO[StartIOCB]				,c3, at[0,2,NilIOCB]; {get hi 10 bits of IOCB address}
NilIOCB:	KCtl ← 0, GOTO[GetCSB]						,c3, at[1,2,GoodIOCB]; {turn off hardware and wait again.}

{
	Re-assemble the IOCB's address in RAdr
}

StartIOCB:	RAdr ← MAR ← [RHRAdr, RCnt+0]					,c1; 
		MCtl ← UMCtl {8C02}						,c2;
		Noop,								,c3;
{
	Now in executing state,
	instruction fetch is first.
	Since all instructions require at least one operand,
	It is fetched also before branching to each command routine
}

GetCmd:		MAR ← [RHRAdr, RAdr]						,c1, at[0,2,DoInc];
GetCmdC2:	RAdr ← RAdr+1, CANCELBR[GetCmdC3, 2]				,c2; {set next IOCB addr}
GetCmdC3:	RCnt ← MD, XHDisp						,c3; {Decode command from bits 0 and 4, there are only four possible actions that may be taken in the first C2 of a command}

FetArg:		MAR ← [RHRAdr, RAdr], RAdr ← RAdr+1,DISP2[SameC2s]		,c1, at[0F,10]; {Start fetch of 1st operand and jump to cmd routine}
		
{
	to save code, the Increment memory, TestStatus, Jump, Set word,
	Memory to Memory move,Load Parameters, FinishIOCB and InitRegs commands
	are lumped together here.
	All do nothing in their first C2 and RCnt ← MD in the following C3.
}

SameC2s:	[] ← RCnt, YDisp, CANCELBR[SameC3s, 2]				,c2, at[0,4,SameC2s]; {decide which command it is}
SameC3s:	RCnt ← MD, USaveRAdr ← RAdr, DISP4[Inr, 8]			,c3; {get first arg and continue with command}

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Command Routines
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
{
	Increment memory location, skip if zero
	This routine is used both to count during loops
	and to increment label and header addresses.
	The word following the command contains
	the physical address of the location to be incremented.
	In the remainder of this click, the address is fetched.
	In the next click, the location's contents are fetched.
	The value is then incremented and tested for zero.
	In the final click, the location is updated
	and the IOCB pointer incremented if the location's new value is zero.
}
{
	fetch the location's contents
}

Inr:		RAdr ← MAR ← [RHRAdr, RCnt+0]					,c1, at[8,10,Inr]; {get address of loc to be incremented}
		{Noop}								,c2;
		RCnt ← MD							,c3; {get value, MD too slow to Incr and test now}
{
	Increment, test and restore the value
}
		MAR ← [RHRAdr, RAdr+0]						,c1; {start store cycle}
		MDR ← RCnt +1, ZeroBr						,c2; {increment and Update location}
IncBr:		RAdr ← USaveRAdr, BRANCH[GetCmd, DoInc]				,c3; {restore IOCB ptr and decide whether to get the next command or the one after it.}

{
	Now start fetch of new command two words beyond the present one,
	can't incremetn ptr on reading it from RSaveRAdr
}

DoInc:		MAR ← [RHRAdr, RAdr+2], RAdr ← RAdr+2, GOTO[GetCmdC2]		,c1, at[1,2,GetCmd];

{
	Jump
	This instruction is used to unconditionally transfer control
	from one point in the IOCB to another.
	The low 16 bits of the physical address follows the command word.
	Note we can do RAdr ← RCnt
	while MAR← forces the upper half of the ALU to RAdr or 0
	because the IOCB does not cross page boundries.
	Hence, only the least significant 8 bits are useful.
}

Jump:		RAdr ← MAR ← [RHRAdr, RCnt], GOTO[GetCmdC2]			,c1, at[0A,10,Inr];

{
	Send Control Word
	This instruction is used generally to send particular commands to the drive,
	like HeadSelect or Step and also to control when the hardware sends
	the service requests that enable the microcode.  
	The control word to be sent follows the command word.
}

SndCtlWd:	ClrKFlags, CANCELBR[$, 2]					,c2, at[1,4,SameC2s]; {reset the latched flags first so a wakeup depending on them will be valid. }
		KCtl ← MD, GOTO[GetCmd]						,c3; {send control wd and get next cmd}
{
	Load Transfer parameters
	It is desirable to separate the loading of the Transfer Command's
	parameters from the start of the command itself.
	This allows one to start the transfer immediately after
	sensing some condition on the disk (like index mark plus n sector pulses).
	This command loads the transfer command's parameters
	from The location given in RCnt in the IOCB into U registers.
	Note that the parameter list must begin on a 16 word boundry.
	Only the fact that the parameters lie in U registers
	instead of main memory allows the transfer command to proceed quickly enough.
	The command word is followed by the address of the start of the parameter list.
	To optimize the code for speed,
	dupicates of some quantities are held in U registers.
	These are the addresses of the file page numbers in the Label template
	and the address of the Head/Sector word in the Header,
	the file page number and the Head/Sector number.
	It is also necessary to load 256 - the number of sectors/track into UMaxSectTst.
	This is done to speed the check for end of track.
}

LoadPar:	RAdr ← MAR ← [RHRAdr, RCnt], GOTO[LoadRCLpC2]			,c1, at[0D,10,Inr]; {start fet of sector count, put the mem addr into RAdr. This will load trash into U reg88 (fZ(Noop) equals 8, U Reg 88 =ULabelLen) but that's ok since it will be fixed up in this loop.}

{
	this is the start of a loop to store the parameters.
	The mem addr register (RAdr) serves as the index into memory,
	the index into the U Registers and the loop index.
	This is why the parameters must begin at word 0 mod 16 in the IOCB.
	Note the NibCarryBr condition only detects the lower nibble of RAdr being zero,
	so all the 8x U regs are loaded here.
	URegs 81-88 are loaded correctly and 89 - 80 are loaded incorrectly.
	This is ok though because they will all be either loaded again below or in the transfer command.
}

LoadRCLp:	MAR ← [RHRAdr, RAdr+1], RAdr ← RAdr+1, NibCarryBr, AltUaddr	,c1; {start fetch of  next parameter, address Ureg for prev. parm.  Done with this loop?}
LoadRCLpC2: 	URCntBlk ← RCnt,  BRANCH[LoadRCLpC3, StartRALp,2]		,c2; {store previous parm, decide ifdone with this loop.}
LoadRCLpC3:	 RCnt ← MD, GOTO[LoadRCLp]					,c3, at[2,4,StartRALp]; {get next parm from list., loop back to store it.}

{
	Now that all the 8x parameters have been loaded,
	load the 7x parameters, or those belonging in U registers numbered 78 to 70.
	This is done by setting RAdr to x7 and starting to fetch again.
	note register 77 (UHeadSectorAddr) is loaded with trash in the first pass through the loop.
	This is ok, it is loaded correctly below anyway.
}

StartRALp:	RAdr ← RAdr-0A							,c3, at[3,4,LoadRCLpC3]; {RAdr←xxx6, so first parameter fetched is one in addr xxx7}

LoadRALp:	MAR ← [RHRAdr, RAdr+1], RAdr ← RAdr+1, NibCarryBr, AltUaddr	,c1; {fet next parm, see if it is the last one and address the U Reg needed to store the previous parm.}
LoadRALpC2: 	URAdrBlk ← RCnt, BRANCH[LoadRALpC3, FinLd,2]			,c2; {store previous parm, decide whether one now being fetched is the last one}
LoadRALpC3: 	RCnt ← MD, GOTO[LoadRALp]					,c3, at[2,4,FinLd]; {fet parm, not the last one}
FinLd:		RCnt ← MD							,c3, at[3,4,LoadRALpC3]; {fetch last parameter}

		UFindSectMkCmd ← RCnt						,c1; {store last parameter}
		RAdr ← RAdr-10							,c2; {get address of SectorCount (beginning of parm list, RAdr always =beginning of list +10 because of end conditions).  USectorCntAddr was trashed in loop that loaded RCnt U regs.  We will fix it below.}
{
	If the Header operation is a Read,
	it shouldn't be incremented.
	This is signalled by having the saved HeadSector word be 8000.
}
		RCnt ← CReadMask						,c3; {get mask used to detect read operation}

		[] ← RCnt and UHeaderCmd, ZeroBr				,c1; {is the header op a read?}
		USectorCntAddr ← RAdr, BRANCH[SaveHeadSect, IsHeaderRead]	,c2; {fix up the address of the Sector Count.}
{
	The header operation is a Read,
	so don't increment it after reading it.
	Flag this condition by seting UHeadSector to 8000.
}

IsHeaderRead: RAdr ← RShift1 0, SE ← 1, GOTO[SetUHeadSect]			,c3, at[1,2,SaveHeadSect]; {load 8000 into RCnt}

{
	The Header operation is not read
	so find the address of the HeadSector word in the Header
}

SaveHeadSect: RAdr ← UHeaderAddr						,c3, at[0,2,IsHeaderRead]; {get addr of beginning of Header in IOCB}

{
	Load the Head/Sector word itself
	so it may be updated without reading the original for each update.
	Save its address for later update
}
		RAdr ← MAR ← [RHRAdr, RAdr+1]					,c1; {start read of the HeadSector word, note The IOCB is on a single page so RHRAdr and the upper byte of RAdr are still valid.}
		UHeadSectorAddr ← RAdr, CANCELBR[LdHeadSectC3,2]		,c2;  {save Head sector address}
LdHeadSectC3:	RAdr ← MD							,c3; {load the Head/Sector word into RAdr}

{
	Save the Head/Sector word,
	either the real one or 8000 => don't change the one in memory since it will be read.
}

SetUHeadSect: UHeadSector ← RAdr						,c1; {save Head Sector word}

{
	Now decide if the Label operation is a read.
	If so, save the address of the Page number in the label as 0.
	this is a flag that the label is not be incremented.
}

FixupDone:	RAdr ← CReadMask						,c2; {get mask used to detect read operations}
		[] ← RAdr and ULabelCmd, ZeroBr					,c3; {Is the Label operation a Read? (Zero=>Yes)}

		RAdr ← USectorCntAddr , BRANCH[SavePgNum, IsLabelRead]		,c1; {Decide whether Label operation is a read. Also put page address back into RAdr.}
IsLabelRead:	 ULabPgAddr ← 0, GOTO[FormHdNotOkMsk]				,c2, at[1,2,SavePgNum]; {the label operation is a Read, so don't increment the read value.}
SavePgNum:	RCnt ← ULabelAddr						,c2, at[0,2,IsLabelRead]; {RCnt ← addr of Label Template in IOCB}
		RCnt ← RCnt + CLabPgOffset					,c3; {RCnt←addr of file pg num in IOCB}
{
	Save the address of the file page number
	and get the lo 16 bits of the file page number,
	it is also duplicated for speed
}
		ULabPgAddr ← MAR ← [RHRAdr, RCnt]				,c1; {save addr of page num and get page num}
		CANCELBR[LoadPgC3,2]						,c2;
LoadPgC3:	RCnt ← MD							,c3; {get lo 16 bits of file page num}

{
	store a pre-incremented version of the file page number
}
		RCnt ← RCnt+1							,c1; {pre-increment for easy storage when time is critical later}
		ULabPgLo ← RCnt							,c2; {save label}
{
	form and save a single mask used to test the Header field of each sector.
	This mask combines the HeaderQuit and HeaderLoop masks.
	Doing this means that a good header need only pass one test, not two.
}

FormHdNotOkMsk: RCnt ← UHeaderQuitMsk						,c3; {get first Header mask}

		RCnt ← RCnt or UHeaderLoopMsk					,c1; {combine with second mask}
{
	Can also here from the Init regs command.
}

FinLdReg:	UHeaderNotOkMsk ← RCnt, GOTO[IncBr]				,c2; {If status passes this mask, Header is ok, if not, either quit or loop}

{
	Transfer Run of Pages
	This command is used to read, write or verify
	the Header, Label and Data fields of a run of sectors on the disk.
	A run of sectors occupies consecutive sectors on consecutive tracks.
	The same operation is done to each sector.
	With the LoadTransfer Pararmeters command,
	the Device Head or diagnostic client has complete control
	over the operation to be performed on each field and the status bits to be tested.
	This command has no arguments following it.
	If an error condition is encountered during the transfer,
	control is returned immediately to the command following this one.
	If this command completes successfully,
	the two words following this command are skipped.
	Thus by putting a JUMP to an abort instruction immediately after this command,
	one may catch all error conditions.
	Bits specifying the error will be left in UStatus.
	The single exception to this rule is found when a Memory error is encountered.
	In that case, the page number in the Label template is still incremented
	before control is returned and UStatus is left cleared.
	The Memory fault will be detected again in the FinishIOCB command
	and the proper status posted.
	The error is detected here so an IOCB may be aborted
	with some hope of knowing where to begin a retry.
	It is assumed that no run of pages may cross a cylinder boundry.
	A run may cross a track boundry, causing the microcode to switch heads.
	Hence the maximum number of pages read by this command is
	(#of heads) * 28 sectors/track when connected to an SA4000 drive.
	It is (# of heads) * 16 sectors/track when connected to an SA1000 drive.
	Processing begins by loading the parameters
	for transferring a header and finding the first sector mark.
	The Header field is then processed
	and the results tested using first the HeaderNotOKMsk.
	If this test is passed, we proceed to the Label field.
	If it fails, we test first against HeaderQuitMsk then the HeaderLoopMsk.
	If any status bits indicated by the Header Quit mask are enabled,
	control returns to the IOCB "program".
	If any status bits indicated by the HeaderLoop mask are set,
	the command decrements the FailCount.
	If non-zero, control is passed back to the start
	of the command to look for another header.
	If the FailCount is zero,
	a Header meeting the requirements cannot be found on this track
	so the command is aborted.
	Note one of the bits tested when connected to an SA1000 drive
	should be the HeaderTag/SectorFound bit.
	This is set by a bit in the address mark of  the Label and Data fields.
	It acts an error bit when looking for Header fields.
	If the status passed the HeaderNotOKMsk test,
	parameters are loaded for the label field and it is processed.
	If it passes the test specified in ULabelQuitMsk,
	the process is repeated for the Data section,
	otherwise the command is aborted.
	One difference between the Header and Label fields
	on the one hand and the Data field on the other is
	that the same two buffers are used to process the Header and Label fields
	of every page while each data page may require a new data buffer.
	Addresses of the string of data buffers are found by incrementing
	and mapping a virtual address in UDataPgNum.
	If the most significant bit of UDataLen is a zero,
	the same virtual data page is used for all sectors in the run.
	Only if it was set is UDataPgNum incremented for each page.
	If the status resulting from processing the Data field 
	meets the conditions specified in UDataQuitMsk,
	one sector has been processed and we usually increment
	the HeadSector number in the Header
	and the lo 16 bits of the file page number in the label.
	If the Header was just read, is is not changed,
	the same is true of the Label.
	First,the sector number
	in the low byte of the second Header template word is incremented.
	If less than 28 (0-27) for the SA4000 drive or 16 (0-15) for the SA1000 drive,
	we decide whether to increment the Label.
	If the sector number is greater than or equal to the number of sectors per track,
	the Head number is incremented in the Header Template
	and both command words where it appears
	(UFreezeCmd, UFindSectMkCmd).
	No test is made to see if the maximum head number has been exceeded
	as it is assumed this will never happen.
	The lower 16 bits of the page number is in the Label field template word 5.
	The upper 8 bits of file page number and the flags are not touched by this code.
	This means runs of pages may not cross page 0 to page 1 boundries of a file
	(the flags change) or 64k page boundries.
	After this, the sector count is decremented.
	If it becomes zero, we finish.  Otherwise, control passes to NewSector.
}

TransferRun:	RAdr ← RAdr-1, CANCELBR[$, 2]					,c2, at[2,4,SameC2s]; {no args for this cmd}
		USaveRAdr ← RAdr						,c3; {so save RAdr pointing to next IOCB wd}

NewSector:	RCnt ← CHeaderField						,c1, at[0,2,DoneQuit]; {set partial status to say}
		UField ← RCnt							,c2; { starting the Header Field}
		RAdr ← UHeaderLen						,c3; {load the word count for the Header field}

		UWdCount ← RAdr							,c1; {pass it to TransferField}
		RAdr ← RHRAdr							,c2; {get hi bits of IOCB addr}
		RHRCnt ← RAdr LRot0						,c3; {set up for TransferField}
{
	return here when looking for another header
}

NewHeader:	RCnt ← UHeaderAddr, ClrKFlags					,c1, at[0,2,NotFound];  {start hardware looking for another header, clear SectorFounf flag}
		RAdr ← UHeaderCmd, pCall4					,c2; {get control wd for Header field}
		KCtl ← UFindSectMkCmd, CALL[TransferField]			,c3, at[0,10]; {start transferring as soon as the sector mark is found.}

HeaderRet:	RCnt ← UHeaderNotOkMsk						,c1, at[0,10,HeaderRet]; {get incorrect Header status mask}
		RCnt ← RCnt and ~KStatus, ZeroBr				,c2; {any reason not to proceed with Label?}
		UStatus ← RCnt , BRANCH[HeaderWrong, DoLabel]			,c3; {stop if so, else process Label Field}

HeaderWrong: RCnt ← UHeaderQuitMsk						,c1, at[0,2,DoLabel]; {get abort after Header status mask}
		RCnt ← RCnt and ~KStatus, ZeroBr				,c2; {should we quit now?}
		BRANCH[HeaderQuit, TstSeenAll]					,c3; {quit if so, test FailCount if not since that was not a fatal error.}

HeaderQuit:	GOTO[FinXferRun]						,c1, at[0,2,TstSeenAll]; {set up error br and quit}

{
	When we reach here,
	the field just read was a fine field,
	but not the header we wanted.
	Test FailCount to see if we have exhausted
	the number of sectors on this track.
	If so, quit.  If not, try the next one.
}

TstSeenAll:	RCnt ← UFailCount						,c1, at[1,2,HeaderQuit]; {get # sectors left before}
		RCnt ← RCnt-1, ZeroBr						,c2; {out of sectors now?}
		UFailCount ← RCnt, BRANCH[NewHeader, NotFound]			,c3; {quit if so, look at next if not}

{
	At this point, we have run out of sectors to inspect,
	so load the final error flags and quit
}

NotFound:	RCnt ← UStatus, GOTO[FinXferRun]				,c1, at[1,2,NewHeader];

{
	Here, the header was processed successfully,
	we proceed with the Label field.
	This is done by loading its parameters,
	setting the partial status to know the field being processed
	and calling TransferField
}

DoLabel:	RCnt ← ULabelLen						,c1, at[1,2,HeaderWrong]; {get word count}
		UWdCount ← RCnt							,c2; {set up for TransferField}
		RCnt ← CLabelField						,c3; {get field number in case we die here}

		UField ← RCnt							,c1; {save it for FinishIOCB}
		RAdr ← ULabelCmd, pCall4					,c2; {get control word for label field}
		RCnt ← ULabelAddr, CALL[TransferField]				,c3, at[1,10]; {go transfer the label field. }
{
	Return here after processing the label field
}

LabelRet:	RCnt ← ULabelQuitMsk						,c1, at[1,10,HeaderRet]; {get quit status mask}
		RCnt ← RCnt and ~KStatus, ZeroBr				,c2; {anything wrong with this label?}
		RAdr ← RRot1 ~1, BRANCH[LabelQuit, DoData]			,c3; {quit if so, else proceed.  Load mask used to  separate "incrementDataPgNum" flag from length of data field.  The status is not updated at this point because it must be the same as the Header status if good and will be set if bad.  The data length is loaded now so setup for the data field will fit into 3 clicks}
{
	At this point,
	we know there was something wrong with the processing of the label field,
	so we quit here
}

LabelQuit:	GOTO[FinXferRun]						,c1, at[0,2,DoData];

{
	The Header and label were ok,
	set up to do the Data field.
	In addition to setting the partial status
	and passing the word count and control word,
	we must map the virtual address before passing it.
	We also conditionally increment the virtual address.
	It is incremented if a new page is to be used for each sector transferred,
	left unchanged if the same virtual page is always to be used.
	The flag used to decide is in the most significant bit of the data field word count.
	It was placed there by the Head.
}

DoData:		RAdr ← RAdr and UDataLen, XHDisp				,c1, at[1,2,LabelQuit]; {get the word count, incr the virtual page number?}
		RCnt ← UDataPgNum, BRANCH[NoIncrDatPtr, IncrDatPtr,2]		,c2; {get the virtual page number, decide whether to increment it after use.}
NoIncrDatPtr: 	RHRCnt ← 1, GOTO[MapDataAddr]					,c3, at[2,4,IncrDatPtr]; {just point to Map in mem}
IncrDatPtr:	RHRCnt ← 1, RCnt ← RCnt+1					,c3, at[3,4,NoIncrDatPtr]; {also increment virt page number before use}

MapDataAddr:	MAR ← UDataPgNum ← [RHRCnt, RCnt]				,c1; {Start Mapping to get physical data page address.  Store incremented (or not) data page number.}
		RCnt ← CDataField, CANCELBR[GetPhysDatAddr, 2]			,c2; {Get constant used to say we are now processing the data field.}
GetPhysDatAddr: UField ← RCnt, RHRCnt ← RCnt ← MD				,c3; {update the state of the microcode w.r.t. this page and get the physical page # of this data page}

		RCnt ← MAR ← [RHRCnt, 0+0]					,c1; {get address of the first word in the data page.}
		UWdCount ← RAdr, pCall4						,c2;  {finally store the word count}
		RAdr ← UDataCmd, CALL[TransferField]				,c3, at[2,10]; {go transfer the data (finally!)}
{
	Return here after processing the data field.
	Test to see if there was a fatal error in processing it
}

DataRet:	RCnt ← UDataQuitMsk						,c1, at[2,10,HeaderRet]; {get msk specifying fatal errors in the data field}
		RCnt ← RCnt and ~KStatus, ZeroBr				,c2; {any fatal errors?}
{
	arrive here after testing the data status above (obviously)
	and after setting the memory error bit after a memory error
}

DataAndMemErrBr: RAdr ← ULabPgAddr, ZeroBr, BRANCH[DataQuit, NextSector]	,c3; {quit if so, else load addr of Page number and see if done with Run. If addr =0, we did a label read, so don't increment the read value.}

{
	there was an fatal error in processing the Data field,
	so quit now
}

DataQuit:	CANCELBR[FinXferRun]						,c1, at[0,2,NextSector]; {set up error br and quit}

{
	At least one sector has been successfully transferred and more may follow.
	If necessary, the pointer to the next virtual memory page
	was incremented when it was used above.
	Increment the page number found in word 4 of the label.
	Only the low 16 bits of this page label will be incremented.
	The Head guarantees never to specify a run of pages
	that crosses a page-0-to-page-1 boundry or a 64K page boundry.
	Thus the most significant word of the label
	will be taken care of in the Head, not here.
}

NextSector:	MAR ← [RHRAdr, RAdr], RAdr ← UHeadSectorAddr+0, SuppressTimingWarning, BRANCH[StoLabel, NoStoLabel]	,c1, at[1,2,DataQuit]; {start store of l.s. 16 bits of page #, can never get pageCarry here. Decide if L.S. 16 bits to be stored at all.  Note that a bogus mem operationwill be done here is the pg addr was set to zero. Can this cause a Memory error?}
StoLabel:	MDR ← RCnt ← ULabPgLo, GOTO[IncLabel]				,c2, at[0,2,NoStoLabel];  {update page num in Label template}
NoStoLabel:	RAdr ← UHeadSectorAddr						,c2, at[1,2,StoLabel]; {don't update the number in the label that as read.  Do fixup address of Head- Sector Word in RAdr.  The RAdr← in the MAR← above onlyloads the bottom half and the top half will have been zeroed bythe test value.  This load loads all 16 bits of physical address properly. RCnt is lift with trash, but its value is never used so that's ok.}
IncLabel:	RCnt ← RCnt+1, Xbus ← MStatus, XDisp				,c3, at[2,4]; {pre-increment for next store, got a memory error on last page transferred? Must check now so the Head can restore the proper file page num, sector count, etc and retry without remembering the initial parameters for each transfer in each IOCB.  This instruction is at [2,4] in case the Timing for the U+0 doesn't work.}

{
	save the low 16 bits of the page number
	and fetch the Head/Sector word so it can be updated
}

SaveLoLabl:	ULabPgLo ← RCnt, RCnt ← 0, DISP4[NoMemErr,7]			,c1; {save l.s. 16 bits of page num, decide about memory error. }
MemErr:		RCnt ← CMemError, GOTO[DataAndMemErrBr]				,c2, at[0F,10,NoMemErr]; {set mem error status and kill time until we can quit}
NoMemErr:	RCnt ← UHeadSector, NegBr					,c2, at[7,10,MemErr]; {get the old Head/Sector word, Is the "no increment" flag on?}
		RCnt ← RCnt+1, BRANCH[IncSect, NoIncSect]			,c3; {increment the sector number and decide whether to store it or not.}
{
	update the new Head/Sector word in the Header Template
}

IncSect:	MAR ← [RHRAdr, RAdr], RAdr ← UMaxSectTst+0, SuppressTimingWarning	,c1; {start store of Head/Sector wordand increment the sector number}
		MDR ← UHeadSector ← RCnt, RAdr ← RAdr+RCnt+1, PgCarryBr, GOTO[UpDtHdSctC3]	,c2; {update new Head/ Sector word, assume its ok and check to see if the last sector was just stored.  Note the "+1" exists so the U register may be written.  Because of it, the number in UMaxSectTst is one less than the desired value.}
NoIncSect:	{Noop}								,c1, at[1,2,IncSect]; {leave the Head/Sector number just read unchanged.}
		{Noop}								,c2; {Note we may only read Headers in runs of one page, soalways take the SectorTest branch below.}
UpDtHdSctC3: RAdr ← UHeadSectorAddr, BRANCH[SectorTest, NewHead]		,c3, at[2,4]; {prepare to update Head # in Header if necessary and and decide whether that was the last sector in the track.  This instruction is at  [2,4] in case the Timing for the U+0 doesn't work.}

{
	The run of pages has just crossed a track boundry,
	so the Head number must be incremented everywhere it exists.
	The most obvious place is in the Header word just stored.
	We create the proper HeadSector word by setting the Sector number
	to zero and incrementing the Head number,
	then store the correct head and sector numbers.
	Then we go through both  commands that contains a Head number.
	The Freeze command is done first,
	so the disk has the longest time to change heads.
	After this command is changed, is sent to the hardware.
}

NewHead:	RCnt ← RCnt and ~0FF						,c1, at[1,2,SectorTest]; {set sector number to 0}
		RCnt ← RCnt  + 0FF + 1						,c2; {increment the head number}
		UHeadSector ← RCnt						,c3; {update the U reg version of the word also}

		MAR ← [RHRAdr, RAdr+0]						,c1; {start update of head# in Template}
		MDR ← RCnt, RAdr ← CHeadIncr					,c2; {store new head#, sector 0.  load byte-swapped version of field used to incr head # in the commands}
		RAdr ← RAdr LRot8						,c3; {set up const to increment head # in control words}
{
	increment the head number in both control words
}
		RCnt ← UFreezeCmd						,c1; {start with the Freeze command}
		RCnt ← RCnt+RAdr						,c2; {incr head #, Can't send command here because of timing problems}
		UFreezeCmd ← RCnt						,c3; {save new version of Freeze control word}

		RCnt ← UFindSectMkCmd						,c1; {update control wd for header field}
		RCnt ← RCnt+RAdr, KCtl ← UFreezeCmd				,c2; {incr Head # and sent new command so disk can switch heads}
		UFindSectMkCmd ← RCnt						,c3; {save new Header field control wd}
{
	One sector has been successfully processed.
	Was that the last sector?
	Decrement the sector count and find out.
}

SectorTest:	RCnt ← USectorCount						,c1, at[0,2,NewHead]; {get old # remaining sects}
		RCnt ← RCnt-1, ZeroBr						,c2; {get new # sectors left, are we done?}
		USectorCount ← RCnt, BRANCH[NewSector, DoneQuit]		,c3; {update count, quit if all done}
{
	This is the exit taken if all pages were transferred sucessfully.
}

DoneQuit:	{Noop}								,c1, at[1,2,NewSector]; {set up no error br and quit}

{
	finish up the run of pages command.
	Coming into the statement,
	each statement did a test of the error status.
	The command will skip the next two bytes if it complleted error free,
	otherwise it continues directly on.
}

FinXferRun:	UStatus ← RCnt, ZeroBr, GOTO[IncBr]				,c2; {goto restore IOCB ptr and get next command or the one after that}

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	End of main body of Transfer Run of Pages Command
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

{
	Rest of Code in DiskDLionB.mc
}
{eof...}