-- LoaderImpl.Mesa, last edit January 6, 1983 2:19 pm
-- Mesa 7.0/ Pilot 6.0
-- procedures to load and start modules in a Model

-- this modeller loader is separate from the modelloer
-- so that it can maintain its own load state tru successive modellers
-- interfaceseqs and loadinfoseqs and dummymapseqs are all allocated here

-- 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

DIRECTORY
  BcdDefs: TYPE USING [Base, CTIndex, CTNull, EVIndex, FTIndex, FTSelf,
   GFTIndex, Link, MTIndex, NameRecord, NullLink, SGIndex, SpaceID, SPIndex,
   SPRecord, UnboundLink, VarLimit, VersionID],
  BcdOps: TYPE USING [BcdBase, CTHandle, EXPHandle, FTHandle, MTHandle, NameString,
   ProcessConfigs, ProcessModules, ProcessSegs, SGHandle, SPHandle],
  CWF: TYPE USING [FWF2, FWF4],
  Directory: TYPE USING [UpdateDates],
  Environment: TYPE USING [PageCount, PageNumber, wordsPerPage],
  File: TYPE USING [Capability, LimitPermissions, PageCount, PageNumber, read, write],
  Frame: TYPE USING [GetReturnLink],
  Heap: TYPE USING [FreeMDSNode, MakeMDSNode, systemMDSZone],
  Inline: TYPE USING [BITAND],
  IO: TYPE USING[Handle, PutChar],
  LowLoader: TYPE USING [DummyMapSeq, DummyMapSeqRecord, ImpExpSeq, ImpExpSeqRecord,
   InterfaceSeq, InterfaceSeqRecord, LoadInfoSeq, LoadInfoSeqRecord, ReplaceResult],
  Mopcodes: TYPE USING [zALLOC],
  PrincOps: TYPE USING [ControlLink, ControlModule, Frame, GFTIndex,
   GlobalFrameHandle, NullControl, NullLink, StateVector, UnboundLink],
  PrincOpsRuntime: TYPE USING [GetFrame, GFT],
  RTOS: TYPE USING [CheckForModuleReplacement],
  Runtime: TYPE USING [IsBound, ValidateGlobalFrame],
  RuntimeInternal: TYPE USING [Codebase, EnterGlobalFrame, MakeFsi],
  SDDefs: TYPE USING [SD, sStart, sSwapTrap],
  Space: TYPE USING [Create, defaultBase, Delete, Error, GetHandle, GetWindow, Handle, 
  	LongPointer, MakeReadOnly, MakeWritable, Map, nullHandle, PageCount, 
	PageFromLongPointer, virtualMemory, WindowOrigin],
  SpecialSpace: TYPE USING [CreateForCode, MakeResident],
  Subr: TYPE USING [CopyString, FindMappedSpace, FreeString, 
  	LongZone],
  TimeStamp: TYPE USING [Stamp],
  Trap: TYPE USING [ReadOTP];

LoaderImpl: MONITOR 
IMPORTS BcdOps, CWF, Directory, File, Frame, Heap, 
	Inline, IO, PrincOpsRuntime, RTOS, Runtime, RuntimeInternal, Space, 
	SpecialSpace, Subr, Trap
EXPORTS LowLoader
SHARES File = {

RTCallable: BOOL = TRUE;

-- MDS Usage!

waitCodeTrapCV: CONDITION;
-- link space data
links: LONG POINTER TO ARRAY[0 .. 0) OF PrincOps.ControlLink ← NIL;
writeable: BOOL ← FALSE;
long: BOOL ← FALSE;
-- end of MDS usage



  SGItem: TYPE = RECORD [sgh: BcdOps.SGHandle, space: Space.Handle];

  SGList: TYPE = DESCRIPTOR FOR ARRAY CARDINAL [0..0) OF SGItem;


LoadBcdAndCount: PUBLIC PROC[cap: File.Capability, bcdsfn: LONG STRING,
		oldnwords: CARDINAL]
	RETURNS[loadinfoseq: LowLoader.LoadInfoSeq, newnwords, nbcdheaderpages, 
		ncodepages: CARDINAL] = {
bcdbase: BcdOps.BcdBase;

	ForEachModule: PROC[mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
	 	RETURNS[stop: BOOL] = {
	usecodelinks: BOOL ← mth.linkLoc = code;
	stop ← FALSE;
	IF usecodelinks THEN 
		ncodepages ← ncodepages + (LinkSegmentLength[mth, bcdbase]
			/Environment.wordsPerPage)+1
	ELSE 	-- use frame links
		newnwords ← newnwords + LinkSegmentLength[mth, bcdbase];
	newnwords ← NextMultipleOfFour[newnwords];
	newnwords ← newnwords + mth.framesize;
	};

	CountSegments: PROC[sgh: BcdOps.SGHandle, sgi: BcdDefs.SGIndex]
	 	RETURNS[stop: BOOL] = {
	stop ← FALSE;
	IF sgh.class ~= code THEN RETURN;
	ncodepages ← ncodepages + sgh.pages;
	};

ncodepages ← 0;
newnwords ← oldnwords;
[bcdbase, nbcdheaderpages] ← LoadUpBcd[cap, 1];
loadinfoseq ← AllocateLoadInfoSeq[bcdbase];
loadinfoseq.bcdbase ← bcdbase;
loadinfoseq.size ← 0;
[] ← BcdOps.ProcessModules[bcdbase, ForEachModule];
[] ← BcdOps.ProcessSegs[bcdbase, CountSegments];
};

LoadFrame: PUBLIC PROC[loadinfoseq: LowLoader.LoadInfoSeq, oldframeptr: POINTER,
	window: IO.Handle, cap: File.Capability,
	beginning, ending: LONG CARDINAL, oldConfigGfi: PrincOps.GFTIndex] 
	RETURNS[newframeptr: POINTER, newConfigGfi: PrincOps.GFTIndex] = {
mod: CARDINAL ← 0;
dummymapseq: LowLoader.DummyMapSeq;
bcdbase: BcdOps.BcdBase;
ncodepages: CARDINAL;

	WFPut: PROC[ch: CHAR] = {
	window.PutChar[ch];
	};

	ForEachModule: PROC[mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
	 	RETURNS[stop: BOOL] = {
	optr: POINTER;
	diff: CARDINAL;
	usecodelinks: BOOL ← mth.linkLoc = code;
	mname: STRING ← [100];
	frame: PrincOps.GlobalFrameHandle;
	gfi: CARDINAL;
	n: CARDINAL;
	stop ← FALSE;
	IF LOOPHOLE[LONG[newframeptr], LONG CARDINAL] < beginning THEN ERROR;
	optr ← newframeptr;
	IF NOT usecodelinks THEN
		newframeptr ← newframeptr + LinkSegmentLength[mth, bcdbase];
	newframeptr ← NextMultipleOfFour[newframeptr];
	frame ← newframeptr;
	newframeptr ← newframeptr + mth.framesize;
	IF LOOPHOLE[LONG[newframeptr], LONG CARDINAL] > ending THEN ERROR;
	-- frame is zeroed when space is created
	gfi ← RuntimeInternal.EnterGlobalFrame[frame, mth.ngfi];
	frame↑ ← [gfi: gfi, copied: FALSE, alloced: FALSE, started: FALSE, 
		shared: FALSE, trapxfers: FALSE, codelinks: usecodelinks, global:, code:];
	loadinfoseq[mod] ← [frame, mth.framesize, mth.ngfi];
	newConfigGfi ← newConfigGfi + mth.ngfi;
	-- now store in cgfi to rgfi map
	FOR i: CARDINAL IN [0 .. mth.ngfi) DO
		dummymapseq[mth.gfi + i] ← [ind: gfi, whichone: i];
		ENDLOOP;
	dummymapseq.size ← dummymapseq.size + mth.ngfi;
	mod ← mod + 1;
	CopyNStoLS[mname, bcdbase, mth.name];
	n ← mth.framesize;
	CWF.FWF4[WFPut, "%s: gfi = %bB, gfh = %bB, framesize = %u, "L, 
		mname, @gfi, @frame, @n];
	n ← LinkSegmentLength[mth, bcdbase];
	CWF.FWF2[WFPut, "#%slinks = %u\n"L, 
		IF frame.codelinks THEN "code"L ELSE "frame"L, @n];
	diff ← newframeptr - optr;
	-- CWF.FWF3[WFPut, "Old = %bB, new = %bB, diff = %u.\n"L, @optr, @newframeptr, @diff];
	};

cap ← Directory.UpdateDates[cap, File.read];
newframeptr ← oldframeptr;
newConfigGfi ← oldConfigGfi;
loadinfoseq.configGfi ← newConfigGfi;
bcdbase ← loadinfoseq.bcdbase;
dummymapseq ← loadinfoseq.dummymapseq;
dummymapseq[0] ← [0, 0];
dummymapseq.size ← 1;	-- dummy module indices start at 1, not 0
[] ← BcdOps.ProcessModules[bcdbase, ForEachModule];
ncodepages ← FindAndMapInCode[bcdbase, 1, cap, loadinfoseq];
-- CWF.FWF1[WFPut, "%u code pages.\n"L, @ncodepages];
SetLinksToNull[bcdbase, loadinfoseq];
loadinfoseq.cm ← AssignControlModules[bcdbase, loadinfoseq];
loadinfoseq.size ← mod;
};

WaitForBroadcast: ENTRY PROC[frame: PrincOps.GlobalFrameHandle] = {
WHILE frame.code.out DO
	WAIT waitCodeTrapCV;
	ENDLOOP;
};

-- can only be called for modules (not configs)
LoadIncremental: PUBLIC ENTRY PROC[bcdcap: File.Capability, 
	loadinfoseq: LowLoader.LoadInfoSeq, window: IO.Handle] 
	RETURNS[replaceResult: LowLoader.ReplaceResult] = {
savemodellercode: PROC ← NIL;
codetrapframe: PrincOps.GlobalFrameHandle ← NIL;
{
ENABLE UNWIND => IF savemodellercode ~= NIL THEN
			SDDefs.SD[SDDefs.sSwapTrap] ← savemodellercode;
	
	WFPut: PROC[ch: CHAR] = {
	window.PutChar[ch];
	};

  ModellerCodeTrap: PROC = { 
    start: PROC[PrincOps.ControlModule];
    dest: PrincOps.ControlLink;
    state: PrincOps.StateVector;
    frame: PrincOps.GlobalFrameHandle;
    state ← STATE;
    dest ← Trap.ReadOTP[];
    state.dest ← Frame.GetReturnLink[];
    DO
      IF dest.proc THEN {
      	frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[dest.gfi]]; 
	EXIT}
      ELSE
        IF dest.indirect THEN dest ← dest.link↑
        ELSE {frame ← dest.frame.accesslink; EXIT}; -- frame

      ENDLOOP;
    IF frame = codetrapframe THEN {
    	-- this halts outside process until my procedure is finished
	WaitForBroadcast[frame];
	RETURN;
    	};
    IF ~frame.started THEN {
    	start ← LOOPHOLE[SDDefs.SD[SDDefs.sStart]];
	start[[frame[frame]]];
    	};
    frame.code.out ← FALSE;
    RETURN WITH state
    };

n: CARDINAL;
mname: STRING ← [100];
gfi: CARDINAL;
bcdbase: BcdOps.BcdBase;
mth: BcdOps.MTHandle;
frame: PrincOps.GlobalFrameHandle ← loadinfoseq[0].frame;

replaceResult ← ok;
IF loadinfoseq.size ~= 1 THEN RETURN[configNotReplaceable];
[bcdbase] ← LoadUpBcd[bcdcap, 1];
loadinfoseq.bcdbase ← bcdbase;
loadinfoseq.cm ← LOOPHOLE[frame];
-- recompute these since bcdbase.firstdummy, bcdbase.nDummies, and bcdbase.nImports
-- may have changed
ReSizeMaps[loadinfoseq];
mth ← @LOOPHOLE[bcdbase + bcdbase.mtOffset, BcdDefs.Base][FIRST[BcdDefs.MTIndex]];
-- checking
IF LOOPHOLE[frame + mth.framesize, CARDINAL] > 
	LOOPHOLE[NextMultipleOfFour[frame + loadinfoseq[0].framesize], CARDINAL] THEN 
		RETURN[frameTooBig];
IF mth.ngfi > loadinfoseq[0].ngfi THEN RETURN[ngfiTooBig];

-- now think of monitor:
-- set lock by setting code trap, then call check procedure
-- then do replacement, then release lock
-- this is to avoid the case where some local frames
-- are created after the call on RTOS.CheckForModuleReplacemeent
-- but before I swap the code
codetrapframe ← frame;
savemodellercode ← SDDefs.SD[SDDefs.sSwapTrap];
SDDefs.SD[SDDefs.sSwapTrap] ← ModellerCodeTrap;
frame.code.out ← TRUE;	-- force code trap
IF RTCallable 
AND Runtime.IsBound[RTOS.CheckForModuleReplacement] 
AND NOT RTOS.CheckForModuleReplacement[frame] THEN {
	SDDefs.SD[SDDefs.sSwapTrap] ← savemodellercode;
	RETURN[checkForMRFailed];
	};
[] ← FindAndMapInCode[bcdbase, 1, bcdcap, loadinfoseq];
-- havinge set the code base, we can now release the lock
frame.code.out ← FALSE;	
BROADCAST waitCodeTrapCV;
SDDefs.SD[SDDefs.sSwapTrap] ← savemodellercode;
savemodellercode ← NIL;

SetLinksToNull[bcdbase, loadinfoseq];
CopyNStoLS[mname, bcdbase, mth.name];
n ← mth.framesize;
gfi ← frame.gfi;
CWF.FWF4[WFPut, "%s: gfi = %bB, gfh = %bB, framesize = %u, "L, 
	mname, @gfi, @frame, @n];
n ← LinkSegmentLength[mth, bcdbase];
CWF.FWF2[WFPut, "#%slinks = %u\n"L, 
	IF frame.codelinks THEN "code"L ELSE "frame"L, @n];
RETURN[ok];
}};

-- old contents are preserved
ReSizeMaps: PROC[loadinfoseq: LowLoader.LoadInfoSeq] =
	{
	longzone: UNCOUNTED ZONE ← Subr.LongZone[];
	olddummymapseq: LowLoader.DummyMapSeq ← loadinfoseq.dummymapseq;
	oldimpexpseq: LowLoader.ImpExpSeq ← loadinfoseq.impexpseq;
	minSize: CARDINAL;
	
	loadinfoseq.dummymapseq ← longzone.NEW[LowLoader.DummyMapSeqRecord[
					loadinfoseq.bcdbase.firstdummy +loadinfoseq.bcdbase.nDummies]];
	loadinfoseq.impexpseq ← longzone.NEW[
		LowLoader.ImpExpSeqRecord[loadinfoseq.bcdbase.nImports]];
	minSize ← MIN[olddummymapseq.size, loadinfoseq.dummymapseq.maxsize];
	FOR i: CARDINAL IN [0 .. minSize) DO
		loadinfoseq.dummymapseq[i] ← olddummymapseq[i];
		ENDLOOP;
	loadinfoseq.dummymapseq.size ← minSize;
	minSize ← MIN[oldimpexpseq.size, loadinfoseq.impexpseq.maxsize];
	FOR i: CARDINAL IN [0 .. minSize) DO
		loadinfoseq.impexpseq[i] ← oldimpexpseq[i];
		ENDLOOP;
	loadinfoseq.impexpseq.size ← minSize;
	longzone.FREE[@olddummymapseq];
	longzone.FREE[@oldimpexpseq];
	};

SetLinksToNull: PROC[bcdbase: BcdOps.BcdBase, loadinfoseq: LowLoader.LoadInfoSeq] = {

	ForEachModule: PROC[mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
	 	RETURNS[stop: BOOL] = {
	-- set all the links to null
	frame: PrincOps.GlobalFrameHandle;
	stop ← FALSE;
	frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[
		loadinfoseq.dummymapseq[mth.gfi].ind]];
	Runtime.ValidateGlobalFrame[frame];
	[] ← OpenLinkSpace[frame, mth, bcdbase];
	FOR i: CARDINAL IN [0..LinkSegmentLength[mth, bcdbase]) DO
		WriteLink[
	    		offset: i,
	    		link: SELECT IthLink[mth, i, bcdbase].vtag FROM
	      			var, type => PrincOps.NullLink, 
	      			ENDCASE => PrincOps.UnboundLink];
		ENDLOOP;
	CloseLinkSpace[frame];
	};
	
[] ← BcdOps.ProcessModules[bcdbase, ForEachModule];
loadinfoseq.linksresolved ← FALSE;
};

BuildInterface: PUBLIC PROC[loadinfoseq: LowLoader.LoadInfoSeq, 
	eth: BcdOps.EXPHandle] RETURNS[interfaceseq: LowLoader.InterfaceSeq] = {
clink: PrincOps.ControlLink;
cgfi: PrincOps.GFTIndex;	-- dummy
bcdbase: BcdOps.BcdBase;
intname: STRING ← [100];
fth: BcdOps.FTHandle;
IF eth.size = 0 THEN RETURN[NIL];
bcdbase ← loadinfoseq.bcdbase;
CopyNStoLS[intname, bcdbase, eth.name];
interfaceseq ← AllocateInterfaceSeq[intname, eth.size];
FOR i: CARDINAL IN [0 .. eth.size) DO
	IF eth.links[i].vtag = var THEN {
		[clink] ← FindVariableLink[eth.links[i], loadinfoseq,
			 NIL, NIL];
		interfaceseq[i] ← [clink: clink, blink: BcdDefs.NullLink]
		}
	ELSE
		{
		realgfi: PrincOps.GFTIndex;
		realgfi ← loadinfoseq.dummymapseq[eth.links[i].gfi].ind;
		[clink, cgfi] ← ConvertDummyLinkToControlLink[eth.links[i], 
			realgfi, bcdbase, loadinfoseq];
		interfaceseq[i] ← [clink: clink, 
			blink: [procedure[gfi: cgfi, ep: eth.links[i].ep, tag: eth.links[i].tag]]];
		};
	ENDLOOP;
interfaceseq.size ← eth.size;
fth ← @LOOPHOLE[bcdbase + bcdbase.ftOffset, BcdDefs.Base][eth.file];
interfaceseq.versstamp ← fth.version;
};
	
-- can't be used for configs
BuildFramePtrInterface: PUBLIC PROC[bcdbase: BcdOps.BcdBase, 
	frame: PrincOps.GlobalFrameHandle] 
	RETURNS[interfaceseq: LowLoader.InterfaceSeq] = {
intname: STRING ← [100];
mth: BcdOps.MTHandle;
IF bcdbase.nModules ~= 1 THEN ERROR;
mth ← @LOOPHOLE[bcdbase + bcdbase.mtOffset, BcdDefs.Base][FIRST[BcdDefs.MTIndex]];
CopyNStoLS[intname, bcdbase, mth.name];
interfaceseq ← AllocateInterfaceSeq[intname, 1];
IF mth.file = BcdDefs.FTSelf THEN
	interfaceseq.versstamp ← bcdbase.version
ELSE {
	fth: BcdOps.FTHandle;
	fth ← @LOOPHOLE[bcdbase + bcdbase.ftOffset, BcdDefs.Base][mth.file];
	interfaceseq.versstamp ← fth.version;
	};
interfaceseq[0] ← [clink: LOOPHOLE[frame], blink: BcdDefs.NullLink];
interfaceseq.size ← 1;
};
	
-- used by both BuildInterface and the loadstate
FindVariableLink: PUBLIC PROC[blink: BcdDefs.Link, loadinfoseq: LowLoader.LoadInfoSeq,
	frame: PrincOps.GlobalFrameHandle, bcdbase: BcdOps.BcdBase] 
	RETURNS[clink: PrincOps.ControlLink] = {
trueep: CARDINAL;
evi: BcdDefs.EVIndex;
evb: BcdDefs.Base;
mth: BcdOps.MTHandle;

	FindModule: PROC [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex] 
		RETURNS [BOOL] =  BEGIN
	  mgfi: PrincOps.GFTIndex ← mth.gfi;
	  IF blink.vgfi IN [mth.gfi..mgfi + mth.ngfi) THEN
	    BEGIN trueep ← BcdDefs.VarLimit*(blink.vgfi - mgfi); RETURN[TRUE] END;
	  RETURN[FALSE]
	  END;

IF bcdbase = NIL THEN bcdbase ← loadinfoseq.bcdbase;
mth ← BcdOps.ProcessModules[bcdbase, FindModule].mth;
IF mth = NIL THEN RETURN[PrincOps.NullLink];
IF frame = NIL THEN
	frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[
		loadinfoseq.dummymapseq[blink.vgfi].ind]];
evb ← LOOPHOLE[bcdbase + bcdbase.evOffset];
trueep ← trueep + blink.var;
IF trueep = 0 THEN RETURN[LOOPHOLE[frame]];
evi ← mth.variables;
RETURN[LOOPHOLE[frame + evb[evi].offsets[trueep]]];
};

-- only works for exported BcdDefs.Links in the export table
ConvertDummyLinkToControlLink: PROC[bl: BcdDefs.Link, realgfi: CARDINAL,
	bcdbase: BcdOps.BcdBase, loadinfoseq: LowLoader.LoadInfoSeq] 
	RETURNS[cl: PrincOps.ControlLink, newcgfi: PrincOps.GFTIndex] = {
cgfi: PrincOps.GFTIndex;

	ForEachModule: PROC [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex] 
		RETURNS [BOOL] =  BEGIN
	  mgfi: PrincOps.GFTIndex ← mth.gfi;
	  IF cgfi IN [mth.gfi..mgfi + mth.ngfi) THEN  {
	    	newcgfi ← newcgfi + (cgfi - mgfi);
		realgfi ← realgfi + (cgfi - mgfi); 
		RETURN[TRUE];
		};
	  RETURN[FALSE]
	  END;

newcgfi ← loadinfoseq.configGfi;
IF bl = BcdDefs.UnboundLink THEN RETURN[PrincOps.UnboundLink, newcgfi];
SELECT bl.vtag FROM
var => {
	cgfi ← bl.vgfi;
	IF BcdOps.ProcessModules[bcdbase, ForEachModule].mth = NIL THEN
		RETURN[PrincOps.NullLink, newcgfi];
	cl ← [procedure[gfi: realgfi, ep: bl.var, tag: FALSE]];
	};
proc0, proc1 => {
	cgfi ← bl.gfi;
	IF BcdOps.ProcessModules[bcdbase, ForEachModule].mth = NIL THEN
		RETURN[PrincOps.UnboundLink, newcgfi];
	cl ← [procedure[gfi: realgfi, ep: bl.ep, tag: TRUE]];
	};
type => cl ← LOOPHOLE[bl.typeID];
ENDCASE;
};

EqualStringAndName: PUBLIC PROC[sym: LONG STRING, namestring: BcdOps.NameString,
	name: BcdDefs.NameRecord] RETURNS[equal: BOOL] = {
IF sym.length ~= namestring.size[name] THEN RETURN[FALSE];
FOR i: CARDINAL IN [0 .. sym.length) DO
	IF sym[i] ~= namestring.string.text[name + i] THEN RETURN[FALSE];
	ENDLOOP;
RETURN[TRUE];
};

CopyNStoLS: PUBLIC PROC[resultstr: LONG STRING, bcdbase: BcdOps.BcdBase, 
	name: BcdDefs.NameRecord] = {
namestring: BcdOps.NameString;
namestring ← LOOPHOLE[bcdbase + bcdbase.ssOffset];
IF namestring.size[name] > resultstr.maxlength THEN ERROR;
FOR i: CARDINAL IN [0 .. namestring.size[name]) DO
	resultstr[i] ← namestring.string.text[name + i];
	ENDLOOP;
resultstr.length ← namestring.size[name];
};

FindAndMapInCode: PROC[bcdbase: BcdOps.BcdBase, bcdSpaceBase: File.PageNumber,
	bcdCap: File.Capability, loadinfoseq: LowLoader.LoadInfoSeq] 
	RETURNS[ncodepages: CARDINAL] = {
sgList: SGList;

	GetCode: PROC[mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex] 
		RETURNS [stop: BOOL] =  {
	frame: PrincOps.GlobalFrameHandle;
	stop ← FALSE;
	frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[
		loadinfoseq.dummymapseq[mth.gfi].ind]];
	IF frame = NIL THEN ERROR;
	Runtime.ValidateGlobalFrame[frame];
	IF NOT bcdbase.tableCompiled AND mth.altoCode THEN 
		InvalidModule[bcdbase, mth];
	frame.code.longbase ← SgiToLP[bcdbase, mth.code.sgi, sgList] + mth.code.offset;
	IF frame.code.longbase = NIL THEN ERROR;
	frame.code.out ← TRUE;
	};
	

sgList ← FindSegments[bcdbase];
ncodepages ← AllocateCodeSpaces[bcdbase, sgList, bcdSpaceBase, bcdCap];
[] ← BcdOps.ProcessModules[bcdbase, GetCode
	! UNWIND => ReleaseCode[sgList]];
Heap.FreeMDSNode[z: Heap.systemMDSZone, p: BASE[sgList]];
};

Zero: PUBLIC PROC[lp: LONG POINTER, nwords: CARDINAL] = {
FOR i: CARDINAL IN [0 .. nwords) DO
	(lp + i)↑ ← 0;
	ENDLOOP;
};

AllocateLoadInfoSeq: PUBLIC PROC[bcdbase: BcdOps.BcdBase] 
	RETURNS[loadinfoseq: LowLoader.LoadInfoSeq] = {
longzone: UNCOUNTED ZONE ← Subr.LongZone[];
loadinfoseq ← longzone.NEW[LowLoader.LoadInfoSeqRecord[bcdbase.nModules]];
loadinfoseq.dummymapseq ← longzone.NEW[
	LowLoader.DummyMapSeqRecord[bcdbase.firstdummy +bcdbase.nDummies]];
loadinfoseq.impexpseq ← longzone.NEW[
	LowLoader.ImpExpSeqRecord[bcdbase.nImports]];
};

FreeLoadInfoSeq: PUBLIC PROC[loadinfoseq: LowLoader.LoadInfoSeq] = {
longzone: UNCOUNTED ZONE;
IF loadinfoseq = NIL THEN RETURN;
longzone ← Subr.LongZone[];
IF loadinfoseq.bcdbase ~= NIL THEN
	Space.Delete[Space.GetHandle[Space.PageFromLongPointer[loadinfoseq.bcdbase]]
		! Space.Error => CONTINUE];
longzone.FREE[@loadinfoseq.impexpseq];
longzone.FREE[@loadinfoseq.dummymapseq];
longzone.FREE[@loadinfoseq];
};

AllocateInterfaceSeq: PUBLIC PROC[intname: LONG STRING, size: CARDINAL] 
	RETURNS[interfaceseq: LowLoader.InterfaceSeq] = {
longzone: UNCOUNTED ZONE ← Subr.LongZone[];
interfaceseq ← longzone.NEW[LowLoader.InterfaceSeqRecord[size]];
interfaceseq.intname ← Subr.CopyString[intname];
};

FreeInterfaceSeq: PUBLIC PROC[interfaceseq: LowLoader.InterfaceSeq] = {
longzone: UNCOUNTED ZONE;
IF interfaceseq = NIL OR interfaceseq.isfromloadstate THEN RETURN;
longzone ← Subr.LongZone[];
Subr.FreeString[interfaceseq.intname];
longzone.FREE[@interfaceseq];
};

-- from [Ivy]<CedarLang>LowLoader>PilotLoaderSupport.Mesa

  ReleaseCode: PROC [sgList: SGList] =
    BEGIN
    i: CARDINAL;
    space1, space2: Space.Handle;
    space1 ← Subr.FindMappedSpace[sgList[i ← 0].space];
    i ← 1;
    DO
      DO
	IF i = LENGTH[sgList] THEN EXIT;
	IF (space2 ← Subr.FindMappedSpace[sgList[i].space]) # space1 THEN EXIT;
	i ← i + 1;
	ENDLOOP;
      Space.Delete[space1];
      IF i = LENGTH[sgList] THEN EXIT;
      space1 ← space2;
      i ← i + 1;
      ENDLOOP;
    RETURN;
    END;

  FindSegments: PROC [bcd: BcdOps.BcdBase] RETURNS [sgList: SGList] =
    BEGIN
    n: CARDINAL;
    sgItem: SGItem;
    CountSegs: PROC [sgh: BcdOps.SGHandle, sgi: BcdDefs.SGIndex] RETURNS [BOOL] = {
      IF sgh.class # code THEN RETURN[FALSE];
      IF sgh.file # BcdDefs.FTSelf THEN BadFile[bcd, sgh.file];
      n ← n + 1;
      RETURN[FALSE]};
    AddSeg: PROC [sgh: BcdOps.SGHandle, sgi: BcdDefs.SGIndex] RETURNS [BOOL] = {
      IF sgh.class # code THEN RETURN[FALSE];
      sgList[n] ← [sgh: sgh, space: Space.nullHandle];
      n ← n + 1;
      RETURN[FALSE]};
    SiftUp: PROC [low, high: CARDINAL] = {
      k, son: CARDINAL;
      sgItem: SGItem;
      k ← low;
      DO
	IF k*2 > high THEN EXIT;
	IF k*2 + 1 > high OR sgList[k*2 + 1 - 1].sgh.base < sgList[
	  k*2 - 1].sgh.base THEN son ← k*2
	ELSE son ← k*2 + 1;
	IF sgList[son - 1].sgh.base < sgList[k - 1].sgh.base THEN EXIT;
	sgItem ← sgList[son - 1];
	sgList[son - 1] ← sgList[k - 1];
	sgList[k - 1] ← sgItem;
	k ← son;
	ENDLOOP;
      RETURN};
    n ← 0;
    [] ← BcdOps.ProcessSegs[bcd, CountSegs];
    sgList ← DESCRIPTOR[
    	Heap.MakeMDSNode[z: Heap.systemMDSZone, n: n*SIZE[SGItem]], n];
    n ← 0;
    [] ← BcdOps.ProcessSegs[bcd, AddSeg];
    FOR n DECREASING IN [1..LENGTH[sgList]/2] DO
      SiftUp[n, LENGTH[sgList]] ENDLOOP;
    FOR n DECREASING IN [1..LENGTH[sgList]) DO
      sgItem ← sgList[1 - 1];
      sgList[1 - 1] ← sgList[n + 1 - 1];
      sgList[n + 1 - 1] ← sgItem;
      SiftUp[1, n];
      ENDLOOP;
    END;

  AllocateCodeSpaces: PROC [bcd: BcdOps.BcdBase, sgList: SGList,
  	bcdSpaceBase: File.PageNumber, bcdCap: File.Capability] 
	RETURNS[ncodepages: CARDINAL] =
    BEGIN
    start, end, startBase, pages, offset: CARDINAL;
    space, subSpace: Space.Handle;
    ncodepages ← 0;
    IF bcd.nModules = 1 THEN {
    	-- this is a bcd produced by the compiler
	biaspages: CARDINAL;
	mth: BcdOps.MTHandle;
	IF LENGTH[sgList] ~= 1 THEN ERROR;
	mth ← @LOOPHOLE[bcd + bcd.mtOffset, BcdDefs.Base][FIRST[BcdDefs.MTIndex]];
	-- non-0 if code links
	biaspages ← IF mth.linkLoc = code THEN (LinkSegmentLength[mth, bcd]/ Environment.wordsPerPage) + 1 ELSE 0;
	pages ← sgList[0].sgh.pages;
	ncodepages ← pages + biaspages;
	space ← SpecialSpace.CreateForCode[
		size: pages + biaspages, parent: Space.virtualMemory, 
		base: Space.defaultBase];
	-- if the bcd is being loaded with code links,
	-- the cedar compiler will have left room for them
	Space.Map[space: space,
		window: Space.WindowOrigin[
			file: File.LimitPermissions[bcdCap, File.read],
			base: bcdSpaceBase + sgList[0].sgh.base - 1]];
	sgList[0].space ← space;
	RETURN;
	};
    start ← end ← 0;
    DO
      startBase ← sgList[start].sgh.base;
      pages ← sgList[start].sgh.pages;
      DO
	end ← end + 1;
	IF end = LENGTH[sgList] OR pages + sgList[end].sgh.pages > 255 THEN
	  EXIT;
	pages ← pages + sgList[end].sgh.pages;
	ENDLOOP;
      space ← SpecialSpace.CreateForCode[
	size: pages, parent: Space.virtualMemory, base: Space.defaultBase];
      ncodepages ← ncodepages + pages;
      Space.Map[
	space: space,
	window: Space.WindowOrigin[
	file: File.LimitPermissions[bcdCap, File.read],
	base: bcdSpaceBase + sgList[start].sgh.base - 1]];
      offset ← 0;
      FOR index: CARDINAL IN [start..end) DO
	sgh: BcdOps.SGHandle ← sgList[index].sgh;
	sph: BcdOps.SPHandle ← FindSPHandle[bcd, sgh];
	IF sph = NIL THEN
	  subSpace ← Space.Create[size: sgh.pages, parent: space, base: offset]
	ELSE {
	  FOR sp: CARDINAL DECREASING IN [0..sph.length) DO
	    subSpace ← Space.Create[
	      size: sph.spaces[sp].pages, parent: space,
	      base: offset + sph.spaces[sp].offset];
	    IF sph.spaces[sp].resident THEN SpecialSpace.MakeResident[subSpace];
	    ENDLOOP};
	sgList[index].space ← subSpace;
	offset ← offset + sgh.pages;
	ENDLOOP;
      IF end = LENGTH[sgList] THEN EXIT ELSE start ← end;
      ENDLOOP;
    RETURN;
    END;

  FindSPHandle: PROC [bcd: BcdOps.BcdBase, sgh: BcdOps.SGHandle]
    RETURNS [sph: BcdOps.SPHandle] =
    BEGIN
    sgb: BcdDefs.Base = LOOPHOLE[bcd + bcd.sgOffset];
    spb: BcdDefs.Base = LOOPHOLE[bcd + bcd.spOffset];
    spi: BcdDefs.SPIndex;
    FOR spi ← FIRST[BcdDefs.SPIndex], 
      spi + SIZE[BcdDefs.SPRecord] + sph.length*SIZE[BcdDefs.SpaceID]
      UNTIL spi = bcd.spLimit DO
      sph ← @spb[spi]; IF @sgb[sph.seg] = sgh THEN RETURN[sph]; ENDLOOP;
    RETURN[NIL]
    END;

  SgiToLP: PROC [bcd: BcdOps.BcdBase, sgi: BcdDefs.SGIndex, sgList: SGList]
    RETURNS [LONG POINTER] =
    BEGIN
    sgb: BcdDefs.Base = LOOPHOLE[bcd + bcd.sgOffset];
    i: CARDINAL;
    FOR i IN [0..LENGTH[sgList]) DO
      IF @sgb[sgi] = sgList[i].sgh THEN
	RETURN[Space.LongPointer[sgList[i].space]];
      ENDLOOP;
    RETURN[NIL]
    END;

  BadCode: SIGNAL [name: STRING] = CODE;
  MissingCode: SIGNAL [name: STRING] = CODE;

  AppendName: PROC [s: STRING, ssb: BcdOps.NameString, name: BcdDefs.NameRecord] =
    BEGIN
    i: CARDINAL;
    FOR i IN [s.length..MIN[s.maxlength, ssb.size[name] + s.length]) DO
      s[i] ← ssb.string.text[name + i]; s.length ← s.length + 1; ENDLOOP;
    END;

 InvalidModule: PROC [bcd: BcdOps.BcdBase, mth: BcdOps.MTHandle] =
    BEGIN
    name: STRING ← [40];
    dummy: BOOL ← TRUE;
    AppendName[s: name, ssb: LOOPHOLE[bcd + bcd.ssOffset], name: mth.name];
    IF dummy THEN ERROR BadCode[name];
    END;

  BadFile: PROC [bcd: BcdOps.BcdBase, fti: BcdDefs.FTIndex] =
    BEGIN
    name: STRING ← [40];
    fth: BcdOps.FTHandle = @LOOPHOLE[bcd + bcd.ftOffset, BcdDefs.Base][fti];
    dummy: BOOL ← TRUE;
    AppendName[s: name, ssb: LOOPHOLE[bcd + bcd.ssOffset], name: fth.name];
    IF dummy THEN ERROR MissingCode[name];
    END;


  Alloc: PROC [CARDINAL] RETURNS [POINTER] = MACHINE CODE
    BEGIN Mopcodes.zALLOC END;

  InvalidFile: SIGNAL [File.Capability] = CODE;

  LoadUpBcd: PROC [file: File.Capability, offset: File.PageCount] 
  	RETURNS [bcd: BcdOps.BcdBase, pages: CARDINAL] =
    BEGIN
    bcdSpaceBase: File.PageNumber;
    bcdSpace: Space.Handle;
    bcdSpaceBase ← offset;
    bcdSpace ← Space.Create[size: 1, parent: Space.virtualMemory];
    Space.Map[space: bcdSpace, window: [file: file, base: bcdSpaceBase]];
    bcd ← Space.LongPointer[bcdSpace];
    pages ← bcd.nPages;
    IF bcd.versionIdent # BcdDefs.VersionID OR bcd.definitions 
    OR (NOT bcd.tableCompiled AND ~bcd.spare1) THEN
      ERROR InvalidFile[file ! UNWIND => Space.Delete[bcdSpace]];
    IF pages > 1 THEN
      BEGIN
      Space.Delete[bcdSpace];
      bcdSpace ← Space.Create[size: pages, parent: Space.virtualMemory];
      Space.Map[space: bcdSpace, window: [file: file, base: bcdSpaceBase]];
      bcd ← Space.LongPointer[bcdSpace];
      END;
    Space.MakeReadOnly[bcdSpace];	-- make bcd header spaces ReadOnly, as the code is already
    RETURN
    END;
  
  LinkSegmentLength: PUBLIC PROC[mth: BcdOps.MTHandle, bcd: BcdOps.BcdBase] 
  RETURNS[CARDINAL] =
    BEGIN
    WITH mth: mth SELECT FROM
      direct   =>  RETURN[mth.length];
      indirect =>  RETURN[LOOPHOLE[bcd + bcd.lfOffset, BcdDefs.Base][mth.links].length];
      multiple =>  RETURN[LOOPHOLE[bcd + bcd.lfOffset, BcdDefs.Base][mth.links].length];
     ENDCASE => ERROR;
    END;

  IthLink: PUBLIC PROC[mth: BcdOps.MTHandle, i: CARDINAL, bcd: BcdOps.BcdBase] 
  	RETURNS[BcdDefs.Link] =
    BEGIN
    WITH mth: mth SELECT FROM
      direct   =>  RETURN[mth.frag[i]];
      indirect =>  RETURN[LOOPHOLE[bcd + bcd.lfOffset, BcdDefs.Base][mth.links].frag[i]];
      multiple =>  RETURN[LOOPHOLE[bcd + bcd.lfOffset, BcdDefs.Base][mth.links].frag[i]];
     ENDCASE => ERROR;
    END;

-- link space operations

  OpenLinkSpace: PUBLIC PROC [frame: PrincOps.GlobalFrameHandle, 
  	mth: BcdOps.MTHandle, bcd: BcdOps.BcdBase ← NIL] 
	RETURNS[LONG POINTER] =
    BEGIN
    IF frame.codelinks THEN {
      long ← TRUE; 
      links ← RuntimeInternal.Codebase[LOOPHOLE[frame]];
      IF links = NIL THEN ERROR;
      }
    ELSE {
      long ← FALSE; 
      links ← LOOPHOLE[LONG[frame]]
      };
    links ← links - LinkSegmentLength[mth, bcd];
    writeable ← FALSE;
    RETURN[links];
    END;

  ReadLink: PUBLIC PROC [offset: CARDINAL] 
  RETURNS [link: PrincOps.ControlLink] =
    BEGIN RETURN[links[offset]]; END;

 WriteLink: PUBLIC PROC [offset: CARDINAL, link: PrincOps.ControlLink] = {
    IF long AND NOT writeable THEN {
      space: Space.Handle;
      cap: File.Capability;
      writeable ← TRUE;
      space ← Subr.FindMappedSpace[Space.GetHandle[
      		Space.PageFromLongPointer[links]]];
      cap ← Space.GetWindow[space].file;
      -- questionable????
      -- IF Inline.BITAND[cap.permissions, File.write] = 0 THEN    
      cap.permissions ← cap.permissions + File.write;
      Space.MakeWritable[space, cap]
      };
    links[offset] ← link};

  CloseLinkSpace: PUBLIC PROC [frame: PrincOps.GlobalFrameHandle] = {
    IF long AND writeable THEN
      Space.MakeReadOnly[Subr.FindMappedSpace[Space.GetHandle[
      		Space.PageFromLongPointer[links]]]]
  };



-- from CedarLoaderCore.Mesa

  NextMultipleOfFour: PROC [n: UNSPECIFIED] RETURNS [UNSPECIFIED] =
    BEGIN RETURN[n + Inline.BITAND[-LOOPHOLE[n, INTEGER], 3B]]; END;
    
  CMMapItem: TYPE = RECORD [cti: BcdDefs.CTIndex, cm: PrincOps.ControlModule, level: CARDINAL];

  AssignControlModules: PROC [bcd: BcdOps.BcdBase, loadinfoseq: LowLoader.LoadInfoSeq]
    RETURNS [cm: PrincOps.ControlModule] =
    BEGIN OPEN PrincOps;
    ctb: BcdDefs.Base ← LOOPHOLE[bcd + bcd.ctOffset];
    mtb: BcdDefs.Base ← LOOPHOLE[bcd + bcd.mtOffset];
    cti: BcdDefs.CTIndex;
    mapIndex, maxLevel: CARDINAL ← 0;
    i: CARDINAL;
    cmMap: POINTER TO ARRAY [0..0) OF CMMapItem;
    MapControls: PROC [cth: BcdOps.CTHandle, cti: BcdDefs.CTIndex] RETURNS [BOOL] =
      BEGIN OPEN PrincOps, PrincOpsRuntime;
      cm: ControlModule;
      c: BcdDefs.CTIndex;
      level: CARDINAL ← 0;
      IF cth.nControls = 0 THEN cm ← NullControl
      ELSE {
	mti: BcdDefs.MTIndex;
	i: CARDINAL;
	cm.list ← Alloc[
	  RuntimeInternal.MakeFsi[cth.nControls + SIZE[CARDINAL] + SIZE[ControlModule]]];
	cm.list.nModules ← cth.nControls + 1;
	FOR i IN [0..cth.nControls) DO
	  WITH v: cth.controls[i] SELECT FROM	-- new bcddefs
	  module =>  mti ← v.mti;
	  ENDCASE => ERROR;
	  -- mti ← cth.controls[i];		old bcddefs
	  cm.list.frames[i+1] ← 
	  	GetFrame[GFT[loadinfoseq.dummymapseq[mtb[mti].gfi].ind]];  
	  ENDLOOP;
	cm.multiple ← TRUE};
      FOR c ← ctb[cti].config, ctb[c].config UNTIL c = BcdDefs.CTNull DO
	level ← level + 1; ENDLOOP;
      cmMap[mapIndex] ← [cti: cti, cm: cm, level: level];
      mapIndex ← mapIndex + 1;
      maxLevel ← MAX[maxLevel, level];
      RETURN[FALSE];
      END;
    GetControl: PROC [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex] RETURNS [BOOL] =
      BEGIN OPEN PrincOps, PrincOpsRuntime;
      frame: GlobalFrameHandle ← GetFrame[GFT[loadinfoseq.dummymapseq[mth.gfi].ind]];
      IF mth.config # cti THEN RETURN[FALSE];
      IF frame.global[0] = NullControl THEN frame.global[0] ← GetModule[cm];
      RETURN[FALSE];
      END;
    IF bcd.nModules = 1 THEN
      BEGIN OPEN PrincOpsRuntime;
      frame: GlobalFrameHandle ← GetFrame[GFT[loadinfoseq.dummymapseq[1].ind]];
      frame.global[0] ← NullControl;
      RETURN[[frame[frame]]];
      END;
    cmMap ← Heap.MakeMDSNode[z: Heap.systemMDSZone, n: bcd.nConfigs*SIZE[CMMapItem]];
    [] ← BcdOps.ProcessConfigs[bcd, MapControls];
    FOR level: CARDINAL DECREASING IN [0..maxLevel] DO
      FOR index: CARDINAL IN [0..mapIndex) DO
	list: ControlModule;
	IF cmMap[index].level # level OR (cm ← cmMap[index].cm) = NullControl THEN LOOP;
	list ← cm;
	list.multiple ← FALSE;
	list.list.frames[1] ← SetLink[cm, list.list.frames[1]].frame;
	FOR i: CARDINAL IN [2..list.list.nModules) DO
	  list.list.frames[i] ←
	    SetLink[GetModule[[frame[list.list.frames[1]]]], list.list.frames[i]].frame;
	  ENDLOOP;
	cti ← cmMap[index].cti;
	[] ← BcdOps.ProcessModules[bcd, GetControl];
	ENDLOOP;
      ENDLOOP;
    FOR index: CARDINAL IN [0..mapIndex) DO
      parent: CARDINAL;
      list: ControlModule;
      IF (list ← cmMap[index].cm) = PrincOps.NullControl THEN LOOP;
      list.multiple ← FALSE;
      IF (cti ← ctb[cmMap[index].cti].config) = BcdDefs.CTNull THEN cm ← PrincOps.NullControl
      ELSE {
        FOR parent IN [0..mapIndex) DO IF cmMap[parent].cti = cti THEN EXIT; ENDLOOP;
        cm ← GetModule[cmMap[parent].cm]};
      list.list.frames[0] ← cm.frame;
      ENDLOOP;
    FOR i IN [0..mapIndex) DO
      IF ctb[cmMap[i].cti].config = BcdDefs.CTNull THEN {
	cm ← GetModule[cmMap[i].cm]; EXIT};
      ENDLOOP;
    Heap.FreeMDSNode[z: Heap.systemMDSZone, p: cmMap];
    END;

  SetLink: PROC [
    cm: PrincOps.ControlModule, frame: PrincOps.GlobalFrameHandle] 
    RETURNS [PrincOps.ControlModule] = {
    t: PrincOps.ControlModule = frame.global[0];
    frame.global[0] ← cm;
    RETURN[IF t = PrincOps.NullControl THEN [frame[frame]] ELSE t]};

  GetModule: PROC [cm: PrincOps.ControlModule] RETURNS [PrincOps.ControlModule] = {
    list: PrincOps.ControlModule;
    DO
      IF ~cm.multiple THEN RETURN[cm];
      list ← cm;
      list.multiple ← FALSE;
      cm.frame ← list.list.frames[1];
      ENDLOOP
    };

  ConvertLink: PUBLIC PROC [bl: BcdDefs.Link] RETURNS [cl: PrincOps.ControlLink] =
    BEGIN
    IF bl = BcdDefs.UnboundLink THEN RETURN[PrincOps.UnboundLink];
    SELECT bl.vtag FROM
      var => cl ← [procedure[gfi: bl.vgfi, ep: bl.var, tag: FALSE]];
      proc0, proc1 => cl ← [procedure[gfi: bl.gfi, ep: bl.ep, tag: TRUE]];
      type => cl ← LOOPHOLE[bl.typeID];
      ENDCASE;
    RETURN
    END;


}.