// IfsInitSwap.bcpl -- IFS swappable initialization
// Copyright Xerox Corporation 1979, 1980, 1981, 1982, 1983
// Last modified September 23, 1983  2:49 PM by Taft

get "IfsSystemInfo.decl"
get "IfsRs.decl"
get "IfsDirs.decl"
get "AltoFileSys.d"
get "IfsInit.decl"
get "Pup0.decl"
get "Pup1.decl"
get "Ifs.decl"

external
[
// outgoing procedures
InitIFSPart3; FreeStackEvent

// incoming procedures
MakeFree; OpenIFSPart2; CreateIFSPart2; IFSError; CheckDirectory
InitPupLevel1; PupChecksum; IfsPupChecksum; InitRSMgr; InitXMOverlays
InitFtpServ; InitTelnet; InitMail; InitMiscellaneous; InitEventMgr
InitAllocSpy; InitVMemSpy; InitBackup; InitPrintError; InitLeaf; InitPress
InitMemoryError; InitCachedDIF; InitGrapevine; InitGroupName
CreateOFT; DestroyOFT; SetAllocation
OpenVFile; IFSOpenFile; PositionPage; Closes; MoveBlock; SetBlock; Usc
CallersFrame; AddToZone; Zero; ReadCalendar
InitializeContext; Enqueue; ExtractSubstring
CreateEvent; InstallSysParams; FreePointer; VFileReadPage; VFileWritePage

// outgoing statics
infoVMD

// incoming statics
sysZone; bigZone; smallZone; ifsCtxQ; primaryIFS; system
numVMemBufs; numPBIs; lenJobT; dPSIB
CtxRunning; rtpStackSize; isb; monthNames; numOvXMPages
leafPresent; leafEnabled; spyBuffer; maxRetainedFreePages
]

static [ infoVMD = 0 ]

manifest
[
stkLim = 335b
cursorBitMap = 431b
]

//----------------------------------------------------------------------------
let InitIFSPart3() be
//----------------------------------------------------------------------------
// This procedure will be called in as an overlay. It cuts up the resident
// initialization code into vmem buffers, finishes opening the primary file
// system, calls swappable initialization in other modules, and arranges to
// turn the remaining stack into vmem buffers.
// Much of this initialization is very delicate, and it is crucial that
// it be done in the correct order.
[

// Set up dummy context in which we pretend to run (for access checks, etc.)
let saveCtxRunning = CtxRunning
let v = vec lenRSCtx; CtxRunning = v
Zero(v, lenRSCtx)
CtxRunning>>RSCtx.userInfo = system

// Finish opening (or creating) primary file system.
// I hope we have enough VMem buffers to do this now!
if isb>>ISB.creatingPrimary then CreateIFSPart2(primaryIFS)
let ec = nil
unless OpenIFSPart2(primaryIFS, lv ec) do IFSError(ec)

// Verify that the directory is well-formed
if isb>>ISB.verifyTree then CheckDirectory(primaryIFS)

// Open the <System>Info file
OpenSystemInfo()

// Now, at last, we can decide how to carve up the remaining
// resident initialization, move code into XM, etc., which is not possible
// until we can look at SysParam.enableLeaf in the Info file.
leafEnabled = VFileReadPage(infoVMD, spPage)>>SysParams.enableLeaf
if leafEnabled then leafPresent = true  // can also be forced by /L switch

// Prepare to discard leaf resident code if Leaf is not to be present
unless leafPresent do
   [
   let lenLeafResident = isb>>ISB.pcI - isb>>ISB.pcL
   isb>>ISB.pcI = isb>>ISB.pcL
   isb>>ISB.initStart = isb>>ISB.initStart - lenLeafResident
   if spyBuffer ne 0 then spyBuffer = spyBuffer - lenLeafResident
   ]

// InitIFSPart3 (cont'd)

// Attempt to load overlays into extended memory.
// If this is successful it will consume some of the storage
// formerly occupied by initialization code, updating isb>>ISB.initStart.
InitXMOverlays()

// Determine how many VMem buffers are potentially available, and let
// InitRSMgr decide how many jobs can be supported and how much
// additional space must be given up for zones and PBIs.
let potentialVMemBufs = numVMemBufs +
 (isb>>ISB.bigZoneBot - isb>>ISB.initStart) rshift logStdPageLength
if isb>>ISB.residentCodeXM then
   potentialVMemBufs = potentialVMemBufs - 1 + // -1 for breakage
    (isb>>ISB.pcI - isb>>ISB.pcX) rshift logStdPageLength
InitEventMgr()
InitRSMgr(potentialVMemBufs)  // must follow InitEventMgr -- calls CreateEvent

// Turn additional space over to the zones
if isb>>ISB.smallZoneIncr ne 0 then
   [
   AddToZone(smallZone, isb>>ISB.initStart, isb>>ISB.smallZoneIncr)
   isb>>ISB.initStart = isb>>ISB.initStart + isb>>ISB.smallZoneIncr
   ]
if isb>>ISB.bigZoneIncr ne 0 then
   [
   // round up to next page boundary, since scraps will go to bigZone anyway
   let newFree = ((isb>>ISB.initStart + isb>>ISB.bigZoneIncr) %
    (1 lshift logStdPageLength -1)) +1
   AddToZone(bigZone, isb>>ISB.initStart, newFree - isb>>ISB.initStart)
   isb>>ISB.initStart = newFree
   ]

// Turn the remaining initialization code into vMem buffers.
MakeFree(bigZone, isb>>ISB.initStart, isb>>ISB.bigZoneBot-isb>>ISB.initStart)

// If we moved resident code into XM, turn it into free storage as well.
// Must do this after the preceding MakeFree to avoid the AddToZone bug.
if isb>>ISB.residentCodeXM then
   MakeFree(bigZone, isb>>ISB.pcX, isb>>ISB.pcI - isb>>ISB.pcX)

// Conditionally permit IFSFree to retain freed 1-page buffers
maxRetainedFreePages = isb>>ISB.residentCodeXM? 5,
 numOvXMPages ne 0? 2, 0

// Initialize Pup package, with routing table big enough to cope with
// simultaneous connections from many networks.
InitPupLevel1(sysZone, ifsCtxQ, numPBIs, 0, 30)
PupChecksum = IfsPupChecksum  //install microcoded checksum routine

// Limit allocation of rendezvous and transient unregistered sockets.
// (Registered sockets' allocations are managed by DistributedPBIs.)
SetAllocation(dPSIB - offset PupSoc.psib/16, 6, 5, 5)

// Extra RTP stack space needed to handle overlay faults.
// Even more space needed with Extended Emulator, since RTPFSM references
// a large table.
rtpStackSize = (numOvXMPages gr 0? 300, 180)

// InitIFSPart3 (cont'd)

// Execute all other swappable initialization
InitTimeIO()
InitAllocSpy()
InitVMemSpy()
InitTelnet()
InitMiscellaneous()	// must follow OpenSystemInfo -- uses infoVMD
InitPress()		// must follow OpenSystemInfo -- uses infoVMD
InitBackup()		// must follow OpenSystemInfo -- uses infoVMD
InitFtpServ()
InitMail()		// must follow OpenSystemInfo and InitFtpServ
if leafPresent then
   [
   InitLeaf()
   // primaryIFS must have a bigger OFT if Leaf is present.
   // n.b. this depends on there being no open files at this time!
   unless DestroyOFT(primaryIFS) do IFSError(ecCantDestroyOFT)
   CreateOFT(primaryIFS, 128)  // must be power of 2
   ]
InstallSysParams(true)	// must follow all of the Init routines above
InitGrapevine(sysZone)
InitPrintError()
InitMemoryError()
InitCachedDIF()
InitGroupName()

// Record starting time and Ifs.run creation time
let sp = VFileWritePage(infoVMD, spPage)
ReadCalendar(lv sp>>SysParams.ifsStartTime)
MoveBlock(lv sp>>SysParams.runFileCreated, lv isb>>ISB.runFileCreated, 2)

// Create an event to free up all of the remaining stack.
isb>>ISB.stkBase = @stkLim
isb>>ISB.stkLength = CallersFrame()-isb>>ISB.stkBase-2
CreateEvent(FreeStackEvent)
@stkLim = CallersFrame()-1
CtxRunning = saveCtxRunning

// Change cursor to 'IFS'
MoveBlock(cursorBitMap, table
   [
   0; 0; 0; 0; 71706b; 21011b; 21010b; 21606b
   21001b; 21011b; 71006b; 0; 77777b; 0; 0; 0
   ], 16)
]

//----------------------------------------------------------------------------
and FreeStackEvent(ecb) be
//----------------------------------------------------------------------------
[
MakeFree(bigZone, isb>>ISB.stkBase, isb>>ISB.stkLength)
FreePointer(lv ecb, lv isb>>ISB.xepTable, lv isb)
]

//---------------------------------------------------------------------------
and OpenSystemInfo() be
//---------------------------------------------------------------------------
[
let name = "<System>Info!1"
infoVMD = OpenVFile(name, systemInfoPages)
if infoVMD eq 0 then
   [
   let str = IFSOpenFile(name, 0, modeWrite)
   if str eq 0 then IFSError(ecCreateEssentialFile)
   PositionPage(str, systemInfoPages)
   Closes(str)
   infoVMD = OpenVFile(name, systemInfoPages)
   if infoVMD eq 0 then IFSError(ecTridentFile, name)
   ]
]

//---------------------------------------------------------------------------
and InitTimeIO() be  // make legal constant string for modified IfsTimeIO
//---------------------------------------------------------------------------
monthNames = ExtractSubstring("x*007January*000*000*010February*000*005March*000*000*000*000*005April*000*000*000*000*003May*000*000*000*000*000*000*004June*000*000*000*000*000*004July*000*000*000*000*000*006August*000*000*000*011September*007October*000*000*010November*000*010December*000") +1