-- MDLoadImpl.Mesa
-- last edit by Schmidt, January 4, 1983 3:11 pm
-- last edit by Satterthwaite, February 17, 1983 4:03 pm
-- Mesa 7.0/ Pilot 6.0
-- procedures to load and start modules in a Model

-- can't use PilotLoaderOps since it is not exported by CoPilotDorado.Config
-- may use PilotLoadStateOps, however

-- links:
--	IF gfi > firstdummy, then gfi is index into Import table 
--		and ep is index into the export record pared with that import
--		binding is simply to copy control link in the export record
--		into this link
-- 	IF gfi < firstdummy, then gfi in this link is an index into the config's
--		moduletable.  Do not alter the ep

-- spaces:
--	assume there are n modules
--	there will be 
--	1 space in MDS for all the frames (and frame links)
--	1 space in VM for the Fake Config Bcd for the load state
--	n ReadOnly spaces for Code
--		n (up to n) subspaces for the code links
--		n subspaces for the code segments
--			(unless the bcd is Binder-output)
--	n spaces for the Bcd headers
--		(deleted on UnLoad)

DIRECTORY
  BcdDefs: TYPE USING [Base, BCD, EPLimit, EXPIndex, EXPRecord, 
  	FTIndex, FTRecord, GFTIndex, IMPIndex, IMPRecord, 
	Link, MTIndex, MTRecord, NameRecord],
  BcdOps: TYPE USING [BcdBase, EXPHandle, FTHandle, IMPHandle, MTHandle, NameString,
   ProcessExports, ProcessImports, ProcessModules],
  CWF: TYPE USING [FWF1, FWF2, FWF3, WF0, WF1, WF2, WF3, WF4],
  Dir: TYPE USING [FileInfo],
  Environment: TYPE USING [wordsPerPage],
  File: TYPE USING [Capability],
  Heap: TYPE USING [FreeMDSNode, MakeMDSNode],
  IO: TYPE USING[Handle],
  LowLoader: TYPE USING [AddToLoadState, AllocateInterfaceSeq, BuildFramePtrInterface,
   BuildInterface, CloseLinkSpace, ConvertLink, CopyNStoLS, DummyMapSeq, EqualStringAndName, 
	FindVariableLink, FreeInterfaceSeq, FreeLoadInfoSeq, GetIntFromLoadState, ImpExpSeq, 
	IncorporateLoadStateChanges, InterfaceSeq, IthLink, LinkSegmentLength, 
	LoadBcdAndCount, LoadFrame, LoadIncremental, 
	LoadInfoSeq, OpenLinkSpace, ReadLink, ReplaceResult, WriteLink, Zero],
  LongString: TYPE USING[EquivalentString],
  MDComp: TYPE USING [HandlePlus, SetVersAndModulename],
  MDLoad: TYPE USING [BuildFakeBcd],
  MDMain: TYPE USING [DebugWP, modellerIsIdle, PrintSeparatorLine],
  MDModel: TYPE USING [AddToEndOfList, APPLSymbol, GetFileInfo, LISTSymbol, LocForAppl, 
  	LocForType, LOCSymbol, LookForInstBcd, MODELSymbol, NarrowToAPPL, NarrowToLIST,
  	NarrowToLOC, NarrowToPROC, NarrowToTYPE, numberofbcdsmapped, numberofsourcesparsed,
  	PROCSymbol, Symbol, SymbolSeq, TraverseList, TraverseTree, TYPESymbol],
  PilotLoadStateOps: TYPE USING [ConfigIndex, GetMap, InputLoadState, Map, NullConfig, 
  	ReleaseLoadState, ReleaseMap, RemoveConfig],
  PrincOps: TYPE USING [ControlLink, GFTIndex, GFTNull, GlobalFrameHandle, NullLink,
   UnboundLink],
  Process: TYPE USING [Detach],
  RTOS: TYPE USING[GetCurrent, RegisterCedarProcess],
  RTLoader USING [AcquireTypesAndLiterals],
  Runtime: TYPE USING [IsBound, ValidateGlobalFrame],
  RuntimeInternal: TYPE USING [Codebase],
  Space: TYPE USING [Create, Delete, GetHandle, Handle, Map, mds, 
  	nullHandle, PageFromLongPointer, Pointer, virtualMemory, wordsPerPage],
  Subr: TYPE USING [FindMappedSpace, TTYProcs],
  System: TYPE USING [GetClockPulses, PulsesToMicroseconds],
  Time: TYPE USING [Current],
  TimeStamp: TYPE USING [Null];
			
MDLoadImpl: PROGRAM 
IMPORTS BcdOps, CWF, Heap, LowLoader, LongString, MDComp,  
	MDLoad, MDMain, MDModel, PilotLoadStateOps, Process, 
	RTLoader, RTOS, Runtime, RuntimeInternal, Space, Subr, System, Time
EXPORTS MDLoad  = {

-- no MDS usage!

LoadBcdsAndResolveImports: PUBLIC PROC[spmodel: MDModel.MODELSymbol, 
	symbolseq: MDModel.SymbolSeq, tryreplacement: BOOL,
	tty: Subr.TTYProcs, window: IO.Handle] = {

ENABLE UNWIND => PilotLoadStateOps.ReleaseLoadState[];
p: LONG CARDINAL;
time: LONG CARDINAL ← Time.Current[];
MDModel.numberofbcdsmapped ← MDModel.numberofsourcesparsed ← 0;
-- unload any bcds that may be around from the last invocation
-- regardless of "replacement", old config in load state is unloaded
-- IF NOT tryreplacement THEN UnLoad[spmodel, symbolseq, FALSE]
-- ELSE -- IF spmodel.configindex ~= PilotLoadStateOps.NullConfig THEN
	DeleteLoadStateEntry[spmodel.configindex];
spmodel.configindex ← PilotLoadStateOps.NullConfig;
-- fix up PLUS and THEN
MDComp.HandlePlus[symbolseq];

-- this will build all interface records
LoadBcds[spmodel, symbolseq, window];

-- now fill in parameters 
p ← System.PulsesToMicroseconds[System.GetClockPulses[]];
CWF.WF0["Phase 3: Fill in interface records... \n"L];
FillInFromLoadState[spmodel, symbolseq, tty];
-- fill in any PLUS nodes (may plus together load state parameters)
FillInPLUSandTHEN[spmodel, symbolseq, tryreplacement];
-- IF Subr.debugflg THEN PrintInterfaceSeqs[spmodel, symbolseq];
-- this will build a bcd with everything we need
spmodel.configindex ← PilotLoadStateOps.InputLoadState[];
MDLoad.BuildFakeBcd[spmodel, symbolseq];
p ← (System.PulsesToMicroseconds[System.GetClockPulses[]] - p)/1000;
CWF.WF1["done (%lu millisec).\nPhase 4: Fill in imports (links) ... \n"L, @p];
p ← System.PulsesToMicroseconds[System.GetClockPulses[]];
-- now fill in all the frame links
ResolveImports[spmodel, symbolseq];
-- IF Subr.debugflg THEN PrintFrames[spmodel, symbolseq];
-- now put all the exported interface records into the modeller load state
PutExportsInModellerLoadState[spmodel, symbolseq];
-- now call Cedar related procedures to finish the loading
ProcessCedarBcds[spmodel, symbolseq, tryreplacement];
time ← Time.Current[] - time;
p ← (System.PulsesToMicroseconds[System.GetClockPulses[]] - p)/1000;
CWF.WF2["done (%lu millisec).\nTotal time to load: %lu seconds.\n"L, @p, @time];
CWF.FWF2[MDMain.DebugWP, "# bcds mapped in %u, # sources parsed %u.\n"L, @MDModel.numberofbcdsmapped,
	@MDModel.numberofsourcesparsed];
};

MAXCONTROL: CARDINAL = 30;
StartArray: TYPE = ARRAY[0 .. MAXCONTROL) OF RECORD[
	prog: PROGRAM,
	frame: PrincOps.GlobalFrameHandle
	];

StartAllControlBcds: PUBLIC PROC[spmodel: MDModel.MODELSymbol, 
	symbolseq: MDModel.SymbolSeq] = {
starr: StartArray;
nst: CARDINAL ← 0;
p: PROCESS;

	-- calls itself recursively
	OnAList: PROC[sp: MDModel.Symbol] = {
	WITH spt: sp SELECT FROM
	typeAPPL => IF spt.appltype = symbolseq.controlv THEN 
			TryToStart[MDModel.LocForAppl[MDModel.NarrowToAPPL[sp]]];
	typeLET => MDModel.TraverseList[spt.letgrp, OnAList];
	typePROC => MDModel.TraverseList[MDModel.NarrowToLIST[spt.procval], 
		OnAList];
	typeMODEL => MDModel.TraverseList[spt.model, OnAList];
	ENDCASE => NULL;
	};
	
	TryToStart: PROC[sploc1: MDModel.Symbol] = {
	prog: PROGRAM;
	sploc: MDModel.LOCSymbol;
	IF sploc1 = NIL OR sploc1.stype ~= typeLOC THEN RETURN;
	sploc ← MDModel.NarrowToLOC[sploc1];
	IF sploc.fi = NIL OR sploc.fi.loadInfoSeq = NIL THEN RETURN;
	FOR i: CARDINAL IN [0 .. sploc.fi.loadInfoSeq.size) DO
		Runtime.ValidateGlobalFrame[sploc.fi.loadInfoSeq[i].frame];
		IF sploc.fi.loadInfoSeq[i].frame.started THEN {
			CWF.WF1["Error - %s.Bcd has already been started.\n"L, 
				sploc.tail];
			RETURN;
			};
		ENDLOOP;
	CWF.WF1["Will start %s.Bcd\n"L, sploc.tail];
	prog ← LOOPHOLE[sploc.fi.loadInfoSeq.cm];
	IF nst >= LENGTH[starr] THEN 
		CWF.WF0["Too many control modules.\n"L]
	ELSE {
		starr[nst] ← [prog: prog, frame: sploc.fi.loadInfoSeq[0].frame];
		nst ← nst + 1;
		};
	};
	
MDModel.TraverseList[spmodel.model, OnAList];
CWF.WF0["Starting modules ... \n"L];
MDMain.modellerIsIdle ← TRUE;
MDMain.PrintSeparatorLine[];
p ← FORK StartProcedure[starr, nst];
Process.Detach[p];
};

UnLoad: PUBLIC PROC[spmodel: MDModel.MODELSymbol, 
	symbolseq: MDModel.SymbolSeq, unloadthebcd: BOOL] = {
nunl: CARDINAL ← 0;
frameptr: PrincOps.GlobalFrameHandle ← NIL;

	ProcAnalyze: PROC[sp: MDModel.Symbol, spmodel: MDModel.MODELSymbol] 
		RETURNS[proceed: BOOL ← TRUE] = {
	WITH spt: sp SELECT FROM
	typeAPPL => {
		IF spt.interfaceseq ~= NIL THEN
			LowLoader.FreeInterfaceSeq[spt.interfaceseq];
		spt.interfaceseq ← NIL;
		};
	typeLOC => {
		IF spt.fi ~= NIL AND spt.fi.loadInfoSeq ~= NIL THEN {
			nunl ← nunl + 1;
			IF unloadthebcd THEN {
				-- remember that a binder bcd loaded by
				-- the modeller will have only ONE mapped
				-- space for all its code
				FOR i: CARDINAL IN [0 .. spt.fi.loadInfoSeq.size) DO
					prog: PROGRAM ← LOOPHOLE[spt.fi.loadInfoSeq[i].frame];
					space: Space.Handle;
					IF frameptr = NIL THEN 
						frameptr ← spt.fi.loadInfoSeq[i].frame;
					IF RuntimeInternal.Codebase[prog] ~= NIL THEN {
						space ← Space.GetHandle[
							Space.PageFromLongPointer[
								RuntimeInternal.Codebase[prog]]];
						IF space ~= Space.nullHandle 
						AND space ~= Space.virtualMemory THEN
							Space.Delete[Subr.FindMappedSpace[space]];
						};
					ENDLOOP;
				};
			LowLoader.FreeLoadInfoSeq[spt.fi.loadInfoSeq];
			spt.fi.loadInfoSeq ← NIL;
			};
		};
	ENDCASE => NULL;
	};
	
	
MDModel.TraverseTree[spmodel, symbolseq, ProcAnalyze, TRUE];
IF unloadthebcd THEN {
	IF spmodel.configindex ~= PilotLoadStateOps.NullConfig THEN {
		DeleteLoadStateEntry[spmodel.configindex];
		spmodel.configindex ← PilotLoadStateOps.NullConfig;
		};
	IF frameptr ~= NIL THEN {
		space: Space.Handle;
		space ← Space.GetHandle[Space.PageFromLongPointer[frameptr]];
		IF space ~= Space.nullHandle 
		AND space ~= Space.virtualMemory 
		AND space ~= Space.mds THEN
			Space.Delete[Subr.FindMappedSpace[space]];
		};
	IF spmodel.fakebcdspace ~= Space.nullHandle THEN {
		Space.Delete[spmodel.fakebcdspace];
		spmodel.fakebcdspace ← Space.nullHandle;
		};
	IF nunl > 0 THEN {
		CWF.WF2["%u Module%s unloaded.\n"L, @nunl, 
			IF nunl > 1 THEN "s"L ELSE ""L];
		CWF.WF0["All code spaces and frames have been freed.  DO NOT TRY TO USE THEM!*N"L];
		};
	};
};

-- internal procedures

DeleteLoadStateEntry: PROC[index: PilotLoadStateOps.ConfigIndex] = {
ENABLE UNWIND => PilotLoadStateOps.ReleaseLoadState[];
map: PilotLoadStateOps.Map;
[] ← PilotLoadStateOps.InputLoadState[];
map ← PilotLoadStateOps.GetMap[index];
PilotLoadStateOps.RemoveConfig[map, index];
PilotLoadStateOps.ReleaseMap[map];
PilotLoadStateOps.ReleaseLoadState[];
};


-- the parameter array is passed by value!
StartProcedure: PROC[starr: StartArray, nst: CARDINAL] = {
{
ENABLE ABORTED => GOTO out;
i: CARDINAL;
IF FALSE AND Runtime.IsBound[RTOS.RegisterCedarProcess] THEN 
	RTOS.RegisterCedarProcess[RTOS.GetCurrent[]];
FOR i IN [0 .. nst) DO
	IF starr[i].frame.started THEN 
		CWF.WF1["Error - %u element of start list has already been started.\n"L, @i]
	ELSE START starr[i].prog;
	ENDLOOP;
EXITS
out => NULL;
};
CWF.WF1["All %u modules have been started.\n"L, @nst];
};

LoadBcds: PROC[spmodel: MDModel.MODELSymbol, symbolseq: MDModel.SymbolSeq, 
	window: IO.Handle] = {
nwordstotal: CARDINAL ← 0;
frameptr: POINTER ← NIL;
beginning, ending: LONG CARDINAL ← 0;
bcdpages, codepages: CARDINAL ← 0;
p: LONG CARDINAL;
configGfi: PrincOps.GFTIndex ← 1;

	ProcAnalyzeSum: PROC[sptop: MDModel.Symbol, spmodel: MDModel.MODELSymbol] 
		RETURNS[proceed: BOOL ← TRUE] = {
	fi: Dir.FileInfo;
	np, ncp: CARDINAL;
	sploc: MDModel.LOCSymbol;
	shouldLoad: BOOL;
	IF sptop.stype ~= typeAPPL AND sptop.stype ~= typeLET THEN RETURN[TRUE];
	[shouldLoad, , sploc] ← SplitUpLoc[sptop];
	IF NOT shouldLoad THEN RETURN[TRUE];
	fi ← MDModel.GetFileInfo[sploc];
	IF NOT fi.bcdPresent THEN RETURN[TRUE];	-- can't load it
	IF fi.bcdFileName.length < 4 THEN ERROR;
	np ← ncp ← 0;
	IF NOT fi.bcdPresent THEN 
		CWF.WF1["Error - can't load %s since its not on the disk.\n"L, 
			fi.bcdFileName]
	ELSE {
		IF fi.loadInfoSeq = NIL THEN {
			[fi.loadInfoSeq, nwordstotal, np, ncp] ← 
				LowLoader.LoadBcdAndCount[fi.bcdCap, fi.bcdFileName,
					nwordstotal];
			MDModel.numberofbcdsmapped ← MDModel.numberofbcdsmapped + 1;
			}
		ELSE IF fi.loadInfoSeq.mustreplace THEN {
			replaceResult: LowLoader.ReplaceResult;
			replaceResult ← LowLoader.LoadIncremental[fi.bcdCap, fi.loadInfoSeq, window];
			SELECT replaceResult FROM
			ok => fi.loadInfoSeq.mustreplace ← FALSE;
			configNotReplaceable => CWF.WF1["Load of %s failed, is a config.\n"L, fi.bcdFileName];
			frameTooBig => CWF.WF1["Load of %s failed, frame too big.\n"L, fi.bcdFileName];
			ngfiTooBig => CWF.WF1["Load of %s failed, # gfis too big.\n"L, fi.bcdFileName];
			checkForMRFailed => CWF.WF1["Load of %s failed, outstanding local frames(?).\n"L, fi.bcdFileName];
			ENDCASE => ERROR;
			MDModel.numberofbcdsmapped ← MDModel.numberofbcdsmapped + 1;
			}
		ELSE CWF.FWF1[MDMain.DebugWP, "%s does not need to be reloaded.\n"L, 
			fi.bcdFileName];
		};
	-- CWF.WF2["%s: %u code pages.\n"L, sploc.tail, @ncp];
	bcdpages ← bcdpages + np;
	codepages ← codepages + ncp;
	RETURN[TRUE];
	};

	ProcAnalyzeLoad: PROC[sptop: MDModel.Symbol, spmodel: MDModel.MODELSymbol] 
		RETURNS[proceed: BOOL ← TRUE] = {
	fi: Dir.FileInfo;
	sploc: MDModel.LOCSymbol;
	splist: MDModel.LISTSymbol;
	shouldLoad: BOOL;
	IF sptop.stype ~= typeAPPL AND sptop.stype ~= typeLET THEN RETURN[TRUE];
	[shouldLoad, splist, sploc] ← SplitUpLoc[sptop];
	IF NOT shouldLoad THEN RETURN[TRUE];
	fi ← MDModel.GetFileInfo[sploc];
	IF fi.loadInfoSeq ~= NIL AND fi.loadInfoSeq.size = 0 THEN {
		[frameptr, configGfi] ← LowLoader.LoadFrame[fi.loadInfoSeq, 
			frameptr, window, fi.bcdCap, beginning, ending, configGfi];
		SetUpExports[splist, sploc];
		};
	RETURN[TRUE];
	};

	-- determines whether a bcd should be loaded based on whether it exports an interface record or not
	SplitUpLoc: PROC[sptop: MDModel.Symbol] 
		RETURNS[shouldLoad: BOOL, splist: MDModel.LISTSymbol, sploc: MDModel.LOCSymbol] = {
	shouldLoad ← FALSE;
	splist ← NIL;
	sploc ← NIL;
	WITH spt: sptop SELECT FROM
	typeAPPL => {
		IF spt.applval = NIL THEN RETURN; 	-- must be parameter
		IF spt.applval.stype = typeLIST THEN RETURN; 	-- must be PLUS
		sploc ← MDModel.NarrowToLOC[spt.applval];
		splist ← MDModel.AddToEndOfList[NIL, sptop, normal, symbolseq];
		shouldLoad ← TRUE;
		};
	typeLET => {
		sploc ← MDModel.NarrowToLOC[spt.letval];
		splist ← spt.letgrp;
		WHILE splist ~= NIL DO
			-- should load only if LET has at least one interface record in it
			IF splist.first.stype = typeAPPL THEN {
				shouldLoad ← TRUE;
				EXIT;
				};
			splist ← splist.rest;
			ENDLOOP;
		splist ← spt.letgrp;
		};
	ENDCASE => NULL;
	};
	
p ← System.PulsesToMicroseconds[System.GetClockPulses[]];
CWF.WF0["Phase 1: Load in bcd headers... \n"L];
MDModel.TraverseTree[spmodel, symbolseq, ProcAnalyzeSum, TRUE];
CWF.WF0["done.\n"L];
IF nwordstotal > 0 THEN {
	space: Space.Handle;
	npages: CARDINAL ← (nwordstotal + Space.wordsPerPage - 1) 
		/ Space.wordsPerPage;
	CWF.WF1["%u pages for bcd headers, "L, @bcdpages];
	space ← Space.Create[size: npages, parent: Space.mds];
	Space.Map[space];
	frameptr ← Space.Pointer[space];
	LowLoader.Zero[frameptr, npages * Environment.wordsPerPage];
	beginning ← LOOPHOLE[LONG[frameptr]];
	ending ← beginning + nwordstotal;
	CWF.WF2["total MDS for frames: %u words, %u pages.\n"L, @nwordstotal, 
		@npages];
	};
p ← (System.PulsesToMicroseconds[System.GetClockPulses[]] - p)/1000;
CWF.WF2["Total pages for code: %u pages  (%lu millisec).\n"L, @codepages, @p];
p ← System.PulsesToMicroseconds[System.GetClockPulses[]];
CWF.WF0["Phase 2: Allocate frames and map in code... \n"L];
MDModel.TraverseTree[spmodel, symbolseq, ProcAnalyzeLoad, TRUE];
p ← (System.PulsesToMicroseconds[System.GetClockPulses[]] - p)/1000;
CWF.WF1["done (%lu millisec).\n"L, @p];
};

PutExportsInModellerLoadState: PROC[spmodel: MDModel.MODELSymbol, 
	symbolseq: MDModel.SymbolSeq] = {
spproc: MDModel.PROCSymbol;
splist: MDModel.LISTSymbol;
splist ← spmodel.model;
WHILE splist ~= NIL DO
	IF splist.first.stype = typePROC THEN {
		spproc ← MDModel.NarrowToPROC[splist.first];
		EXIT;
		};
	splist ← splist.rest;
	ENDLOOP;
IF spproc = NIL THEN RETURN;
splist ← spproc.procret;
WHILE splist ~= NIL DO
	IF splist.first.stype = typeAPPL THEN {
		spappl: MDModel.APPLSymbol;
		spappl ← MDModel.NarrowToAPPL[splist.first];
		IF spappl.interfaceseq ~= NIL THEN
			LowLoader.AddToLoadState[spappl.interfaceseq];
		};
	splist ← splist.rest;
	ENDLOOP;
};

-- fill in links
ResolveImports: PROC[spmodel: MDModel.MODELSymbol, 
	symbolseq: MDModel.SymbolSeq] = {

	ProcAnalyze: PROC[sptop: MDModel.Symbol, spmodel: MDModel.MODELSymbol] 
		RETURNS[proceed: BOOL ← TRUE] = {
	
		-- calls itself recursively
		-- spmodel, sptop is passed in
		ProcLoc: PROC[spl: MDModel.Symbol] = {
		IF spl = NIL THEN RETURN;
		IF spl.stype = typeLIST THEN 
			MDModel.TraverseList[MDModel.NarrowToLIST[spl], ProcLoc]
		ELSE IF spl.stype = typeLOC THEN {
			fi: Dir.FileInfo;
			sploc: MDModel.LOCSymbol;
			sploc ← MDModel.NarrowToLOC[spl];
			fi ← MDModel.GetFileInfo[sploc];
			IF fi.loadInfoSeq ~= NIL THEN {
				-- only for things loaded
				FillInImports[sploc, spmodel, symbolseq];
				};
			};
		};
		
	WITH spt: sptop SELECT FROM
	typeAPPL => ProcLoc[spt.applval];
	typeLET => ProcLoc[spt.letval];
	ENDCASE => NULL;
	RETURN[TRUE];
	};


MDModel.TraverseTree[spmodel, symbolseq, ProcAnalyze, TRUE];
};


FillInFromLoadState: PROC[spmodel: MDModel.MODELSymbol, 
	symbolseq: MDModel.SymbolSeq, window: Subr.TTYProcs] = {
askCompiler: BOOL ← LongString.EquivalentString[spmodel.modelfilename, "model.model"L];

	ProcAnal: PROC[sp: MDModel.Symbol, spmodel: MDModel.MODELSymbol] 
		RETURNS[proceed: BOOL ← TRUE] = {
	fi: Dir.FileInfo;
	sploc: MDModel.LOCSymbol;
	spappl: MDModel.APPLSymbol;
	sptype: MDModel.TYPESymbol;
	IF sp.stype ~= typeAPPL THEN RETURN;
	spappl ← MDModel.NarrowToAPPL[sp];
	-- LocForAppl won't work since the value may be a list
	IF spappl.applval ~= NIL OR spappl.letparent ~= NIL THEN RETURN;
	-- must be a parameter as it has no value
	sptype ← MDModel.NarrowToTYPE[spappl.appltype];
	sploc ← MDModel.LocForType[sptype];
	IF sploc = NIL THEN {
		CWF.WF3["Error - %s in parameter (%s: %s) is undefined.\n"L, 
			sptype.typesym, spappl.applsym, sptype.typesym];
		RETURN;
		};
	-- this handles the case where the file has not been
	-- analyzed at all or the bcdvers has been set to the create time
	fi← MDModel.GetFileInfo[sploc];
	IF fi.bcdVers.net = 0 THEN 
		-- may not have been set before
		MDComp.SetVersAndModulename[sploc];
	spappl.interfaceseq ← LowLoader.GetIntFromLoadState[sptype.typeName, fi.bcdVers];
	IF spappl.interfaceseq = NIL THEN 
		NULL -- commented out, since unbound link error messages will be printed later
		     -- CWF.WF1["Warning - can't import %s from load state.\n"L, sptype.typeName]
	ELSE fi.bcdVers ← spappl.interfaceseq.versstamp;	-- just in case it was Null
	};
	
LowLoader.IncorporateLoadStateChanges[window, askCompiler];	-- looks for new additions
MDModel.TraverseTree[spmodel, symbolseq, ProcAnal];
};

EmptyLink: PROC[link: PrincOps.ControlLink] RETURNS[empty: BOOL] = {
RETURN[link = PrincOps.UnboundLink OR link = PrincOps.NullLink];
};
	
FillInPLUSandTHEN: PROC[spmodel: MDModel.MODELSymbol, 
	symbolseq: MDModel.SymbolSeq, tryreplacement: BOOL] = {

	ProcAnal: PROC[sp: MDModel.Symbol, spmodel: MDModel.MODELSymbol] 
		RETURNS[proceed: BOOL ← TRUE] = {
	from, target: LowLoader.InterfaceSeq;
	mode: {plus, then};
	spappl, targetappl: MDModel.APPLSymbol;
	splist: MDModel.LISTSymbol;
	IF sp.stype ~= typeAPPL THEN RETURN;
	targetappl ← MDModel.NarrowToAPPL[sp];
	IF targetappl.applval = NIL 
	OR targetappl.applval.stype ~= typeLIST THEN RETURN;
	target ← targetappl.interfaceseq;
	splist ← MDModel.NarrowToLIST[targetappl.applval];
	mode ← IF splist.listtype = plus THEN plus ELSE then;
	IF -- tryreplacement AND -- target ~= NIL THEN {
		-- force recomputation in repl mode
		LowLoader.FreeInterfaceSeq[target];
		targetappl.interfaceseq ← target ← NIL;
		};
	WHILE splist ~= NIL DO
		spappl ← MDModel.NarrowToAPPL[splist.first];
		from ← spappl.interfaceseq;
		IF from = NIL THEN {
			IF spappl.applval = NIL THEN 
				CWF.WF2["Error - unable to %s together export %s, it has no exporter.\n"L,
					IF mode = plus THEN "PLUS"L ELSE "THEN"L, spappl.applsym]
			ELSE {
				sploc: MDModel.LOCSymbol ← MDModel.NarrowToLOC[spappl.applval];
				fi: Dir.FileInfo ← sploc.fi;
				CWF.WF3["Error - unable to %s together export %s from %s\n"L,
					IF mode = plus THEN "PLUS"L ELSE "THEN"L,
					spappl.applsym, fi.bcdFileName];
				};
			splist ← splist.rest;
			LOOP;
			};
		IF target = NIL THEN {
			-- must fill in from scratch
			targetappl.interfaceseq ← target ← 
				LowLoader.AllocateInterfaceSeq[from.intname, 
					from.size];
			target.versstamp ← from.versstamp;
			target.size ← from.size;
			FOR i: CARDINAL IN [0 .. from.size) DO
				target[i] ← from[i];
				ENDLOOP;
			}
		ELSE  {
			-- fill in already existing
			IF from.size ~= target.size THEN ERROR;
			IF mode = then THEN {
				FOR i: CARDINAL IN [0 .. from.size) DO
					IF (target[i].clink = PrincOps.NullLink 
			    		AND from[i].clink ~= PrincOps.UnboundLink) 
					OR (target[i].clink = PrincOps.UnboundLink 
			    		AND from[i].clink ~= PrincOps.NullLink) THEN
						target[i] ← from[i];
					ENDLOOP;
				}
			ELSE {	-- PLUS
				i: CARDINAL;
				sptype: MDModel.TYPESymbol;
				FOR i IN [0 .. from.size) DO
					IF NOT EmptyLink[target[i].clink] 
					AND NOT EmptyLink[from[i].clink] THEN {
						sptype ← MDModel.NarrowToTYPE[
							spappl.appltype];
						CWF.WF2["Error -- more than one exporter of item #%u in interface %s.\n"L, 
							@i, sptype.typeName];
						}
					ELSE IF EmptyLink[target[i].clink] THEN
						target[i] ← from[i];
					ENDLOOP;
				};
			};
		splist ← splist.rest;
		ENDLOOP;
	};
	
MDModel.TraverseTree[spmodel, symbolseq, ProcAnal];
};

-- fill in exported interface records from the bcd
SetUpExports: PROC[splist: MDModel.LISTSymbol, sploc: MDModel.LOCSymbol] = {
sptype: MDModel.TYPESymbol;
spappl: MDModel.APPLSymbol;
fi: Dir.FileInfo ← MDModel.GetFileInfo[sploc];
WHILE splist ~= NIL DO
	IF splist.first.stype ~= typeAPPL THEN {
		splist ← splist.rest;
		LOOP;
		};
	spappl ← MDModel.NarrowToAPPL[splist.first];
	sptype ← MDModel.NarrowToTYPE[spappl.appltype];
	IF MDModel.LocForType[sptype] = NIL THEN {
		splist ← splist.rest;
		LOOP;
		};
	IF MDModel.LocForAppl[spappl] = MDModel.LocForType[sptype] THEN 
		-- when type and value are the same then this is a module frampe ptr
		spappl.interfaceseq ← LowLoader.BuildFramePtrInterface[
			fi.loadInfoSeq.bcdbase, fi.loadInfoSeq[0].frame]
	ELSE {
		eth: BcdOps.EXPHandle;
		eth ← LookForExport[fi.loadInfoSeq.bcdbase, spappl, sptype];
		IF eth ~= NIL THEN 
			spappl.interfaceseq ← LowLoader.BuildInterface[fi.loadInfoSeq, 
				eth];
		};
	splist ← splist.rest;
	ENDLOOP;
};

-- if there is no time stamp (bcd not present) then sticks in the one that is exported
LookForExport: PROC[bcdbase: BcdOps.BcdBase, spappl: MDModel.APPLSymbol,
	sptype: MDModel.TYPESymbol] RETURNS[eth: BcdOps.EXPHandle] = {
namestring: BcdOps.NameString;
sploc: MDModel.LOCSymbol;
fi: Dir.FileInfo;
fillIn: BOOL ← FALSE;

	ForEachExports: PROC[eth: BcdOps.EXPHandle, eti: BcdDefs.EXPIndex] 
		RETURNS[stop: BOOL] = {
	fth: BcdOps.FTHandle;
	fth ← @LOOPHOLE[bcdbase + bcdbase.ftOffset, BcdDefs.Base][eth.file];
	-- only examine time since sploc.bcdVers may be from a bcd that's not on the local disk
	-- fth.version may be Null, e.g. SequinImpl.Bcd
	IF (fillIn OR fth.version.time = fi.bcdVers.time OR fth.version = TimeStamp.Null) 
	AND LowLoader.EqualStringAndName[sptype.typeName, namestring, eth.name]
		THEN {
			-- stick in version being exported
			fi.bcdVers ← fth.version;
			RETURN[TRUE];
			};
	RETURN[FALSE];
	};
	
sploc ← MDModel.LocForType[sptype];
fi ← MDModel.GetFileInfo[sploc];
IF fi.bcdVers = TimeStamp.Null THEN 
	-- may not have been set before
	MDComp.SetVersAndModulename[sploc];
fillIn ← NOT fi.bcdPresent AND fi.bcdVers = TimeStamp.Null;
namestring ← LOOPHOLE[bcdbase + bcdbase.ssOffset];
eth ← BcdOps.ProcessExports[bcdbase, ForEachExports].eth;
IF eth = NIL THEN {
	exporter: STRING ← [100];
	LowLoader.CopyNStoLS[exporter, bcdbase, bcdbase.source];
	CWF.WF3["Warning - can't match %s of %v with any export of %s.\n"L,
		fi.bcdFileName, @fi.bcdVers, exporter];
	};
};

-- called for loaded bcd to fill in all its links!
FillInImports: PROC[sploc: MDModel.LOCSymbol, spmodel: MDModel.MODELSymbol, symbolseq: MDModel.SymbolSeq] = {
bcdbase: BcdOps.BcdBase;
dummymapseq: LowLoader.DummyMapSeq;
impexpseq: LowLoader.ImpExpSeq;
mod, imp: CARDINAL ← 0;
fi: Dir.FileInfo ← sploc.fi;
bcdFileName: STRING ← [100];
namestring: BcdOps.NameString;
splist: MDModel.LISTSymbol;

	-- assumes parmlist is in same order as the bcd import table, which is assumed
	-- to be in the order as the source file
	-- for each import we setup correspondence with parameter list
	-- if not on parameter list, we add it
	ForEachImport: PROC[ith: BcdOps.IMPHandle, iti: BcdDefs.IMPIndex] 
		RETURNS[stop: BOOL] = {
	spappl: MDModel.APPLSymbol ← NIL;
	fth: BcdOps.FTHandle;
	stop ← FALSE;
	FOR i: CARDINAL IN [0 .. ith.ngfi) DO
		dummymapseq[ith.gfi + i] ← [ind: imp, whichone: i];
		ENDLOOP;
	-- handle funny cases where two instances are imported
	-- of the same interface
	IF ith.gfi = dummymapseq.size THEN
		dummymapseq.size ← dummymapseq.size + ith.ngfi;
	fth ← @LOOPHOLE[bcdbase + bcdbase.ftOffset, BcdDefs.Base][ith.file];
	WHILE splist ~= NIL AND splist.first.stype ~= typeAPPL DO
		splist ← splist.rest;
		ENDLOOP;
	{
	impexpseq[imp] ← NIL;
	IF splist ~= NIL THEN {
		-- found import on list
		sptype: MDModel.TYPESymbol;
		splocinner: MDModel.LOCSymbol;
		spappl ← MDModel.NarrowToAPPL[splist.first];
		sptype ← MDModel.NarrowToTYPE[spappl.appltype];
		splocinner ← MDModel.LocForType[sptype];
		IF splocinner = NIL THEN 
			CWF.WF3["Error - in the parameter list of @%s, %s, the type of parameter %s, is undefined.\n"L, 
				fi.bcdFileName, sptype.typesym, spappl.applsym]
		ELSE {
			fiInner: Dir.FileInfo ← MDModel.GetFileInfo[splocinner];
			IF fiInner.bcdVers = TimeStamp.Null THEN
				MDComp.SetVersAndModulename[splocinner];
			IF fiInner.bcdVers = TimeStamp.Null THEN {
				IF LowLoader.EqualStringAndName[sptype.typeName, 
				namestring, ith.name] THEN {
      					fiInner.bcdVers ← fth.version;
					impexpseq[imp] ← spappl.interfaceseq;
					};
				}
			-- only check time since fi.bcdVers may be from a bcd not on the
			-- local disk
			ELSE IF fth.version.time = fiInner.bcdVers.time THEN 
				impexpseq[imp] ← spappl.interfaceseq;
			};
		splist ← splist.rest;
		GOTO out;
		};
	-- not found, must be hidden import, will connect it up but not add it to model
	LowLoader.CopyNStoLS[bcdFileName, bcdbase, fth.name];
	CWF.FWF3[MDMain.DebugWP, "FillIn hidden imports of %s of %v by %s.\n"L, bcdFileName,
		@fth.version, fi.bcdFileName];
	[spappl] ← MDModel.LookForInstBcd[bcdFileName, fth.version,
		symbolseq, spmodel, NIL];
	-- if spappl is NIL then it wasn't defined in the model
	impexpseq[imp] ← IF spappl = NIL THEN NIL ELSE spappl.interfaceseq;
	IF spappl = NIL THEN 
		CWF.WF3["Error - cannot find a variable for %s to import %s of %v.\n"L,
			fi.bcdFileName, bcdFileName, @fth.version];
	EXITS
	out => NULL;
	};
	-- impexpseq[imp] may be NIL
	IF FALSE -- impexpseq[imp] = NIL -- THEN {
		-- this message is uneccesary; it will be given when links cannot
		-- be resolved
		sym: STRING ← [100];
		LowLoader.CopyNStoLS[sym, bcdbase, ith.name]; 
		CWF.WF4["Warning - can't satisfy %s of %v (the %uth import) of %s.\n"L, 
			sym, @fth.version, @imp, fi.bcdFileName];
		};
	imp ← imp + 1;
	};
	
	ForEachModule: PROC[mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex] 
		RETURNS[stop: BOOL] = {
	resolved, bound: BOOL;
	clink: PrincOps.ControlLink;
	stop ← FALSE;
	resolved ← TRUE;
	[] ← LowLoader.OpenLinkSpace[fi.loadInfoSeq[mod].frame, mth, bcdbase];
	FOR i: CARDINAL IN [0 .. LowLoader.LinkSegmentLength[mth, bcdbase]) DO
		clink ← LowLoader.ReadLink[i];
		[clink, bound] ← NewLink[blink: LowLoader.IthLink[mth, i, bcdbase], 
			oldclink: clink, sploc: sploc];
		IF bound THEN 
			LowLoader.WriteLink[offset: i, link: clink]
		ELSE resolved ← FALSE;
		ENDLOOP;
	LowLoader.CloseLinkSpace[fi.loadInfoSeq[mod].frame];
	IF NOT resolved THEN fi.loadInfoSeq.linksresolved ← FALSE;
	mod ← mod + 1;
	};
	
IF fi = NIL OR fi.loadInfoSeq = NIL THEN RETURN;		-- must be defs file
bcdbase ← fi.loadInfoSeq.bcdbase;
namestring ← LOOPHOLE[bcdbase + bcdbase.ssOffset];
IF bcdbase.nImports = 0 THEN RETURN;	-- no imports
dummymapseq ← fi.loadInfoSeq.dummymapseq;
impexpseq ← fi.loadInfoSeq.impexpseq;
-- the first part of dummymapseq, the map between config gfi's and real gfi's,
--	has already been computed
-- set up map between dummygfi's and the import table
-- IF dummymapseq.size > bcdbase.firstdummy THEN ERROR;
dummymapseq.size ← bcdbase.firstdummy;	-- adjust for dummies to come
splist ← sploc.parmlist;
[] ← BcdOps.ProcessImports[bcdbase, ForEachImport];
impexpseq.size ← imp;
-- check to see that all parameters were consumed
WHILE splist ~= NIL AND splist.first.stype ~= typeAPPL DO
	splist ← splist.rest;
	ENDLOOP;
IF splist ~= NIL THEN 
	CWF.WF1["Error - more parameters to %s than I can handle.*n"L, fi.bcdFileName];
IF NOT fi.loadInfoSeq.linksresolved THEN {
	fi.loadInfoSeq.linksresolved ← TRUE;
	-- now run thru the frame links looking for imports to fill in
	[] ← BcdOps.ProcessModules[bcdbase, ForEachModule];
	};
};

NewLink: PROC[blink: BcdDefs.Link, oldclink: PrincOps.ControlLink,
	sploc: MDModel.LOCSymbol] RETURNS[newclink: PrincOps.ControlLink,
	 resolved: BOOL] = {
loadinfoseq: LowLoader.LoadInfoSeq;

	FindLink: PROC[blink: BcdDefs.Link] 
		RETURNS[PrincOps.ControlLink, BOOL] = {
	IF blink.gfi < loadinfoseq.bcdbase.firstdummy THEN {
		SELECT blink.vtag FROM
		proc0, proc1 => {
			rgfi: PrincOps.GFTIndex;
			rgfi ← loadinfoseq.dummymapseq[blink.gfi].ind;
			newclink ← LowLoader.ConvertLink[blink];
			newclink.gfi ← rgfi + 
				loadinfoseq.dummymapseq[blink.gfi].whichone;
			resolved ← rgfi ~= PrincOps.GFTNull;
			};
		var => {
			newclink ← LowLoader.FindVariableLink[blink, loadinfoseq, NIL, NIL];
			resolved ← newclink ~= PrincOps.NullLink;
			};
		ENDCASE => NULL;
		}
	ELSE {
		trueep: CARDINAL;
		intno: CARDINAL;
		interfaceseq: LowLoader.InterfaceSeq;
		intno ← loadinfoseq.dummymapseq[blink.gfi].ind;
		interfaceseq ← loadinfoseq.impexpseq[intno];
		trueep ← blink.ep + (loadinfoseq.dummymapseq[blink.gfi].whichone 
			* BcdDefs.EPLimit);
		-- import not satisfied?
		IF interfaceseq = NIL OR EmptyLink[interfaceseq[trueep].clink] THEN {
			ith: BcdOps.IMPHandle;
			fth: BcdOps.FTHandle;
			sym: STRING ← [100];
			ith ← GetImpHandle[loadinfoseq.bcdbase, intno];
			fth ← @LOOPHOLE[loadinfoseq.bcdbase + loadinfoseq.bcdbase.ftOffset, BcdDefs.Base][ith.file];
			LowLoader.CopyNStoLS[sym, loadinfoseq.bcdbase, ith.name];
			CWF.WF3["Warning - Unable to resolve import of item #%u from interface %s\n\tof %v "L,
				@trueep, sym, @fth.version];
			CWF.WF2["(the %uth import of  %s).\n"L,
				@intno, sploc.fi.bcdFileName];
			RETURN[oldclink, FALSE];
			};
		-- at this point module and variable links are
		-- set to their absolute addresses
		newclink ← interfaceseq[trueep].clink;
		resolved ← TRUE;
		};
	RETURN[newclink, resolved];
	};
	
loadinfoseq ← sploc.fi.loadInfoSeq;
newclink ← oldclink;
resolved ← FALSE;
SELECT blink.vtag FROM
proc0, proc1 => IF oldclink = PrincOps.UnboundLink THEN
	[newclink, resolved] ← FindLink[blink];
var => IF oldclink = PrincOps.NullLink THEN
	[newclink, resolved] ← FindLink[blink];
ENDCASE => newclink ← LOOPHOLE[blink.typeID];
};
	
-- intno starts at 0
GetImpHandle: PROC[bcdbase: BcdOps.BcdBase, intno: CARDINAL] 
	RETURNS[ith: BcdOps.IMPHandle] = {
RETURN[@LOOPHOLE[bcdbase + bcdbase.impOffset, BcdDefs.Base] 
	[LOOPHOLE[intno*SIZE[BcdDefs.IMPRecord], BcdDefs.IMPIndex]]];
};

-- call Paul Rovner's procedure to fixup the Cedar Atoms and Strings section
ProcessCedarBcds: PROC[spmodel: MDModel.MODELSymbol, 
	symbolseq: MDModel.SymbolSeq, replacement: BOOL] = {

	ProcAnalyze: PROC[sptop: MDModel.Symbol, spmodel: MDModel.MODELSymbol] 
		RETURNS[proceed: BOOL ← TRUE] = {
	sploc: MDModel.LOCSymbol;
	loadinfoseq: LowLoader.LoadInfoSeq;
	bcdbase: BcdOps.BcdBase;
	map: PilotLoadStateOps.Map;
	IF sptop.stype ~= typeLOC THEN RETURN;
	sploc ← MDModel.NarrowToLOC[sptop];
	IF sploc.fi = NIL OR sploc.fi.loadInfoSeq = NIL THEN RETURN;
	loadinfoseq ← sploc.fi.loadInfoSeq;
	bcdbase ← loadinfoseq.bcdbase;
	IF NOT bcdbase.extended THEN RETURN;
	IF replacement AND NOT loadinfoseq.mustreplace THEN RETURN;
	map ← DESCRIPTOR[Heap.MakeMDSNode[n: bcdbase.firstdummy * 
		SIZE[PrincOps.GFTIndex]], bcdbase.firstdummy];
	-- the dummy bcd #'s start at 1
	FOR i: CARDINAL IN [0 .. bcdbase.firstdummy) DO
		map[i] ← loadinfoseq.dummymapseq[i].ind;
		ENDLOOP;
	RTLoader.AcquireTypesAndLiterals[bcd: bcdbase, map: map];
	Heap.FreeMDSNode[p: BASE[map]];
	};
	
MDModel.TraverseTree[spmodel, symbolseq, ProcAnalyze, TRUE];
};

}.