{Page Numbers: Yes X: 500 Y: -.5" First Page: 1
Heading:
}{SAx000InitialQ.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 Sturgis: 13-Feb-84 10:59:40: modify germ request to always say "SA4000", rather than distinguishing between SA4000 and SA1000. The new Cedar 5 Nucleus (Or FS or something) seems to only want to deal with SA4000, and no one seems to distinguish between SA1000 and SA4000. The Head does its own test of the hardware bits to decide what the disk is.

LastEdited by Sturgis: 22-Jul-83 13:22:26: add quantum test and constant model after [idun]<apilot100>MicrocodePrivate>dlion>sax1000initial.mc

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 ← germSA4000,c2; {always look like SA1000, for Cedar 5}
{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;
noEmul
ator:acR← bootNoEmulator, GOTOABS[Maintenance2Loc],c1;
noGerm:
acR← bootNoGerm, GOTOABS[Maintenance2Loc],c1;
noDiagnostics:
acR← bootNoDiagnostics, GOTOABS[Maintenance3Loc],c2;

{t
his 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[SA1000OrQ2040Left, $, 2],c3;

{SA4008, pages left in cylinder= (28-sector)+(8-head-1)*28}
MAR← [rDrh, headerAddr+1],c1, at[3,4,SA1000OrQ2040Left];
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}
{however, might be a Q2040}
SA1000OrQ2040Left:
{noop}c1, at[2,4,SA1000OrQ2040Left];
Xbus ← KTest, XwdDisp, c2;
DISP2[IsSA1000OrQuantum,1],c3;

{SA1004, pages left in cy
linder= (16-sector)+(4-head-1)*16}
MAR← [rDrh, headerAddr+1],c1, at[1,4,IsSA1000OrQuantum];
rB ← 3, CANCELBR[SA1000OrQ2040Continue, 0],c2;

{q2040, pages left in cylinder = (16-sector)+(8-head-1)*16}
MAR← [rDrh, headerAddr+1],c1, at[3,4,IsSA1000OrQuantum];
rB ← 7, CANCELBR[SA1000OrQ2040Continue, 0],c2;

SA1000OrQ2040Continue:
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;}