// SpruceUtilsRes.Bcpl -- Resident Utilities
// Errors 100 (old Spruce), 2500

get "SpruceFiles.D"
//get "Spruce.D"
	get "SpruceInLdOutLd.d"		// InLd/OutLd messages for spooler <-> service communication
//	get "SprucePrinters.d"
	get "SpruceDoc.d"
//	get "SpruceFont.d"
//	get "SpruceBand.d"
	get "spruceMisc.d"
get "AltofileSys.D"
get "PressFile.D"

compileif newname SpruceSw then [ manifest [ SpruceSw = true ] ]
manifest SprintSw = not SpruceSw

// defined here
external
	[
	EarOn		// sets up Ether "Ear"
	LoadBase
	InitRam
	IsOverlayPresent
	Max
	Min
	Overlay
	SetRMR
	SprintUserFinishProc
	SwapOnSpoolRequest
	SwappedOut
	SwapSystem
	Umax
	Umin
	]

// incoming procedures
external
	[
  // OS
	CallSwat
	DisableInterrupts
	EnableInterrupts
	GotoLabel
	InLd
	MoveBlock
	OutLd
	StartIO
	Zero

// Sprint
	RestartSprint

// SpruceError
	SpruceCondition
	SpruceError
	Scream
	SpruceTrap

  // SpruceMl
	Usc; InitFrameRuntime

  // SpruceEarMl
	PollEther
	ResetEther

  // ISF
	IndexedPageIO

  // LoadRam
	LoadRam

// SpruceLoadOrbitMC
	LoadOrbitMC
	]

// Incoming statics

external
	[
	canContinue
	Capabilities
	CurRMR
	Debug
	DebugSystem
	didContinue
	earMask
	errorPending
	inMsg
	knockResult		// if on, somebody knocked
	lvUserFinishProc
	lvSwatContextProc
	numActiveOverlays
	numMustPrint
	numPrinted
	numFilesSpooled
	outMsg
	overLayout
	OverlayTable
	OverlayTop
	printDoc
	restartFrame
	RunFile
	savedSCP
	sprintFPRD
	spruceFPRD
	SprintSavedUFP
	stopsPrinting
	tickCount
	tickCount0
	tickMask
	tridentDisk
	tridentDrive
	tridentUsed
	Which
	xmFonts
	]

manifest verticalInterval = #421
manifest DiskCommand = #521
manifest [
	EmulatorBankControlReg = #177740 // 177740+0, Emulator is task 0
	OrbitBankControlReg = #177741 // 177740+1, Orbit is task 1
	XMFontsSetting = 1 // 00, 01 are normal and alternate banks
	]

// Procedures

let Umax(a,b) = Usc(a,b) > 0? a, b
and Umin(a,b) = Usc(a,b) < 0? a, b

and SwappedOut() be SpruceError(105)

and InitRam(adr, rmr; numargs na) be
	[
//	compileif SpruceSw then [
		if adr ne 0 & LoadRam(adr, false) ne 0 then SpruceError(210)
//		]
	if na > 1 then CurRMR = rmr
	SetRMR(CurRMR)	// Enable emulator, (orbit), T80 tasks
	StartIO(#100000)	// Get back in
	// Boot will reinitialize system -- to suppress, must set RMR at point of suppression
	compileif TridentSw then
		[ if tridentUsed&tridentDisk then StartIO(#40) // restart T80
		]
	compileif SprintSw then
		[ if xmFonts then [ @OrbitBankControlReg = XMFontsSetting
			@EmulatorBankControlReg = XMFontsSetting ] ]
	]

and SetRMR(x) = ( table [ #61010; #1401 ])(x, #20)

compileif SprintSw then [

let Overlay(overlayNo, overlayLevel, lvInitRoutine; numargs na) = valof
 [
// overlayNo: index into OverlayTable identifying disk location of overlay's code
//		if 0, don't load new overlay
// overlayLevel: omitted or 0: put new overlay at end of overlay space
//		  otherwise, replace overlays [overlayLevel to numActiveOverlays]
// lvInitRoutine: if supplied and non-zero, pointer to static that will contain an
//   initialization routine after the overlay arrives. The overlay routine and all that
//   follows it will be returned to free storage after the routine executes.
// returns overlayLevel of new overlay

// See SprintInstall, SpruceInstall, SpruceInit for overlay initialization code


	if na<2 % overlayLevel eq -1 then overlayLevel = numActiveOverlays+1
	if na<3 then lvInitRoutine = 0
	if overlayLevel le numActiveOverlays &
	   overlayNo eq overLayout>>OVLayout↑overlayLevel.overlayNo then
		[ overlayLevel = overlayLevel+1; overlayNo = 0 ] // just release higher ones

	let oldOverlayTop = OverlayTop

  // If anything is going away, take care of that (reset OverlayTop, mark routines swapped out)

	for level = numActiveOverlays by -1 to overlayLevel do
	  [
	  let ovReloc= overLayout>>OVLayout↑level.bottom
	  OverlayTop = ovReloc
	  ovReloc = ovReloc + ovReloc!3
	  for p=1 to (ovReloc!0)*2 by 2 do @(ovReloc!p)=SwappedOut
	  ]

	numActiveOverlays = overlayLevel-(overlayNo? 0, 1)
	if overlayNo eq 0 resultis overlayLevel	// don't load new
	let bottom = OverlayTop
	overLayout>>OVLayout↑overlayLevel.bottom = bottom
	overLayout>>OVLayout↑overlayLevel.overlayNo = overlayNo

	let pn=OverlayTable!overlayNo
	let npg=OverlayTable!(overlayNo+1)-pn

	IndexedPageIO(RunFile>>SPruceFile.map, pn, OverlayTop, npg, isfRead)

	let ovReloc=OverlayTop+OverlayTop!3
	let nRel=ovReloc!0 *2
	for p=1 to nRel by 2 do
		@(ovReloc!p)=ovReloc!(p+1)+OverlayTop+16

	OverlayTop=OverlayTop+OverlayTop!4

// Call init routine, if any.  Then release its space, relocating reloc table.
// Init routine statics are not subsequently marked "swapped out".
 
	if lvInitRoutine then
		[
		let bot=@lvInitRoutine
		bot() // call the routine
		MoveBlock(bot, ovReloc, nRel+1)
		bottom!3 = bottom!3 - (ovReloc-bot) // reloc table pointer
		OverlayTop=ovReloc+nRel+1
		]

	resultis overlayLevel
 ]

and IsOverlayPresent(overlayNo) = valof
	[
	for i = 1 to numActiveOverlays do
		if overLayout>>OVLayout↑i.overlayNo eq overlayNo resultis i
	resultis false
	]

and LoadBase() be // be sure T80 is in if necessary, Base is in, Init is in
	[
	let iT8 = IsOverlayPresent(OVT80)
	compileif TridentSw then
	    [ if tridentUsed&not iT8 then [ Overlay(OVT80, 1); iT8=1 ] ]
	if IsOverlayPresent(OVBase) ne iT8+1 then Overlay(OVBase, iT8+1)
	Overlay(OVInit, iT8+2, lv LoadOrbitMC) // conditional on non-existence
	]

and SprintUserFinishProc() be
 [
	compileif ReportSw then
	[
	if Report then
	   [
	   GetTime(lv Report>>REP.totalTime)
	   //Send it out over the ether to 3#200#31
	   if DoEtherReport then EventReport(Report, size REP/16, table [ #1600; #0; #31 ] )
	   ]
	]
	@lvSwatContextProc = savedSCP
	if inMsg>>FP.leaderVirtualDa then // called from Spooler, RAM is loaded
		[ SetRMR(#177776); StartIO(#100000) ]  // Silent boot
	@lvUserFinishProc=SprintSavedUFP
 ]

and SwapSystem(canContinue) be
	[
	// First, OutLd
	EarOn(0) // precautionary
	InitFrameRuntime(false) // turn off use of microcoded GetFrame, return (if on)
	// Be sure disk activity is terminated
	    while @DiskCommand do [] // Model 31
	    (@lvSwatContextProc)(0) // Trident
	DisableInterrupts()
	let oldDid = didContinue
	didContinue = canContinue
	// ~~ note using printDoc static here
	let fileCode = printDoc>>DocG.PressFile>>SPruceFile.fileCode
	outMsg>>TOSpoolerMsg.stopsPrinting = stopsPrinting
	let code = oldDid%canContinue? OutLd(sprintFPRD, inMsg), 0
	Which = lv sprintFPRD
	if code then // Return from spooler
		[
		EnableInterrupts()
		InitFrameRuntime(true) // use microcoded frame allocation
		errorPending = 0
		InitRam(0) // Restart RAM tasks
		// continue if possible and requested, else restart
		EarOn(1) // print may limit duty cycle by calling EarOn(2)
		Capabilities = inMsg>>TOPrinterMsg.Capabilities  // might have been changed while we were away
		if inMsg>>TOPrinterMsg.inProgress & canContinue &
		  (inMsg>>TOPrinterMsg.fileCode eq fileCode) then
		    [
		    Zero(outMsg, 18)
		    numPrinted = 0
		    let n = inMsg>>TOPrinterMsg.numSpooled
		    if n then
			[
			numFilesSpooled = n
			numMustPrint = inMsg>>TOPrinterMsg.numMustPrint
			DebugSystem = inMsg>>TOPrinterMsg.DebugSystem
			]
		    return
		    ]
		GotoLabel(restartFrame, RestartSprint)
		]
	// outMsg is set up by caller with description of reason for termination
	unless inMsg>>FP.leaderVirtualDa finish // first time, system now installed
	SetRMR(#177776)
	StartIO(#100000)		// Silent boot -- turn off tasks
	InLd(spruceFPRD, outMsg)	// Bye....
	]

and SwapOnSpoolRequest() be
	[
	// DebugSystem's 20 bit suppresses spooler activation
	if (DebugSystem&#20) ne 0 return // never swap
     // ** knockResult is true if interrupt routines have detected spool request
	if knockResult&numPrinted<numMustPrint then
		knockResult = knockResult+1 // 0 if was -1
	unless knockResult return
	// Spooling status request has been received and replied to, or ESC was down on kbd -- Swap
	DisableInterrupts(); knockResult = false; EnableInterrupts() // ??
	outMsg>>TOSpoolerMsg.inProgressCode =
		printDoc>>DocG.PressFile>>SPruceFile.fileCode
	outMsg>>TOSpoolerMsg.completionCode = CCKnock
	SwapSystem(true)
	]

// Reset ether interface.
// code:
//  0 -- disable Ether and Timer interrupts
//  1 -- enable Ether and Timer interrupts, allow Ether to restart after each interrupt
//  2 -- enable Ether and Timer interrupts, allow Ether to restart only each tickCount
//	clock ticks
// return local host
// tickCount has following properties:
//   <=0: PollEther routine may restart receiver after processing possible packet
//   =0: Ticker will call PollEther, which will then be allowed to restart receiveru;
//	Ticker will reset PollEther to small positive value limiting polling duty cycle
//   >0: Ticker will not call PollEther, but will count down tickCount

and EarOn(code) = valof
	[
	DisableInterrupts()
	let res = ResetEther(code? earMask, 0) // ether will cause/not cause interrupts on its channel
	knockResult = false
	tickCount = 0
	if code then PollEther() // turn receiver on
	switchon code into
	  [
	  case 0:
		@verticalInterval = @verticalInterval & (-1 xor tickMask) // decouple Ticker
		endcase
	  case 2: tickCount = tickCount0 // enables Ticker control of ether receiver -- fall thru
	  case 1:
		@verticalInterval = @verticalInterval % tickMask
		endcase
	  default: CallSwat("Repair EarOn call")
	  ]
	EnableInterrupts()
	resultis res	// return host
	]

and Max(a,b) = a>b? a, b
and Min(a,b) = a<b? a,b

] // SprintSw

compileif true then [
let HighStack() = valof for i = @#335 to #177677 do if @i resultis i
]

// DCS, January 21, 1978  12:09 AM, from SpruceUtils, others
// March 10, 1978  9:06 AM, add LoadBase for OVInit
// March 15, 1978  10:36 AM, Trident bug in LoadBase
// May 11, 1978  6:27 PM, modify SwapOnSpoolRequest to assume interrupt-driven Ether poll
// May 12, 1978  11:14 AM, add tickMask control, ticker
// August 28, 1978  8:34 PM, EarOn(code) replaces EarOn(mask) -- Ticker moves to assembly code
// September 4, 1978  5:33 PM, simplify SwapOnSp...., allow to respond more readily to ESC
// September 5, 1978  9:25 AM, InitRam no longer sets up for silent boot
// September 25, 1978  9:27 AM, Ether interrupt does not schedule Ticker()
// January 28, 1979  4:11 PM, wait for disk activity to cease before executing SwapSystem code
// August 1, 1979  12:14 AM, pass bin stuff thru inld/outld
// August 9, 1979  10:02 AM, pass Capabilities thru TOPrinterMsg
// August 29, 1979  12:56 PM, stop passing bin stuff thru inld/outld
// January 29, 1980 4:21 PM, init routine for OVInit in LoadBase
//		InitRam does LoadRam for Sprint, too and takes RMR parm
// /