{Page Numbers: Yes X: 500 Y: -.5" First Page: 1
Heading:
}{SAx000Initial.mcPage}
{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,
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}

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

{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}
Set[germPageHigh, 3E];
{virtual page number of origin of germ -- 37002B}
Set[germPageLow, 2];
Set[germRequest, 0F0];
{offset in first page of germ, POINTER TO Request= LOOPHOLE[1360B]}
Set[bootPhysicalVolume, 2];
{first word of request}
Set[germDevice, 0F1];
{tells germ the boot device}
Set[germSA4000, 3];
Set[germSA1000, 2];

DoneOnceOnlyInit:
{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: acRrh ← 0,
c3, at[2,4,SetSA1IOCB]; {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 ← rEc3; {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}

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

acR← present,c1, at[4, 10, subrRet];
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}
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;

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

{read the emulator boot file or the hard microcode}
emuHard:
[]← uDiagnostic, ZeroBr,c1, at[0, 10, subrRet];
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;

{hard microcode (quick exit), pick up diagnostic table entry from first page of boot file}
rB← 2,c3; {boot file starts at 200}
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← 9,c2;
acR← DiskFileIDSize, CALL[BLT],c3;

L2← 1,c1, at[9, 10, subrRet];
CALL[readBoot3],c2;

{move the germ out of the visible display region}
rC← nextPage,c1, at[1, 10, readBootRet];
rC← rC+1,c2; {throw in an extra page}
rB← rC LRot8,c3;
rB← rB-1,c1; {last word of germ}
acR← germStart,c2;
acR← rC-acR,c3;
transferCount← acR, ZeroBr,c1; {number pages in germ}
rC← rC xor ~rC, BRANCH[moveGerm1, $],c2;
acR← bootNullGerm, GOTOABS[Maintenance1Loc],c3;
moveGerm1:
germStart← rC,c3;
acR← acR LRot8,c1; {number words in germ}
Noop,c2;
[]← acR, ZeroBr,c3;
moveGerm:
MAR← [rBrh, rB+0], BRANCH[$, setGermReq],c1;
germStart← rC,c2; {first word of germ}
rE← MD,c3;
MAR← [rCrh, rC+0],c1;
MDR← rE,c2;
rB← rB-1,c3;
rC← rC-1,c1;
Noop,c2;
acR← acR-1, ZeroBr, GOTO[moveGerm],c3;

setGermReq:
Noop,c2;
rC← germStart,c3;
MAR← [rCrh, germRequest+0],c1;
MDR← bootPhysicalVolume,c2;
Xbus← KStatus, XwdDisp,c3;
MAR← [rCrh, germDevice+0], BRANCH[setGerm1000, $, 2],c1;
MDR← germSA4000, GOTO[setGerm1],c2;
setGerm1000:
MDR← germSA1000,c2;
setGerm1:
Noop,c3;
MAR← [rCrh, germDevice+1],c1;
MDR← 0, CANCELBR[restoreMap, 0],c2;

{start playing with the map, restore the low 256 pages of the map, and unmap the area where their state was saved}
restoreMap:
rBrh← 1,c3;
rCrh← 1,c1;
rB← topPage,c2;
rC← 0, L0← 5,c3;
acR← 0FF+1, CALL[BLT2],c1;

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

MAR← [rBrh, rB+0],c1;
MDR← vacant, L0← 0B,c2;
acR← acR-1, CALL[BLT],c3;

{get machine ready for the emulator, first map in the germ}
mapGerm:
rE← germPageHigh,c1, at[0B, 10, subrRet];
rE← rE LRot8,c2;
rE← rE or germPageLow,c3;
acR← transferCount,c1; {number pages in germ}
rC← germStart, L0← 0,c2;
rC← rC and ~0FF, CALL[mapZap],c3;

{This enterprise maps the page before the germ (which just happens to be in the display bank) to virtual page 0FE. The germ uses this page as a disk buffer. In order to avoid losing the page initially mapped to 0FE, we first make page 0FE vacant by swapping it with the first vacant page in the map.}

{first swap entries for virtual page 0FE and page just past end (left in rE by mapZap)}
rB← 0FE,c1, at[0, 10, miscRet];
rE← topPage, L0← 0E,c2;
rBrh← 1, CALL[memSwap],c3;

rE← rE+1, {now the next available page in the map}c1, at[0E, 10, subrRet];
acR← germStart, {page 0FE is now unmapped}c2;
acR← acR-0FF-1,c3;
MAR← [rBrh, rB+0],c1; {map in page before germ}
MDR← acR or present,c2;
topPage← rE, 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;
Noop,c1, at[1, 10, subrRet];
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← 2,c3;
acR← 6, CALL[BLT2],c1; {+low 16 bits of page #}

rE← rE and 7,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.}
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← 7,c2;
acR← labelSize, CALL[BLT],c3;
MAR← [rDrh, labelPageLow+0],c1, at[7, 10, subrRet];
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← 0, BRANCH[noDiagnostics, $],c1;
[]← germStart, ZeroBr,c2;
BRANCH[noGerm, noEmulator],c3;
noEmulator:acR← bootNoEmulator, GOTOABS[Maintenance2Loc],c1;
noGerm:acR← bootNoGerm, GOTOABS[Maintenance2Loc],c1;
noDiagnostics:acR← bootNoDiagnostics, GOTOABS[Maintenance3Loc],c2;

{this code tries a disk boot if no hard microcode is installed
noDiagnostics:
uDiagnostic← acR,c2;
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}
acR← 1 RRot1,c3; {increment page bit}
MAR← [rDrh, rE+dataPage],c1;
MDR← rB or acR, 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[diskOp1],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;
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[diskOp1],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← 3, 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;}