{File name:  BLisp11SAx000Initial.mc
Purcell  6-Feb-85  0:28:37 Daybreak compatable initial
  Purcell    5-Feb-85 12:32:38  observe IFPAGE[NActivePages] as FPTOVP limit
  Purcell   4-Feb-85  9:34:22 Declare DLion small VM DTiger Large VM {%5}
  Lichtenberg  9-Jan-85  8:39:28 - trimmed commented-out code
  Lichtenberg   2-Aug-84  1:28:03 fixed obscure leader pages bug.
  Lichtenberg  13-Jul-84 18:15:31 uIOPage finally right this time.
  Purcell     13-Jul-84 18:02:02  uIOPage was incorrectly set for lisp 
  Purcell     2-Jul-84 14:15:55  zero oldIOPage 
  Purcell    2-Jul-84  4:44:35  set uIOPage like mesa 10 before lisp {??}
Purcell   1-Jul-84 23:37:15  use newIOPage and buffer it in oldIOPage 
 Purcell  1-Jul-84 22:25:58  Added support for Quantum Q2080
 Purcell  1-Jul-84 22:25:58  Accomodate changes for Germ as a result of going over to 32-bit procedure descriptors
 Purcell  1-Jul-84 22:25:58  Use IOPage offsets from Dandelion.df!
 Purcell 14-May-84 22:03:08  Mesa 11.0 Upgrade 2080 & germ
 Purcell 23-Feb-84 23:23:07  detect multibanks
Purcell 15-Feb-84  2:29:05  load 4mb; scrap to page 0FF
 Purcell February 14, 1984  8:03 PM  skip "zero banks" in FPTOVP
 Purcell February 6, 1984  12:40 PM  Lisp MCtl ← 8002h
 Purcell January 19, 1984  7:53 PM; MIN[topPage, AC0] removed
 Purcell January 18, 1984  4:14 PM; mesa: MCtl 0, MIN[topPage, AC0]
 Purcell January 17, 1984  11:21 AM; added NewInit section
 Purcell January 17, 1984; renamed from nLispSAx000Initial.mc
 Purcell January 16, 1984  6:34 PM; correct test of vp>2↑14
 Purcell December 30, 1983  3:53 PM; max real mem 3-8MB
 Purcell December 27, 1983  3:06 PM; max real mem 2.0MB
 Purcell November 26, 1983  2:25 PM; {*0} max real mem 1.5MB
 Purcell April 16, 1983  5:32 PM; uses password 15e3 at 17
 Purcell April 16, 1983  4:22 PM; try 800h pages $$
 Purcell April 16, 1983  1:16 PM; don't bother refresh
 Purcell April 13, 1983  10:47 AM; uLispBootMsg{0DE}=55 to boot mesa
 Purcell April 6, 1983  8:44 AM; skip over interface page
 Purcell April 1, 1983  4:53 PM; new sysout booting
 Purcell March 21, 1983  10:27 PM; Quantum support
 Purcell March 6, 1983  2:56 PM; define uLinkPtr
 Purcell March 6, 1983  9:45 AM; store boot disk address in IOCBs.cpr
 Purcell December 6, 1982  6:39 PM: map for 4MB boot file
 Purcell November 28, 1982  1:18 PM: LispSAx000Initial: save boot link for Lisp
 Purcell November 22, 1982  9:03 AM: link ← 0D for BLT
 Purcell November 19, 1982  4:54 PM: boot Lisp instead of diagnostics
File name:  [Idun]<WDLion>SAx000Initial.mc
 Description: first microcode read from the disk, gets emulator microcode into CP memory,
 Author: Jarvis,
 Created: August 25, 1980,
 JGS November 18, 1981  8:58 AM: move germ to mds 0.
 Last Edited by Neely January 14, 1982  2:40 PM - change cons. for extracting filePageHi from BootId from 7 to 7F.
 Last Edited by Jim Frandeen October 22, 1981  1:12 PM: If diagnostics are not installed, boot Emulator instead of crashing.
 Last Edited by Fasnacht June 18, 1981  10:30 AM: delete SetGerm1000 at
 Last Edited by DDavies February 14, 1981  4:35 PM
 Last Edited by Jarvis March 24, 1981  11:46 AM}

{disk definitions}

{RegDef[uLispBootMsg, U, 0DE];} {also in Dandelisp.df}	
RegDef[uLinkPtr, U, 27{uTT}];	{uLinkPtr}
RegDef[uFirstPage,U,49];

Set[hardMicrocodeFirstPage,13'd];

{null findSectorCmd for SA1000} Set[SA1000FindSectCmd, 20];
{have SA1000 reject Label and Data fields when looking for Headers}
	Set[SA1000HeaderLoopMsk, 43];
	
{control bits, low byte}
	{Set[stepBit, 80], Set[inBit, 40], Set[firmwareEnable, 20];}
	Set[SA4FindSect, 6], Set[SA1FindSect, 0];
{control bits, high byte}
	{Set[driveSelectBit, 4], Set[faultClearBit, 2];}
	
{IOCB status error indicators}	
	Set[badBits, 3F]; Set[verifyBit, 1];

{IO page offsets}	{Set[diskCSB, 1];}{%}

{IOCB page offsets}
Set[statusAddr, 3];	{status word}
	{not used, used to be pointer to next IOCB, always 0}
Set[seekAddr, 5];	{pointer to seek IOCB}
Set[transferAddr, 6];	{pointer to transfer IOCB}
Set[vrrAddr, 7];	{pointer to vrr parameter area}
Set[vvrAddr, 8];	{pointer to vrr parameter area}
Set[headerAddr, 9];	{header -- 2 words}
Set[labelAddr, 0B];	{label -- 0A words}
    Set[labelPageLow, 10];	{low 16 bits of file page number}
    Set[miscBitsAddr, 11];	{filePageHi[0..6], unused[0..5], immutable[0], temporary[0], zeroSize[0]}
    Set[bootLink, 13];	{disk address of next run of pages in boot file}
    Set[labelSize, 0A];

{parameter area offsets}
Set[sectors, 0];	{number of sectors to transfer}
Set[headerLoopMsk, 6];	{specifies non-fatal errors when looking for a header}
Set[dataPage, 0D];	{page number of data area in main storage}
Set[haltWord, 0F];	{requires head in high 5 bits}
Set[findWord, 10];	{requires head in high 5 bits}

{transfer IOCB offsets}
Set[parameters, 1];	{pointer to parameter area}

{seek IOCB offsets}
Set[cylinders, 0];	{-(number of cylinders to move heads)}
Set[stepHigh, 6];	{set step level high}
Set[stepLow, 8];	{set step level low}

{Miscellaneous disk constants}
Set[SA1000HeadCount,	Sub[4,1]];
Set[SA4000HeadCount,	Sub[4,1]];
Set[Q2040HeadCount,	Sub[8,1]];
Set[Q2080HeadCount,	Sub[7,1]];

RegDef[uVMFileSize, U, 6A];
Set[vmSizeLoc, 100'b];
Set[bigVMrefFlg, 75'b];
Set[NActivePages, 24'b];
Set[MachineType, 15'b];
Set[DANDELION, 6'b];
Set[NRealPages, 70'b];
Set[FPTOVPStart, 73'b];

{physical volume boot files}
{useful data formats extracted from Boot.mesa

	BootFileType: TYPE= {hardMicrocode, softMicrocode, germ, pilot, debugger, debugee};
	DiskFileID: TYPE= RECORD[fID: File.ID, firstPage: File.PageNumber, da: DiskAddress];
	PVBootFiles: POINTER TO ARRAY BootFileType[hardMicrocode..pilot] OF DiskFileID= 2;

After reading from the disk, PVBootFiles moved to memory location 2}

Set[bootingInfo, 8];	{origin of PVBootFiles in physical volume root page}
Set[DiskFileIDSize, 9];	{number of words in DiskFileID}
Set[bootingInfoSize, Mul[DiskFileIDSize, 4]];	{size of PVBootFiles}

{things salted away in page 0}
Set[availableDevices, 0];	{devices available for booting, set up by Phase 0}
Set[bootReadyFlag, 1];	{non-zero indicates boot file is ready}
Set[hardFileID, 2];	{hard microcode DiskFileID, copied from physical volume root page}
Set[emulatorFileID, 0B];	{emulator microcode DiskFileID, copied from physical volume root page}
Set[germFileID, 14];	{germ DiskFileID, copied from physical volume root page}
Set[bootFileID, 1D];	{working DiskFileID, copied from one of the above fileID's}
Set[bootPage, 22];
Set[bootDiskAddr, 24];
Set[labelTemplate, 26];	{template label used by boot loading routines}

{germ definitions Mesa 11}

Set[sLastGermRequest,                  Add[sFirstGermRequestLow, 57'b]];	{end of germ request in SD}
Set[RequestSize,                        60'b];	{size of germ request in SD}

Set[SD.Request.version,                Add[sFirstGermRequestLow, 00'b]];

  Set[RequestVersionHigh,                  7];
  Set[RequestVersionLow,                56'b];

Set[SD.Request.action,                 Add[sFirstGermRequestLow, 01'b]];
  Set[bootPhysicalVolume,                  2];
Set[SD.Request.location,               Add[sFirstGermRequestLow, 02'b]];
Set[SD.Request.location.deviceType,    Add[sFirstGermRequestLow, 02'b]];
  Set[germPilotDisk,                    64'd];
Set[SD.Request.location.deviceOrdinal, Add[sFirstGermRequestLow, 03'b]];

{germ definitions mesa10}

DoneOnceOnlyInit:
{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}
	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}
SetSA1IOCB:
{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]}


	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 {sa1000}, GOTO[FinishIsSA1000orQuantum], 	c1, at[1,4,IsSA1000orQuantum];
	
	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 {Q2080}, GOTO[FinishIsSA1000orQuantum], 		c1, at[1,4,IsQ2040orQ2080];
	rB ← Q2040HeadCount {Q2040}, GOTO[FinishIsSA1000orQuantum], 		c1, at[3,4,IsQ2040orQ2080]; 

{IsSA1000orQuantum:
	rB ← 7 {sa1000}, GOTO[FinishIsSA1000orQuantum], 		c2, at[3,4,IsSA1000orQuantum]; 
	rB ← 3 {quantum}, GOTO[FinishIsSA1000orQuantum], 		c2, at[1,4,IsSA1000orQuantum];} 

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

{JOIN}
	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;

	acRrh← 1,	c1; {set up stuff for IO page}
	currentCylinder← rE,	c2;
	acR← badBits,	c3;
	uBadBits← acR,	c1;
	acR← driveSelectBit,	c2;
	acR← acR LRot8,	c3;
	acR← acR or firmwareEnable,	c1;
	seekMask← acR, {seek Mask← drive select or firmwareEnable}	c2;
	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}

	acR← uDiagnostic, ZeroBr,	c1;
	[]← acR{uDiagnostic} - 1, NZeroBr, BRANCH[pLispBoot, pMesaBoot],	c2;
pLispBoot:	BRANCH[LispBoot, MesaBoot],	c3;
pMesaBoot: CANCELBR[MesaBoot, 1],	c3;

{map virtual pages 0-255 to real pages 0-255, first we must save the current mapping of the lowest virtual pages}

MesaBoot2: uDiagnostic← 0,	c2;
	Noop,	c3;

MesaBoot:
	rB ← 0AA{Lisp},	c1;
	uLispBootMsg ← rB,	c2;
	{MCtl ← 0} Noop,	c3;


mbjoin:	rBrh← 1,	c1;
	rCrh← 1,	c2;
	rB← 0,	c3;
	rC← topPage, L0← 4,	c1;
	acR← 0FF+1, CALL[BLT3],	c2;
{BLT3[from: 10000, to: 1topPage, cnt: 100h]}
{block transfer, takes count in acR, from in rB, and to in rC, returns first word past from block in rE}

MesaIDMap:
	acR← present,	c1, at[4, 10, subrRet];
	rC← 0FF+1,	c2;
	rB← 0, rBrh← 1,	c3;

{set up identity map for benefit of the disk microcode}
identityMap:	MAR← [rBrh, rB+0], BRANCH[$, idDone],	c1;
	MDR← acR, rB← rB+1,	c2;
	acR← acR+rC, CarryBr, GOTO[identityMap],	c3;
idDone:	Noop,	c2;
	Noop, GOTO[rootPage],	c3;


{** Lisp uses identity map for all of real memory for benefit of the disk microcode}

LispBoot:


	rB← 0,	c1;
	Q ← 55{Mesa},	c2;
	[]← Q xor uLispBootMsg, ZeroBr,	c3;
	
	acR← 033{dirtyRef{+3 %B}}, BRANCH[$, MesaBoot2],	c1;
	Q{100}← 0FF+1,	c2;
nextId3:	rBrh← 1,	c3;

{acR{dirtyRef}, rB{10000}, Q{100},   map 2↑12 pages as identity{+3seg %B} -- map in 1st 2 megabytes}

nextId:	MAR← [rBrh, rB+0],	c1;
	MDR← acR, acR ← acR + Q{100}, CarryBr,	c2;
	rB← rB+1, BRANCH[nextId, $],	c3;

incSeg:	acR ← acR + 1, NibCarryBr{2↑12 pages},	c1;
	rC ← topPage{virtual}, BRANCH[nextId3, $],	c2;
doneId:	acR{dirty;page FF}← ~0DF,	c3;

{	,	c1;
	rC← 0{FirstRealPageToMap},	c2;     {**32mb WAS rC ← 40 for regular memory}
	rC{20000} ← rC LRot8,	c3;
}

{acR{FF20}, rB{12000}, rCrh,rC{20000},  remaining vp's mapped to FF}

Scrap:	MAR← [rBrh, rB+0], BRANCH[$, doneScrp],	c1;
Scrap2:	MDR← acR{-1}, rB← rB+1,	c2;
	Ybus ← rB {- rC{0}}, ZeroBr, GOTO[Scrap],	c3;

doneScrp:	rCrh←1,	c2;     {**32mb: WAS rCrh ← 1  for regular memory}
		rC ← ~0FF, {=FF00}		c3; {**32MB: We want to do 65k map entries}

{acR{-1}, rCrh,rC,  change 256 map entries to protect the map and old iopage (as known to sysout) to point to FF{-1}}

{For Big VM, we have to protect all 256 entries + 1 for I/O page, since the map consumes all of segment 1 -- MPL}


Protect256:	Map{20000}← [rCrh, rC+0],	c1;
	MDR← acR{-1}, rC← rC, ZeroBr,	c2;
	rC← rC - 0FF - 1, BRANCH[Protect256, $],	c3;
	

{protect map entry for I/O page}
	rCrh ← 2, c1;
	rC ← 0, c2;
	, c3;

	Map ← [rCrh, rC+0], c1;   {**32MB:  Protect I/O page}
	MDR ← acR, c2;
	GOTO[MesaIDMap], c3;
	

{Start flailing away at the disk.  First read the physical volume root page}
rootPage:	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}
	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:  Ybus← acR and uBadBits, ZeroBr,	c1, at[0, 10, transferRet];
	transferIOCB← rE, BRANCH[$, here1],	c2;
	  acR← bootDeviceError, GOTOABS[Maintenance1Loc],	c3;

{save the useful stuff from the physical volume root page}
here1:	rC← 2,	c3; {copy PVBootFiles}
	rB← rC LRot8,	c1;
	rB← rB+bootingInfo, L0← 0,	c2;
	acR← bootingInfoSize, CALL[BLT],	c3;
	
	rC ← 2, c1, at[0, 10, subrRet];
	rC ← rC LRot8, c2;
	rCrh ← 0, c3;
	
	MAR ← [rCrh, hardMicrocodeFirstPage + 0], c1;
	, c2;
	rC ← MD, c3;
	
	uFirstPage ← rC, c1;
	, c2;
	, c3;

{read the emulator boot file or the hard microcode}
emuHard:	[]← uDiagnostic, ZeroBr,	c1 ;
	rC← bootFileID, BRANCH[$, emuHard0],	c2;
	  rB← hardFileID, L0← 8, GOTO[emuHard1],	c3;
emuHard0:	  rB← emulatorFileID, L0← 8,	c3;
emuHard1:	acR← DiskFileIDSize, CALL[BLT2],	c1;

	acR← 2, L2← 0,	c1, at[8, 10, subrRet];
	nextPage← acR, CALL[readBoot3],	c2;
	

	rC← uDiagnostic, ZeroBr,	c1, at[0, 10, readBootRet];
	acR← nextPage, BRANCH[$, readGerm],	c2;
	Noop,	c3;

	Noop,	c1;
	Ybus← rC{uDiagnostic}-1, NZeroBr,	c2;
	Noop, BRANCH[$, doDiag],	c3;
{test if we have lisp or diagnostics in real memory}

	rC ← 15{passH},	c1;
	rB ← 3,	c2;
	rBrh{0} ← rB{300} ← rB LRot8,	c3;

	MAR← [rBrh, rB+0F{passA}],	c1;
	rC{passH} ← rC LRot8, CANCELBR[$, 2],	c2;
	rC ← MD xor rC,	c3;

	rC ← rC xor 0E3{passL},	c1;
	Ybus ← rC, NZeroBr,	c2;
	Noop, BRANCH[$, notLisp],	c3;

{ We have Lisp; Restore the map.}
{set map[0..3FFFF] to vacant and interpret FPTOVP}

LispInit:	 rBrh← 1,	c1;
	rB ← rB xor ~rB,	c2;  {***32MB: we want it all!}
	rC ← 60{vacant},	c3;
	
	rDrh← 0,	c1;
	rD← 0FF+1,	c2; {base address of IOCB page}
	rE ← labelPageLow, c3;
	MAR ← [rDrh, rE+0], c1;
	, c2;
	rE ← MD, c3;  {read number of pages xferred}
	
	uVMFileSize ← rE, c1;
	, c2;
	, c3;


vacLp:	MAR← [rBrh, rB+0], ZeroBr,	c1;
	MDR← rC{vacant}, rB← rB-1, NibCarryBr, BRANCH[$, vDone],	c2;
vac3:	Noop, BRANCH[$, vacLp]{match to vacRef+1},	c3;

vacRef:	{Refresh,}	c1;
	Xbus ← 1, XDisp, GOTO[vac3],	c2;

vDone: Noop, CANCELBR[$, 1],	c3;

{update Interface page and interpret FPTOVP}

{Map[InterfacePageVp] ← 3}
	acRrh ← 6'b{INTERFACEspace}, CANCELBR[$, 1],	c1;
	acR ← 0'b{INTERFACEbase},	c2;
	acR ← acR LRot8,	c3;

	rB ← 3{real interface page},	c1;
	rB{300} ← rB LRot8,	c2;
	rBrh ← rB ← rB + 030{%B}{dirty},	c3;

	Map{InterfacePage} ← [acRrh,acR],	c1;
	MDR ← rB{330{%B}},	c2;
	,	c3;
 
	MAR← [rBrh, 15'b{MachineType} + 0],	c1;
	MDR ← DANDELION{6},	c2;
	Q ← uVMFileSize,	c3;

{**32MB: Say we can't do big VM refs (DTiger will change to 1 later)}{%5}
	MAR ← [rBrh, bigVMrefFlg  + 0], c1;
	MDR ← 0 {false}, c2;
	, c3;
	
	MAR ← [rBrh, vmSizeLoc + 0], c1;
	MDR ← Q, c2;
	, c3;

	Q ← topPage{virtual},	c1;
	Q ← Q + 40,{ map not included}{topPage was measured as if map were 32KB}	c2;
	Q{NRealPages} ← Q + 0FF{+1}{bank 0 was not incl but},	c3;

{%B 3 less segs
	Q ← Q - 0FF -1,	c1;
	Q ← Q - 0FF -1,	c2;
	Q ← Q - 0FF -1,	c3;
%B}

	MAR← [rBrh, {24'b}NActivePages + 0],	c1;
	,	c2;
	rE{NActivePages} ← MD,	c3;
	
{%B 3 more segs}
	rE ← rE + 0FF + 1,	c1;
	rE ← rE + 0FF + 1,	c2;
	rE ← rE + 0FF + 1,	c3;
{%B}

	{MAR← [rBrh, {70'b}NRealPages + 0]},	c1;
	{MDR ← Q{NRealPages}},	c2;
	Ybus ← Q - rE{NActivePages}, CarryBr{NRealPages>NActivePages},	c3;

	MAR← [rBrh, {73'b}FPTOVPStart + 0], BRANCH[$, activeLm],	c1;
	rE ← Q{NRealPages}, GOTO[limJn],	c2;
activeLm: Q ← rE{NActivePages}, GOTO[limJn],	c2;
limJn:	acR{F2Vpage} ← MD,	c3;

	rE ← rE - 0FF - 1,	c1;
	rE ← rE - 0FF - 1,	c2;
	rE ← rE - 0FF - 1,	c3;

	MAR← [rBrh, {70'b}NRealPages + 0],	c1;
	MDR ← rE{NRealPages-gap},	c2;
	,	c3;

	
{interpret FPTOVP setup}{assume FPTOVP doesn't cross segment}
{for i{rC} ← {201h}IOPage+1 to MIN[MaxRealPage, NActivePages{rD}]}
{	unless FPTOVP[i-1]=none{-1}	}
{	do MAP[FPTOVP[i-1]]:vp ← i	}

Set[fpvpPageOff, Add[IOPageHigh, 1{file1=real2}]];
Set[IOPagePage, Rshift[IOPage, 8]];

{%B offset 3{dbGap} seg's}
	rC ← 3{dbGap}, c1;
	rC ← rC LRot8, c2;
	acR ← acR + rC, c3;
{%B}

	acR{F2VrealPage+IOPageHigh} ← acR + fpvpPageOff,	c1;
	acRrh ← acR ← acR LRot8,	c2;
	acR ← acR and ~0FF,	c3;

	acR{F2V+IOPagePage} ← acR + IOPagePage,	c1;
	,	c2;
	rBrh ← 1{MapSpace}, c3;

Set[dbIOPageHigh, Add[IOPageHigh, 3{dbGap}]];
	rC ← dbIOPageHigh, c1;		{***32MB:  Count = 201H now}
	rC ← rC LRot8, c2;
	rC ← rC + IOPagePage +1, c3;

{interpret FPTOVP Loop 	}
{acR:	FPTOVP[i-1] ptr 	}
{rB:	FPTOVP[i-1]:vp	}
{rC:	i{+dbGap} ← {201h}IOPage+1		}
{Q:	MIN[NRealPages{-dbGap},NActivePages]+dbGap	}
{rBrh:	10000{Map}	}

{spin2:	GOTO[spin2],	c*;}{%%%}

f2vLp:	MAR← acR ← [acRrh, acR + 0],	c1;
	{rB{7FFF} ← (rB xor ~rB) RShift1,}	c2;
	rB{vp} ← MD {and rB},	c3;

	Ybus ← rB + 1, CarryBr{=1?},	c1;
	rC ← rC LRot8, XRefBr, BRANCH[$, none],	c2;
	Ybus ← rC + 0F, NibCarryBr{nondisplay bank?}, BRANCH[$, fvMod],	c3;

	MAR← [rBrh{1}, rB + 0], BRANCH[fvNo, $],	c1;
	MDR ← rC{i}, GOTO[none],	c2;
fvNo:	Noop, GOTO[none],	c2;

fvMod:	MAR← [rBrh{1}, rB + 0], BRANCH[fvMNo, $],	c1;
	MDR ← rC{i} + 70{move over flags to "dp" bit}, GOTO[none],	c2;
fvMNo:	Noop, GOTO[none],	c2;
none:	rC ← rC LRot8, CANCELBR[$, 1],	c3;

	rC ← rC + 1,	c1;
	Ybus ← rC xor Q{NActivePages}, ZeroBr,	c2;
	acR ← acR + 1, BRANCH[f2vLp, $],	c3;


{done with interpret FPTOVP}

	rB ← 0FF,	c1;
	rC ← 2,		c2;		{***32MB:  I/O Page back where Mesa wanted it!!!}
	rBrh ← 1,	c3;

{Map[IOPageVp=0FF] ← {140}500'b}

	MAR{100FF}← [rBrh{1}, rB + 0],	c1;
	MDR{2} ← rC ,	c2;
	rC{100} ← 0FF + 1,	c3;

	rC{200} ← rC + rC,	c1;
	rC{400} ← rC + rC,	c2;
	uBootStart← rC{400}, 	c3;
	
bankTest: Q ← 0,	c1;
	Bank ← rB ← 0,	c2;
btLoop: rC ← ~ PPort, BRANCH[$, MultiBank],	c3;

btLL: rC ← (rC LRot8) xor Q,	c1;
	Ybus ← rC and 3, NZeroBr,	c2;
	Q ← Q + 1, BRANCH[$, StartNewInit{UniBank}],	c3;   {**32MB: Goes to StartNewInit now (used to be GoNewInit just before the I/O page was cleared)}
	
	Bank ← rB +4,	c1;
	rB ← rB +4, NibCarryBr,	GOTO[btLoop],	c2;
	
MultiBank:        rB← 4,	c1;
	rBrh{0} ← rB{400h}← rB LRot8, c2; {boot file starts at 200}
	rC← 3{2nd .db},	c3;
	
	MAR← [rBrh, rC+0],	c1;
	rC{400q}{100} ← 0FF + 1,	c2;
	rC← MD,	c3;
	
	rC← rC+rB,	c1;
	uBootStart← rC{400},	c2;
	, c3;
		
{set vmsize large}
	acRrh ← 6'b{INTERFACEspace}, CANCELBR[$, 1],	c1;
	acR ← 0'b{INTERFACEbase},	c2;
	acR ← acR LRot8,	c3;
	
	Map{InterfacePage} ← [acRrh,acR],	c1;
	,	c2;
	rBrh ← rB ← MD,	c3;

{**32MB: DTigers can always do big VM refs}{%5}
	MAR ← [rBrh, bigVMrefFlg  + 0], c1;
	MDR ← 1 {true}, c2;
	GOTO[StartNewInit], c3;

{... magic things happen here...

   IF ANY I/O PAGE CLEANUP HAPPENS, DO IT BEFORE GOING TO NEWINIT!
 } 	
				
BackFromNewInit:

{**32MB: put I/O page in new format}
	TOS{8002h} ← RRot1 5, c1;
	MCtl ← TOS{8002h}, TOS ← 2,	c2;
	uIOPage ← TOS{for 20000H}, GOTO[exitToEmulator],	c3;

{hard microcode (quick exit), get diagnostic table entry from first page of boot file}
doDiag:		rB ← 055{Mesa}, GOTO[doDiag2],	c1;
notLisp:		rB ← 055{Mesa}, GOTO[doDiag2],	c1;
doDiag2:	uLispBootMsg ← rB,	c2;
	rB← 2,	c3; {boot file starts at 200}

mbjoin1: 
	rB← rB LRot8,	c1;
	rC← uDiagnostic,	c2;
	rC← rC{uDiagnostic=1}+1,	c3;
	MAR← [rBrh, rC+0],	c1;
	rC{400q}{100} ← 0FF + 1,	c2;
	rC← MD,	c3;
	rC← rC+rB,	c1;
	uBootStart← rC{400},	c2;
	MCtl← 0, GOTO[exitToEmulator],	c3;{%mesa small mem}


{read the germ}
readGerm:	germStart← acR,	c3;
	rB← germFileID,	c1;
	rC← bootFileID, L0← 9,	c2;
	acR← DiskFileIDSize, CALL[BLT],	c3;

	L2← 1,	c1, at[9, 10, subrRet];
	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← 5,	c1;
	acR← 0FF+1, CALL[BLT3],	c2;

{vacate the pages just used to restore the low ones}
	rB← topPage,	c1, at[5, 10, subrRet];
	rC← rB+1,	c2;
	acR← 20{0FF+1},	c3;

	MAR← [rBrh, rB+0],	c1;
	MDR← vacant, L0← 0B,	c2;
	acR← acR LRot8{acR-1}, CALL[BLT],	c3;
{Move germ to MDS 0, page 1}

	rC ← germStart,	c1, at[0B, 10, subrRet];
	rB ← rC LRot8,	c2;
	rBrh ← 0,	c3;

	rD ← nextPage,	c1;
	rD ← rD - rC, ZeroBr,	c2;
	transferCount ← rD, BRANCH[moveGerm1, $],	c3; {number pages in germ}

	  acR← bootNullGerm, GOTOABS[Maintenance2Loc],	c1;

moveGerm1:	germStart← acR xor ~acR,	c1;
	rFrh ← germPageHigh, rF ← 0 + germPageLow,	c2;
	rF ← rF LRot8,	c3;

mg2:	Map ← [rFrh, rF],	c1;
	Noop,	c2;
	rCrh ← rC ← MD,	c3;

	acR ← 0FF + 1,	c1;
	rC ← rC and ~0FF, L0 ← 0C,	c2;
	CALL[BLT],	c3;

	rF ← rF + 0FF + 1,	c1, at[0C, 10, subrRet];
	rD ← rD - 1, ZeroBr,	c2;
	rE ← LShift1 0FF, SE ← 1, BRANCH[mg2, InitRequest],	c3;


{Initialize Request in germ's SD}

InitRequest: 		{rE has 1FF = start of SD -1}
	rC ← sFirstGermRequestHigh,		c1;
	rC ← rC LRot8,				c2;  {Constants use an 8-bit data path.}
	rE ← rE + rC + 1,  {correct page}	c3;
	
	Map ← rF ← [rFrh, rE + 0],		c1;
	rC ← sFirstGermRequestLow,		c2;
	rBrh ← rB ← MD,				c3;
	
{InitRequest:
	Map ← rF ← [rFrh, rE + 1],	c1;
	rC ← sFirstGermRequest,	c2;
	rBrh ← rB ← MD,	c3;
}

ZeroReq:	MAR ← [rBrh, rC+0],	c1;
	MDR ← rD, rC ← rC + 1, PgCarryBr,	c2; {rD zero from above}
	BRANCH[ZeroReq, $],	c3;

	rD ← RequestVersionHigh,	c1;
	rD ← rD LRot8,	c2;
	rD ← rD or RequestVersionLow,	c3;

	MAR ← [rBrh, SD.Request.version+0],	c1;
	MDR ← rD,	c2;
	Noop,	c3;

	MAR ← [rBrh, SD.Request.action+0],	c1;
	MDR ← bootPhysicalVolume,	c2;
	Noop,	c3;

	MAR ← [rBrh, SD.Request.location.deviceType+0],	c1;
	MDR ← germPilotDisk,	c2;
doneReq:	MCtl ← 0, GOTO[exitToEmulator],	c3;

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

{block transfer, takes count in acR, from in rB, and to in rC, returns first word past from block in rE}
	rB← bootDiskAddr,	c1, at[1, 10, subrRet];
	rC← rD or 80{uLinkPtr}, L0← 0E,	c2; {save for lisp}
	acR← 2, CALL[BLT],	c3;

	rB ← 82,	c1, at[0E, 10, subrRet];
	uLinkPtr ← rB,	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← 2,	c3;
	acR← 6, CALL[BLT2],	c1; {+low 16 bits of page #}

	rE← rE and 7F,	c1, at[2, 10, subrRet];
	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, {reading first page is a special case}	c1;
	filePage← rC,	c2; {restore disk address}
	rC← rD+headerAddr, L0← 3,	c3;
	acR← 2, CALL[BLT2],	c1;

	rB← nextPage, L1← 5,	c1, at[3, 10, subrRet];
	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← 6,	c3;
	rC← labelTemplate, CALL[BLT2],	c1;

{read the rest of the file from the Pilot Volume}
readLoop:	L1← 1,	c1, at[6, 10, subrRet];
	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, {zero is okay}	c3;
	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.}
{*1,2 Check for end of file before copying boot link to header}
{*0 stop reading after 768Kw max real memory}
	MAR← [rDrh, labelPageLow+0],	c1;
	Noop,	c2;
	acR← MD,	c3;

	acR← acR LRot8,	c1;
	Ybus ← acR - 0C, {NibCarryBr{vPage> 768*4},}	c2;
	Noop, BRANCH[$, endRead1],	c3;

	MAR← [rDrh, bootLink+0],	c1;
	Noop,	c2;
	acR← MD,	c3;
	MAR← [rDrh, bootLink+1],	c1;
	CANCELBR[$, 0],	c2;
	rB← MD,	c3;
	Noop,	c1;
	Noop,	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:	Noop,	c2;
	Noop,	c3;
	MAR← [rDrh, headerAddr+0],	c1;
	MDR← acR-1,	c2;
	,	c3;
	MAR← [rDrh, headerAddr+1],	c1;
	MDR← rB-1, CANCELBR[$, 0],	c2;
	Q← uLinkPtr,	c3;

	Temp ← filePage, c1;
	rK ← uFirstPage, c2;
	, c3;

	MAR← [rDrh, Q{uLinkPtr}+0],	c1;
	MDR←  Temp - rK,	c2;
	Noop,	c3;
	MAR← [rDrh, Q{uLinkPtr}+1],	c1;
	MDR← acR-1, CANCELBR[$, 0],	c2;
	Noop,	c3;
	MAR← [rDrh, Q{uLinkPtr}+2],	c1;
	MDR← rB - 1, CANCELBR[$, 0],	c2;
	Q{uLinkPtr} ← Q +3, PgCarryBr,	c3;

	acR← labelSize, BRANCH[there1, $],	c1;
	{Noop,} L0← 7, GOTO[there2],	c2;
there1:	uLinkPtr ← Q{uLinkPtr+3}, L0← 7,	c2;
there2:	rB← labelTemplate, CALL[BLT],	c3;

	MAR← [rDrh, labelPageLow+0],	c1, at[7, 10, subrRet];
	MDR← filePage,	c2;
	GOTO[readLoop],	c3;

endRead:	Noop,	c3;
endRead1:Noop,	c1;
	pRet2,	c2;
	RET[readBootRet],	c3;

{here if boot file pointer was zero}
nilBootFile:	[]← uDiagnostic, ZeroBr,	c3;
	acR← 1 {disk Boot}, BRANCH[noDiagnostics, $],	c1;
	[]← 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 {disk Boot}, GOTO[emuHard],	c3;

{subroutines}

doVvr3:	Noop,	c3;
doVvr:	MAR← [rDrh, vvrAddr+0],	c1;
	GOTO[doTransfer],	c2;

{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}
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}
{diskOp1:	Noop,	c1;
diskOp2:	Noop,	c2;
diskOp:	acR← IOPageLow,	c3, at[2, 4];
	MAR← [acRrh, diskCSB+0],	c1;}diskOp:
	Noop,					c1;
	acRrh ← IOPageHigh,			c2;
{AEF 9/14/83 - why is this instruction at[2, 4]?}
	acR ← uIOPage,				c3, at[2, 4];

	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, {enable firmware, drive select}	c3;
	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)+(4-head-1)*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*;



{E N D}