{
 File name: TridentInitial.mc
 Description: first microcode read from the Trident disk, gets emulator microcode into CP memory,
 Last Edited: bj, 23-Jul-86  7:51:02
 Fiala: 11-Aug-86  9:55:09 Commented out bjLoop for bj.
}
{
	Copyright (C) 1981, 1982, 1983, 1986 by Xerox Corporation.  All rights reserved.
}

DoneOnceOnlyInit:

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bjLoop:		{Noop}								,c1;
		{Noop}								,c2;
		GOTO[bjLoop]							,c3;
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

bjResume:
		rE ← 0								,c1;  {Initialization}
		germStart ← rE							,c2;
		{Noop}								,c3;

		acR ← 2								,c1;
		acR ← acR LRot8							,c2;
		uBootStart ← acR						,c3; {boot file starts at 200}
{
	map virtual pages 0-255 to real pages 0-255,
	first we must save the current mapping of the lowest virtual pages
}
		rBrh ← 1							,c1;
		rCrh ← 1							,c2;
		rB ← 0								,c3;

		rC ← topPage, L3 ← 0						,c1;
		acR ← 0FF+1, CALL[L3BLT3]					,c2;

		acR ← present							,c1, at[0, 10, L3BLTRet];
		rC ← 0FF+1							,c2;
		rB ← 0								,c3;
{
	set up identity map
	(for benefit of the disk microcode)
}

identityMap:	MAR ← [rBrh, rB+0], BRANCH[$, InitIOCB]				,c1;
		MDR ← acR, rB ← rB+1						,c2;
		acR ← acR+rC, CarryBr, GOTO[identityMap]			,c3;


InitIOCB:	rBrh ← 0							,c2;
		rB ← 0FF+1							,c3; {addr of IOCB}

		rD ← bUBitSignAdr						,c1;
		KCtl ← rD LRot0							,c2; {Select boot drive}
		{Noop}								,c3;
{
	Initialize IOCB.clientHeader=0/0/0
}
		MAR ← [rBrh, IOCBcHeaderCyl+0]					,c1;
		MDR ← 0								,c2; 
		rCrh ← 0							,c3;

		MAR ← [rBrh, IOCBcHeaderHS+0]					,c1;
		MDR ← 0								,c2; 
		rDrh ← 0							,c3;
{
	Merge drive info into IOCB.
	Uses rB=100=IOCBaddr & rD=bUBitSignAdr
}
		rC ← rB+IOCBseekDrive, L0 ← 0					,c1;
		CALL[merge3]							,c2;

		rC ← rB+IOCBhdrCtl, L0 ← 1					,c1, at[0, 10, mergeRet];
		CALL[merge3]							,c2;

		rC ← rB+IOCBdataCtl, L0 ← 2					,c1, at[1, 10, mergeRet];
		CALL[merge3]							,c2;

		rC ← rB+WPrdLblCtl, L0 ← 3					,c1, at[2, 10, mergeRet];
		CALL[merge3]							,c2;

		rC ← rB+WPverLblCtl, L0 ← 4					,c1, at[3, 10, mergeRet];
		CALL[merge3]							,c2;

		[] ← rD LRot8,XDisp						,c1, at[4, 10, mergeRet];
		DISP4[CSBIndex]							,c2;
		rC ← 0, GOTO[IOCBDA]						,c3, at[8, 10, CSBIndex]; {drive 0}
		rC ← 4, GOTO[IOCBDA]						,c3, at[4, 10, CSBIndex]; {drive 1}
		rC ← 8, GOTO[IOCBDA]						,c3, at[2, 10, CSBIndex]; {drive 2}
		rC ← 0C								,c3, at[1, 10, CSBIndex]; {drive 3}

IOCBDA:		MAR ← [rBrh, IOCBdiskAdr+0]					,c1;
		MDR ← rC							,c2; 
		acR ← rC							,c3;

		acR ← RShift1 acR						,c1;
		acR ← RShift1 acR						,c2;
		uDevOrd ← acR							,c3;

InitCSB:	rB ← rB+WPinitCSB						,c1; {adr Init vals for CSB}
		rC ← rC+rB+1							,c2; 
		acR ← 0FF+1							,c3;

		MAR ← [rCrh, rC+0]						,c1;
		MDR ← acR							,c2; 
		rCrh ← cedarIOPageHigh						,c3; 

		rC ← uIOPage, L3 ← 1						,c1; {CSB address}
		acR ← 10, CALL[L3BLT3]						,c2;
{
	Set tiUnumHeads based on boot drive type(i.e. T80/T300)
	then select head 5
}
		rB ← U0400							,c1, at[1, 10, L3BLTRet];
		rB ← rB or vCmdHd5, L0 ← 0					,c2; {Select Head 5}
		rC ← vCmdTagHd, CALL[SetRst1]					,c3; {delays after tagReset} 

		Xbus ← KStatus, XwdDisp						,c3, at[0, 10, SetRst3Ret]; {test bit 9&10}

		DISP2[DiskType, 1]						,c1; 
typeT300:	acR ← vT300Heads, GOTO[setHeads]				,c2, at[3, 10, DiskType];
typeT80:	acR ← vT80Heads							,c2, at[1, 10, DiskType];
setHeads:	tiUnumHeads ← acR						,c3; 

		rB ← U0400							,c1;
		rB ← rB or vCmdResetRecal, L0 ← 1				,c2;
		rC ← vCmdTagCtl, CALL[SetRst1]					,c3; {Reset Disk Check}

		rCrh ← 0							,c3, at[1, 10, SetRst3Ret];
{
	Now we're ready to read the root page. 
	There is only one IOCB. DCB[0] points to it.
	DCB[0] can be used for any drive.
	The IOCB is initialized with a da=0/0/0.
}

rootPage:	rD ← 0FF+1							,c1; {rD(rh)= addr of IOCB}
		rF ← 2, L3 ← 0							,c2; {read data into pg. 2}
		acR ← 1, CALL[doVrr1]						,c3;  
{
	save the useful stuff from the physical volume root page
}
		rC ← 2								,c1, at[0, 10, xferGoodRet];
		rB ← rC LRot8							,c2;
		rB ← rB+bootingInfo, L3 ← 2					,c3;

		acR ← bootingInfoSize, CALL[L3BLT2]				,c1;
{
	read the emulator boot file
	or the hard microcode
}

emuHard:	[] ← uDiagnostic, ZeroBr					,c1, at[2, 10, L3BLTRet];
		rC ← bootFileID, BRANCH[$, emuHard0]				,c2;
		rB ← hardFileID, L0 ← 8, GOTO[emuHard1]				,c3;
emuHard0:	rB ← emulatorFileID, L0 ← L0.BLTr8				,c3;

emuHard1:	acR ← DiskFileIDSize, CALL[BLT2]				,c1;

		acR ← 2, L2 ← 0							,c1, RtnBLT[L0.BLTr8];
		nextPage ← acR, CALL[readBoot3]					,c2;

		rC ← uDiagnostic, ZeroBr					,c1, at[0, 10,readBootRet];
		acR ← nextPage, BRANCH[$, readGerm]				,c2;
		rB ← 2								,c3; {boot file starts at 200}
{
	hard microcode (quick exit),
	pick up diagnostic table entry from first page of boot file
}
		rB ← rB LRot8							,c1;
		rC ← rC+1							,c2;
		{Noop}								,c3;
		
		MAR ← [rBrh, rC+0]						,c1;
		{Noop}								,c2;
		rC ← MD								,c3;
		
		rC ← rC+rB							,c1;
		uBootStart ← rC							,c2;
		GOTO[exitToEmulator]						,c3;
{
	read the germ
}

readGerm:	germStart ← acR							,c3;

		rB ← germFileID							,c1;
		rC ← bootFileID, L0 ← L0.BLTr9					,c2;
		acR ← DiskFileIDSize, CALL[BLT]					,c3;

		L2 ← 1								,c1, RtnBLT[L0.BLTr9];
		CALL[readBoot3]							,c2;
{
	We now have the germ in real memory.
	Restore the map for pages 0-255.
	Then move the germ to MDS 0, starting at page 1.
	Initialize the Request in the germ's SD.
}

RestoreMap:	rBrh ← 1							,c1, at[1,10,readBootRet];
		rCrh ← 1							,c2;
		rB ← topPage							,c3;

		rC ← 0, L0 ← L0.BLTr5						,c1;
		acR ← 0FF+1, CALL[BLT3]						,c2;
{
	vacate the pages just used to restore the low ones
}

		rB ← topPage							,c1, RtnBLT[L0.BLTr5];
		rC ← rB+1							,c2;
		acR ← 0FF+1							,c3;

		MAR ← [rBrh, rB+0]						,c1;
		MDR ← vacant, L0 ← L0.BLTrB					,c2;
		acR ← acR-1, CALL[BLT]						,c3;
{
	Move germ to MDS 0, page 1
}
		rB ← germStart							,c1, RtnBLT[L0.BLTrB];
		rB ← rB LRot8, GOTO[MoveGerm]					,c2;
{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    MoveGerm is *not* device specific and lives in CoreInitial (for simplicity)
    Entry is with
    rB=start of germ (as the address of the first word!),
    nextPage=the page after the germ
    The return is via GOTO[SetRequest] at c3.
    
    SetRequest is responsible for settting SD[request] to indicate the method
    that was used to Boot, then continue w/exitToEmulator
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Initialize Request in germ's SD

	Here are the appropriate entries from the Cedar6.0 release:
	
	BootFile.Location: TYPE = MACHINE DEPENDENT RECORD[
	    deviceType(0): DiskFace.Type,
	    deviceOrdinal(1): CARDINAL,
	    vp(2): SELECT OVERLAID * FROM
		disk => [diskFileID(2): DiskFileID],
		ethernet => [bootFileNumber(2): CARDINAL, net(3), host(4): CARDINAL ← 0],
		any => [a(2), b(3), c(4), d(5), e(6), f(7), g(10B), h(11B): WORD],
		ENDCASE
	    ];
	
	DiskFace.Type: TYPE = MACHINE DEPENDENT {
		null(0), sa800(1), sa1000(2), sa4000(3), cdc9730(4), ethernet(5),(LAST[CARDINAL])
	    };
	
	-- Identification of a boot file for loading it.
	
	BootFile.DiskFileID: TYPE = MACHINE DEPENDENT RECORD[
	    fID: DiskFace.FileID, -- for disk label
	    firstPage: INT, -- for disk label
	    firstLink: DiskFace.DontCare -- initial boot chain link
	    ];
	
	DiskFace.FileID: TYPE = MACHINE DEPENDENT RECORD[
	    id(0): SELECT OVERLAID * FROM
		rel => [relID(0): RelID, fill4(4): CARDINAL ← 0],
		abs => [absID(0): AbsID],
		ENDCASE
	    ];
	
	DiskFace.RelID: TYPE[4];
	
	DiskFace.AbsID: TYPE[5];
	
	-- Ignored in label verification.
	-- Typically used for boot chain links, in which case it's actually a DiskAddress.
	
	DiskFace.DontCare: TYPE[2];
	
	Corresponding values from Pilot12.0:
	
	Device.Type: TYPE = PRIVATE RECORD [CARDINAL];
	Ethernet:  TYPE = CARDINAL [5..16);
	PilotDisk: TYPE = CARDINAL [64..1024);
	nullType:  Type = [0];
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
{
	Set up a "Boot Physical Volume" request for the Germ.
	rE has @SD[] - 1 relative to the MDS value
	(=1FF for PrincOps4.0, =23F for PrincOps3.0)
	rFrh has germPageHigh (or cedarGermPageHigh, which is the Germ's MDS!)
}

InitRequest:
SetRequest:	{Noop}								,c1;
		rE ← cedarGermPageLow						,c2;
		rE ← rE LRot8							,c3;
		
		Map ← rF ← [rFrh, rE + 0]					,c1; {.mv cedarGerm}
		rC ← germRequest						,c2;
		rBrh ← rB ← MD							,c3; {.mr cedarGerm}

		MAR ← [rBrh, cedar.Request.Action+0]				,c1;
		MDR ← bootPhysicalVolume					,c2;
		{Noop}								,c3;	
		
		MAR ← [rBrh, cedar.Request.deviceType+0]			,c1;
		MDR ← cedarGermSAx000						,c2;
		{Noop}								,c3;
		
		MAR ← [rBrh, cedar.Request.devOrd+0]				,c1;
		MDR ← diskDeviceOrdinal						,c2;
doneReq:	GOTO[exitToEmulator]						,c3;

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	readBoot Subroutine
	
	Read the first page of the bootFile.
	Calculate the proper label by reading the label
	from the disk and filing in the correct fileID and page number
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

readBoot3:	{Noop}								,c3;

readBoot:	rB ← bootDiskAddr						,c1;
		rC ← rD+IOCBcHeader, L3 ← 3					,c2; {disk address of boot}
		acR ← 2, CALL[L3BLT]						,c3;

		{Noop}								,c1, at[3, 10, L3BLTRet];
		{Noop}								,c2;
		rB ← bootDiskAddr						,c3; {nil disk address?}

		MAR ← [rBrh, rB+0]						,c1;
		rB ← rB+1							,c2;
		acR ← MD							,c3;

		MAR ← [rBrh, rB+0]						,c1;
		{Noop}								,c2;
		acR ← acR or MD							,c3;

		[] ← acR, ZeroBr						,c1;
		BRANCH[$, nilBootFile]						,c2;
		rF ← nextPage, L3 ← 1						,c3;

		acR ← 1, CALL[doVrr2]						,c1; {read 1 starting at page 2}

		rC ← rD+IOCBclientLabel						,c1, at[1, 10, xferGoodRet];
		rB ← bootFileID, L3 ← 4						,c2;
		acR ← 6, CALL[L3BLT]						,c3; 

		rE ← rE and 7F							,c1, at[4, 10, L3BLTRet];
		rE ← rE LRot8							,c2; {rE ←  high bits of page#}
		rE ← LShift1 rE							,c3;

		MAR ← [rCrh, rC+0]						,c1;
		acR ←  7							,c2;
		acR ← MD and acR						,c3;

		MAR ← [rCrh, rC+0]						,c1;
		MDR ← acR or rE							,c2;
		{Noop}								,c3;

		MAR ← [rDrh, IOCBcLblFilePageLo+0]				,c1;
		rB ← bootDiskAddr						,c2;
		rC ← MD								,c3; {read low 16 bits of page #}

		rC ← rC+1							,c1; {reading first page is a special case}
		filePage ← rC							,c2; {restore disk address}
		rC ← rD+IOCBcHeader, L0 ← L0.BLTr3				,c3;

		acR ← 2, CALL[BLT2]						,c1;

		rF ← nextPage, L3 ← 2						,c1, RtnBLT[L0.BLTr3];
		acR ← 1, CALL[doVvr3]						,c2; {read 1 starting at page 2}

		acR ← bootDeviceError, GOTOABS[Maintenance2Loc]			,c1, at[2, 10, xferLblVerRet];

		acR ← nextPage							,c1, at[2, 10, xferGoodRet];
		acR ← acR+1							,c2;
		nextPage ← acR							,c3; 

		MAR ← [rDrh, IOCBcLblFilePageHi+0]				,c1;
		acR ← ~7							,c2;
		acR ← MD and acR						,c3; {Mask out 3bit Label flags} 

		MAR ← [rDrh, IOCBcLblFilePageHi+0]				,c1;
		MDR ← acR							,c2; 
		{Noop}								,c3;
{
	check for VERY special case
	of a boot link in the first page
}
		MAR ← [rDrh, IOCBcLblBootLink+0]				,c1;
		{Noop}								,c2;
		acR ← MD							,c3;

		MAR ← [rDrh, IOCBcLblBootLink1+0]				,c1;
		{Noop}								,c2;
		rB ← MD								,c3;

		[] ← acR or rB, ZeroBr						,c1;
		BRANCH[rbLink, rbNotLink]					,c2;
rbLink:		{Noop}								,c3; {boot link in the first page}

		MAR ← [rDrh, IOCBcHeader+0]					,c1;
		MDR ← acR							,c2;
		{Noop}								,c3;

		MAR ← [rDrh, IOCBcHeader+1]					,c1;
		MDR ← rB, CANCELBR[$, 0]					,c2;
		acR ← 0								,c3; {to zero out boot link}

		MAR ← [rDrh, IOCBcLblBootLink+0]				,c1;
		MDR ← acR							,c2; 
		{Noop}								,c3;

		MAR ← [rDrh, IOCBcLblBootLink1+0]				,c1;
		MDR ← acR							,c2; 
rbNotLink:	rC ← labelTemplate						,c3;

		rB ← rD+IOCBclientLabel, L0 ← L0.BLTr6				,c1;
		acR ← vLabelSize, CALL[BLT3]					,c2;
{
	read the rest of the file
	from the Pilot Volume
}

readLoop:	L1 ← 1								,c1, RtnBLT[L0.BLTr6];
		CALL[pagesLeftInCylinder]					,c2;

		transferCount ← acR, NZeroBr					,c1, at[1, 10, PLCRet];
		BRANCH[$, readRun]						,c2;
		{Noop}								,c3;
{
	no pages left in cylinder,
	advance to next cylinder
}
		MAR ← [rDrh, IOCBcHeaderHS+0]					,c1;
		MDR ← 0								,c2; {start at head 0, sector 0}
		{Noop}								,c3;

		MAR ← [rDrh, IOCBcHeaderCyl+0]					,c1;
		{Noop}								,c2;
		acR ← MD							,c3;

		MAR ← [rDrh, IOCBcHeaderCyl+0], L1 ← 2				,c1;
		MDR ← acR+1, CALL[pagesLeftInCylinder]				,c2; {step in one cylinder}

		{Noop}								,c1, at[2, 10, PLCRet];
		transferCount ← acR						,c2;
readRun:	rF ← nextPage, L3 ← 3						,c3;

		CALL[doVvr2]							,c1;

		rB ← transferCount						,c1, at[3, 10, xferGoodRet];
		acR ← filePage							,c2;
		acR ← acR+rB							,c3; {next page in file}

		filePage ← acR							,c1;
		acR ← nextPage							,c2;
		acR ← acR+rB							,c3; {next page in memory}

		nextPage ← acR							,c1;
		{Noop}								,c2;
		GOTO[readLoop]							,c3;
{
	burped while reading run of pages,
	controllerStatus in rF.
}

readBurp:	MAR ← [rDrh, IOCBpageCount+0]					,c1, at[3, 10, xferLblVerRet]; {get # of sectors remaining}
		rB ← transferCount						,c2; 
		acR ← MD							,c3; {number sectors remaining}

		acR ← rB - acR							,c1; {number sectors transferred}
		transferCount ← acR						,c2; {zero is okay}
		rE ← filePage							,c3; 

		rE ← rE+acR+1							,c1; {file page for next label} {+1 because one sec read via Vrr}
		filePage ← rE							,c2;
		rB ← nextPage							,c3;

		rB ← rB+acR+1							,c1; {next available page for vvr} {+1 because one sec read via Vrr}
		nextPage ← rB							,c2;
		rF ← rB-1, L3 ← 4						,c3; {page used by vrr}

		acR ← 1, CALL[doVrr2]						,c1; {transfer 1 page}
{
	Copy boot link to header,
	and check for end of file,FFFF in both words of boot link.
}
		MAR ← [rDrh, IOCBdLblBootLink+0]				,c1, at[4, 10, xferGoodRet];
		{Noop}								,c2;
		acR ← MD							,c3;

		MAR ← [rDrh, IOCBcHeader+0]					,c1;
		MDR ← acR							,c2;
		{Noop}								,c3;

		MAR ← [rDrh, IOCBdLblBootLink+1]				,c1;
		CANCELBR[$, 0]							,c2;
		rB ← MD								,c3;

		MAR ← [rDrh, IOCBcHeader+1]					,c1;
		MDR ← rB, CANCELBR[$, 0]					,c2;
		[] ← acR or rB, ZeroBr						,c3;

		acR ← acR+1, BRANCH[rdBurp1, $]					,c1;
		acR ← bootBrokenChain, GOTOABS[Maintenance3Loc]			,c2; {boot chain link is zero}
rdBurp1:	rB ← rB+1							,c2;
		[] ← acR or rB, ZeroBr						,c3;{FFFF+1 or FFFF+1 = 0  => end}

		rC ← rD+IOCBclientLabel, BRANCH[rdBurp2, endRead]		,c1;
rdBurp2:	rB ← labelTemplate, L0 ← L0.BLTr7				,c2;
		acR ← vLabelSize, CALL[BLT]					,c3;

		MAR ← [rDrh, IOCBcLblFilePageLo+0]				,c1, RtnBLT[L0.BLTr7];
		MDR ← filePage							,c2;
		GOTO[readLoop]							,c3;

endRead:	pRet2								,c2;
		RET[readBootRet]						,c3;
{
	here if boot file pointer was zero
}

nilBootFile:	[] ← uDiagnostic, ZeroBr					,c3;

		acR ← 1, BRANCH[noDiagnostics, $]				,c1; {disk Boot}
		[] ← germStart, ZeroBr						,c2;
		BRANCH[noGerm, noEmulator]					,c3;

noEmulator:	acR ← bootNoEmulator, GOTOABS[Maintenance2Loc]			,c1;

noGerm:		acR ← bootNoGerm, GOTOABS[Maintenance2Loc]			,c1;

{
	this code tries a disk boot
	if no hard microcode is installed
}

noDiagnostics:	uDiagnostic ← 0							,c2;
		bootDevice ← acR, GOTO[emuHard]					,c3; {disk Boot}

{subroutines}

{doVvr and doVrr do either a vvr or vrr operation to the boot drive. 
 TridentBootIOCB.bravo set up an IOCB that has.
     .microNext=0
     .dcbOffset=0 (DCB[0] can be and is used for any drive)
     .headerParam=verify, IncrDataPtr=TRUE, firstIOCB=TRUE
     .dataParam=read
 Early parts of TridentInitial do the following.
     Set up DCB[0].microNext=ptr-to-the-only-IOCB, (never changed).
     Particularize IOCB based on boot drive.
 Additionally both vrr and vvr REQUIRE: 
     acR=pageCount, 
     rD(rh)=IOCB addr.,
     rF=MemoryPageNumber, 
     IOCB.clientHeader=Header, 
 vvr also REQUIRES: 
     IOCB.clientLabel=Label     
 Both vrr and vvr RETURN: 
     rD(rh) unchanged,
     rF=ControllerStatus,
     IOCB.clientHeader=last in run. 
     IOCB.pageCount=upDated (0 If good completion) 
     IOCB.dataPtr=last in run. 
 vvr also RETURNS: 
     IOCB.clientLabel=updated     
 vrr also RETURNS: 
     IOCB.clientLabel = IOCB.diskLabel as read
}

doVrr2:		{Noop}								,c2;
doVrr3:		{Noop}								,c3;

doVrr1:		L1 ← 0								,c1;
		rB ← rD+WPvrrLabelParam, CALL[doXfer]				,c2;

		rB ← rD+IOCBdiskLabel						,c1, at[0, 10, doXferGoodRet];
		rC ← rD+IOCBclientLabel, L0 ← L0.BLTr1				,c2; 
		acR ← 0A, CALL[BLT]						,c3;

xUpdateHS:	MAR ← [rDrh, IOCBcHeaderHS+0]					,c1, RtnBLT[L0.BLTr1];
		acR ← vSecsPerTrack						,c2; 
		rB ← MD								,c3; {rB ← head sector} 

		rC ← rB+1							,c1;
		rC ← rC and 0FF							,c2;
		[] ←  rC xor acR, ZeroBr					,c3;

		BRANCH[$, xLastSec]						,c1; 
		GOTO[xIncrHS]							,c2;
xLastSec:	rB ← rB or 0FF							,c2;{so +1 yields sec=0 head+1} 
xIncrHS:	rB ← rB+1							,c3; 

		MAR ← [rDrh, IOCBcHeaderHS+0]					,c1;
		MDR ← rB, pRet3							,c2; {update head sector}
		RET[xferGoodRet]						,c3;

doVvr2:		{Noop}								,c2;
doVvr3:		{Noop}								,c3;

doVvr1:		L1 ← 1								,c1;
		rB ← rD+WPvvrLabelParam, CALL[doXfer]				,c2; 

		MAR ← [rDrh, IOCBcLblFilePageLo+0]				,c1, at[1, 10, doXferGoodRet];
		{Noop}								,c2; 
		rB ← MD								,c3; 

		MAR ← [rDrh, IOCBcLblFilePageLo+0]				,c1;
		MDR ← rB+1							,c2; {update client label}
		GOTO[xUpdateHS]							,c3;
{
	doXfer completes the set-up of the IOCB and starts the microcode.
	It then waits for the inProgress bit to go off.
	If there were no errors,
	then it returns via pRet1/doXferGoodRet.
	If there is a PURE label verify error,
	then it returns via pRet3/xferLblVerRet.
	In either case rF has the status.
	For other error conditions doXfer retries the operation
	and if IOCB.tries goes to zero it goes to Maintenance2Loc with a bootDeviceError code.
}

doXfer:		rF ← rF LRot8							,c3;

		MAR ← [rDrh, IOCBpageCount+0]					,c1;
		MDR ← acR							,c2; {set IOCB.pageCount} 
		rC ← rD+IOCBlabelParam						,c3; 

		MAR ← [rDrh, IOCBdataPtrLo+0]					,c1;
		MDR ← rF, L0 ← L0.BLTr2						,c2; {set IOCB.dataPtrLo} 
		acR ← 4, CALL[BLT]						,c3; {vvr/vrr to labelParam}

		rB ← rD+IOCBcHeader, GOTO[Xzzy]					,c1, RtnBLT[L0.BLTr2]; 
XRetry:		rB ← rD+IOCBcHeader						,c1; 
Xzzy:		rC ← rD+IOCBdHeader, L0 ← L0.BLTr4				,c2; 
		acR ← 2, CALL[BLT]						,c3;

		MAR ← [rDrh, IOCBcLblFilePageLo+0]				,c1, RtnBLT[L0.BLTr4];
		acR ← 0C0							,c2; 
		rB ← MD								,c3; 

		MAR ← [rDrh, IOCBfilePageLo+0]					,c1;
		MDR ← rB							,c2; {set IOCB.filePageLo}
		acR ← acR LRot8							,c3;

		MAR ← [rDrh, IOCBcStatus+0]					,c1;
		MDR ← acR							,c2; {C000=InPrg&GoodComp}
		acR ← acR LRot12						,c3; {acR = 0C00}

		MAR ← [rDrh, IOCBcHeader+1]					,c1; {get head, sector}
		CANCELBR[$, 2], LOOPHOLE[wok]					,c2; 
		rB ← MD								,c3; 

		rB ← rB LRot8							,c1; {sector, head}
		rB ← rB and 0FF							,c2; 
		rB ← rB or acR							,c3; 

		MAR ← [rDrh, IOCBseekHead+0]					,c1;
		MDR ← rB							,c2; {set IOCB.seekHead}
		rC ← vCStatusVerErrLRot8					,c3; {used in err recovery}

		MAR ← [rDrh, IOCBcHeader+0]					,c1; {get cylinder}
		rC ← rC LRot8							,c2;  {used in err recovery}
		rB ← MD								,c3; 

		MAR ← [rDrh, IOCBseekCyl+0]					,c1;
		MDR ← rB or acR							,c2; {set IOCB.seekCyl}
		KCmd ← acR LRot0						,c3; {0C00=ctlTest&firmEnab}

		MAR ← [rDrh, IOCBlabelErrMask+0]				,c1;
		rB ← vCStatusLastField						,c2; {used in err recovery}
		rB ← MD or rB							,c3; {used in err recovery}

XWait:		MAR ← [rDrh, IOCBcStatus+0], CANCELBR[$]			,c1;
		rC ← rC or vCStatusLblField					,c2; {used in err recovery}
		rF ← MD								,c3;

		acR ← U4000							,c1;
		[] ← rF, NegBr							,c2; {Neg => still in Progress}
		[] ← acR and rF, ZeroBr, BRANCH[$, XWait]			,c3;

XDone:		rB ← rB and rF, BRANCH[XOK, XErr]				,c1;
XOK:		pRet1								,c2;
		RET[doXferGoodRet]						,c3; 

XErr:		[] ← rB xor rC, ZeroBr						,c2; {0 => label verify err}
		BRANCH[XNotLblVer,XLblVer]					,c3; 

XLblVer:	{Noop}								,c1;
		pRet3								,c2;
		RET[xferLblVerRet]						,c3;

XNotLblVer:	rB ← rB and vCStatusLastField					,c1; 
		rB ← rB xor vCStatusDataField, ZeroBr				,c2; {0=> data fld err}
		BRANCH[XChkRetry,XAdjIOCB]					,c3; 

XAdjIOCB:	MAR ← [rDrh, IOCBpageCount+0]					,c1;
		{Noop}								,c2;
		rB ← MD								,c3; 

		MAR ← [rDrh, IOCBpageCount+0]					,c1;
		MDR ← rB+1							,c2;
		{Noop}								,c3;

		MAR ← [rDrh, IOCBdataPtrLo+0]					,c1;
		{Noop}								,c2;
		rB ← MD								,c3; 

		MAR ← [rDrh, IOCBdataPtrLo+0]					,c1;
		MDR ← rB-1							,c2;
		{Noop}								,c3;

XChkRetry:	MAR ← [rDrh, IOCBtries+0]					,c1;
		{Noop}								,c2;
		rB ← MD								,c3; 

		MAR ← [rDrh, IOCBtries+0]					,c1;
		MDR ← rB-1, ZeroBr						,c2;
		BRANCH[XRetry, XHardErr]					,c3;

XHardErr:	acR ← bootDeviceError, GOTOABS[Maintenance2Loc]			,c1;

{
	Uses data left in last header to compute pages left in cylinder, 
	returns result in acR.
}

pagesLeftInCylinder:
		{Noop}								,c3;

		MAR ← [rDrh, IOCBcHeader+1]					,c1;
		rB ← tiUnumHeads, CANCELBR[$, 0]				,c2;
		rE ← MD								,c3;
		
		acR ← rE LRot8							,c1;
		acR ← acR and 0FF						,c2; {head}
		rE ← rE and 0FF							,c3; {sector}
		
		acR ← rB-acR-1, NegBr						,c1;
		rB ← vSecsPerTrack, BRANCH[multiply, OffEnd]			,c2;
OffEnd:		acR ← 0								,c3; {off the end of the cylinder}

		rE ← 0								,c1;
		pRet1, GOTO[pgLft1]						,c2;
{
	multiply,
	multiplier in rB,
	multiplicand in acR,
	uses rC,
	result left in Q
	
  timing:
	45 cycles  main loop is 3 cycles times 15 iterations
	 4 cycles last iteration
	 3 cycles preamble
	 1 cycles postamble
  total	53 cycles  (2 MOD 3)
}

multiply:	{Noop}								,c3;

		Q ← acR								,c1;
		acR ← 0'x							,c2;
		rC ← 10'x							,c3;

mult0:		[] ← Q and 1, ZeroBr						,c1;
		rC ← rC-1, ZeroBr, BRANCH[mult1, mult2]				,c2;
mult1:		acR ← DARShift1 (acR+rB), BRANCH[mult0, mult3]			,c3;
mult2:		acR ← acR DARShift1, BRANCH[mult0, mult3]			,c3;

mult3:		Q ← ~Q								,c1;
		acR ← Q+vSecsPerTrack, pRet1, GOTO[pgLft1]			,c2;
pgLft1:		acR ← acR-rE, RET[PLCRet]					,c3;

{
	This subroutine is used to transfer commands to the selected disk.
	Upon entry rB has the Bus value and rC has the Tag constant shifted right by 8.
	Subroutine must set the Bus in c1 and the Tag in c3 of click1.
	Then it must wait at least two clicks to reset the tag.
}

SetRst3:	{Noop}								,c3;

SetRst1:	KCmd ← rB LRot0							,c1;
		rC ← rC LRot8							,c2;
		KCmd ← (rB or rC) LRot0						,c3;

		rC ← 10								,c1; {used in wait following tag reset}
		{Noop}								,c2;
		{Noop}								,c3;
{
	In c3 of click3 the tag is reset.
}
		{Noop}								,c1;
		{Noop}								,c2;
		KCmd ←  rB LRot0						,c3;

SRWait:		CANCELBR[$]							,c1; 
		rC ← rC - 1, ZeroBr						,c2;
		BRANCH[$, SRWait]						,c3;

		pRet0								,c1;
		RET[SetRst3Ret]							,c2;
{
	L3BLT exists because there are too many calls on BLT
}

L3BLT2:		{Noop}								,c2;
L3BLT3:		{Noop}								,c3;

L3BLT:		L0 ← L0.BLTr0							,c1;
		CALL[BLT3]							,c2; 

		{Noop}								,c1, RtnBLT[L0.BLTr0];
		pRet3								,c2;
		RET[L3BLTRet]							,c3;
{
	This subroutine performs:
	(rC↑) ← (rC↑) or (rD).
	Destroys acR
}

merge3:		{Noop}								,c3;

merge1:		MAR ← [rCrh, rC+0]						,c1;
		{Noop}								,c2; 
		acR ← MD							,c3;

		MAR ← [rCrh, rC+0]						,c1;
		MDR ← acR or rD, pRet0						,c2;
		RET[mergeRet]							,c3;

{eof...}