-- Germ>BootSwapGerm.mesa
-- Last Edited by: Taft, February 25, 1983 3:00 pm

-- Note: I think the code in initialize assumes the machine starts the germ
-- with memory mapped at address 0

DIRECTORY
  Boot USING [
    bootPhysicalVolume, inLoad, Location, MDSIndex, outLoad, pRequest,
    pInitialLink, ReadMDS, teledebug],
  BootChannel USING [Create, Operation, Handle, Transfer, transferCleanup, transferWait],
  BootFile USING [
    Continuation, currentVersion, Entry, Header, InLoadMode, maxEntriesPerHeader,
    maxEntriesPerTrailer, Trailer],
  BootSwap USING [
    countSkip, Initialize, InitializeMDS, mdsiGerm, pCountGerm, pMon,
    ResponseKind],
  Environment USING [Long, maxPagesInMDS, PageCount, PageNumber, wordsPerPage],
  Frame USING [Free, GetReturnFrame, GetReturnLink],
  HeadStartChain USING [Start],
  Inline USING [BITAND],
  Mopcodes USING [zRET, zSLB],
  ProcessorFace USING [SetMP, Start],
  PageMap USING [
    Assoc, Flags, flagsClean, flagsVacant, GetF, RealPageNumber, SetF, Value,
    valueVacant],
  PhysicalVolumeFormat USING [currentVersion, Descriptor, Seal],
  PilotMP USING [
    cCantSwap, cGerm, cGermAction, cGermBadBootFile,
    cGermBadPhysicalVolume, cGermControlFault, cGermDeviceError, cGermDriver,
    cGermERROR, cGermFinished, cGermStartFault, Code],
  PrincOps USING [
    ControlLink, ControlModule, FrameHandle, GlobalFrameHandle, MainBodyIndex,
    NullGlobalFrame, NullLink, returnOffset, StateVector, UnboundLink],
  PrincOpsRuntime USING [GetFrame, GFT],
  ProcessOperations USING [WriteWDC],
  PSB USING [Monitor, UnlockedEmpty],
  ResidentMemory USING [AllocateMDS, AllocateMDSInternal],
  SDDefs USING [
    sControlFault, SD, sError, sSignal, sStart, sSwapTrap],
  Teledebug USING [Debug],
  Trap USING [ReadOTP];

BootSwapGerm: MONITOR LOCKS residentMemoryLock -- LOCKS BootSwap.pMon, --
  IMPORTS
    Boot, BC: BootChannel, BootSwap, Frame, Heads: HeadStartChain, Inline,
    ProcessorFace, PageMap, PrincOpsRuntime, ProcessOperations,
    importedResidentMemory: ResidentMemory, Teledebug, Trap
  -- IMPORTs ResidentMemory so that we can use Pilot's during testing.

  EXPORTS BootChannel, HeadStartChain, ResidentMemory
  SHARES Boot, BootSwap, PageMap, ResidentMemory =
  BEGIN OPEN BootChannel, BootFile, BootSwap, Environment, PageMap, PilotMP;
  -- Do NOT save any status between DoOutLoad and DoInLoad relating to the request.
  -- The next time everything in the outside world may have changed. 
  -- The booting action defined by the Principles of Operation should include:
  --   1. Put all usable real memory somewhere in virtual memory.
  --   2. Read countGerm pages of a "boot swap germ" into virtual memory beginning at
  --      page pageGerm+countSkip (steal real memory from high end to do this).
  --   3. Set Boot.pRequest↑ to [inLoad, locationOfBootFile].
  --   4. Set WDC>0, NWW=0, MDS=pageGerm, STKP=0.
  --   5. Xfer[dest: Boot.pInitialLink].

  -- At present, buffer space allocated for the BootChannel's is reclaimed when the request is complete.  Currently only the BootChannelEther allocates buffer space.  When non-initial-booting operations via the ether are implemented, we will have to hang onto this buffer space forever.

  countVM: PageCount = --ProcessorFace.?--40000B;
  pageGerm: PageNumber = GetPageMDS[] + countSkip;
    -- should be a constant engraved in stone (Boot?)
  pageBuffer: PageNumber; -- page used to read/write boot file map.
  pageGermKeep: PageNumber;
    -- highest (mapped) page of germ VM that we retain for all time.
  pageAfterGerm: PageNumber ← pageGerm + pCountGerm↑;
    -- current end of (mapped) germ storage (includes allocated buffer space,
    -- doesn't include pageTemp).
    -- Note that DoInLoad uses a scratch VM page at pageAfterGerm.

  dFirst64KStorage: LONG DESCRIPTOR FOR ARRAY OF WORD = DESCRIPTOR[
    LOOPHOLE[LONG[177200B], LONG POINTER], 200B];
    -- a piece of the first 64K that we know nobody uses, for allocating IOCB's.
    -- When the germ moves to the first 64K, this should be put in its global frame.

  -- Boot: ENTRY PROC =
  --   BEGIN
  --   continuation: Continuation;
  --   ProcessorFace.SetMP[cGerm];
  --   continuation ← DoInLoad[BC.Create[@Boot.pRequest.location]];
  --   pMon.state ← response; ++ i.e. not request
  --   WITH continuation SELECT FROM
  --     initial =>
  --       BEGIN
  --       pMon.responseKind ← initiated;
  --       mon.message↑ ← . . . boot parameter . . .;
  --       Set up psb with mds, destination; requeue it
  --       END;
  --     resumptive =>
  --       BEGIN
  --       pMon.responseKind ← resumed;
  --       NOTIFY pMon.condResponse
  --       END;
  --     ENDCASE =>
  --       Error[cGermBadBootFile];
  --   JumpCall0[OutLoadProcess]
  --   END;

  -- OutLoadProcess: INTERNAL PROC =
  --   BEGIN
  --   Condition: TYPE = MACHINE DEPENDENT RECORD [queue, timeout: UNSPECIFIED];
  --   DO
  --     Wait for request and process it
  --     WHILE pMon.state~=request DO WAIT pMon.condRequest ENDLOOP;
  --     ProcessorFace.SetMP[cGerm];
  --     DoOutLoad[BC.Create[@Boot.pRequest.location], pMon.inLoadMode,
  --       Continuation[
  --         fill: 0,
  --         vp: resumptive[condResponse: LOOPHOLE[pMon.condResponse, Condition].queue]]];
  --     Send response
  --     pMon.state ← response;
  --     pMon.responseKind ← outLoaded;
  --     NOTIFY pMon.condResponse
  --     ENDLOOP
  --   END;

  Run: PROC = -- after the very first execution, the germ entry point is here
    BEGIN
    handle: BootChannel.Handle;
    ProcessorFace.SetMP[cGermAction];
    BootSwap.InitializeMDS[]; -- set pMon
    DO
      -- inLoad exits through JumpCall2, outLoad does explicit EXIT
      -- bootPhysicalVolume turns itself into an inLoad
      SELECT Boot.pRequest.action FROM
	Boot.inLoad =>
	  BEGIN
	  continuation: Continuation;
	  responseKind: BootSwap.ResponseKind;
	  mdsiOther: Boot.MDSIndex;
	  destOther: UNSPECIFIED;
	  handle ← BC.Create[@Boot.pRequest.location, read, dFirst64KStorage];
	  [continuation, pMon.pStartListHeader] ← DoInLoad[handle];
	  WITH continuation SELECT FROM
	    initial --[mdsi, destination]-- =>
	      BEGIN
	      responseKind ← initiated;
	      mdsiOther ← mdsi;
	      destOther ← destination
	      END;
	    resumptive --[mdsi, resumee]-- =>
	      BEGIN
	      responseKind ← resumed;
	      mdsiOther ← mdsi;
	      destOther ← resumee
	      END;
	    ENDCASE => Error[cGermBadBootFile];
	  BootSwap.Initialize[mdsiOther]; -- set (pMon and) WriteMDS machinery
	  pMon.responseKind ← responseKind;
	  ProcessorFace.SetMP[cGermFinished];
	  JumpCall2[arg1: mdsiOther, arg2: destOther, Proc: pMon.CrossMDSCall];
	    -- free our frame
	  END;
	Boot.outLoad =>
	  BEGIN
	  handle ← BC.Create[@Boot.pRequest.location, write, dFirst64KStorage];
	  DoOutLoad[handle, pMon.inLoadMode, pMon.continuation];
	  pMon.responseKind ← outLoaded;
	  EXIT;
	  END;
	Boot.bootPhysicalVolume =>
	  BEGIN
	  pvDesc: POINTER TO PhysicalVolumeFormat.Descriptor = PointerFromPage[
	    pageBuffer];
	  Boot.pRequest.location.diskFileID.da ← [0, 0]; --kludge!
	  handle ← BC.Create[@Boot.pRequest.location, rawRead, dFirst64KStorage];
	  Transfer[handle, pageBuffer, 1];
 	  Transfer[handle, NULL, transferWait];
	  IF pvDesc.seal ~= PhysicalVolumeFormat.Seal OR pvDesc.version ~=
	    PhysicalVolumeFormat.currentVersion THEN
	    Error[cGermBadPhysicalVolume];
	  Transfer[handle, 0, 0]; -- shut down channel
	  Boot.pRequest.location.diskFileID ← pvDesc.bootingInfo[pilot];
	  Boot.pRequest.action ← Boot.inLoad;
	  -- now get the boot file and go
	  END;
	Boot.teledebug =>
	  IF IsBound[Teledebug.Debug] THEN {
	    Teledebug.Debug[pageBuffer, dFirst64KStorage]; EXIT}
	  ELSE Error[cCantSwap];
	ENDCASE => Error[cGermERROR];
      ENDLOOP;
    ProcessorFace.SetMP[cGermFinished];
    END;

  DoInLoad: PROC [channel: BootChannel.Handle]
    RETURNS [continuation: Continuation, pStartListHeader: POINTER] =
    BEGIN
    inLoadMode: InLoadMode;
    realPageTemp: RealPageNumber; -- SkipPageHack
    count, countData, countGroup, countRemaining: PageCount;
    page, pageLastMapped, pageNext, pageRun: PageNumber;
    pBuffer: POINTER = PointerFromPage[pageBuffer];
    nEntry: CARDINAL;
    pEntry, pEntryGroup: POINTER TO Entry;
    value: Value;
    SkipPage: PROC = -- SkipPageHack
      BEGIN
      pageTemp: PageNumber = pageAfterGerm;
      Assoc[pageTemp, Value[FALSE, flagsClean, realPageTemp]];
      Transfer[channel, pageTemp, 1];
      Transfer[channel, NULL, transferWait];
      Assoc[pageTemp, valueVacant];
      END; --
    -- Read first page, containing header
    Transfer[channel, pageBuffer, 1];
    Transfer[channel, NULL, transferWait];
    BEGIN OPEN header: LOOPHOLE[pBuffer, POINTER TO Header];
    IF header.version ~= BootFile.currentVersion THEN Error[cGermBadBootFile];
    pStartListHeader ← header.pStartListHeader;
    SELECT (inLoadMode ← header.inLoadMode) FROM
      load =>
	BEGIN
	CompactVM[];
	pageLastMapped ← FIRST[PageNumber] + GetCountRunMapped[FIRST[PageNumber]] - 1;
	realPageTemp ← PageMap.GetF[pageLastMapped].realPage; -- SkipPageHack
	END;
      restore => NULL
      ENDCASE;
    continuation ← header.continuation;
    countData ← header.countData;
    pEntryGroup ← @header.entries[0];
    nEntry ← maxEntriesPerHeader;
    END;
    --
    -- Restore real memory and hardware map from boot file, beginning
    -- with data of first group
    page ← 0; -- next page to restore
    countRemaining ← countData;
    DO
      -- Calculate group size
      countGroup ← MIN[nEntry, countRemaining];
      -- Set up map entries to be vacant or nonvacant (and writable), as appropriate
      pEntry ← pEntryGroup;
      THROUGH [0..countGroup) DO
	WHILE page < pEntry.page DO
	  -- nongerm pages not in boot file are vacant
	--IF ~InGerm[page] THEN
	  IF ~(page IN [pageGerm..pageAfterGerm) OR page = 376B OR page = 377B)
	    THEN
	    BEGIN
	    value ← SetF[page, valueVacant];
	    IF inLoadMode = load AND ~IsVacant[value] THEN
	      Assoc[pageLastMapped ← pageLastMapped + 1, value]
	    END;
	  page ← page + 1;
	  ENDLOOP;
	IF inLoadMode = restore THEN
	  Assoc[page, Value[FALSE, flagsClean, pEntry.value.realPage]];
	pEntry ← pEntry + SIZE[Entry];
	page ← page + 1;
	ENDLOOP;
      -- Transfer data to nonvacant pages
      pEntry ← pEntryGroup;
      count ← countGroup;
      WHILE count ~= 0 DO
	-- find and transfer one run
	pageNext ← pageRun ← pEntry.page;
	IF pageRun = 376B OR pageRun = 377B THEN -- SkipPageHack
	  {SkipPage[]; pEntry ← pEntry + SIZE[Entry]; count ← count - 1}
	ELSE
	  BEGIN
	  DO -- until end of run found
	    count ← count - 1;
	    pageNext ← pageNext + 1;
	    pEntry ← pEntry + SIZE[Entry];
	    IF count = 0 OR pEntry.page ~= pageNext OR pageNext = 376B OR pageNext
	      = 377B --SkipPageHack-- THEN EXIT;
	    ENDLOOP;
	  Transfer[channel, pageRun, pageNext - pageRun];
	  END;
	ENDLOOP; -- Restore map entries
      Transfer[channel, NULL, transferWait];
      pEntry ← pEntryGroup;
      THROUGH [0..countGroup) DO
	[] ← SetF[pEntry.page, pEntry.value];
	pEntry ← pEntry + SIZE[Entry]
	ENDLOOP;
      -- Prepare for next group
      countRemaining ← countRemaining - countGroup;
      IF countRemaining = 0 THEN EXIT;
      BEGIN OPEN trailer: LOOPHOLE[pBuffer, POINTER TO Trailer];
      Transfer[channel, pageBuffer, 1];
      Transfer[channel, NULL, transferWait];
      IF trailer.version ~= BootFile.currentVersion THEN Error[cGermBadBootFile];
      pEntryGroup ← @trailer.entries[0];
      nEntry ← maxEntriesPerTrailer;
      END;
      ENDLOOP;
    -- All groups are now loaded
    Transfer[channel, NULL, transferCleanup];  -- final call to allow cleanup
    SELECT inLoadMode FROM
      load =>
	BEGIN
	-- Recover germ buffer storage:  We can't do this when non-initial-booting
         -- operations via the ether are implemented.
	FOR page DECREASING IN [pageGermKeep..pageAfterGerm) DO
	  Assoc[pageLastMapped ← pageLastMapped + 1, SetF[page, valueVacant]];
	  ENDLOOP;
	pageAfterGerm ← pageGermKeep;
	END;
      restore => -- nongerm pages not in boot file are set vacant:
	WHILE page < countVM DO
	--IF ~InGerm[page] THEN
	  IF ~(page IN [pageGerm..pageAfterGerm) OR page = 376B OR page = 377B)
	    THEN Assoc[page, valueVacant];
	  page ← page + 1;
	  ENDLOOP;
      ENDCASE;
    END;

  DoOutLoad: PROC [
    channel: BootChannel.Handle, inLoadMode: InLoadMode,
    continuation: Continuation] =
    -- Assumptions: interrupts disabled, all devices quiesced
    BEGIN
    count, countData, countGroup, countRemaining: PageCount;
    page, pageNext, pageRun: PageNumber;
    pBuffer: POINTER = PointerFromPage[pageBuffer];
    nEntry: CARDINAL;
    pEntry, pEntryGroup: POINTER TO Entry; --
    -- Construct header in first map page
    -- Number of pages to save is total nonvacant minus those in germ
    page ← countData ← 0;
    DO
      --IF ~IsVacant[PageMap.GetF[page]] THEN
      IF ~ValueAnd[PageMap.GetF[page], valueVacant] = valueVacant THEN
	countData ← countData + 1;
      IF (page ← page + 1) = countVM THEN EXIT;
      ENDLOOP;
    countData ← countData - (pageAfterGerm - pageGerm) - 2 --i.e. 376B and 377B--;
    BEGIN OPEN header: LOOPHOLE[pBuffer, POINTER TO Header];
    header ← [creationDate:, -- fill this when we have a Pilot T.O.D clock?
	pStartListHeader: NIL, inLoadMode: inLoadMode, continuation: continuation,
	countData: countData, entries:];
    pEntryGroup ← @header.entries[0];
    nEntry ← maxEntriesPerHeader;
    END;
    --
    -- Write sequence of (map, data) groups
    page ← 0;
    countRemaining ← countData;
    DO
      -- Calculate group size
      countGroup ← MIN[nEntry, countRemaining]; -- Write map page
      pEntry ← pEntryGroup;
      THROUGH [0..countGroup) DO
	page ← AdvancePage[page];
	pEntry↑ ← [page, PageMap.GetF[page]];
	pEntry ← pEntry + SIZE[Entry];
	page ← page + 1
	ENDLOOP;
      Transfer[channel, pageBuffer, 1]; -- Write data pages
      pEntry ← pEntryGroup;
      count ← countGroup;
      WHILE count ~= 0 DO
	-- find and transfer one run
	pageNext ← pageRun ← pEntry.page;
	DO
	  -- until end of run found
	  count ← count - 1;
	  pageNext ← pageNext + 1;
	  pEntry ← pEntry + SIZE[Entry];
	  IF count = 0 OR pEntry.page ~= pageNext THEN EXIT;
	  ENDLOOP;
	Transfer[channel, pageRun, pageNext - pageRun];
	ENDLOOP; -- Prepare for next group
      Transfer[channel, NULL, transferWait];
      countRemaining ← countRemaining - countGroup;
      IF countRemaining = 0 THEN EXIT;
      BEGIN OPEN trailer: LOOPHOLE[pBuffer, POINTER TO Trailer];
      pEntryGroup ← @trailer.entries[0];
      nEntry ← maxEntriesPerTrailer;
      trailer ← [entries:]; -- set version and any other defaultable fields
      END;
      ENDLOOP;
    Transfer[channel, NULL, transferCleanup]; -- final call to allow cleanup
    END;

  AdvancePage: PROC [page: PageNumber] RETURNS [PageNumber] =
    -- Return first page at or after given page which is not vacant or part of the germ
    BEGIN
    --WHILE IsVacant[PageMap.GetF[page]] OR InGerm[page]
    WHILE PageMap.GetF[page].flags = flagsVacant OR
      (page IN [pageGerm..pageAfterGerm) OR page IN [376B..377B]) DO
      page ← page + 1 ENDLOOP;
    RETURN[page]
    END;

  -- Find all real pages currently mapped to virtual pages
  --   and map contiguous virtual addresses starting at 0 to them
  -- expansions of InGerm[] and IsVacant[] are for speed (compiler thinks
  -- is has to fabricate actual booleans
  -- Treat Page 1 like an IO page (i.e. never relocate) until the DCB is moved out of it.
  CompactVM: PROC =
    BEGIN
    pageDest: PageNumber ← 0;
    FOR pageSource: PageNumber IN [0..countVM) DO
      value: Value;
      IF pageSource IN [pageGerm..pageAfterGerm) OR pageSource IN [376B..377B] THEN LOOP;
      IF pageSource = 1 THEN LOOP;
--    IF ~IsVacant[value ← SetF[pageSource, valueVacant]] THEN LOOP;
      IF (value ← SetF[pageSource, valueVacant]).flags=flagsVacant THEN LOOP;
      value.flags.writeProtected ← FALSE;
      Assoc[pageDest, value];  -- put back in the map 
      WHILE ((pageDest ← pageDest + 1) IN [pageGerm..pageAfterGerm)
             OR pageDest = 1 OR pageDest IN [376B..377B]) DO ENDLOOP;
      ENDLOOP;
    END;

  -- Backstop Create routine to plug end of chain of BootChannel interfaces.
  Create: PUBLIC PROC [
    POINTER TO Boot.Location, BootChannel.Operation,
    LONG DESCRIPTOR FOR ARRAY OF WORD]
    RETURNS [handle: BootChannel.Handle] =
    {Error[cGermDeviceError]; -- nobody implements this type of device!--};

  -- Find length of run of virtual pages all mapped to real pages
  GetCountRunMapped: PROC [pageStarting: PageNumber]
    RETURNS [countReal: PageCount] =
    BEGIN
    FOR countReal ← 0, countReal + 1 DO
      IF PageMap.GetF[pageStarting + countReal].flags=flagsVacant THEN EXIT
      ENDLOOP
    END;

  GetPageMDS: PROC RETURNS [PageNumber] = {RETURN[Boot.ReadMDS[]*maxPagesInMDS]; };

  IsVacant: PROC [value: Value] RETURNS [BOOLEAN] = INLINE {
    RETURN[value.flags=flagsVacant]};

  -- Virtual memory is from 0 through 0+countVM[]-1, except pages
  -- p for which InGerm[p]=TRUE

  -- The following procedure has been MANUALLY expanded inline, so changes
  -- to it must be propagated
  -- In particular, see code which sets countData in DoOutLoad

  InGerm: PROC [page: PageNumber] RETURNS [BOOLEAN] = INLINE
    BEGIN
    RETURN[page IN [pageGerm..pageAfterGerm) OR page IN [376B..377B]]
    END;

  JumpCall0: PROC [Proc: PROC] = MACHINE CODE
    BEGIN Mopcodes.zSLB, PrincOps.returnOffset; Mopcodes.zRET END;

  JumpCall2: PROC [
    arg1, arg2: UNSPECIFIED, Proc: PROC [UNSPECIFIED, UNSPECIFIED]] =
    LOOPHOLE[JumpCall0];

  PageFromLongPointer: PROC [p: LONG POINTER TO UNSPECIFIED]
    RETURNS [PageNumber] =
    {OPEN l: LOOPHOLE[p, Long]; RETURN[l.highbits*256 + l.lowbits/256]};

  PageFromPointer: PROC [p: POINTER] RETURNS [page: PageNumber] =
    {RETURN[LOOPHOLE[LOOPHOLE[p, CARDINAL]/wordsPerPage + GetPageMDS[]]]};

  PointerFromPage: PROC [page: PageNumber] RETURNS [POINTER] = {
    RETURN[LOOPHOLE[(page - GetPageMDS[])*wordsPerPage]]};

  Start: PUBLIC PROC = { -- plug the start chain--};

  Transfer: PUBLIC PROC [handle: BC.Handle, page: PageNumber, count: PageCount] =
    -- Only public to make compiler happy
    BEGIN
    ProcessorFace.SetMP[cGermDriver];
    BC.Transfer[handle, page, count];
    ProcessorFace.SetMP[cGermAction];
    END;

  ValueAnd: PROC [Value, Value] RETURNS [Value] = LOOPHOLE[Inline.BITAND];

  Error: SIGNAL [code: PilotMP.Code] = CODE;

  --
  -- Simple Mesa runtime
  Halt: PROC = INLINE BEGIN DO ENDLOOP END;

  -- We can't set up handlers to catch Frame, Page, and Write Faults because they require that the ProcessDataArea be already initialized, and it's not until Pilot comes to life.  If the germ is not working, try putting a Midas break at the fault handling code.

  IsBound: PROC [link: UNSPECIFIED] RETURNS [BOOLEAN] = {
    RETURN[link ~= PrincOps.UnboundLink AND link ~= PrincOps.NullLink]};

  ControlFaultHandler: PROC = -- entered via sControlFault
    BEGIN
    state: RECORD [a, b: UNSPECIFIED, v: PrincOps.StateVector];
    state.v ← STATE; -- resets stack pointer
    ProcessorFace.SetMP[cGermControlFault];
    Halt[]
    END;

  InitializeMonitor: PROC [monitor: LONG POINTER TO MONITORLOCK] =
    {LOOPHOLE[monitor, LONG POINTER TO PSB.Monitor]↑ ← PSB.UnlockedEmpty};

  -- THIS DOES NOT HANDLE CONTROL MODULES (Single or Multiple)
  -- Copy code from Traps when/if needed.
  -- entered via sStart KFCB (or call from SwapTrapHandler)

  StartModule: PROC [cm: PrincOps.ControlModule] =
    BEGIN OPEN PrincOps;
    control: GlobalFrameHandle;
    state: StateVector;
    state ← STATE;
    IF ~cm.multiple THEN
      BEGIN OPEN dest: cm.frame;
      IF @dest = NullGlobalFrame OR dest.started OR (control ← dest.global[0]) #
	NullGlobalFrame THEN ERROR Error[cGermStartFault];
      -- IF (control←dest.global[0])#NullGlobalFrame AND ~control.started THEN
      -- StartModule[[frame[control]]];
      IF ~dest.started THEN
	BEGIN
	state.dest ← ControlLink[
	  procedure[gfi: dest.gfi, ep: MainBodyIndex, tag: TRUE]];
	state.source ← Frame.GetReturnLink[];
	dest.started ← TRUE;
	RETURN WITH state
	END
      ELSE IF state.stkptr # 0 THEN ERROR Error[cGermStartFault];
      END
    ELSE ERROR Error[cGermStartFault];
    END;

  SwapTrapHandler: PROC = -- entered via sSwapTrap
    BEGIN OPEN PrincOps, PrincOpsRuntime;
    dest: ControlLink;
    state: StateVector;
    frame: GlobalFrameHandle;
    state ← STATE;
    dest ← Trap.ReadOTP[];
    state.dest ← Frame.GetReturnLink[];
    state.source ← NullLink;
    DO
      SELECT TRUE FROM
	dest.proc => {frame ← GetFrame[GFT[dest.gfi]]; EXIT};
	dest.indirect => dest ← dest.link↑;
	ENDCASE -- frame -- => {frame ← dest.frame.accesslink; EXIT};
      ENDLOOP;
    IF ~frame.started THEN StartModule[[frame[frame]]];
    frame.code.out ← FALSE;
    RETURN WITH state;
    END;

  -- entered via sSignal or sError KFCB
  SignalHandler: PROC [signal: SIGNAL, code: PilotMP.Code] =
    { ProcessorFace.SetMP[code]; Halt[] };


  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- ResidentMemory implementation, for allocating storage in the germ's MDS.
  -- AllocateMDS may be called only during module initialization!
  -- (The configuration of memory must be static during inLoad and outLoad.
  -- Also, spare real memory may not be available after the system is initially loaded)

  residentMemoryLock: PUBLIC MONITORLOCK; -- (PRIVATE in interface)
  allocateMDSInternal: PUBLIC --INTERNAL-- ResidentMemory.AllocateMDSInternal ←
    [AllocateMDSLocal]; -- (PRIVATE in Interface)

  -- Guaranteed not to do an ALLOC from the frame heap.
  -- note the page is writable thanks to CompactVM
  AllocateMDSLocal: PROC [pages: CARDINAL] RETURNS [p: POINTER TO UNSPECIFIED] =
    BEGIN
    p ← PointerFromPage[pageAfterGerm];
    THROUGH [0..pages) DO
      Assoc[
	pageAfterGerm,
	SetF[
	  page: FIRST[PageNumber] + GetCountRunMapped[FIRST[PageNumber]] - 1,
	  flagsNew: valueVacant]];
      -- grab last real page (can only do during an initial boot (inLoadMode=inLoad))
      pageAfterGerm ← pageAfterGerm + 1;
      ENDLOOP;
    END;

  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  testing: BOOLEAN;  -- TRUE if we are running in a test environment on top of Pilot.
  Initialize: PROC = -- Assume called from main body
    -- Assume all SD entries zero
    BEGIN OPEN SDDefs;
    pSD: POINTER TO ARRAY [0..0) OF UNSPECIFIED ← SD;
    state: PrincOps.StateVector;
    mainbody: PrincOps.FrameHandle = Frame.GetReturnFrame[];
    testing ← pageGerm ~= (mdsiGerm*maxPagesInMDS) + countSkip;
      -- TRUE if not loaded in normal place
    pSD[sControlFault] ← ControlFaultHandler;
    pSD[sSwapTrap] ← SwapTrapHandler;
    pSD[sSignal] ← pSD[sError] ← SignalHandler;
    pSD[sStart] ← StartModule;
    ProcessorFace.Start[];
    InitializeMonitor[@residentMemoryLock];
    --check to see if the page following the germ is already mapped.
    --This may be the case with micfocode swapping
    IF PageMap.GetF[pageAfterGerm].flags # PageMap.flagsVacant THEN
      pageAfterGerm ← (pageBuffer ← pageAfterGerm) + 1
    ELSE
      pageBuffer ← PageFromPointer[importedResidentMemory.AllocateMDS[pages: 1]];
    pageGermKeep ← pageAfterGerm;
    -- we keep the buffer page forever, deallocate all else after each request.
    -- We can't deallocate when non-initial-booting operations via the
    --    ether are implemented.
    Heads.Start[];     -- start the heads (face implementations)
    state.instbyte ← state.stkptr ← 0;
    -- Subsequent entries to the germ go directly to Run[]:
    state.dest ←
      Boot.pInitialLink↑ ←
                  LOOPHOLE[Run, PrincOps.ControlLink];
    state.source ← mainbody.returnlink;
    Frame.Free[mainbody];
    RETURN WITH state -- to Run[], never to return to Initialize
    END;

  ProcessorFace.SetMP[cGerm]; -- let them know we are here
  ProcessOperations.WriteWDC[1]; -- temporary hack for Dandelion...delete soon
  Initialize[]; -- never returns

  END.

LOG
For earlier log entries see Amargosa archive version.

April 10, 1980  11:59 AM   Forrest   Implement call to Teledebug.Debug.

April 17, 1980  12:44 AM   Forrest   Get teledebug from Boot not Bootswap

April 17, 1980  10:39 PM   Luniewski   AllocateMDSPages=>AllocateMDS

April 18, 1980  3:58 PM   Knutsen   Export allocateMDSInternal, residentMemoryLock

May 15, 1980  6:18 PM   McJones   Mesa 6; call OISProcessorFace.Start; temporarily initialize WDC

June 10, 1980  9:16 AM   Forrest   PrincOps traps/use physical volume Format

June 23, 1980  2:42 PM   McJones   OISProcessorFace => ProcessorFace

July 20, 1980  9:18 PM   Forrest   PrincOpsRuntime

August 5, 1980  9:17 AM   Forrest   Add test for Page after germ being mapped, use GetFlags

September 9, 1980  5:50 PM   Forrest/McJones   Add cGermFinished

December 10, 1980  9:51 AM   Knutsen   New name for PSB.Monitor.

February 4, 1981  11:37 AM   Knutsen   PrincOps fields changed names.  Fault Notification: deleted Alloc and Page trap handlers.

March 21, 1981  8:10 PM   Taft   Transplant my changes from Mokelumne: deal with asynchronous Transfer I/O.