MBLoaderCore.mesa
Last modified by Sandman on 6-Aug-81 15:41:04
Last modified by Lewis on 23-Sep-81 15:59:10
Last modified by Levin on June 20, 1983 1:58 pm
DIRECTORY
Ascii USING [CR],
BcdDefs
USING [
Base, BcdBase, Link, CTHandle, CTIndex, CTNull, EVNull, FPHandle, FPIndex, ModuleIndex, MTHandle, MTIndex, NameString, VarLimit, VersionID],
BcdOps USING [ProcessConfigs, ProcessFramePacks, ProcessModules],
ConvertUnsafe USING [AppendRope],
IO USING [card, PutChar, PutF, PutFR, PutRope, rope, STREAM],
MB
USING [
Abort, AllocateFrames, BHandle, BIndex, EnumerateControlList, EnumerateFramePacks, EnumerateGlobalFrames, Error, Handle, ModuleInfo, ModuleInfoSequence, MT],
MBLoaderOps
USING [
Bind, ConfigIndex, EnterModule, FileNotFound, FindFiles, FindCode, GetModule, GetNextGFI, GetVirtualLinks, InputLoadState, LinkFragLength, ModuleInfo, ProcessUnboundImports, RealLinks, RealLinksTable, SetGFTEntry, UpdateLoadState, VirtualLinks, WriteLoadState, WriteLinks],
MBVM USING [AllocFile, Base, CopyWrite, FileSeg, HyperSpace, Read, Write],
PrincOps
USING [
AV, ControlLink, ControlModule, FrameCodeBase, GFTIndex, GlobalFrame, GlobalFrameHandle, NullControl, NullLink, UnboundLink],
PrincOpsUtils USING [BITAND],
RuntimeInternal USING [MakeFsi],
Segments
USING [
BaseFromSegment, DeleteSegment, FHandle, FileFromSegment, HardUp, MoveSegment, NewFile, NewSegment, PagesFromSegment, Read, SegmentAddress, SHandle, SwapIn, Unlock];
MBLoaderCore:
PROGRAM
IMPORTS
BcdOps, ConvertUnsafe, IO, MB, MBLoaderOps, MBVM, PrincOpsUtils, RuntimeInternal, Segments
EXPORTS MB, MBLoaderOps =
BEGIN
OPEN MB;
CMTable: TYPE = RECORD [SEQUENCE length: NAT OF CMEntry];
CMEntry: TYPE = RECORD [bh: MB.BHandle, cm: PrincOps.ControlModule];
data: MB.Handle ← NIL;
controlModules: REF CMTable ← NIL;
InitLoaderCore: PUBLIC PROC [h: MB.Handle] = {data ← h};
FinishLoaderCore:
PUBLIC
PROC = {
FOR i:
MB.BIndex
IN [0..data.inputBCDs.nBcds)
DO
loadee: MB.BHandle = data.inputBCDs.bcds[i];
IF loadee.bcdSegment ~=
NIL
THEN {
Segments.Unlock[loadee.bcdSegment];
Segments.DeleteSegment[loadee.bcdSegment];
};
ENDLOOP;
data ← NIL;
};
Load:
PUBLIC
PROC = {
typescript: IO.STREAM = data.typescript;
loadmap: IO.STREAM = data.loadmap;
controlModules ← NEW[CMTable[data.inputBCDs.nBcds]];
data.nModules ← 0;
FOR i:
MB.BIndex
IN [0..data.inputBCDs.nBcds)
DO
loadee: MB.BHandle = data.inputBCDs.bcds[i];
name: STRING ← [40];
missingCodeFile: BOOL ← FALSE;
config: MBLoaderOps.ConfigIndex;
ConvertUnsafe.AppendRope[to: name, from: loadee.name];
typescript.PutF["Loading %g...", IO.rope[loadee.name]];
IF (loadee.bcdSegment ← LoadBcd[Segments.NewFile[name]]) =
NIL
THEN {
typescript.PutF["\N! Invalid file %g", IO.rope[loadee.name]];
ERROR MB.Abort
};
loadmap.PutF["Global Frames for Modules in %g:\N", IO.rope[loadee.name]];
loadee.bcd ← Segments.SegmentAddress[loadee.bcdSegment];
IF ~data.germ
THEN {
loadee.bcdSeg ←
MBVM.AllocFile[
file: Segments.FileFromSegment[loadee.bcdSegment],
fileBase: Segments.BaseFromSegment[loadee.bcdSegment], base: MBVM.HyperSpace,
pages: Segments.PagesFromSegment[loadee.bcdSegment]];
loadee.bcdSeg.segment ← loadee.bcdSegment;
loadee.bcdSeg.bIndex ← i;
};
MBLoaderOps.FindFiles[loadee
! MBLoaderOps.FileNotFound => {
typescript.PutF["\N! Can't find file %g", IO.rope[name]];
missingCodeFile ← TRUE;
RESUME
}
];
IF missingCodeFile THEN {typescript.PutChar[Ascii.CR]; MB.Error["Missing code files"]};
config ← LoadModules[loadee];
typescript.PutRope["looking up code..."];
MBLoaderOps.FindCode[loadee];
typescript.PutRope["binding modules..."];
controlModules[i] ← [bh: loadee, cm: AssignControlModules[loadee]];
IF config = FIRST[MBLoaderOps.ConfigIndex] THEN JustRelocateLinks[loadee]
ELSE MBLoaderOps.Bind[loadee, config];
loadmap.PutChar[Ascii.CR];
typescript.PutRope["done\N"];
ENDLOOP;
data.header.controlList ← BuildTopLevelControlList[];
MBLoaderOps.WriteLoadState[];
data.nGFIs ← MBLoaderOps.GetNextGFI[reserve: 0];
loadmap.PutF["\NTotal of %d modules, %d GFIs\N",
IO.card[data.nModules], IO.card[data.nGFIs]];
MBLoaderOps.ProcessUnboundImports[];
typescript.PutRope["Finished loading.\N"];
};
LoadBcd:
PROC [bcdfile: Segments.FHandle]
RETURNS [bcdseg: Segments.SHandle] = {
b: BcdDefs.BcdBase;
pages: CARDINAL;
bcdseg ← Segments.NewSegment[file: bcdfile, base: 1, pages: 1, access: Segments.Read];
Segments.SwapIn[seg: bcdseg, info: Segments.HardUp];
b ← Segments.SegmentAddress[bcdseg];
IF b.versionIdent ~= BcdDefs.VersionID
OR b.definitions
--OR ~b.spare1--
THEN {
Segments.Unlock[bcdseg];
Segments.DeleteSegment[bcdseg];
RETURN[NIL]
};
IF (pages ← b.nPages) > 1
THEN {
Segments.Unlock[bcdseg];
Segments.MoveSegment[bcdseg, 1, pages];
Segments.SwapIn[seg: bcdseg, info: Segments.HardUp];
};
SetupModuleTable:
PROC [loadee:
MB.BHandle]
RETURNS [ngfi:
CARDINAL ← 0] = {
The firstdummy field in the bcd header is not to be trusted. If the bcd consists of a single module, it was probably generated by the Compiler. The Compiler sets the firstdummy field to an arbitrary value which is higher than the number of gfis needed to load the module. The ngfi field of the module table entry is the correct one to use. For a multi-module bcd (created by the Binder), bcd.firstdummy-1 is the number of gfis needed. The code sidesteps this ugliness by using a two-pass algorithm to set up the module table.
CountEntries:
PROC [mth: BcdDefs.MTHandle, mti: BcdDefs.MTIndex]
RETURNS [stop: BOOL ← FALSE] = {
ngfi ← ngfi + mth.ngfi;
};
SetEntriesForOneModule:
PROC [mth: BcdDefs.MTHandle, mti: BcdDefs.MTIndex]
RETURNS [stop: BOOL ← FALSE] = {
FOR i:
CARDINAL
IN [mth.gfi..mth.gfi+mth.ngfi)
DO
loadee.mt[i] ← MB.ModuleInfo[mth: mth, frame: NIL, code: NIL];
ENDLOOP;
};
[] ← BcdOps.ProcessModules[loadee.bcd, CountEntries];
loadee.mt ← NEW[MB.ModuleInfoSequence[ngfi+1]]; -- +1 because index 0 isn't used
loadee.mt[0] ← MB.ModuleInfo[mth: NIL, frame: NIL, code: NIL];
[] ← BcdOps.ProcessModules[loadee.bcd, SetEntriesForOneModule];
};
LoadModules:
PROC [loadee:
MB.BHandle]
RETURNS [config: MBLoaderOps.ConfigIndex] =
BEGIN
bcd: BcdDefs.BcdBase = loadee.bcd;
mt: MT;
ngfi: CARDINAL;
gfiOffset: CARDINAL;
framePtr: POINTER;
frameSpace: CARDINAL ← 0;
useFrameHeap: BOOL = (bcd.nModules = 1);
inFrameHeap: BOOL;
framesResident: BOOL ← FALSE;
ProcessOneFramePack:
PROC [fph: BcdDefs.FPHandle, fpi: BcdDefs.FPIndex]
RETURNS [BOOL] = {
mtb: BcdDefs.Base = LOOPHOLE[bcd + bcd.mtOffset];
frameSpace ← 0;
framesResident ← FALSE;
FOR i:
CARDINAL
IN [0..fph.length)
DO
GetFrameSize[@mtb[fph.modules[i]]];
ENDLOOP;
[framePtr, inFrameHeap] ←
MB.AllocateFrames[
size: NextMultipleOfFour[frameSpace],
single: useFrameHeap,
resident: framesResident OR ResidentFramePack[fph] -- specified in bootmesa file--];
FOR i:
CARDINAL
IN [0..fph.length)
DO
FrameInit[@mtb[fph.modules[i]]];
ENDLOOP;
RETURN[FALSE]
};
GetFrameSize:
PROC [mth: BcdDefs.MTHandle] = {
IF ~(mth.linkLoc = code
AND mth.code.linkspace)
THEN
-- frame links
frameSpace ← frameSpace + MBLoaderOps.LinkFragLength[loadee, mth];
frameSpace ← NextMultipleOfFour[frameSpace] + mth.framesize;
framesResident ← framesResident OR mth.residentFrame;
};
FrameInit:
PROC [mth: BcdDefs.MTHandle] = {
frame: PrincOps.GlobalFrameHandle;
gf: PrincOps.GlobalFrame;
gfi: PrincOps.GFTIndex = gfiOffset + mth.gfi; -- module's biased gfi
framelinks: BOOL = ~(mth.linkLoc = code AND mth.code.linkspace);
nLinks: CARDINAL = MBLoaderOps.LinkFragLength[loadee, mth];
data.nModules ← data.nModules + 1;
allocate global frame
IF framelinks THEN framePtr ← (framePtr + nLinks);
frame ← NextMultipleOfFour[framePtr];
framePtr ← (frame + mth.framesize);
record global frame handle in module table (mt)
FOR i:
CARDINAL
IN [0..mth.ngfi)
DO
mt[mth.gfi + i].frame ← frame;
ENDLOOP;
enter into GFT, and loadstate if not making germ
MBLoaderOps.SetGFTEntry[frame: frame, gfi: gfi, ngfi: mth.ngfi];
FOR i:
CARDINAL
IN [0..mth.ngfi)
DO
MBLoaderOps.EnterModule[
rgfi: (gfi + i),
module: MBLoaderOps.ModuleInfo[
config: config, module: (mth.gfi + i), resolved: (nLinks = 0)]
];
ENDLOOP;
initialize global frame
gf ← PrincOps.GlobalFrame[
gfi: gfi, copied: FALSE, alloced: inFrameHeap, shared: FALSE,
started: FALSE, trapxfers: FALSE, codelinks: ~framelinks, global: ,
code: PrincOps.FrameCodeBase[
offset[offset: mth.code.offset, highHalf: NIL]]
];
gf.code.out ← TRUE;
MBVM.CopyWrite[from: @gf, to: frame, nwords: SIZE[PrincOps.GlobalFrame]];
MBVM.Write[@frame.global[0], PrincOps.NullControl]; -- no control module
PrintLoadmapEntry[bcd: bcd, mth: mth, frame: frame, gfiOffset: gfiOffset];
};
OtherFrameSizes:
PROC [mth: BcdDefs.MTHandle, mti: BcdDefs.MTIndex]
RETURNS [
BOOL] = {
IF mt[mth.gfi].frame = NIL THEN GetFrameSize[mth];
RETURN[FALSE]
};
OtherFrameInit:
PROC [mth: BcdDefs.MTHandle, mti: BcdDefs.MTIndex]
RETURNS [
BOOL] = {
IF mt[mth.gfi].frame = NIL THEN FrameInit[mth];
RETURN[FALSE]
};
ngfi ← SetupModuleTable[loadee];
mt ← loadee.mt;
config ← MBLoaderOps.InputLoadState[];
gfi 0 in each input BCD is not used. Thus, gfiOffset is set one less than the first gfi actually allocated to us, since all subsequent indexing uses the 0-origin gfi's in the input BCD.
loadee.gfiOffset ← gfiOffset ← MBLoaderOps.GetNextGFI[reserve: ngfi] - 1;
[] ← BcdOps.ProcessFramePacks[bcd, ProcessOneFramePack];
frameSpace ← 0; framesResident ← FALSE;
[] ← BcdOps.ProcessModules[bcd, OtherFrameSizes];
IF frameSpace ~= 0
THEN {
-- there are frames not in frame packs
[framePtr, inFrameHeap] ←
MB.AllocateFrames[
size: NextMultipleOfFour[frameSpace],
single: useFrameHeap,
resident: framesResident OR AnyResidentGlobalFrames[loadee]];
[] ← BcdOps.ProcessModules[bcd, OtherFrameInit]};
END;
ResidentFramePack:
PROC [f: BcdDefs.FPHandle]
RETURNS [resident:
BOOL] = {
CheckOne:
PROC [bh:
MB.BHandle, fph: BcdDefs.FPHandle]
RETURNS [
BOOL] = {
RETURN[resident ← (f = fph)]};
MB.EnumerateFramePacks[resident, CheckOne];
};
AnyResidentGlobalFrames:
PROC [loadee:
MB.BHandle]
RETURNS [resident:
BOOL] = {
CheckOne:
PROC [bh:
MB.BHandle, mth: BcdDefs.MTHandle]
RETURNS [
BOOL] = {
RETURN[resident ← (bh = loadee)]};
MB.EnumerateGlobalFrames[resident, CheckOne];
};
NextMultipleOfFour:
PROC [x:
UNSPECIFIED]
RETURNS [
UNSPECIFIED] =
INLINE {
RETURN[x + PrincOpsUtils.BITAND[-LOOPHOLE[x, INTEGER], 3B]]};
PrintLoadmapEntry:
PROC [
bcd: BcdDefs.BcdBase,
mth: BcdDefs.MTHandle, frame: POINTER, gfiOffset: CARDINAL] = {
ssb: BcdDefs.NameString ← LOOPHOLE[bcd + bcd.ssOffset];
data.loadmap.PutF[" New: g = %06n ", IO.card[LOOPHOLE[frame.LONG]]];
FOR i:
CARDINAL
IN [mth.name .. mth.name+ssb.size[mth.name])
DO
data.loadmap.PutChar[ssb.string.text[i]];
ENDLOOP;
data.loadmap.PutF[" [%n]\N",
IO.card[LOOPHOLE[PrincOps.ControlLink[procedure[gfi: gfiOffset + mth.gfi, ep: 0, tag: FALSE]]].LONG]];
};
control module information for each (sub)config in bcd
CMMapItem:
TYPE =
RECORD [
cti: BcdDefs.CTIndex,
cm: PrincOps.ControlModule,
depth: CARDINAL
];
AssignControlModules:
PROC [loadee:
MB.BHandle]
RETURNS [cm: PrincOps.ControlModule] = {
bcd: BcdDefs.BcdBase = loadee.bcd;
mt: MB.MT = loadee.mt;
ctb: BcdDefs.Base ← LOOPHOLE[bcd + bcd.ctOffset];
mtb: BcdDefs.Base ← LOOPHOLE[bcd + bcd.mtOffset];
CMMap:
TYPE =
RECORD [
nConfigs: CARDINAL,
configs: SEQUENCE length: CARDINAL --subconfig#-- OF CMMapItem];
cmMap: REF CMMap;
maxDepth: CARDINAL ← 0;
cti: BcdDefs.CTIndex;
EnterControlInfoForConfig:
PROC [
cth: BcdDefs.CTHandle, cti: BcdDefs.CTIndex] RETURNS [stop: BOOL] = {
cm: PrincOps.ControlModule;
depth: CARDINAL ← 0; -- depth of config in Bcd's config structure
IF cth.nControls = 0 THEN cm ← PrincOps.NullControl
ELSE {
list.frames[0] is bkwds pointer to cm for enclosing config (st. first)
cm ← AllocateControlList[cth.nControls];
MBVM.Write[@cm.list.nModules, (cth.nControls + 1)];
FOR i:
CARDINAL
IN [0..cth.nControls)
DO
WITH cItem: cth.controls[i]
SELECT
FROM
module => MBVM.Write[@cm.list.frames[i+1], mt[mtb[cItem.mti].gfi].frame];
config => MB.Error["Configurations in control lists aren't supported yet."];
ENDCASE;
ENDLOOP;
cm.multiple ← TRUE;
FOR c: BcdDefs.CTIndex ← ctb[cti].config, ctb[c].config
UNTIL c = BcdDefs.CTNull
DO
depth ← depth + 1;
ENDLOOP;
};
maxDepth ← MAX[maxDepth, depth];
cmMap.configs[cmMap.nConfigs] ← CMMapItem[cti: cti, cm: cm, depth: depth];
cmMap.nConfigs ← cmMap.nConfigs + 1;
RETURN[FALSE]
};
IF bcd.nModules = 1
THEN {
frame: PrincOps.GlobalFrameHandle ← mt[1].frame;
MBVM.Write[@frame.global[0], PrincOps.NullControl];
RETURN [[frame[frame]]]
};
create subconfig# -> (subconfig & control module info) map
cmMap ← NEW[CMMap[bcd.nConfigs]];
cmMap.nConfigs ← 0;
[] ← BcdOps.ProcessConfigs[bcd, EnterControlInfoForConfig];
FOR depth:
CARDINAL
DECREASING
IN [0..maxDepth]
DO
FOR index:
CARDINAL
IN [0..cmMap.nConfigs)
DO
list, listHead, oldCm: PrincOps.ControlModule;
SetModulesControl:
PROC [
mth: BcdDefs.MTHandle, mti: BcdDefs.MTIndex] RETURNS [stop: BOOL] = {
frame: PrincOps.GlobalFrameHandle ← mt[mth.gfi].frame;
IF mth.config ~= cti THEN RETURN[FALSE];
IF
MBVM.Read[@frame.global[0]] = PrincOps.NullControl
THEN
MBVM.Write[@frame.global[0], GetLink[cm]];
RETURN[FALSE]
};
IF cmMap.configs[index].depth ~= depth
OR
(cm ← cmMap.configs[index].cm) = PrincOps.NullControl THEN LOOP;
list ← cm;
list.multiple ← FALSE;
cti ← cmMap.configs[index].cti;
list.frames[1]'s global[0] has ControlModule (possibly list) for config
listHead ← SetLink[cm: cm, frame: MBVM.Read[@list.list.frames[1]]];
MBVM.Write[@list.list.frames[1], listHead]; -- restore old value
FOR i:
CARDINAL
IN [2..ctb[cti].nControls+1)
DO
oldCm ← SetLink[cm: GetLink[listHead], frame: MBVM.Read[@list.list.frames[i]]];
MBVM.Write[@list.list.frames[i], oldCm];
ENDLOOP;
for each module, set global[0] to the actual cm frame for config
[] ← BcdOps.ProcessModules[bcd, SetModulesControl];
ENDLOOP;
ENDLOOP;
for each config, set backwards pointer to cm frame for the enclosing config
FOR index:
CARDINAL
IN [0..cmMap.nConfigs)
DO
parent: CARDINAL;
list: PrincOps.ControlModule ← cmMap.configs[index].cm;
IF list = PrincOps.NullControl THEN LOOP;
list.multiple ← FALSE;
IF (cti ← ctb[cmMap.configs[index].cti].config) = BcdDefs.CTNull
THEN
cm ← PrincOps.NullControl
ELSE {
FOR parent
IN [0..cmMap.nConfigs)
DO
IF cmMap.configs[parent].cti = cti THEN EXIT;
ENDLOOP;
cm ← GetLink[cmMap.configs[parent].cm];
};
MBVM.Write[@list.list.frames[0], cm];
ENDLOOP;
set "cm" to the actual cm frame for the outermost config
FOR i:
CARDINAL
IN [0..cmMap.nConfigs)
DO
IF ctb[cmMap.configs[i].cti].config = BcdDefs.CTNull
THEN {
cm ← GetLink[cmMap.configs[i].cm];
EXIT
};
ENDLOOP;
};
AllocFault: PUBLIC ERROR = CODE;
Alloc:
PUBLIC
PROC [fsi:
CARDINAL]
RETURNS [p:
POINTER] = {
DO
p ← MBVM.Read[PrincOps.AV + fsi];
SELECT
LOOPHOLE[p,
CARDINAL]
MOD 4
FROM
0 => EXIT; -- a free frame
1, 3 => ERROR AllocFault;
2 => fsi ← LOOPHOLE[p, CARDINAL]/4; -- use an fsi for larger frames
ENDCASE;
ENDLOOP;
MBVM.Write[PrincOps.AV + fsi, MBVM.Read[p]];
RETURN[p]
};
AllocateControlList:
PUBLIC
PROC [nControls:
CARDINAL]
RETURNS [cm: PrincOps.ControlModule] = {
fsi: CARDINAL = RuntimeInternal.MakeFsi[words: (nControls + 1) + 1];
cm.list ← Alloc[fsi
! AllocFault =>
MB.Error[IO.PutFR["Larger frame heap needed (fsi=%d) for control list", IO.card[fsi]]]
];
};
GetLink:
PROC [
cm: PrincOps.ControlModule] RETURNS [--frame--PrincOps.ControlModule] = {
list: PrincOps.ControlModule;
DO
-- search up backward pointers for the actual frame to start
IF ~cm.multiple THEN RETURN[cm];
list ← cm;
list.multiple ← FALSE;
cm ← MBVM.Read[@list.list.frames[1]];
ENDLOOP;
SetLink:
PROC [cm: PrincOps.ControlModule, frame: PrincOps.GlobalFrameHandle]
RETURNS [PrincOps.ControlModule] = {
old: PrincOps.ControlModule = MBVM.Read[@frame.global[0]];
MBVM.Write[@frame.global[0], cm];
RETURN[IF old ~= PrincOps.NullControl THEN old ELSE [frame[frame]]]
};
JustRelocateLinks:
PROC [loadee:
MB.BHandle] = {
bcd: BcdDefs.BcdBase = loadee.bcd;
mt: MB.MT = loadee.mt;
realLinks: MBLoaderOps.RealLinks = NEW[MBLoaderOps.RealLinksTable];
RelocateLinksForModule:
PROC [mth: BcdDefs.MTHandle, mti: BcdDefs.MTIndex]
RETURNS [BOOL] = {
mi: BcdDefs.ModuleIndex = mth.gfi;
frame: PrincOps.GlobalFrameHandle ← mt[mi].frame;
resolved: BOOL ← TRUE;
virtualLinks: MBLoaderOps.VirtualLinks ← MBLoaderOps.GetVirtualLinks[loadee, mth];
IF
LENGTH[virtualLinks] ~= 0
THEN {
-- relocate external links
FOR i:
CARDINAL
IN [0..
LENGTH[virtualLinks])
DO
link: BcdDefs.Link ← virtualLinks[i];
SELECT link.vtag
FROM
proc1, proc0 =>
IF link.gfi >= bcd.firstdummy
THEN
{resolved ← FALSE; realLinks[i] ← PrincOps.UnboundLink}
ELSE realLinks[i] ← ConvertLink[link];
var =>
IF link.gfi >= bcd.firstdummy
THEN
{resolved ← FALSE; realLinks[i] ← PrincOps.NullLink}
ELSE realLinks[i] ← ConvertVariableLink[link];
ENDCASE => {resolved ← FALSE; realLinks[i] ← PrincOps.NullLink};
ENDLOOP;
MBLoaderOps.WriteLinks[loadee: loadee, mth: mth, links: realLinks];
};
FOR i:
CARDINAL
IN [mi..mi+mth.ngfi)
DO
info: MBLoaderOps.ModuleInfo ← MBLoaderOps.GetModule[i];
info.resolved ← resolved;
MBLoaderOps.EnterModule[i, info];
ENDLOOP;
RETURN[FALSE]
};
ConvertVariableLink:
PROC [link: BcdDefs.Link]
RETURNS [PrincOps.ControlLink] = {
mth: BcdDefs.MTHandle;
frame: PrincOps.GlobalFrameHandle;
mi: BcdDefs.ModuleIndex = link.gfi;
evb: BcdDefs.Base = LOOPHOLE[bcd + bcd.evOffset];
vp: CARDINAL;
[mth: mth, frame: frame] ← mt[mi];
IF mi >= bcd.firstdummy THEN RETURN[PrincOps.NullLink];
vp ← BcdDefs.VarLimit*(mi - mth.gfi) + link.var;
IF vp = 0 THEN RETURN[LOOPHOLE[frame]];
IF mth.variables = BcdDefs.EVNull THEN RETURN[PrincOps.NullLink]
ELSE RETURN[LOOPHOLE[frame + evb[mth.variables].offsets[vp]]]
};
MBLoaderOps.UpdateLoadState[FIRST[MBLoaderOps.ConfigIndex], loadee];
[] ← BcdOps.ProcessModules[bcd, RelocateLinksForModule];
};
ConvertLink:
PROC [bl: BcdDefs.Link]
RETURNS [PrincOps.ControlLink] =
INLINE {
RETURN[LOOPHOLE[bl]]};
BuildTopLevelControlList:
PROC
RETURNS [cm: PrincOps.ControlModule] = {
nControls: CARDINAL ← 0;
index: CARDINAL ← 1;
CountControls:
PROC [bh:
MB.BHandle, cth: BcdDefs.CTHandle]
RETURNS [
BOOL ← FALSE] = {
IF cth.nControls ~= 0 THEN nControls ← nControls + 1;
};
FillInControls:
PROC [bh:
MB.BHandle, cth: BcdDefs.CTHandle]
RETURNS [
BOOL ←
FALSE] = {
IF cth.nControls ~= 0
THEN {
FOR i:
NAT
IN [0..controlModules.length)
DO
IF controlModules[i].bh = bh
THEN {
MBVM.Write[@cm.list.frames[index], controlModules[i].cm];
index ← index + 1;
EXIT
};
ENDLOOP;
};
};
[] ← MB.EnumerateControlList[CountControls];
IF nControls = 0 THEN RETURN[PrincOps.NullControl];
cm ← AllocateControlList[nControls];
MBVM.Write[@cm.list.nModules, nControls + 1];
MBVM.Write[@cm.list.frames[0], PrincOps.NullControl];
[] ← MB.EnumerateControlList[FillInControls];
cm.multiple ← TRUE;
};
END.