{
 File name: SAx000Initial.mc
 Description: first microcode read from the disk, gets emulator microcode into CP memory,
 Last Edited: bj,  6-Jul-86 16:30:59
}
{
	Copyright (C) 1981, 1982, 1983, 1986 by Xerox Corporation.  All rights reserved.
}

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	For the four disk drives, the following applies:
	Q2040 - ~HeadSlelect16 = ~Sector. The two pins are connected physically.
	Q2040 - ~Sector = TRUE. Using positive logic.
	SA4000- ~SA1000/SA4000 = TRUE. Positive logic.
	SA1000- ~SA1000/SA4000 = FALSE & ~Sector = FALSE. Also positive logic.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
{
	First modify the IOCB for an SA1000 if necessary
}

DoneOnceOnlyInit:
		Xbus ← KStatus, XwdDisp						,c1; {what type of disk is this?}
		acR ← 0FF+1, DISP2[SetSA1IOCB, 2]				,c2; {Point to table of IOCBs}
{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	there is an SA1000 drive connected.
	Set the LabelDataTag bit in the HeaderLoopMsk
	and turn off the "Wait for Sector mark" command in the FindWord

	Test for SA1000 or Quantum depending on bit 9 of KTest after writing to bit H16 of KCtl:
	KTest[9]=0 => SA1000
	KTest[9]=1 => Quantum Q2040. For the Quantum Q2080 KCtl[0]=~KTest[9]
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

SetSA1IOCB:	rB ← 80								,c3, at[2,4,SetSA1IOCB];
		
		KCtl ← rB LRot8							,c1; {Test for an SA1000 or Quantum}
		Xbus ← KTest, XwdDisp						,c2; 
		DISP2[IsSA1000orQuantum,1]					,c3;

IsSA1000orQuantum:
		rB ← SA1000HeadCount, GOTO[FinishIsSA1000orQuantum]		,c1, at[1,4,IsSA1000orQuantum]; {sa1000}
		
		KCtl ← 0							,c1, at[3,4,IsSA1000orQuantum]; {Test for a Quantum Q2040 or Q2080}
		Xbus ← KTest, XwdDisp						,c2; 
		DISP2[IsQ2040orQ2080,1]						,c3;
		
IsQ2040orQ2080:	rB ← Q2080HeadCount, GOTO[FinishIsSA1000orQuantum]		,c1, at[1,4,IsQ2040orQ2080]; {Q2080}
		rB ← Q2040HeadCount, GOTO[FinishIsSA1000orQuantum]		,c1, at[3,4,IsQ2040orQ2080]; {Q2040}

FinishIsSA1000orQuantum:
		uQuantumSA1000MaxHeadNum ← rB					,c2;
		acRrh ← 0							,c3;  {Point to table of IOCBs}

		MAR ← [acRrh, vrrAddr+0]					,c1; {get addr of vrr parameters}
		rE  ← SA1FindSect						,c2; {prepare to set FindSectCmd}
		acR ← MD							,c3;

		MAR ← [acRrh, acR+headerLoopMsk]				,c1; {set up new HeaderLoopMsk}
		MDR ← SA1000HeaderLoopMsk, LOOPHOLE[wok], CANCELBR[$,2]		,c2;
		UFindSect ← rE							,c3; {set FindSect mark cmd for SA1000.}

		MAR ← [acRrh, vvrAddr+0]					,c1; {get addr of vvr parameters}
		rE  ← 0								,c2; {set up source of 0 for below}
		acR ← MD							,c3;

		MAR ← [acRrh, acR+headerLoopMsk]				,c1; {set up new HeaderLoopMsk}
		MDR ← SA1000HeaderLoopMsk, LOOPHOLE[wok], CANCELBR[DiskInitDone, 2]	,c2;
{
	Connected to an SA4000
	so set the Find Sector Mark command properly
}

SetSA4IOCB:	acR ← SA4FindSect						,c3, at[3,4,SetSA1IOCB];

		UFindSect ← acR							,c1;
		rE ← 0								,c2; {source of 0}
DiskInitDone:	germStart ← rE							,c3;

		{Noop}								,c1;
		currentCylinder ← rE						,c2;
		acR ← badBits							,c3;

		uBadBits ← acR							,c1;
		acR ← driveSelectBit						,c2;
		acR ← acR LRot8							,c3;

		acR ← acR or firmwareEnable					,c1;
		seekMask ← acR							,c2; {seek Mask ← drive select or firmwareEnable}
		acR ← 4								,c3;

		acR ← acR LRot8							,c1;
		acR ← acR or 20							,c2;
		haltBits ← acR							,c3; {halt bits ← 420}

		acR ← 0F8							,c1;
		acR ← acR LRot8							,c2;
		headMask ← acR							,c3; {headMask ← F800}

		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, L0 ← L0.BLTr4					,c1;
		acR ← 0FF+1, CALL[BLT3]						,c2;

BLTr4:		acR ← present							,c1, RtnBLT[L0.BLTr4];
		rC ← 0FF+1							,c2;
		rB ← 0								,c3;
{
	set up identity map for benefit of the disk microcode
}

identityMap:	MAR ← [rBrh, rB+0], BRANCH[$, rootPage]				,c1;
		MDR ← acR, rB ← rB+1						,c2;
		acR ← acR+rC, CarryBr, GOTO[identityMap]			,c3;
{
	Start flailing away at the disk.
	First read the physical volume root page
}

rootPage:	{Noop}								,c2;
		{Noop}								,c3;

		rBrh ← 0							,c1;
		rCrh ← 0							,c2;
		rDrh ← 0							,c3;

		rErh ← 1							,c1;
		rD ← 0FF+1							,c2; {base address of IOCB page}
		rE ← headerAddr							,c3;

		MAR ← [rDrh,  rE+0]						,c1; {cylinder 0}
		MDR ← 0								,c2;
		rB  ← 2								,c3; {read starting at page 2}
{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bjLoop:		{Noop}								,c1;
		{Noop}								,c2;
		GOTO[bjLoop]							,c3;
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

bjResume:	MAR ← [rDrh, rE+1]						,c1; {head 0, sector 0}
		MDR ← 0, CANCELBR[$, 0], L1 ← 0					,c2;
		acR ← 1, CALL[doVrr]						,c3; {read 1 sector}

transferRet:	[] ← acR and uBadBits, ZeroBr					,c1, at[0, 10, transferRet];
		transferIOCB ← rE, BRANCH[$, here1]				,c2;
		acR ← bootDeviceError, GOTOABS[Maintenance1Loc]			,c3;
here1:		rC ← 2								,c3; {copy PVBootFiles}

{
	save the useful stuff from the physical volume root page
}
		rB ← rC LRot8							,c1;
		rB ← rB+bootingInfo, L0 ← L0.BLTr0				,c2;
		acR ← bootingInfoSize, CALL[BLT]				,c3;
{
	read the emulator boot file
	or the hard microcode
}

emuHard:	[] ← uDiagnostic, ZeroBr					,c1, RtnBLT[L0.BLTr0];
		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;

readGerm:	germStart ← acR							,c3; {read the germ}

		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.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

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+headerAddr, L0 ← L0.BLTr1				,c2; {disk address of boot}
		acR ← 2, CALL[BLT]						,c3;

		{Noop}								,c1, RtnBLT[L0.BLTr1];
		{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;
		rB ← nextPage, L1 ← 1						,c3;

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

		[] ← acR and uBadBits, ZeroBr					,c1, at[1, 10, transferRet];
		rC ← rD+labelAddr, BRANCH[$, here2]				,c2; {write fileID into label}
		acR ← bootDeviceError, GOTOABS[Maintenance1Loc]			,c3;

here2:		rB ← bootFileID, L0 ← L0.BLTr2					,c3;

		acR ← 6, CALL[BLT2]						,c1; {+low 16 bits of page #}

		rE ← rE and 7F							,c1, RtnBLT[L0.BLTr2];
		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;
		rC ← rC-1							,c3;

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

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

		acR ← 2, CALL[BLT2]						,c1;

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

		[] ← acR and uBadBits, ZeroBr					,c1, at[5, 10, transferRet];
		acR ← nextPage, BRANCH[$, here3]				,c2;
		acR ← bootDeviceError, GOTOABS[Maintenance1Loc]			,c3;
here3:		acR ← acR+1							,c3;

		nextPage ← acR							,c1; {label template}
		acR ← labelSize							,c2;
		rB ← rD+labelAddr, L0 ← L0.BLTr6				,c3;

		rC ← labelTemplate, CALL[BLT2]					,c1;

{read the rest of the file from the Pilot Volume}

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

		transferCount ← acR, ZeroBr					,c1, at[1, 10, miscRet];
		L1 ← 4, BRANCH[readRun, $]					,c2;

{no pages left in cylinder, advance to next cylinder}

		{Noop}								,c3; {step in one cylinder}

		MAR ← [rDrh, headerAddr+1]					,c1;
		MDR ← 0, CANCELBR[$, 0]						,c2; {start at head 0, sector 0}
		{Noop}								,c3;

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

		MAR ← [rDrh, headerAddr+0], L1 ← 2				,c1;
		MDR ← acR+1, CALL[pagesLeftInCylinder3]				,c2;

		{Noop}								,c1, at[2, 10, miscRet];
		transferCount ← acR, L1 ← 4					,c2;
readRun:	rB ← nextPage, CALL[doVvr]					,c3;

		rE ← acR and uBadBits, ZeroBr					,c1, at[4, 10, transferRet];
		rB ← transferCount, BRANCH[readBurp, $]				,c2;
		acR ← filePage							,c3;

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

		acR ← acR+rB							,c1; {next page in memory}
		nextPage ← acR							,c2;
		GOTO[readLoop]							,c3;

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	burped while reading run of pages,
	status in acR.  
	rB has number of sectors requested
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

readBurp:	[] ← rE and ~verifyBit, ZeroBr					,c3;

		MAR ← [rDrh, vvrAddr+0], BRANCH[$, verifyError]			,c1; {get # of sectors remaining}
		acR ← bootDeviceError, GOTOABS[Maintenance3Loc]			,c2; {not a verify error}
verifyError:	rC ← acR LRot4							,c2; {shift field bits}
		rE ← MD								,c3; {parameter area}

		MAR ← [rDrh, rE+sectors]					,c1;
		rC ← rC and 0C							,c2;
		acR ← MD							,c3; {number sectors remaining}

		[] ←  rC xor 8, ZeroBr						,c1; {label verify error?}
		acR ← rB-acR, BRANCH[$, verErr1]				,c2; {number sectors transferred}
		acR ← bootDeviceError, GOTOABS[Maintenance1Loc]			,c3; {not in label}
verErr1:	transferCount ← acR						,c3; {zero is okay}

		rE ← filePage							,c1;
		rE ← rE+acR+1							,c2; {file page for next label}
		filePage ← rE							,c3;

		rB ← nextPage							,c1;
		rB ← rB+acR+1							,c2; {next available page for vvr}
		nextPage ← rB							,c3;

		rB ← rB-1, L1 ← 6						,c1; {page used by vrr}
		acR ← 1, CALL[doVrr3]						,c2; {transfer 1 page}

		[] ← acR and uBadBits, ZeroBr					,c1, at[6, 10, transferRet];
		BRANCH[$, verErr3]						,c2;
		acR ← bootDeviceError,  GOTOABS[Maintenance1Loc]		,c3;
verErr3:	{Noop}								,c3;

{Copy boot link to header,  and check for end of file, FFFF in both words of boot link.}

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

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

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

		MAR ← [rDrh, headerAddr+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;

		rC ← rD+labelAddr, BRANCH[rdBurp2, $]				,c1;
		GOTO[endRead]							,c2; {found end of chain, return}
rdBurp2:	rB ← labelTemplate, L0 ← L0.BLTr7				,c2;
		acR ← labelSize, CALL[BLT]					,c3;

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

endRead:	{Noop}								,c3;

		{Noop}								,c1;
		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
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	does verify, read, read operation to the disk.
	Takes number of sectors in acR,
	page number in rB,
	and disk address in header area.
	Returns status in acR;
	Clobbers rE
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

doVvr3:		{Noop}								,c3;

doVvr:		MAR ← [rDrh, vvrAddr+0]						,c1;
		GOTO[doTransfer]						,c2;
doVrr2:		{Noop}								,c2;
doVrr3:		{Noop}								,c3;

doVrr:		MAR ← [rDrh, vrrAddr+0]						,c1; {base of vrr parameters}
		CANCELBR[$, 0]							,c2;
doTransfer:	rE ← MD								,c3;

		MAR ← [rDrh, rE+sectors]					,c1; {set up paramter area}
		MDR ← acR							,c2; {sector count}
		{Noop}								,c3;

		MAR ← [rDrh, rE+dataPage]					,c1;
		MDR ← rB - 1, CANCELBR[$, 0]					,c2; {first page in main storage}
		{Noop}								,c3;

		MAR ← [rDrh, rD+ transferAddr]					,c1; {base of transferIOCB}
		acR ← rE, CANCELBR[$, 0]					,c2; {base of vrr parameter area}
		rE ← MD								,c3;

		MAR ← [rDrh, rE+parameters]					,c1;
		MDR ← acR, CANCELBR[$, 0]					,c2;
		rC ← acR							,c3;

		MAR ← [rDrh, headerAddr+1]					,c1; {read head}
		CANCELBR[$, 0]							,c2;
		acR ← MD							,c3;

		acR ← acR LRot4							,c1;
		acR ← acR RRot1							,c2;
		acR ← acR and headMask						,c3;

		acR ← acR or haltBits						,c1;
		{Noop}								,c2;
		{Noop}								,c3;

		MAR ← [rDrh, rC+haltWord]					,c1; {write halt word}
		MDR ← acR, CANCELBR[$, 0]					,c2;
		acR ← acR or UFindSect						,c3;

		MAR ← [rDrh, rC+findWord]					,c1; {write find word}
		MDR ← acR, CANCELBR[$, 0], L0 ← 2				,c2;
		rC ← rE, CALL[seek]						,c3;

		[] ← acR and uBadBits, ZeroBr, L0 ← 3				,c1, at[2, 10, transferRet];
		BRANCH[transferError, $]					,c2;
		rE ← rC, CALL[diskOp]						,c3;

transferRet1:	{Noop}								,c1, at[3, 10, transferRet];
		pRet1								,c2;
transferRet2:	RET[transferRet]						,c3;
transferError:	GOTO[transferRet1]						,c3;

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	call with IOCB in rE,
	returns when disk operation complete
	with status in acR
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

diskOp:		{Noop}								,c1;
		acRrh ← cedarIOPageHigh						,c2;
		acR ← uIOPage							,c3, at[2, 4]; {AEF 9/14/83 - why is this instruction at[2, 4]?}
{
	Note that Cedar conventions say uIOPage = [page, bank]
	but, that's ok with MAR ← [acRrh, <const>]
}
		MAR ← [acRrh, DiskCSBOffsetIOCB+0]				,c1;
		MDR ← rE							,c2;
		{Noop}								,c3;

		MAR ← [rDrh, statusAddr+0]					,c1; {set invalid status}
		MDR ← badBits							,c2;
		acR ← firmwareEnable						,c3; {start the transfer}

		KCtl ← acR LRot0						,c1;
transferWait:	acR ← ~KStatus, CANCELBR[$, 0]					,c2, at[0F, 10, transferWait];
		[] ← acR LRot8, XDisp						,c3; {test firmware busy}

		MAR ← [rDrh, statusAddr+0], BRANCH[$, transferWait]		,c1;
		pRet0, LOOPHOLE[natc]						,c2, at[0E, 10, transferWait];
		acR ← MD, RET[transferRet]					,c3; {return status in acR}
{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	call with current cylinder in currentCylinder,
	and desired cylinder in cylinder field of header
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

seek:		MAR ← [rDrh, headerAddr+0]					,c1;
		rB ← currentCylinder, CANCELBR[$, 0]				,c2;
		acR ← MD							,c3; {target}

		rB ← rB-acR, ZeroBr						,c1; {delta number of cylinders}
		currentCylinder ← acR, BRANCH[$, noSeek]			,c2;
		[] ← rB, NegBr							,c3;

		acR ← 0, BRANCH[seekOut, $]					,c1;
		acR ← inBit, GOTO[seek1]					,c2;
seekOut:	rB ← -rB							,c2;
seek1:		acR ← acR or seekMask						,c3; {enable firmware, drive select}

		MAR ← [rDrh, seekAddr+0]					,c1; {address of seek IOCB}
		CANCELBR[$, 0]							,c2;
		rE ← MD								,c3;

		MAR ← [rDrh, rE+cylinders]					,c1;
		MDR ← rB							,c2;
		{Noop}								,c3;

		MAR ← [rDrh, rE+stepHigh]					,c1;
		MDR ← acR or stepBit, CANCELBR[$, 0]				,c2;
		{Noop}								,c3;

		MAR ← [rDrh, rE+stepLow]					,c1;
		MDR ← acR, CANCELBR[$, 0]					,c2;
		rE ← rE+1, GOTO[diskOp]						,c3;

noSeek:		acR ← 0								,c3;

		{Noop}								,c1;
		pRet0, GOTO[transferRet2]					,c2;
{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Be careful,
	these routines are device dependent!
	Only routines for SA1004 and SA4008 are supplied
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

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

pagesLeftInCylinder3:
		{Noop}								,c3;
pagesLeftInCylinder:
		{Noop}								,c1; {SA4000 or SA1000?}
		Xbus ← KStatus, XwdDisp						,c2;
		BRANCH[SA1000Left, $, 2]					,c3;

{SA4008, pages left in cylinder= (28-sector)+(8-head-1)*28}

		MAR ← [rDrh, headerAddr+1]					,c1, at[3,4,SA1000Left];
		rB ← 7, 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, NegBr, L0 ← 0					,c1;
		rB ← 28'd, BRANCH[multiply, $]					,c2;
		acR ← 0								,c3; {off the end of the cylinder}

OffEnd:		rE ← 0								,c1;
		pRet1, GOTO[pgLft1]						,c2;

{return from multiply}

divMultRet:	acR ← Q+28'd, pRet1, GOTO[pgLft1]				,c2, at[0, 10, divMultRet];

{SA1004, pages left in cylinder= (16-sector)+(uQuantumSA1000MaxHeadNum-head)*16}

SA1000Left:	MAR ← [rDrh, headerAddr+1]					,c1, at[2,4,SA1000Left];
		rB ← uQuantumSA1000MaxHeadNum, 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, NegBr						,c1;
		rB ← acR LRot4, BRANCH[SA1000More, $]				,c2; {(4-head-1)*16}
		acR ← 0, GOTO[OffEnd]						,c3; {off the end of the cylinder}
SA1000More:	{Noop}								,c3;

		{Noop}								,c1;
		acR ← rB+16'd, pRet1						,c2;
pgLft1:		acR ← acR-rE, RET[miscRet]					,c3;

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	multiply,
	call with 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:	Q ← acR								,c*;
		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, pRet0							,c1;
		RET[divMultRet]							,c*;
{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	divide,
	call with dividend in acR,
	divisor in rB,
	uses rC,
	result left in Q
	remainder in acR

	timing:
  	64 cycles  main loop is 4 cycles times 16 iterations
	 3 cycles preamble
	 2 cycles postamble
	total	69 cycles (0 MOD 3)
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

{
divide:
		Q ← acR								,c*;
		acR ← 0								,c2;
		rC ← 10'x							,c3;
div0:		acR ← acR DALShift1, Cin ← 1					,c*;	{shifts in a zero!!!!!}
		acR ← acR-rB, NegBr						,c2;
		rC ← rC-1, ZeroBr, BRANCH[div1, div2]				,c3;
div1:		Q ← Q or 1, BRANCH[div0, div3]					,c1;
div2:		acR ← acR+rB, BRANCH[div0, div3]				,c1;
div3:		pRet0								,c2;
		RET[divMultRet]							,c*;
}

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	compare two blocks of memory,
	takes count in acR,
	and addresses in rB, and rC,
	clobbers rE,

	returns count in acR where compare first lost,
	(acR=0)=> compare succeeded
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
{
compare3:	{Noop}								,c3;

compare:	MAR ← [rBrh, rB+0]						,c1;
		[] ← acR, ZeroBr						,c2;
		rE ← MD, BRANCH[comp1, $]					,c3;

		GOTO[endBLT1]							,c1;
comp1:		MAR ← [rCrh, rC+0]						,c1;
		{Noop}								,c2;
		rE ← rE xor MD							,c3;

		{Noop}								,c1;
		{Noop}								,c2;
		[] ← rE, ZeroBr							,c3;

		acR ← acR-1, BRANCH[$, comp2]					,c1;
		acR ← acR+1, pRet0, GOTO[endBLT2]				,c2;
comp2:		rB ← rB+1							,c2;
		rC ← rC+1, GOTO[compare]					,c3;
}

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	increments header
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
{
incrHdr:	MAR ← [rDrh, headerAddr+1]					,c1;
		CANCELBR[$, 0]							,c2;
		rE ← MD								,c3;

		acR ← rE and 0FF						,c1; {sector}
		acR ← acR+1							,c2;
		[] ← acR-27'd, NegBr						,c3;

		rE ← rE and ~0FF, BRANCH[incHd, $]				,c1; {head}
		rE ← rE or acR							,c2;
incHdr0:	{Noop}								,c3;
incHdr1:	MAR ← [rDrh, headerAddr+1]					,c1;
		MDR ← rE, pRet0, CANCELBR[$, 0]					,c2;
		RET[subrRet]							,c3;

incHd:		rE ← rE LRot8							,c2;
		rE ← rE+1							,c3;

		[] ← rE-7, NegBr						,c1;
		rE ← rE LRot8, BRANCH[$, incHdr0]				,c2;
incCyl:		rE ← 0								,c3;

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

		MAR ← [rDrh, headerAddr+0]					,c1;
		MDR ← acR+1, GOTO[incHdr0]					,c2;
}
{
	am I a sa1000 or quantum???
	assume on cyl 0, disk ready;
	send 257 step pulses, one back; if on cyl 0 => sa1000
}
			 
{eof...}