//MINIT0.BCPL First init

// This file is loaded with the program.  After execution, its
// storage is added to free storage.  When "Midas/I" is typed, a
// new Midas.State file is created after about 5 seconds of
// initialization.  When "Midas/R" is typed, Midas
// resumes from its previous AltIO call.  Otherwise, Midas restores its
// state from the existing Midas.State file.

get "streams.d"
get "altofilesys.d"
get "mdecl.d"		// Need def of MaxBlockPages

external [
// OS
	CallSwat; SetBlock; MoveBlock; Zero; diskKd; sysZone
	fpSysDir; Allocate; Free; WriteDiskDescriptor; OpenFile
	Closes; Gets; Puts; Wss; ReadBlock; WriteBlock; Endofs; Resets
	PositionPage; FileLength; keys

// KEYBOARD
	CreateKeyboardStream

// MIDAS
	ElapsedTime; FirstStatic; LastStatic
	TimeJunta; TimeLookup; TimeOutLdFP; TimeOvScan
	TimeQFiles; TimeFont

// MDISP
	FontP; Evec; PseudoFontVec; LineCtrlBlockPtrsVector

// MRGN
	ControlV; ScreenTV; BlankS

// MINIT2
	VecInit; GetHStorage; GetZStorage; MakeTVS

// State package
	GetStorage; StorageInit; BeginSave; SaveStatics; Storage; EndStorage

// MTV
	TVSpareTVs; TVSpareTVec

// MSYM
	TVtoString; @StringVec

// MCMD
	NPrograms; ProgramAct; NQuickFiles; FileBlock

// Overlay package
	OverlayScan

// MDI
	LookupEntries

// Machine dependent
	InitHardware1; ScreenHeight; ScreenWidth

//Defined here
	MidasFP; MidasRFP; BlockStoreFP; FixUpFP; ErrsFP; mdsInitFP
	StateStream; ZoneErr; Init0
]

static [
	MidasFP; MidasRFP; BlockStoreFP; FixUpFP; ErrsFP; mdsInitFP
	StateStream
	StackSize = #4400; SysZoneSize = #5000
	ZoneErr = 0	//0 disables checking
]

manifest [ NFNames = 8		// Number of names being looked up
	NumOverlays = 8	// Max number of overlays (was 7 on
				// 10 February 77)
	OvTSize = 5*NumOverlays+25
	MaxQuickFiles = 60	// Maximum no. files in table
]

let Init0(CFA,Switch) = valof
[	StorageInit(FirstStatic,LastStatic,SysZoneSize,StackSize)
	keys = CreateKeyboardStream()
	ElapsedTime(lv TimeJunta)

	InitHardware1()

//Build a table of file names to be looked up in the System directory
	let SysDirStream = OpenFile(0,ksTypeReadOnly,wordItem,0,fpSysDir)
	let ProgramsFP,FontFP = nil,nil
	let NV,SV = vec NFNames,vec NFNames
	let PrVec = GetStorage(lDV*NFNames)
//Names should be upper case so that they will compare eq later
	SetupFPN(lv MidasFP,NV,SV,0,PrVec,"MIDAS.STATE",-1)
	SetupFPN(lv BlockStoreFP,NV,SV,1,PrVec,"MIDAS.SYMTAB",MaxBlockPages)
	SetupFPN(lv FixUpFP,NV,SV,2,PrVec,"MIDAS.FIXUPS",-1)
	SetupFPN(lv ErrsFP,NV,SV,3,PrVec,"MIDAS.ERRORS",-1)
	SetupFPN(lv MidasRFP,NV,SV,4,PrVec,"MIDAS.RESUME",-1)
	SetupFPN(lv ProgramsFP,NV,SV,5,PrVec,"MIDAS.PROGRAMS",0)
	SetupFPN(lv FontFP,NV,SV,6,PrVec,"GACHA10.AL",0)
	SetupFPN(lv mdsInitFP,NV,SV,7,PrVec,"MIDAS.MIDAS",0)
// Use storage at the low end of the block used by GetStorage
	let Undefined = LookupEntries(SysDirStream,NV,PrVec,NFNames,
		true,Storage,EndStorage-Storage)
	if Undefined ne 0 do
	[ for I = 0 to NFNames-1 do
	  [ if PrVec!(lDV*I) eq 0 then
		CreateFile(NV!I,SV!I,PrVec+(lDV*I))
	  ]
	]
	ElapsedTime(lv TimeLookup)

//First part of MDISP init
	Evec = GetStorage(EvecSize+1)
	PseudoFontVec = GetStorage(PFVecSize+1)
	LineCtrlBlockPtrsVector = GetStorage(ScreenHeight)-1

//First part of init for MRGN
	let SSize = (ScreenWidth rshift 1)+1
	BlankS = GetStorage(SSize); SetBlock(BlankS,20040B,SSize)
	BlankS>>lh = ScreenWidth
	ControlV = GetHStorage(ScreenHeight)
	ScreenTV = GetHStorage(ScreenHeight)
	for I = 1 to ScreenHeight do
	[ ControlV!I = GetZStorage((ScreenWidth+1) rshift 1)
	]

	BeginSave()		//Initialize for SaveState/RestoreState
	if Switch eq 0 do	//No switch = RestoreState
	[ Closes(SysDirStream)
	  StateStream = OpenFile(NV!0,ksTypeReadOnly,wordItem,0,MidasFP)
	  resultis false
	]

	switchon Switch & not 40B into
	[
case $R: Closes(SysDirStream)
	  StateStream = OpenFile(NV!4,ksTypeReadOnly,wordItem,0,MidasRFP)
	  resultis false
default: CallSwat("Bad switch")
case $I: endcase
	]

	OverlayScan(lv CFA>>CFA.fp,GetStorage(OvTSize),OvTSize,
		lv CFA>>CFA.fa)
	ElapsedTime(lv TimeOvScan)

//Init for MTV and MSYM needed by BuildPrograms
	MakeTVS(lv TVSpareTVs,lv TVSpareTVec)
	StringVec = GetStorage(129)

	BuildPrograms(SysDirStream,ProgramsFP); Closes(SysDirStream)
	ElapsedTime(lv TimeQFiles)

	let FontStream = OpenFile(0,ksTypeReadOnly,wordItem,0,FontFP)
	let SizeFont = (FileLength(FontStream)+1) rshift 1
	PositionPage(FontStream,1); FontP = GetStorage(SizeFont)+2
	ReadBlock(FontStream,FontP-2,SizeFont); Closes(FontStream)
	ElapsedTime(lv TimeFont)
	SaveStatics(lv StringVec)
	StateStream = OpenFile(NV!0,ksTypeWriteOnly,wordItem,
		verLatestCreate,MidasFP)
	resultis true
]


and SetupFPN(lvFP,NV,SV,X,PrVec,Name,Size) be
[	NV!X = Name; SV!X = Size
	rv lvFP = PrVec+(offset DV.fp/16)+lDV*X
]


and CreateFile(Name,npgs,FileDV) be
[	if npgs eq 0 then CallSwat("File doesn't exist: ",Name)
	Zero(FileDV,lDV)
	if npgs eq -1 then return	//-1 = Optional file
	let FileFP = FileDV+(offset DV.fp/16)
	let BTsize = diskKd>>KD.diskBTsize
	let BitTable = lv (diskKd>>KD.diskBitTable)
	let bt = Allocate(sysZone,BTsize+1)
	MoveBlock(bt,BitTable,BTsize)	//Copy the bit table
	bt!BTsize = -1	
	let best = 0
	let maxp, max, cur = nil, 0, 0
	for i = 0 to BTsize do
	  test bt!i eq 0
	  ifso cur = cur+1
	  ifnot
	  [ if cur > max then maxp, max = i-cur, cur
	    cur = 0
	  ]
	if max*16 ge npgs then best = maxp
	let X = BitTable!(best-1)
	if best ne 0 do
	[ SetBlock(BitTable,-1,best-1)	//Allocate pages below best
	  BitTable!(best-1) = X % (-X)	//Use bits from previous word
	  diskKd>>KD.bitTableChanged = true	//So it won't be read
	]
	let scratch = vec #400		//Now write the file
	let newst = OpenFile(Name,ksTypeWriteOnly,wordItem,verNew,FileFP)
	if newst ne 0 then
	[ for i = 1 to npgs do WriteBlock(newst,scratch,#400)
	  Closes(newst)
	]
	if best ne 0 do
	[ MoveBlock(BitTable,bt,best-1)	//Restore bit table
	  BitTable!(best-1) = X % ((BitTable!(best-1)) & not (X % -X))
	  diskKd>>KD.bitTableChanged = true
	  WriteDiskDescriptor()
	]
	Free(sysZone,bt)
	if newst eq 0 then CallSwat("CreateFile failed",Name)
]

//BuildPrograms reads "Midas.Programs" and treats the names there
//as follows:
//   a name containing no "." is made into a "Run-Program" menu
//   item if name.midas exists, and both name.midas and name.mb
//   are entered into the QuickOpenFile table;
//   if the name contains a ".", it is only entered into the QuickOpenFile
//   table.
//In either case no entry is made in the QuickOpenFile table unless the
//file exists.
and BuildPrograms(SysDirStream,ProgramsFP) be
[	let S = OpenFile(0,ksTypeReadOnly,charItem,0,ProgramsFP)
	if S eq 0 then CallSwat("Bug in LookupEntries")
	let NV = vec MaxQuickFiles
//The algorithm below puts pointers to ".Midas" files at the low end
//of NV and to ".mb" and other files at the high end
	let midasPtr,mbPtr = 0,MaxQuickFiles
	while not Endofs(S) do
	[ Resets(TVSpareTVs); let DotFlag = false
	  until Endofs(S) do
	  [ let C = Gets(S)
	    switchon C into
	    [
case $,: case $ : case 15B: case 12B: break	//Separators
case $.:	DotFlag = true
default:    if (C < 40B) % (C > 176B) then
		CallSwat("Illegal chars in Midas.Programs")
	     Puts(TVSpareTVs,C); loop
	    ]
	  ]
	  if TVSpareTVec!0 eq 0 then loop	//Just separators
	  if not DotFlag do
	  [ let NameEnd = TVSpareTVec!0; Wss(TVSpareTVs,".MIDAS")
	    NV!midasPtr = MoveString(TVtoString(TVSpareTVec))
	    midasPtr = midasPtr+1; TVSpareTVec!0 = NameEnd
	    Wss(TVSpareTVs,".MB")
	  ]
	  mbPtr = mbPtr-1
	  NV!mbPtr = MoveString(TVtoString(TVSpareTVec))
	  if mbPtr le midasPtr then
		CallSwat("Too many file names in Midas.Programs")
	  loop
	]
//Now pack NV for LookupEntries
	let NFNames = midasPtr+MaxQuickFiles-mbPtr
	MoveBlock(NV+midasPtr,NV+mbPtr,MaxQuickFiles-mbPtr)
	let PrVec = vec MaxQuickFiles*lDV	//For DV's
	let Undefined = LookupEntries(SysDirStream,NV,PrVec,NFNames,
		true,Storage,EndStorage-Storage)
	NQuickFiles = NFNames - Undefined
	let FBsize = NQuickFiles*lDV; FileBlock = GetZStorage(FBsize)
	ProgramAct = GetZStorage(midasPtr)	//Table of names converted
					//to table of Actions by MCMD
	let FBPtr,Progptr = 0,0
	for I = 0 to NFNames-1 do
	[ let DV = PrVec + (I*lDV)
	  if DV!0 ne 0 do
	  [ let Str = NV!I; let StrSize = (Str>>lh rshift 1)+1
	    DV!0 = Str; MoveBlock(FileBlock+FBPtr,DV,lDV); FBPtr = FBPtr+lDV
	    if FBPtr > FBsize then CallSwat("Bug in LookupEntries")
	    if I < midasPtr do
	    [ let PStr = GetStorage(StrSize-3)	//Truncate ".Midas"
	      MoveBlock(PStr,Str,StrSize-3)
	      PStr>>lh = Str>>lh - 6
	      ProgramAct!Progptr = PStr; Progptr = Progptr+1
	    ]
	  ]
	]
	NPrograms = Progptr
]


and MoveString(String) = valof
[	let Size = (String>>lh rshift 1)+1
	let DestStr = GetStorage(Size)
	MoveBlock(DestStr,String,Size)
	resultis DestStr
]