// Midas.bcpl -- Main program
//	Last edited: 16 November 1979

get "altofilesys.d"
get "streams.d"
get "mdecl.d"
get "mcommon.d"

external [
// OS
	Resets; TruncateDiskStream; Closes; Puts; GetCompleteFa; JumpToFa
	CreateDiskStream; lvUserFinishProc; DoubleAdd; Timer; MyFrame
	GotoLabel

// MASM
	Wss; @MBlock; ErrorProtect; DoubleNeg; DoubleDiv

// MDATA
	LastTimer; TimeStart

// MSYM
	StreamFromTextName

// MTXTBUF
	TxtBNewChar; InputTextBuffer

// MIOC
	DWns; Wns

// MOVERLAY
	KillOverlays

// MDISP
	FinishDisplay; SetDisplay

// MRGN
	DriverLoop

// MMENU
	ExecuteTextCmdStream; EscAction; CFileStream

// MCMD
	CmdDoRC; DisplayError; RunProgMenu; StartCmdOverlay

// MCMDOV
	TextCmdOutStream

// MGO
	StartSetup; HaltWait

// MINIT0
	Init0; TimeStream; MStatus

// MINIT1
	Init1

// MINIT2
	Init2

// ACTIONS
	NoopAction

// Machine dependent
	FinishHardware

// Defined here
	Nmidas; InitRes; StartTimer; ElapsedTime; PrintTime; MidasFinish
	MidasSwat
	TopFrame; Resume; BegCF; @NestedCFiles; lvFinishProc; Initialized
	TimerGoing; SysZoneSize; StackSize; StateFileSize

	TimeJunta; TimeLoadRam; TimeJunkInit; TimeLookup1; TimeCreateFiles
	TimeOvScan; TimeLookup2; TimeQFiles; TimeMSYM; TimeInit1
	TimeLoad; TimeSwapInit2; TimeScreenInit; TimeRCActions
	TimeInitHardware; TimeCmdMenu1; TimePaintRegions; TimeInit2Menu
	TimeCmdMenu2; TimeLoadOvl; TimeComCM; TimeFinish
	CodeOS; StorageSize; BBblockSize; AvailBlockSize; FinalStorage
]

static 	[
	Initialized = false; TimerGoing = false; TopFrame; lvFinishProc
	@NestedCFiles = 0

// Initialization statistics for DoradoMidas on 27 October 79
  TimeJunta	   //(0.011) Runtime after Junta
  TimeJunkInit	   //(0.020) Assorted GetStorage calls
  TimeLoadRam	   //(0.178) After InitHardware1 (LoadRam primarily)
  TimeLookup1	   //(0.637) After building FP's for Midas files
   //**Midas/I only**
   TimeOvScan	   // (2.385) After OverlayScan
   TimeCreateFiles //(.002,4.613) After creating auxiliary files
   TimeLookup2	   // (0.565) After 2nd LookupEntries call
   TimeQFiles	   // (1.130) Build FP's from Midas.Programs &
		   //	      Midas.UserPrograms
   TimeMSYM	   // (0.198) MSYM init, swap in Load overlay
   TimeInit1	   // (0.155) Make MPD, MDFS, RGN structures
   TimeLoad	   // (0.992) Open Midas.Midas + Ld in command file
		   //	(.13 slower if LOADER.MB not dumped)
  TimeRestoreState //(4.707) Rest of Midas.Midas on Midas/I -or-
		   //(3.143) Rest of Midas.Midas if temp files exist -or-
		   //(0.387) RestoreState
  TimeSwapInit2	   //(0.166) Loading Init2 overlay
  TimeScreenInit   //(0.008) Init ScreenTV, ControlV, and display
  TimeRCActions	   //(0.003) Create RdCmds and RunProg actions
  TimeComCM	   //(0.181) Reading Com.CM
  TimeInitHardware //(1.399) InitHardware()
  TimeCmdMenu1	   //(0.042) Forming regular command menu
  TimePaintRegions //(0.057) Paint non-MPD lines
   //**Init2 menu only (select Dorado)**
   TimeInit2Menu   // (indeterminate)
   TimeCmdMenu2	   // (0.044) Forming regular command menu
  TimeLoadOvl	   //(0.495) Loading overlays into space avilable
  TimeFinish	   //(18.131) Midas/i and create temporary files -or-
  		   //(11.293) Midas/i, temp files already exist -or-
  		   //( 3.799) Total time when not Midas/i
  TimeUpdate	   //(0.172) Runtime at call to DriverLoop

//Screen update times determined manually
//  (0.106) in ClearAndScanConvert
//  (0.097) in FormMPDMenu & below
//  (0.071) in MarkMenus & below
//  (0.069) in DatatoStream & below
//  (0.037) in MPDEveryTime
//  (0.012) in MGetxx

// Storage statistics
  CodeOS	   // (21130) OS after Junta to levStreams
		   // (was 25600 when Junta'd to levKeyboard)
//StackSize large enough for SimGo action to be called from a command file.
  StackSize = #4400
  StorageSize	   //(54271) Original size excluding stack and init code
//SysZoneSize = #6400 is enough for #5100 words of SimGo overlay and open
//command and output files.  Other worst case is two open files during
//Ld, plus open command and output files.
   SysZoneSize = #6400
   BBblockSize	   //(26330) Bit buffers
   AvailBlockSize  //(20010) OverlayZone (part of bit buffers)
  		   //(1065 words/line at ScreenWidth eq 76)
  FinalStorage	   //(14315) Free storage available for additional FP's in
		   //Midas.Programs and Midas.UserPrograms & Midas additions;
  		   //used meanwhile for overlays made resident and symbols.

  StateFileSize	   //(15236) Words on Midas.RunProg or Midas.Dtach

// TimeFont (0.261) Reading gacha10.al **no longer do this**
// BBWait (in MDATA) is the loop count waiting for bit buffers (~ 562)
]


manifest [ MaxNestedCFiles = 8 ]

let Nmidas(Layout,userParams,CFA) be
[	StartTimer()
	Init0(Layout,userParams,CFA)	//Never returns
]


//Enter with GotoLabel from Init0A--never returns
and InitRes(nil) be
[	TopFrame = MyFrame()
	let CFileStack = vec lCFA*MaxNestedCFiles
//Init1 returns only on Midas/I; otherwise, does RestoreState(..) to
//"Resume".  CFileStack is used to return the FP for MIDAS.MIDAS
//and as TempStorage for Init0B(..).
	Init1(CFileStack)

//Execute MIDAS.MIDAS comfile which must leave display off and not activate
//hardware interface.  MIDAS.MIDAS must write two state files:
//MIDAS.DTACH for starting Midas from the Exec or from a Dtach;
//MIDAS.RUNPROG for reinitializing on RunProg.
	ErrorProtect(lv ExecuteTextCmdStream,
		CreateDiskStream(CFileStack+(offset DV.fp/16),
			ksTypeReadOnly,charItem))

Resume:	ElapsedTime(lv TimeRestoreState)
//Display is off here, returns non-0 if com-file on input line.
	let CFS = Init2(); KillOverlays()
//If the machine is running at this time, wait for halt, abort, or Dtach.
	if MStatus>>MStatus.MachRunning ne 0 do
	[ SetDisplay(false)	//Display on
	  StartCmdOverlay(lv StartSetup,HaltWait)
	  SetDisplay(true)	//Display off
	]
//On initialization, execute command file from Exec's command line; on
//RunProg, execute command file named on input text line or in menu.
	if CFS then ErrorProtect(lv CmdDoRC,lv RunProgMenu)

//CmdDoRC jumps here with GotoLabel, ensuring that the stack won't get too
//deep during nested command files.  An open stream exists for only one
//command file at-a-time; its callers are remembered on CFileStack.
BegCF:	test InputTextBuffer!0 ne 0	//Start comfile
	ifso
	[ test CFileStream eq 0
	  ifso SetDisplay(true)		//Display off for first comfile
	  ifnot				//Push previous comfile
	  [ test NestedCFiles ge MaxNestedCFiles
	    ifso
	    [ ErrorProtect(lv DisplayError,"Comfile nesting > 8 levels")
	      CFS = CFileStream; goto(RetCF)
	    ]
	    ifnot
	    [ GetCompleteFa(CFileStream,CFileStack+(NestedCFiles*lCFA))
	      NestedCFiles = NestedCFiles+1
	      Closes(CFileStream)
	    ]
	  ]
	  CFileStream = -1
//StreamFromTextName will ErrorExit(..) with the display on if any error
//occurs, resulting in a call on DisplayError because CFileStream is ne 0
	  CFS = ErrorProtect(lv StreamFromTextName,InputTextBuffer,
		".MIDAS",ksTypeReadOnly,charItem)
	]
	ifnot CFS = CFileStream		//Aborted RunProg or RdCmds

RetCF:	CFileStream = 0
	if CFS ne 0 then ExecuteTextCmdStream(CFS)
	TxtBNewChar(#177)
	if NestedCFiles > 0 do	//Pop comfile from stack & continue
	[ NestedCFiles = NestedCFiles-1
	  let CFA = CFileStack+(NestedCFiles*lCFA)
	  CFS = CreateDiskStream(lv CFA>>CFA.fp,ksTypeReadOnly,charItem)
	  JumpToFa(CFS,lv CFA>>CFA.fa)
	  goto(RetCF)
	]
	PrintComputeTime()
	SetDisplay(false); ElapsedTime(lv TimeUpdate)
	EscAction = NoopAction
	DriverLoop(); finish
]


and MidasFinish() be
[	if TextCmdOutStream ne 0 do
	[ TruncateDiskStream(TextCmdOutStream); Closes(TextCmdOutStream)
	]
	FinishDisplay(); FinishHardware()
	@lvUserFinishProc = lvFinishProc
]


//Puts Timer in TimeStart and LastTimer vectors
and StartTimer() be
[	Timer(TimeStart)
//Don't use MBlock here because not initialized at first call
	LastTimer!0,LastTimer!1 = TimeStart!0,TimeStart!1
	TimerGoing = true
]


//TimeV!0←low-order word of Timer-LastTimer, high-order word returned.
//LastTimer←Timer.
and ElapsedTime(TimeV) = valof
[	let X = vec 1; MBlock(X,LastTimer,2); DoubleNeg(X)
	Timer(LastTimer); DoubleAdd(X,LastTimer)
	TimeV!0 = X!1; resultis X!0
]


//Timer covers about 1200 hours
and PrintComputeTime() be
[	MBlock(LastTimer,TimeStart,2)
	let Y = vec 1; Y!0 = ElapsedTime(Y+1)
	TimeFinish = Y!1	//Single-precision result in TimeFinish
	PrintTime(Y)
	TimerGoing = false
]


//Prints the elapsed time in double-precision V on TimeStream
and PrintTime(V) be
[	let TimeParse = vec 3
	DoubleAdd(V,table [ 0; 5 ] )		//For rounding to 100ths
	TimeParse!3 = DoubleDiv(V,1000)/10	//Y←seconds, excess 100ths
	TimeParse!2 = DoubleDiv(V,60)		//Y←minutes, excess sec
	TimeParse!1 = DoubleDiv(V,60)		//Y←hours, excess min
	TimeParse!0 = DoubleDiv(V,24)		//Y←days, excess hrs
	Resets(TimeStream); Wss(TimeStream,"Time: ")
	let Printing = false
	if V!1 ne 0 do
	[ Wns(TimeStream,V!1,0,10); Wss(TimeStream," days ")
	  Printing = true
	]
	for I = 0 to 3 do
	[ if (TimeParse!I ne 0) % (I eq 2) then Printing = true
	  if Printing do
	  [ DWns(TimeStream,TimeParse+I,16,0,10,2,$0)
	    let Char = table [ $:; $:; $.; 0 ] !I
	    if Char ne 0 then Puts(TimeStream,Char)
	  ]
	]
]


and MidasSwat(ErrNo,P1,P2,P3,P4,P5) be
[	(table [ #77403; #1401 ] )("Midas.Errors",lv ErrNo)
]