{File name: DiskDlionA.mc
 Description: Dandelion Disk Controller microcode, Version 2.0
 Author: D. DXD   
 Created: October 30, 1979
 Last Edited: November 19, 1979  4:19 PM
 Last Edited: November 21, 1979  2:55 PM, "minimum code" version completed
 Last Edited: January 9, 1980  8:09 PM, create "transfer run of pages command"
 Last Edited: February 1, 1980  10:48 AM, complies with standard Dandelion register assignments in Dandelion.df
 Last Edited: March 14, 1980  11:14 AM, renamed to SA4DiskDlion from DiskDlion so a new version of the
	microcode may be produced for the SA1000/SA4000 controller.
 Last Edited: March 18, 1980  10:07 AM, update to Rev D CP board which cancels MDR← following an automatic
	PgCrossBr.  Extra code now inserted to make sure last word in page is stored anyway.
 Last Edited: April 11, 1980  1:50 PM, to comply with the new standards for the composite SA1000/SA4000 controller which
	has longer label fields (code overhead reduced).
 Last Edited: April 18, 1980  3:07 PM, take out features that depend on IgnorePgCr.
 Last Edited: April 30, 1980  11:06 AM, perform combined test on Header Field.
 Last Edited: May 16, 1980  4:19 PM, add extra delay before Read or Verify on SA1000 drive to account for extra delay
	after writing a field on that drive.
 Last Edited: May 22, 1980  3:27 PM, add extra two pattern words for SA1000 drive to account for extra delay added above.
 Last Edited: June 11, 1980  7:13 PM add extra word to sync pattern for SA4000 drive to try to decrease intermitent errors.
	Decrease length of sync pattern on SA1000 by one word to make room for 10 word labels eventually.
 Last Edited: June 18, 1980  8:19 PM, modify to work with SA4000HeadDLion.
 Last Edited: June 25, 1980  12:48 PM, fix ancient Inr bug and delete absolute starting address.
 Last Edited: June 27, 1980  2:29 PM, fix ancient bug in FinishIOCB.
 Last Edited: June 30, 1980  4:40 PM, reduce code overhead in an attempt to allow 10 word labels.
 Last Edited: June 30, 1980  10:29 PM, reduce code overhead further and ensure that read headers and labels are not incremented.
 Last Edited: July 1, 1980  1:12 AM, show final Sector Count after a transfer command. 
 Last Edited: July 1, 1980  12:46 PM, detect memory error in midst of run of pages.
 Last Edited: July 2, 1980  9:29 AM, complement KStatus for new HSIO board.
 Last Edited: July 1, 1980  4:51 PM, split into DiskDlionA.mc and DiskDlionB.mc
 Last Edited: July 1, 1980  9:47 PM, Fix bug in logic that doesn't change value read in label read.
 Last Edited: August 2, 1980  3:07 PM, Use AltUaddr and trim down code.
 Last Edited: September 19, 1980  2:24 PM, re-code Inr instruction and discard initialization of constants and page cross
	detection.
 Last Edited: October 8, 1980  4:53 PM, add CANCELBRs found by new burdock.
 Last Edited: October 16, 1980  6:03 PM, take out MDR← not preceeded by MAR←.
 Last Edited: November 4, 1980  11:09 AM, trap illegal instructions.
 Last Edited: March 20, 1981  3:02 PM, Move "incrementDataPage" flag to UDataLen.
 Last Edited: September 9, 1981  1:25 PM, Insert Kludge to work with old Rubicon Head (code looks for "IncrementDataPage"
	flag in UDataPgNum.  If it's there it is moved to UDataLen and UDataPgNum is pre-decremented).
 Last Edited: Jim JXF      November 9, 1981  10:17 AM, Delete kludge for old head. delete bad inst addresses that could result from DISP4[Inr,8]
 Last Edited: AEF AEF      August 3, 1982  1:02 PM, Modify references to IOPage
 Last Edited: AEF AEF      31-Aug-83  9:32:44, Move IOPage for larger VM
 Last Edited: Dennis DEG       1-Sep-84 19:29:39, add copyright notice.
 Last Edited: Ed Fiala  4-Sep-86 10:05:01 Change U0C00 to UMCtl, which has 8C02 for
 	4MB storage rather than 0C00; change IOPageHigh to cedarIOPageHigh; temporarily
	reinsert kludge for old Rubicon head.
 Last edited: Ed Fiala  9-Jan-87 11:22:03 Remove Rubicon kludge temporarily for testing.
}

{ 	Copyright (C) 1979, 1980, 1981, 1982, 1983, 1984, 1986 by Xerox Corporation.  All rights reserved.}

{-----------------------------------  Start of Code  ----------------------------------------}

{ THIS CODE WILL NOT RUN PROPERLY UNLESS InitDLion HAS INITIALIZED REGISTERS UMCtl, 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}

GetCSB:	RAdr ← uIOPage, SetTask[4], StartAddress[GetCSB],	 c1;
	RHRAdr ← cedarIOPageHigh,				c2, at[0F,10];
	Noop,							c3;

{Get IOCB physical address from CSB}
	MAR ← [RHRAdr, RAdr + 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; 
	RCnt ← UMCtl,				c2; {clear the memory error flags before the IOCB
						      starts with constant reg.}
	MCtl ← RCnt,					c3;  {and do the actual clearing}

{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 send 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}


{**************************************************************************
Beginning of the Kludge put in to allow the 256K memory boards to work with the old Rubicon Disk Head.  The following code looks in the most significant bit of the Data page number.  This code assumes that bit is the IncrementDataPgNum flag.  This code will NOT work if a full 64K map (24 bit virtual address) is used.  If that flag was set, it is cleared, the data page number is pre-decremented and the correct flag is set in UDataLen.}
{	RAdr ← RShift1 0, SE ← 1,				c2; {RAdr ← 8000 to test for flag}
	RCnt ← RAdr xor UDataPgNum, NegBr,			c3; {turn flag off if it was on and test to see if it
						      was on (is now off).}

	RCnt ← RCnt-1, BRANCH[CompleteFixup, FixupDone],		c1; {pre-decrement data pg num in case and
						      decide if this fixup is needed.}
CompleteFixup: UDataPgNum ← RCnt,				c2, at[0,2,FixupDone]; {fixup is needed so store
						      the corrected page number}
	RCnt ← RAdr or UDataLen,				c3; {insert flag into UDataLen where it belongs}

	UDataLen ← RCnt,				c1;}
{END of Kludge**************************************************************}


{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 hte 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 is 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 operation
						      will 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 havebeen 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 }