// BootInit.bcpl - OS initialization
// Copyright Xerox Corporation 1979
// Last modified August 11, 1980  12:34 AM by Boggs

get "AltoFileSys.d"
get "Disks.d"
get "Bfs.d"
get "Streams.d"
get "SysDefs.d"
get "AltoDefs.d"
get "SysInternals.d"

external
[
// outgoing procedures
JuntaReturn; CreateSysDisk

// incoming procedures by Junta level
// levBasic
SwatTrap; SwatInterrupt
CallSwat; ParityInterrupt

// levBFSbase
SetBlock; MoveBlock; Zero
Usc; DoubleAdd
MyFrame; GotoLabel; ReturnFrom
Dvec; SysErrT; RealDiskDA
EnableInterrupts; StartIO

// levStreams
Resets; Gets; Puts; Closes; Endofs
ReadBlock; WriteBlock
FileLength; GetCompleteFa

// levAlloc
Allocate; InitializeZone

// levDirectory
OpenFile; OpenFileFromFp
BFSWriteDiskDescriptor

// levDisplay
CreateDisplayStream; ShowDisplayStream

// levKeyboard
CreateKeyboardStream

// levMain
FinishInitOs

// Install
Install; VerifyInstalled; RestoreLev

// MDI
LookupEntries

// BfsInit
BFSInit

// incoming statics by Junta level
// levBasic
OsVersion; OsVersionCompatible
AltoVersion; SerialNumber
OsFinishCode; juntaTable
SysErr; pTopStatics; LevBasic

// levFilePointers
EnumerateFp
fpSysDir; fpDiskDescriptor
fpSysFont; fpUserCm; fpComCm; fpRemCm
fpSysBoot; fpExecutive
sysFontHeight; sysFontSize; sysDisplayLines

// levBFSbase
lvIdle; Idle

// levBFSwrite
sysDiskZone; diskKd

// levKeyboard
keys; CursorLink

// init
stackRoot; page0; initStackLimit

// outgoing statics
sysZone; lvSysZone
protectedSysZone; lProtectedSysZone
sysFont; dsp
lvCursorLink; lvSysErr
sysDisk; sysMECR
]

static
[
// Some of the above have not really been allocated anywhere  (bug?)
sysZone; lvSysZone
protectedSysZone; lProtectedSysZone
sysFont; dsp
lvCursorLink; lvSysErr
sysDisk
sysMECR = defaultMECR

// private to this module
namv; fpv
]

// O S    I N I T I A L I Z A T I O N
//
// You get to JuntaReturn in two cases:
//
// 1) The first way is when returning during CounterJunta --
//    very little of the machine is guaranteed set up at this point.
//
// 2) The other way you can get to JuntaReturn is during once-only
//    processing when the OS is being initialized from a boot file.
//    The file "StartOS.asm" is loaded just before bootinit and
//    comes here for "once-only" processing.
//
// Note that installation (for disk and ethernet booting) uses
// a very different mechanism, which counts on saving the entire
// machine state on a file.

//---------------------------------------------------------------------------
let JuntaReturn() be
//---------------------------------------------------------------------------
// Be very careful here, because page 0 is not guaranteed set up.
// Until *** do not use Bcpl runtime (Getframe, etc.).
[
GotoLabel(stackRoot-JuntaReturn!2, JuntaReturnAux)

JuntaReturnAux:
stackRoot!0 = stackRoot	//link base frame to itself
@335B = initStackLimit	//use area between levMain.asm and page0.asm

if OsFinishCode eq fcOnceOnly then
   [
   MoveBlock(page0, 300b, 100b)	//Save a copy of Bcpl runtime xfer table
   Zero(400b, 171b); Zero(600b, 200b)  //Zero all but time params
   // Patch first instruction of JuntaReturn so that anyone
   // can jump to it without a stack and get going properly
   JuntaReturn!0 = 404b		//jmp.+4
   ]
MoveBlock(300b, page0, 100b)	//install Bcpl runtime xfer table
//*** OK, you can call procedures now

SysErr = SysErrT  //the "True" SysErr

// Reset all interrupts, etc. to clean state
@displayListHead = 0	//No display
@activeInterrupts = 0
@displayInterrupt = 0
for i = 0 to 16b do interruptVector!i = 0
EnableInterrupts()
@wakeupsWaiting = 0	//Flush pending interrupts

// Get the keyboard going as quickly as possible.
CreateKeyboardStream()

// Do this every time in case it is clobbered or booting sans page 1
maskTable!-1 = 0	//Special end condition.
for i = 0 to 15 do maskTable!i = -1 rshift (15-i)

// JuntaReturn (cont'd)

// Now turn Swat on (assuming for the moment that its FPs are valid.
// This will normally be the case, as there was (we can hope) a Swat and
// Swatee present when the operating system was installed.
SetBlock(trapVector, SwatTrap, 40b)
interruptVector!swatInterruptLevel = SwatInterrupt
@activeInterrupts = @activeInterrupts % swatInterruptBit
@displayInterrupt = @displayInterrupt % swatInterruptBit
 
// ********** OS disk's Swat & Swatee ***********
// These are the FPs (with REAL disk addresses) for Swat and Swatee
//  on the OS Sources disk.  They will have to be changed if the
//  files move.  They are here so that very early during once-only
//  initialization, you can get into swat to set breakpoints to debug
//  the rest of the init code.
if RestoreLev(levBasic) then
   [
   MoveBlock(lv SwatTrap>>SCM.Swatee, table [ 0; 161b; 1; 0; 130170b ], 5)
   MoveBlock(lv SwatTrap>>SCM.Swat, table [ 0; 213b; 1; 0; 130460b ], 5)
   if OsFinishCode eq fcOnceOnly then CallSwat("OS debug")
   ]

// Parity initialization -- done AFTER initializing Swat
//  because parity errors call Swat!
@MESR = 0	//Clear any pending errors
@MECR = sysMECR
interruptVector!parityInterruptLevel = ParityInterrupt
@activeInterrupts = @activeInterrupts % parityInterruptBit

if OsFinishCode eq fcOnceOnly then
   [
   if (lv pTopStatics) ne 176777b then CallSwat("pTopStatics")
   if Usc(LevBasic, 175000B) ls 0 then CallSwat("LevBasic too low")
   pTopStatics = lv EnumerateFp
   lvSysZone = lv sysZone
   lvIdle = lv Idle
   lvCursorLink = lv CursorLink
   lvSysErr = lv SysErr
   OsVersion = nOsVersion
   OsVersionCompatible = nOsVersionCompatible
   ]

// Init the cursor
MoveBlock(cursorBitMap, (table [ 100000B; 140000B; 160000B; 170000B;
 174000B; 176000B; 177000B; 170000B; 154000B; 114000B; 006000B; 006000B;
 003000B; 003000B; 001400B; 001400B ]), 16)

// Now we go off to RestOfBootInit re-using this stack frame.
// The stack is set to below OSMain.  This means that we should
// leave a good deal of space below LevMain code when loading.
GotoLabel(stackRoot, RestOfBootInit, stackRoot)
]

//---------------------------------------------------------------------------
and RestOfBootInit(stackRoot) be
//---------------------------------------------------------------------------
// Come here to do most (and subtle) system initialization.
// We can get here either:
//	-- From call above, after CounterJunta or OnceOnly
//	-- From GotoLabel below, after booting.
[
// Turn off display because we may be coming here after booting,
// in which case it is set up.
@displayListHead = 0; for i = 0 to 30000 loop

// Find the serial number and Alto version information.
SerialNumber = StartIO(0) & 377b
AltoVersion = (table [ 61014B; 1401B ])()
@613B = AltoVersion<<VERS.eng gr 1? -1, 0  //used by trident microcode

CreateSysDisk()	//sets sysFontHeight, sysFontSize

// Set up a free storage zone for the OS
let fHeven = (sysFontHeight+1) & -2
let disAreaNeeded = (sysDisplayLines*(lDCB+fHeven*sysDisplayWidthInWords)+1)/2
let lDisZone = sysFontSize + disAreaNeeded + lDS + 12
let disZone = lDisZone
// 2 display streams + 7 disk streams + room to play
lProtectedSysZone = lDS*2 + (lKS+256)*7 + 5*lST + 100
let w = lProtectedSysZone
Dvec(RestOfBootInit, lv disZone, lv w)
disZone = InitializeZone(disZone, lDisZone)
sysZone = InitializeZone(w, lProtectedSysZone)
protectedSysZone = w

// Read in the SysFont and save it in fixed storage.
let f = Allocate(disZone, sysFontSize)
let s = OpenFile("SysFont.al", ksTypeReadOnly, 0, 0, fpSysFont)
ReadBlock(s, f, sysFontSize)
Closes(s)
sysFont = f+2  // sysFont points to "word 2" of the font.

let w = Allocate(disZone, disAreaNeeded)
dsp = CreateDisplayStream(sysDisplayLines, w, disAreaNeeded, 0, 0, 0, disZone)
ShowDisplayStream(dsp, DSalone)

// Now we can really print error messages!
SysErr = SysErrT

// Save place to go on a finish
juntaTable>>JT.FinishAC2 = MyFrame()
juntaTable>>JT.FinishPC = FinishInitOs
juntaTable>>JT.jAtLevel = 0	//No junta yet

// If started cold, ask whether the system should be installed.
// Now is the time to install if the user really wants to.
// Thereafter, whenever the system is booted, we return from Install.
if OsFinishCode eq fcOnceOnly then
   [
   let once = OsFinishCode eq fcOnceOnly
   // Set code to 0 in case user aborts the Install or patches it out
   // (sometimes used for debugging new systems)
   OsFinishCode = 0
   Install(once)  //Booting the OS appears as a return from Install.
   GotoLabel(stackRoot, RestOfBootInit, stackRoot)
   ]

FinishInitOs()	//And run the OS!!!!
]

//---------------------------------------------------------------------------
and CreateSysDisk() be
//---------------------------------------------------------------------------
// Hooks up the OS to the disk currently spinning in DP0.
[
// If starting afresh (Ether boot or initial time only) then
// verify that the system is installed:
if RestoreLev(levBasic) then VerifyInstalled()

// Set up a temporary free storage zone for disking
manifest lTempZone = 450
let v = vec lTempZone
let realSysZone = sysZone
sysZone = InitializeZone(v, lTempZone)

// Get sysDisk set up so the BFS can work.
if RestoreLev(levBFSwrite) then
   [
   sysDiskZone = InitializeZone(sysDiskZone, sysDiskZone!-1)
   sysDisk = BFSInit(sysDiskZone, true, 0, 0, false, sysZone)
   if sysDisk eq 0 then CallSwat("BFSInit failed")
   diskKd = sysDisk>>DSK.diskKd
   MoveBlock(fpSysDir, sysDisk>>DSK.fpSysDir, lFP)
   MoveBlock(fpDiskDescriptor, sysDisk>>DSK.fpDiskDescriptor, lFP)
   ]

if RestoreLev(levFilePointers) then
   [
   // Prepare to do "multiple lookup" in directory
   manifest nNams = 8
   let nams = vec nNams; namv = nams
   nams!0 = "Swat."
   nams!1 = "Swatee."
   nams!2 = "User.Cm."
   nams!3 = "Com.Cm."
   nams!4 = "Rem.Cm."
   nams!5 = "Executive.Run."
   nams!6 = "SysFont.Al."
   nams!7 = "Sys.Boot."
   let dvs = vec nNams*(lDV); fpv = dvs

   // Do a multiple-lookup on all these files. But be careful when reading
   // the directory. Chances are all the files we need are near the
   // beginning, but the end of the directory may be smashed.
   let RetFromBadDirectory() be ReturnFrom(LookupEntries)
   let s = OpenFile("SysDir", ksTypeReadOnly, 0, 0, fpSysDir)
   if s eq 0 then CallSwat("No directory")
   s>>ST.error = RetFromBadDirectory
   LookupEntries(s, namv, fpv, nNams, true)
   Closes(s)

   if RestoreLev(levBasic) then
      [
      GetPage1(0, lv SwatTrap>>SCM.Swat)
      GetPage1(1, lv SwatTrap>>SCM.Swatee)
      ]

   SetFp(2, fpUserCm)
   SetFp(3, fpComCm)
   SetFp(4, fpRemCm)
   SetFp(5, fpExecutive, 5)
   s = SetFp(6, fpSysFont, 7)
     sysFontHeight = Gets(s)
     sysFontSize = FileLength(s)/2
     Closes(s)
     sysDisplayLines = nSysDisplayLines
   SetFp(7, fpSysBoot, 1)
   ]

// If disk descriptor changed (e.g., by making files) write it out.
BFSWriteDiskDescriptor(sysDisk)

// restore the real sysZone
sysZone = realSysZone
]

//---------------------------------------------------------------------------
and GetPage1(n, lvfp) be
//---------------------------------------------------------------------------
// Set up lvfp with a real disk address for page 1.
[
let s = SetFp(n, lvfp, 3)
if s eq 0 then
   [
   // If you fail to find a legal FP, it is essential to leave behind one
   // that will be thoroughly checked by the write operation in OutLd.
   // If you leave a block of zeroes behind, an unsuccessful Swat call
   // will wipe out the boot loader page!!!! (real disk address 0)
   SetBlock(lvfp, 52525b, lFP)
   return
   ]
let cfa = vec lCFA; GetCompleteFa(s, cfa)
RealDiskDA(sysDisk, cfa>>CFA.fa.da, lv lvfp>>FP.leaderVirtualDa) 
Closes(s)
]

//---------------------------------------------------------------------------
and SetFp(n, lvfp, open; numargs na) = valof
//---------------------------------------------------------------------------
// SetFp records an FP found by directory scan, and does other things
// specified by the third argument:
//	0	=> (default) create file if non ex
//	1	=> do not create if non ex
//	2	=> open file and return stream
//	4	=> complain if non-ex
[
if na eq 2 then open = 0
MoveBlock(lvfp, fpv+n*(lDV)+(offset DV.fp/16), lFP)
if lvfp>>FP.version eq 0 then  //file doesn't exist
   [
   if (open & 4) ne 0 then  //bomb
      CallSwat("Essential file missing:", namv!n)
   if (open & 1) eq 0 then  //create
      Closes(OpenFile(namv!n, 0, 0, verLatestCreate, lvfp))
   ]
if (open & 2) ne 0 resultis lvfp>>FP.version eq 0? 0, OpenFile(namv!n,
 ksTypeReadOnly, 0, 0, lvfp)
]