// SpruceInit.Bcpl Spruce Spooler Initialization
// errors 200

// get "Spruce.d"
get "SpruceInLdOutLd.d"
get "SprucePrinters.d"
get "SpruceDoc.d"
get "SpruceMisc.d"
get "SpruceFiles.d"
get "AltoFileSys.d"

external // defined here
[
SpruceInit
]

external // external
[
//Spruce
Sprouller
Spruce
// SpruceUtils
SpruceUserFinishProc
SpruceCondition
FSGetX
FSInit
FillInNames
InitRam
SetRMR

// Spruce Files
InitSpruceFile
ResetSpruceFile
CreateSpruceFile

// User
Post
InitUser

//INSTALL
SpruceInstall
SetupDrive

// CheckPoints
CheckPoint
OpenCheckPointFile
RestoreFromCheckPoint
InitCheckZone

ActOnEntry

//Used for initialization of files, etc. -- removed by Junta
Junta
MyFrame

//Spooler, Queue
AddToQueue
CleanupQueue
InitSpool

// Spruce Ml
MulDiv; InitFrameRuntime; SetupFrameFinishProc

//ALLOC
AddToZone

//GP
ReadParam
SetupReadParam

// SpruceInUtil
CreateFPRD
StatusOf31s
MakeValidSpruceFile
GetLogoInfo

//SpruceUtils
Reclaim

//OS
MoveBlock
Zero
lvUserFinishProc
lvSwatContextProc
sysDisk
InLd
OutLd
CallSwat
InitializeZone
CreateDisplayStream
Closes
OpenFile
ReadCalendar
ShowDisplayStream
EnableInterrupts
DisableInterrupts
StartIO

//TFS
TFSInit
TFSSetDisk
TFSSwatContextProc

//FLOAT
FLDI; FML; FTR

// Context
InitializeContext

// Queue
Enqueue
Dequeue

//Template
PutTemplate

// KBD
KBDinit

//LoadRAM
LoadRam

// statics
DoFileMeter
DoMeter
Debug
DebugSystem
Verbose
Which
DoEtherReport
xmFonts
UseMicroCode

Version
MinorVersion
SprintVersion
Report
PressServer // true or false (stand-alone)

tridentDisk
tridentDrive
tridentUsed
statusOf31s
drive1Disk

SpruceZone
CpZone
sysZone
sysFont
lvSysZone
dsp; firstDCB//firstDCB defined in Spruce.bcpl
keys
OverlayBottom
OverlayTop
PermanentBottom

SpruceSavedUFP
savedSCP
ctxQ
UserName
inMsg; outMsg
sprintFPRD
spruceFPRD
timeUp

BandFile
CheckPointFile
RunFile
SpoolFile
QueueFile
PressFile
// LogFile
ErrorFile
printDoc
checkZoneVec
printing; spooling
reasonVec
numFilesSpooled
SproullerQ
pressFileIndex
seed
SpoolVec
LocalDocsVec

BinCounters; NumBins
printerDevice
Capabilities
PRamImage
ResolutionB
ResolutionS
PaperDimensionB
PaperDimensionS
nVisibleBands
FA
]

// Imported from sysdefs.d (no room)

manifest [
levBFSbase = 5
lInLdMessage = 19
]

// File-wide structure and manifest declarations.

static [
MAINStackSize = 2200 // big enough on 1-3-78
InstallStackSize = 2000
BASEStackSize = 700 // initial post-junta stack, big enough September 18, 1978
// ~~ cld be released somehow after use
numLocalDocs
]

// ------------------------------------------------------
let SpruceInit(userParams, runCfa) = valof
// ------------------------------------------------------

//
Spruce initialization is done in two major phases: pre- and post-junta. In the early phase,
// perform activities requiring the doomed OS components. Install if required (see SpruceInstall()),
// initialize memory structures, including the control RAM, prepare the checkpoint file (much of this
// done in SpruceInstall()), check consistency with the current printing (Sprint) system.
//
Then parse the command line for files to print, printer commands to perform, and installation
// parameters to override (most of this is vestigial in the server version).
//
Finally, perform computations based on the installation parameters, checkpoint the derived values
// for preservation over the junta, and perform same. sysDisk handling is eccentric because it must be
// available to reaccess the checkpoint file. Most of the hair is due to the memory discontinuities
// associated with the junta.

[
PutTemplate(dsp, "Spruce Server $D.$D*N", Version, MinorVersion)

//See if we are installing.
//Install if CheckPoint file version statics are zero, or /i switch specified.
let doInstall=false
for i=1 to 10 switchon CAPS(userParams!i) into // global switches repeated here
[
case $I: case $i: doInstall=true
case 0: break
]

// load RAM, set up checkpoint file, perhaps install (then no return)
SpruceInstall(doInstall, runCfa)

let initEnd = OverlayTop
OverlayTop = SpruceInstall
// Toss out installation code
AddToZone(SpruceZone, OverlayTop, initEnd-OverlayTop)
FSInit()
SpruceSavedUFP=@lvUserFinishProc
@lvUserFinishProc=SpruceUserFinishProc // restore tasks to ROM
SetupFrameFinishProc()
savedSCP = @lvSwatContextProc
@lvSwatContextProc = TFSSwatContextProc
unless SprintVersion do
[
PutTemplate(dsp, "Sprint must be installed before Spruce")
finish
]
unless Version eq SprintVersion do
[
let str = Version>SprintVersion? "Sprint", "Spruce"
PutTemplate(dsp, "$S.Run is too old; please fetch new version and reinstall*N", str)
finish
]

// Obtain important statics as modified during installation
RestoreFromCheckPoint(LEVSharedInstStatics, LEVInstallationStatics)

sprintFPRD = CreateFPRD("Sprint.Boot", false)
unless sprintFPRD do
[ PutTemplate(dsp, "Sprint.Boot not found; please install Sprint and restart*N"); finish ]

// Remaining global switches, used to override installation defaults.
let str=vec 20
let sw=vec 10
SetupReadParam(str, sw)
let deb = DebugSystem
// Don’t use unless /D
DebugSystem = 0
for i=1 to sw!0 do switchon CAPS(sw!i) into
[
case $D: DebugSystem = deb; endcase
case $M: DoMeter= not DoMeter; endcase
case $R: pressFileIndex = 0; endcase
case $U: UseMicroCode= not UseMicroCode; endcase
case $V: Verbose= not Verbose; endcase
default: endcase
]

// SPruceFile structs for local docs will be stored in Spruce.Queue
// To make the right decision about where to put the structs, we
// must restore Spoolvec, LocalDocsvec, and pressfileIndex

RestoreFromCheckPoint(LEVEternalStatics, LEVEternalStatics)

// LocalDocsVec will contain QueueFile page #’s
// unless LocalDocsVec do LocalDocsVec = FSGetX(10, SpruceZone, 0)
LocalDocsVec = FSGetX(10, SpruceZone, 0)
unless SpoolVec do SpoolVec = FSGetX(maxSpooled+1, SpruceZone, 0)

//Set up some defaults before processing local switches:
let x = vec maxBins; BinCounters = x; Zero(x, maxBins)
let pDoc= FSGetX(size DocG/16, SpruceZone, 0)
let length = 0
//
Zero(pDoc, size DocG/16)
PressFile=0
PressServer, spooling, printing = false, false, true
// MoveBlock(lv pDoc>>DocG.CreatStr, UserName, size DocG.CreatStr/16)

// ------- SpruceInit . . .

//local switches: verb-object and noun-object (keyword-value) pairs
while ReadParam($P,-1) ne -1 do
[
//get the keyword
let j=Disambiguate(str); unless j then SpruceCondition(201, ECComTerminate, str)
//...and get its value
let num=0; if j ls 20 then
[ num = ReadParam($D, -1); if num eq -1 then SpruceCondition(202, ECComTerminate) ]
switchon j into
[
case 1: pDoc>>DocG.nCopies=num; endcase
case 2: pDoc>>DocG.UserPageStart=num; //falls through
case 3: pDoc>>DocG.UserPageEnd=num; endcase
case 4: ResolutionB = num*10; ResolutionS=ResolutionB; endcase
case 7: pDoc>>DocG.ScaleOffset=num; endcase
case 8: DebugSystem = num; endcase
case 20: // Find the PRESS file, create corresponding SpruceFile structure:
if ReadParam($P, -1) eq -1 then SpruceCondition(207, ECComTerminate)
MoveBlock(lv pDoc>>DocG.FileStr, str, size DocG.FileStr/16)
MakeValidSpruceFile(lv pDoc>>DocG.PressFile, str, -1, DISK31)
unless pDoc>>DocG.PressFile do SpruceCondition(200, ECComTerminate, str)
MoveBlock(lv pDoc>>DocG.CreatStr, UserName, size DocG.CreatStr/16)
InitSpruceFile(pDoc>>DocG.PressFile, 1, 3)
FillInNames(0, pDoc, pDoc>>DocG.PressFile)
ResetSpruceFile(pDoc>>DocG.PressFile)
//inc. number of files processed
pressFileIndex = pressFileIndex + 1
pDoc>>DocG.PressFile>>SPruceFile.fileCode = pressFileIndex
printDoc = pDoc
LocalDocsVec!0 = (LocalDocsVec!0) + 1//inc. length of vector
length = LocalDocsVec!0
LocalDocsVec!length = pressFileIndex
ActOnEntry(LocalDocsVec!length, false)
// get another DocG
pDoc = FSGetX(size DocG/16, SpruceZone, 0)
endcase
case 100: j = j+21//directive ← 100
case 21: case 22: case 23: PressFile = -1; pDoc>>DocG.printerDirective = j-21; endcase
case 24: PressServer, spooling = true, true; endcase
case 25: Restart()
case 26: xmFonts = true; endcase
case 27: case 28: if (printerDevice eq printerPenguin) do
[ (lv Capabilities)>>Capabilities.cDuplex = j-27
pDoc>>DocG.duplex = j-27 ]; endcase
case 29: case 30: case 31: if (printerDevice eq printerPenguin) do
(lv Capabilities)>>Capabilities.cOutputMode = j-29; endcase
case 35: j = 36
case 32: case 33: case 34: (lv Capabilities)>>Capabilities.cOverflowProtect = j-32; endcase
case 36: case 37: if (printerDevice eq printerPuffin) do
(lv Capabilities)>>Capabilities.cBlack = j - 36; endcase
]
]
Debug = (DebugSystem() ne 0
if Debug then [ DoFileMeter=true; DoEtherReport=false ]
// pDoc>>DocG.PressFile = PressFile

// Compute information for image-generation (set nVisibleBands, FA)
let nBitsPerScan = MulDiv(ResolutionB, PaperDimensionB, 100)
nBitsPerScan=(nBitsPerScan+63)&(-64) // Make multiple of 64
if nBitsPerScan > 4096 then
[
SpruceCondition(205, ECContinue) // Warning only -- may be using non-standard
nBitsPerScan = 4096
]// resolution
FA = (4096-nBitsPerScan)/16 // Given short scan-lines, some offset in Orbit buffer is reqd.

// Following computes number of bands on visible part of page.
let nScanLines=MulDiv(ResolutionS, PaperDimensionS, 100)
nVisibleBands=(nScanLines+BANDWidth-1)/BANDWidth

GetLogoInfo() //sets LogoFont and LogoText statics

seed = FSGetX(2, SpruceZone, 0)
ReadCalendar(seed)
//initialize seed

// Save installation statics, as modified, and initialization statics
// printDoc=pDoc // save command line document descriptor over junta
// temporary -- To save ’new’ state of the queuefile, LEVEternalStatics
CheckPoint(LEVSharedInitStatics, LEVInitialStatics)
CheckPoint(LEVEternalStatics, LEVEternalStatics)
//Get rid of the operating system:
Junta(levBFSbase, SpruceInitAux) // ~~ consider doing less damage
]

// ------------------------------------------------------
and SpruceInitAux() be
// ------------------------------------------------------
//
Called after Junta -- nothing exists except saved sysDisk, the code, and the OS levels that are
// left. The hair at the beginning and end are to recover memory structure and global information after
// the junta, and to prepare memory again after releasing the initialization code to free storage. After file
// structures are retrieved or whenever free storage is rebuilt, files must be reinitialized (see
// SpruceFiles). There are three main jobs here:
//
First, create a reasonable memory structure, with an adequate display bit map.
//
Then, save the initialized system on Spruce.Boot via OutLd. Subsequent returns to the spooler from
// the Sprint printing system will resume this image. Spruce.Boot is never altered after this
// initialization. Spruce determines the difference between the initial state and printer returns by
// examining the inMsg, initially cleared (see InLd/OutLd documentation, Sprouller() in Spruce,
// SwapSystem() in SpruceUtils (Sprint only)).
//
Finally, create contexts for the spooler, a user interface, and the Pup package, initialize running
// structures, eliminate the initialization code, and call the main Spruce() context-driver routine.
[
//Prepare memory for Restore from Checkpoint
let PermanentTop=MyFrame()-BASEStackSize // 700 was enough on September 18, 1978
@#335=PermanentTop
PermanentBottom=PermanentTop
compileif DebugSw then [ Zero(PermanentTop, BASEStackSize-5) ]
//Get all the goodies back, this time permanently -- OpenCheckPointFile creates sysDisk!
sysDisk = 0; OpenCheckPointFile(0) // restore structure, but file must already exist and be full
RestoreFromCheckPoint(LEVSharedInitStatics, LEVInitialStatics)
RestoreFromCheckPoint(LEVEternalStatics, LEVEternalStatics)
CheckPoint(LEVInitialStatics+1, maxLEV, 1)// clear all operating levels
// Create other disk structures, if used
drive1Disk = 0; if statusOf31s eq 1 then SetupDrive(DISK31B, SpruceZone)
tridentDisk = 0; if tridentUsed then SetupDrive(DISKT80, SpruceZone)
// Now have a decent storage zone; restore terminal communications
sysZone, @lvSysZone = SpruceZone, SpruceZone
dsp = CreateDisplayStream(30, FSGetX(13000), 13000, sysFont)
ShowDisplayStream(dsp,DSalone); PutTemplate(dsp,"*N*N")
keys = KBDinit(SpruceZone)
@#421 = @#421 % #10000
// tie kbd handler to vetical interrupt
timeUp = FSGetX(2); ReadCalendar(timeUp)
reasonVec = FSGetX(40); reasonVec!0 = 0
inMsg = FSGetX(lInLdMessage+5, SpruceZone, 0)
DisableInterrupts()
firstDCB = @#420; @#420 = 0 // turn display off
// ------------------------------------------------------
let inLd = OutLd(spruceFPRD, inMsg)
// won’t alter inMsg first time
resumeSpruce:
// Control returns here from Printer
// ------------------------------------------------------
@#420 = firstDCB
EnableInterrupts()
InitFrameRuntime(true) // use fast GetFrame() and return
Which = lv spruceFPRD
if inLd ne 0 & (DebugSystemƐ) ne 0 then CallSwat("Spooler: Return from InLd")
InitRam(0)
// Restart tasks (precaution)

// Enter the Context world, initialize main, spooler, user contexts
ctxQ = FSGetX(2, SpruceZone, 0)
Enqueue(ctxQ, // Spooler runs in main task -- schedule it
InitializeContext(FSGetX(MAINStackSize), MAINStackSize, Sprouller))
InitSpool(ctxQ); InitUser(ctxQ) // initialize and enqueue user interface task
// let printFile = printDoc>>DocG.PressFile
printDoc = 0
numFilesSpooled = 0
unless LocalDocsVec!0 eq 0 % inMsg>>TOSpoolerMsg.completionCode do
[
let numToQueue = 1
while numToQueue le LocalDocsVec!0 do [
// should extract fileIndex from LocalDocsVec
ActOnEntry(LocalDocsVec!numToQueue, true)//read into core
//
SpoolVec!0 = (SpoolVec!0) + 1
//
let length = SpoolVec!0
//
SpoolVec!length = LocalDocsVec!numToQueue
Post(printDoc, 0)
AddToQueue(printDoc)
numFilesSpooled = numFilesSpooled + 1
numToQueue = numToQueue + 1
let printFile = printDoc>>DocG.PressFile
//
if printFile ne -1 then
//[ InitSpruceFile(printFile, 1, 3); FillInNames(0, printDoc, printFile);
//ResetSpruceFile(printFile) ]
]
]
// save new state of Spoolvec
CheckPoint(LEVEternalStatics, LEVEternalStatics)
// Throw away initialization code (including this!)
let initEnd = OverlayTop
OverlayBottom=SpruceInit+1
OverlayTop=OverlayBottom
AddToZone(SpruceZone, OverlayTop, initEnd-OverlayTop)

Spruce(printDoc)
//And run the spooler!
]

// ------------------------------------------------------
and Disambiguate(str) = valof
// ------------------------------------------------------

//
Obtain index for command, if any, that is uniquely specified by substring str.
[
let len=str>>STR.length
let matchNo=nil
let matchCnt=0
for i=1 to 100 do
[
let s=selecton i into
[
case 1: "Copies"
case 2: "Pages"
case 3: "To"
case 4: "Resolution"
case 5: "XOffset"
case 6: "YOffset"
case 7: "Scale"
case 8: "Debug"
case 20: "Print"
case 21: "Reprint"
case 22: "PowerOn"
case 23: "PowerOff"
case 24: "Server"
case 25: "Restart"
case 26: "Extended"//use XM, bank 1 for fonts
case 27: "OneSided"// penguin option
case 28: "TwoSided"// penguin option
case 29: "SingleBin"// default for all but Penguin
case 30: "MailBox"// penguin option
case 31: "MultiBin"// penguin option
case 32: "NoProtection"//overflow protection
case 33: "JobProtection"//overflow protection
case 34: "ContinuousProtection"//overflow protection
case 35: "StopAfterJob"//overflow protection
case 36: "NoBlack"// Puffin option
case 37: "Black"// Puffin option
//case 100: "ValidateFonts" //check fonts for landmines that would break Spruce
default: 0
]
if s eq 0 % len gr s>>STR.length then loop
let match=true
for j=1 to len do
if ((str>>STR.char↑j xor s>>STR.char↑j)&(not #40)) ne 0 then
match=false
if match then
[
matchCnt=matchCnt+1
matchNo=i
]
]
if matchCnt eq 1 then resultis matchNo
resultis false
]

// ------------------------------------------------------
and Restart() be // Resume previous Spooler incarnation, assuming no pending files have been completed
// ------------------------------------------------------
[
outMsg = FSGetX(lInLdMessage+5, SpruceZone, 0)
outMsg>>TOSpoolerMsg.completionCode = CCRestart
@#420 = 0; SetRMR(#177776); StartIO(#100000) // Shut display, tasks down
DisableInterrupts()
InLd(spruceFPRD, outMsg)
]

// ------------------------------------------------------
and CAPS(char) = $a le char & char le $z? char-$a+$A, char // upper case only
// ------------------------------------------------------

// ------- History . . .

// DCS, July 15, 1977 3:52 PM, derived from SpruceInit
// July 16, 1977 3:19 PM, add Spooler initialization
// July 18, 1977 4:55 PM, Much smaller main stack frame
// July 19, 1977 2:39 PM, save and init fonts, displays, keyboards
// July 21, 1977 3:38 PM, extend checkpoint levels, implement shared static version
// July 22, 1977 4:03 PM, add OutLd
// July 29, 1977 3:16 PM, handle stand-alone activities better
// August 28, 1977 7:59 AM, Spruce->Sprint, Sprouller->Spruce
// October 10, 1977 2:51 PM, add "Version" static, can be set from other programs
// October 17, 1977 11:52 AM, move XOffset, Y"" to statics from DocG, version 4.(0,0)
// December 20, 1977 12:09 PM, make Trident code work
// January 2, 1978 8:42 AM, make it work better
// March 3, 1978 7:16 AM, V6.0, OS v14 (new time standard)
// August 31, 1978 10:53 AM, allow "Spruce Server Print foo"
// September 11, 1978 2:47 PM, revision for new installation, Spruce file creation, checkpointing
// September 12, 1978 6:51 AM, increase BASEStackSize
// September 18, 1978 10:42 AM, obtain sprintFPRD here if not avail. already
// September 18, 1978 3:20 PM, cap switches
// September 19, 1978 8:38 AM, format, document
// September 22, 1978 9:41 PM, add Restart option
// October 16, 1978 9:12 AM, modify buffering for fast files
// October 20, 1978 4:30 PM, Enable fast GetFrame, return
// March 7, 1979 11:55 AM add global B switch to request 4-color puffin (TEMPORARY!)
// May 8, 1979 11:20 AM add ValidateFonts, Extended (set xmFonts)
// June 26, 1979 4:21 PM, remove global B switch. Add options to change Capabilities
// August 29, 1979 11:37 AM, initialize BinCounters
// November 15, 1979 3:17 PM, call GetLogoInfo
// January 18, 1980 12:45 PM, ’twosided’ sets cDuplex
// March 4, 1980 9:41 AM, use static firstDCB to prevent loss of display stream
// May 28, 1980, 4:04 PM, os18 lost my kbd interrupt
// June 23, 1980, 4:18 PM, make "OneSided" and "TwoSided" set DocG.duplex
// September 25, 1982 4:14 PM Modified SpruceInit() to save/restore local
// document info.
//
October 25, 1982 2:08 PM added LEVEternalStatics checkpointing calls.