-- LaurelLoaderCore.mesa Edited by Levin & Taft on April 8, 1983 9:32 AM
-- Copyright Xerox Corporation 1979, 1980
DIRECTORY
AltoDefs USING [CharsPerWord],
AltoFileDefs USING [FP, NullSN],
BcdDefs USING [
Base, BCD, CTIndex, CTNull, EPIndex, EPLimit, EVIndex, EVNull, EXPIndex, EXPNull,
FTIndex, FTSelf, IMPIndex, Link, MTIndex, MTNull, MTRecord, PackedString, TMIndex,
TMNull, TMRecord, UnboundLink, VarLimit, VersionStamp],
BcdOps USING [
BcdBase, CTHandle, EXPHandle, FTHandle, IMPHandle, MTHandle, ProcessConfigs,
ProcessExports, ProcessImports, ProcessModules, TMHandle],
ControlDefs USING [
ControlLink, ControlModule, GFT, GFTIndex, GFTNull, GlobalFrameHandle,
NullControl, NullGlobalFrame, NullLink, UnboundLink],
FrameOps USING [Alloc, MakeFsi],
InlineDefs USING [BITAND],
LoaderOps USING [
AllocateFrames, Binding, BindLink, CloseLinkSpace, DestroyMap, FindCode,
FindFiles, FindFrameIndex, GetGfi, InitBinding, FinalizeUtilities,
InitializeMap, InitializeUtilities, OpenLinkSpace, ReadLink, ReleaseBinding,
ReleaseFrames, WriteLink],
LoadStateFormat USING [BcdObject, ModuleInfo],
LoadStateOps USING [
BcdExports, ConfigIndex, EnterModule, GetModule, InputLoadState,
loadstate, Map, MapConfigToReal, ReleaseBcd, ReleaseLoadState, state, UpdateLoadState],
SegmentDefs USING [
DataSegmentAddress, DefaultMDSBase, DeleteDataSegment, DeleteFileSegment,
FileSegmentAddress, FileSegmentHandle, HardDown, HardUp, InsertFile,
MakeDataSegment, MakeSwappedIn, NewFileSegment, Read, Unlock, VMtoDataSegment],
StreamDefs USING [
CreateWordStream, DiskHandle, GetPosition, ReadBlock, SetIndex, SetPosition,
StreamPosition],
StringDefs USING [
AppendSubString, EqualSubStrings, SubStringDescriptor, SubString],
Storage USING [Words, FreeWords, PagesForWords];
LaurelLoaderCore: PROGRAM
IMPORTS
BcdOps, FrameOps, InlineDefs, LoaderOps, LoadStateOps, SegmentDefs, StreamDefs,
StringDefs, Storage
EXPORTS LoaderOps =
BEGIN OPEN BcdDefs, BcdOps;
Binding: TYPE = LoaderOps.Binding;
ConfigIndex: TYPE = LoadStateOps.ConfigIndex;
Map: TYPE = LoadStateOps.Map;
GlobalFrameHandle: TYPE = ControlDefs.GlobalFrameHandle;
ControlModule: TYPE = ControlDefs.ControlModule;
SSD: TYPE = StringDefs.SubStringDescriptor;
VersionMismatch: PUBLIC SIGNAL [name: STRING] = CODE;
New: PUBLIC PROCEDURE [bcd: BcdBase, framelinks, alloc: BOOLEAN]
RETURNS [cm: ControlDefs.ControlModule] =
BEGIN OPEN LoadStateOps, LoaderOps;
system: BcdBase ← NIL;
map: Map ← DESCRIPTOR[NIL, 0];
sMap: Map ← DESCRIPTOR[NIL, 0];
binding: Binding ← DESCRIPTOR[NIL, 0];
frames: POINTER ← NIL;
nbcds, i: CARDINAL;
resolved: BOOLEAN;
CleanUpNew: PROCEDURE =
BEGIN
DestroyMap[map];
[] ← ReleaseBinding[bcd, binding];
LoaderOps.FinalizeUtilities[];
ReleaseBcd[bcd];
ReleaseLoadState[];
RETURN
END;
SetupLoad: PROCEDURE [bcd: BcdBase] RETURNS [map: Map, nbcds: CARDINAL] =
BEGIN OPEN LoaderOps;
InitializeUtilities[bcd];
FindFiles[bcd];
resolved ← bcd.nImports = 0;
map ← InitializeMap[bcd];
nbcds ← InputLoadState[];
RETURN
END;
BEGIN
ENABLE UNWIND => BEGIN ReleaseFrames[bcd, frames, map]; CleanUpNew[]; END;
[map: map, nbcds: nbcds] ← SetupLoad[bcd];
frames ← AllocateFrames[bcd, alloc, framelinks];
cm ← AssignFrameAddresses[frames, bcd, map, nbcds, alloc, framelinks];
binding ← InitBinding[bcd];
BindImports[bcd, bcd, binding];
resolved ← ProcessLinks[bcd, bcd, map, binding, nbcds, TRUE];
binding ← ReleaseBinding[bcd, binding];
FOR i DECREASING IN [0..nbcds) DO
IF ~resolved AND BcdExports[i] THEN
BEGIN
ENABLE UNWIND => ReleaseScrunchedBcd[system];
system ← AcquireScrunchedBcd[i];
binding ← InitBinding[bcd];
BindImports[bcd, system, binding];
resolved ← ProcessLinks[bcd, system, map, binding, i, FALSE];
binding ← ReleaseBinding[bcd, binding];
ReleaseScrunchedBcd[system];
END;
system ← NIL;
ENDLOOP;
UpdateLoadState[nbcds, bcd];
IF bcd.nModules = 1 THEN cm ← LOOPHOLE[frames];
CleanUpNew[];
END;
END;
AssignFrameAddresses: PROCEDURE [
p: POINTER, bcd: BcdBase, map: Map, config: ConfigIndex,
alloc, allframelinks: BOOLEAN] RETURNS [ControlDefs.ControlModule] =
BEGIN
frame: GlobalFrameHandle ← p;
single: BOOLEAN ← bcd.nModules = 1;
ModuleSearch: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
BEGIN
gfi: ControlDefs.GFTIndex;
i: CARDINAL;
framelinks: BOOLEAN;
framelinks ← allframelinks OR mth.links = frame OR ~mth.code.linkspace;
IF ~single AND alloc THEN
BEGIN
p ← NextMultipleOfFour[p + 1];
(p - 1)↑ ← LoaderOps.FindFrameIndex[mth, framelinks];
END;
IF ~single AND framelinks THEN p ← p + mth.frame.length;
frame ← NextMultipleOfFour[p];
p ← frame + mth.framesize;
gfi ← LoaderOps.GetGfi[frame, mth.ngfi];
FOR i IN [0..mth.ngfi) DO
map[mth.gfi + i] ← gfi + i;
LoadStateOps.EnterModule[
gfi + i,
[gfi: mth.gfi + i, config: config, resolved: mth.frame.length = 0]];
ENDLOOP;
frame↑ ←
[gfi: gfi, unused: 0, alloced: alloc OR single, shared: FALSE,
copied: FALSE, started: FALSE, trapxfers: FALSE, codelinks: ~framelinks,
code:, global:];
frame.global[0] ← ControlDefs.NullControl;
RETURN[FALSE];
END;
[] ← BcdOps.ProcessModules[bcd, ModuleSearch];
LoaderOps.FindCode[bcd, map];
RETURN[AssignControlModules[bcd, map]];
END;
NextMultipleOfFour: PROCEDURE [n: POINTER] RETURNS [POINTER] =
BEGIN RETURN[n + InlineDefs.BITAND[-LOOPHOLE[n, INTEGER], 3B]]; END;
BindImports: PROCEDURE [bcd, system: BcdBase, binding: Binding] =
BEGIN
ForEachImport: PROCEDURE [ith: IMPHandle, iti: IMPIndex] RETURNS [BOOLEAN] =
BEGIN
i: CARDINAL;
iname, sysname: SSD;
issb, sysssb: POINTER TO BcdDefs.PackedString;
module: MTIndex;
export: EXPIndex;
ExpMatch: PROCEDURE [eth: EXPHandle, eti: EXPIndex] RETURNS [BOOLEAN] =
BEGIN OPEN StringDefs;
sysname.offset ← eth.name;
sysname.length ← sysssb.size[eth.name];
RETURN[
eth.port = ith.port AND EqualSubStrings[@iname, @sysname] AND
EqualVersions[bcd, system, ith.file, eth.file, @iname]]
END;
ModuleMatch: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
BEGIN OPEN StringDefs;
sysname.offset ← mth.name;
sysname.length ← sysssb.size[mth.name];
RETURN[
EqualSubStrings[@iname, @sysname] AND EqualVersions[
bcd, system, ith.file, mth.file, @iname]]
END;
issb ← LOOPHOLE[bcd + bcd.ssOffset];
iname ← [base: @issb.string, offset: ith.name, length: issb.size[ith.name]];
sysssb ← LOOPHOLE[system + system.ssOffset];
sysname.base ← @sysssb.string;
IF ith.port = interface THEN
BEGIN
export ← BcdOps.ProcessExports[system, ExpMatch].eti;
FOR i IN [0..ith.ngfi) DO
IF export = EXPNull THEN
binding[ith.gfi + i] ← [whichgfi: i, body: notbound[]]
ELSE binding[ith.gfi + i] ← [whichgfi: i, body: interface[export]];
ENDLOOP
END
ELSE
BEGIN
module ← BcdOps.ProcessModules[system, ModuleMatch].mti;
FOR i IN [0..ith.ngfi) DO
IF module = MTNull THEN
binding[ith.gfi + i] ← [whichgfi: i, body: notbound[]]
ELSE binding[ith.gfi + i] ← [whichgfi: i, body: module[module]];
ENDLOOP;
END;
RETURN[FALSE];
END;
[] ← BcdOps.ProcessImports[bcd, ForEachImport];
END;
EqualVersions: PROCEDURE [
bcd1, bcd2: BcdBase, fti1, fti2: BcdDefs.FTIndex, name: StringDefs.SubString]
RETURNS [BOOLEAN] =
BEGIN
v1, v2: POINTER TO BcdDefs.VersionStamp;
f1: FTHandle ← @LOOPHOLE[bcd1 + bcd1.ftOffset, Base][fti1];
f2: FTHandle ← @LOOPHOLE[bcd2 + bcd2.ftOffset, Base][fti2];
v1 ← IF fti1 = FTSelf THEN @bcd1.version ELSE @f1.version;
v2 ← IF fti2 = FTSelf THEN @bcd2.version ELSE @f2.version;
IF v1↑ = v2↑ THEN RETURN[TRUE];
BadVersion[name];
RETURN[FALSE];
END;
BadVersion: PROCEDURE [name: StringDefs.SubString] =
BEGIN
filename: STRING ← [40];
StringDefs.AppendSubString[filename, name];
SIGNAL VersionMismatch[filename];
END;
ProcessLinks: PROCEDURE [
bcd, system: BcdBase, map: Map, binding: Binding, config: ConfigIndex,
initial: BOOLEAN] RETURNS [BOOLEAN] =
BEGIN OPEN ControlDefs;
smtb: Base = LOOPHOLE[system + system.mtOffset];
setb: Base = LOOPHOLE[system + system.expOffset];
unresolved: BOOLEAN ← FALSE;
NewLink: PROCEDURE [old: ControlLink, link: Link]
RETURNS [new: ControlLink, resolved: BOOLEAN] =
BEGIN
gfi: GFTIndex ← 0;
FindLink: PROCEDURE [link: Link]
RETURNS [new: ControlLink, resolved: BOOLEAN] =
BEGIN
ep: EPIndex;
inside: BOOLEAN;
rgfi: GFTIndex ← GFTNull;
bindLink: LoaderOps.BindLink = binding[link.gfi];
new ← ControlDefs.UnboundLink;
IF (inside ← link.gfi < bcd.firstdummy) THEN
BEGIN new ← ConvertLink[link]; rgfi ← map[link.gfi] END
ELSE
WITH b: bindLink SELECT FROM
interface =>
BEGIN
e: EXPHandle = @setb[b.eti];
SELECT e.port FROM
interface =>
BEGIN
ep ← link.ep + (b.whichgfi*EPLimit);
link ← e.links[ep];
rgfi ← LoadStateOps.MapConfigToReal[link.gfi, config];
END;
ENDCASE;
END;
module =>
BEGIN
m: MTHandle = @smtb[b.mti];
link ← [variable[vgfi: m.gfi, var: 0, vtag: var]];
rgfi ← LoadStateOps.MapConfigToReal[m.gfi, config];
END;
ENDCASE;
SELECT link.vtag FROM
var => new ← FindVariableLink[inside, link, rgfi];
proc0, proc1 => BEGIN new ← ConvertLink[link]; new.gfi ← rgfi END;
ENDCASE;
RETURN[new: new, resolved: rgfi # GFTNull]
END;
FindVariableLink: PROCEDURE [inside: BOOLEAN, el: Link, rgfi: GFTIndex]
RETURNS [link: ControlLink] =
BEGIN
ep: CARDINAL;
evi: EVIndex;
evb: Base;
gfi: GFTIndex ← el.vgfi;
mth: MTHandle;
frame: GlobalFrameHandle;
FindModule: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
BEGIN
mgfi: GFTIndex ← mth.gfi;
IF gfi IN [mth.gfi..mgfi + mth.ngfi) THEN
BEGIN ep ← VarLimit*(gfi - mgfi); RETURN[TRUE] END;
RETURN[FALSE]
END;
mth ← BcdOps.ProcessModules[
IF inside THEN bcd ELSE system, FindModule].mth;
IF mth = NIL THEN RETURN[ControlDefs.NullLink];
evb ←
IF ~inside THEN LOOPHOLE[system + system.evOffset, Base]
ELSE LOOPHOLE[bcd + bcd.evOffset, Base];
frame ← ControlDefs.GFT[rgfi].frame;
IF (ep ← ep + el.var) = 0 THEN RETURN[LOOPHOLE[frame]];
IF (evi ← mth.variables) = EVNull THEN RETURN[ControlDefs.NullLink];
RETURN[LOOPHOLE[frame + evb[evi].offsets[ep]]];
END;
new ← old;
resolved ← TRUE;
SELECT link.vtag FROM
proc0, proc1 =>
IF old = ControlDefs.UnboundLink THEN
[new: new, resolved: resolved] ← FindLink[link];
var =>
IF old = ControlDefs.NullLink THEN
[new: new, resolved: resolved] ← FindLink[link];
ENDCASE => new ← LOOPHOLE[link.typeID];
RETURN
END;
ModuleSearch: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
BEGIN OPEN ControlDefs;
i: CARDINAL;
gfi: GFTIndex = map[mth.gfi];
frame: GlobalFrameHandle ← GFT[gfi].frame;
resolved, bound: BOOLEAN;
old, new: ControlLink;
info: LoadStateFormat.ModuleInfo ← LoadStateOps.GetModule[gfi];
IF frame = ControlDefs.NullGlobalFrame OR info.resolved THEN RETURN[FALSE];
LoaderOps.OpenLinkSpace[frame, mth];
IF initial THEN
FOR i IN [0..mth.frame.length) DO
LoaderOps.WriteLink[
offset: i,
link:
SELECT mth.frame.frag[i].vtag FROM
var, type => NullLink,
ENDCASE => UnboundLink];
ENDLOOP;
resolved ← TRUE;
FOR i IN [0..mth.frame.length) DO
old ← LoaderOps.ReadLink[i];
[new: new, resolved: bound] ← NewLink[link: mth.frame.frag[i], old: old];
IF bound THEN LoaderOps.WriteLink[offset: i, link: new]
ELSE resolved ← FALSE;
ENDLOOP;
FOR i IN [gfi..gfi + mth.ngfi) DO
info ← LoadStateOps.GetModule[i];
info.resolved ← resolved;
LoadStateOps.EnterModule[i, info];
ENDLOOP;
LoaderOps.CloseLinkSpace[frame];
RETURN[FALSE];
END;
[] ← BcdOps.ProcessModules[bcd, ModuleSearch];
RETURN[unresolved];
END;
ConvertLink: PROCEDURE [bl: Link] RETURNS [cl: ControlDefs.ControlLink] =
BEGIN
IF bl = UnboundLink THEN RETURN[ControlDefs.UnboundLink];
SELECT bl.vtag FROM
var => cl ← [procedure[gfi: bl.vgfi, ep: bl.var, tag: frame]];
proc0, proc1 => cl ← [procedure[gfi: bl.gfi, ep: bl.ep, tag: procedure]];
type => cl ← LOOPHOLE[bl.typeID];
ENDCASE;
RETURN
END;
ProcessTypeMap: PROCEDURE [
bcd: BcdBase, proc: PROC [TMHandle, TMIndex] RETURNS [BOOLEAN]]
RETURNS [tmh: TMHandle, tmi: TMIndex] =
BEGIN
tmb: Base = LOOPHOLE[bcd + bcd.tmOffset];
FOR tmi ← FIRST[TMIndex], tmi + SIZE[TMRecord] UNTIL tmi = bcd.tmLimit DO
IF proc[tmh ← @tmb[tmi], tmi] THEN RETURN; ENDLOOP;
RETURN[NIL, TMNull];
END;
CheckTypes: PROCEDURE [bcd1, bcd2: BcdBase] =
BEGIN
typeError: STRING = "Exported Type Clash"L;
typb1: Base = LOOPHOLE[bcd1 + bcd1.typOffset];
typb2: Base = LOOPHOLE[bcd2 + bcd2.typOffset];
TypeMap1: PROCEDURE [tmh1: TMHandle, tmi1: TMIndex] RETURNS [BOOLEAN] =
BEGIN
TypeMap2: PROCEDURE [tmh2: TMHandle, tmi2: TMIndex] RETURNS [BOOLEAN] =
BEGIN
IF tmh2.offset = tmh1.offset AND tmh2.version = tmh1.version THEN
BEGIN
IF typb1[tmh1.map] # typb2[tmh2.map] THEN
ERROR VersionMismatch[typeError];
RETURN[TRUE];
END
ELSE RETURN[FALSE];
END;
[] ← ProcessTypeMap[bcd2, TypeMap2];
RETURN[FALSE];
END;
[] ← ProcessTypeMap[bcd1, TypeMap1];
RETURN
END;
CMMapItem: TYPE = RECORD [cti: CTIndex, level: CARDINAL, cm: ControlDefs.ControlModule];
AssignControlModules: PROCEDURE [bcd: BcdBase, map: Map]
RETURNS [cm: ControlDefs.ControlModule] =
BEGIN OPEN ControlDefs;
ctb: Base ← LOOPHOLE[bcd + bcd.ctOffset];
mtb: Base ← LOOPHOLE[bcd + bcd.mtOffset];
cti: CTIndex;
mapIndex, maxLevel: CARDINAL ← 0;
i: CARDINAL;
cmMap: POINTER TO ARRAY [0..0) OF CMMapItem;
MapControls: PROCEDURE [cth: CTHandle, cti: CTIndex] RETURNS [BOOLEAN] =
BEGIN OPEN ControlDefs;
cm: ControlModule;
level: CARDINAL ← 0;
c: CTIndex;
IF cth.nControls = 0 THEN cm ← NullControl
ELSE {
i: CARDINAL;
cm.list ← FrameOps.Alloc[
FrameOps.MakeFsi[cth.nControls + SIZE[CARDINAL] + SIZE[ControlModule]]];
cm.list.nModules ← cth.nControls + 1;
FOR i IN [0..cth.nControls) DO
cm.list.frames[i+1] ← GFT[map[mtb[cth.controls[i]].gfi]].frame; ENDLOOP;
cm.multiple ← TRUE};
FOR c ← ctb[cti].config, ctb[c].config UNTIL c = 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: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
BEGIN OPEN ControlDefs;
frame: GlobalFrameHandle ← GFT[map[mth.gfi]].frame;
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
frame: GlobalFrameHandle ← GFT[map[1]].frame;
frame.global[0] ← NullControl;
RETURN[[frame[frame]]];
END;
cmMap ← Storage.Words[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) = NullControl THEN LOOP;
list.multiple ← FALSE;
IF (cti ← ctb[cmMap[index].cti].config) = CTNull THEN cm ← 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 = CTNull THEN {
cm ← GetModule[cmMap[i].cm]; EXIT};
ENDLOOP;
Storage.FreeWords[cmMap];
END;
SetLink: PROCEDURE [
cm: ControlModule, frame: GlobalFrameHandle] RETURNS [ControlModule] = {
t: ControlModule = frame.global[0];
frame.global[0] ← cm;
RETURN[IF t = ControlDefs.NullControl THEN [frame[frame]] ELSE t]};
GetModule: PROCEDURE [cm: ControlModule] RETURNS [ControlModule] = {
list: ControlModule;
DO
IF ~cm.multiple THEN RETURN[cm];
list ← cm;
list.multiple ← FALSE;
cm.frame ← list.list.frames[1];
ENDLOOP};
AcquireScrunchedBcd: PROCEDURE [config: ConfigIndex] RETURNS [bcd: BcdOps.BcdBase] =
BEGIN OPEN SegmentDefs, LoadStateOps, StreamDefs;
fp: POINTER TO AltoFileDefs.FP;
b: POINTER TO alto LoadStateFormat.BcdObject;
bcdHeaderSeg: FileSegmentHandle;
bcdH: BcdOps.BcdBase;
wordsForScrunchedBcd: CARDINAL;
bcdStream: DiskHandle;
bcdOrigin: StreamPosition;
bcdP: POINTER;
ReadTable: PROC [t: POINTER TO RECORD [offset, limit: CARDINAL]] = {
SetPosition[bcdStream, bcdOrigin + AltoDefs.CharsPerWord*t.offset];
[] ← ReadBlock[bcdStream, bcdP, t.limit];
t.offset ← bcdP - bcd;
bcdP ← bcdP + t.limit;
};
ReadAndCompressModuleTable: PROC = {
SetPosition[bcdStream, bcdOrigin + AltoDefs.CharsPerWord*bcd.mtOffset];
bcd.mtOffset ← bcdP - bcd;
FOR i: CARDINAL IN [0..bcd.nModules) DO
mth: BcdOps.MTHandle = LOOPHOLE[bcdP];
[] ← ReadBlock[bcdStream, bcdP, SIZE[BcdDefs.MTRecord]];
SetPosition[bcdStream, GetPosition[bcdStream] +
mth.frame.length * SIZE[BcdDefs.Link] * AltoDefs.CharsPerWord];
mth.frame.length ← 0;
bcdP ← bcdP + SIZE[BcdDefs.MTRecord];
ENDLOOP;
bcd.mtLimit ← LOOPHOLE[bcd.nModules * SIZE[BcdDefs.MTRecord]];
};
WITH object: loadstate.bcds[config] SELECT FROM
alto => b ← @object;
ENDCASE => ERROR;
fp ← IF b.fp.serial = AltoFileDefs.NullSN THEN @state.file.fp ELSE ERROR;
bcdHeaderSeg ← NewFileSegment[InsertFile[fp, Read], b.base, 1, Read];
MakeSwappedIn[seg: bcdHeaderSeg, base: DefaultMDSBase, info: HardDown];
bcdH ← FileSegmentAddress[bcdHeaderSeg];
wordsForScrunchedBcd ← SIZE[BcdDefs.BCD] + bcdH.ssLimit +
bcdH.nModules * SIZE[BcdDefs.MTRecord] + LOOPHOLE[bcdH.expLimit, CARDINAL] +
LOOPHOLE[bcdH.evLimit, CARDINAL] + LOOPHOLE[bcdH.ftLimit, CARDINAL];
bcd ← DataSegmentAddress[
MakeDataSegment[pages: Storage.PagesForWords[wordsForScrunchedBcd],
info: HardUp]];
bcd↑ ← bcdH↑;
bcdStream ← CreateWordStream[bcdHeaderSeg.file, Read];
SetIndex[bcdStream, [page: b.base - 1, byte: 0]];
bcdOrigin ← GetPosition[bcdStream];
Unlock[bcdHeaderSeg];
DeleteFileSegment[bcdHeaderSeg];
bcd.nImports ← 0;
bcd.typeExported ← FALSE;
bcd.ctLimit ← LOOPHOLE[0];
bcd.mtLimit ← LOOPHOLE[0];
bcd.impLimit ← LOOPHOLE[0];
bcd.sgLimit ← LOOPHOLE[0];
bcd.ntLimit ← LOOPHOLE[0];
bcd.typLimit ← LOOPHOLE[0];
bcd.tmLimit ← LOOPHOLE[0];
bcdP ← bcd + SIZE[BcdDefs.BCD];
ReadTable[LOOPHOLE[@bcd.ssOffset]];
ReadAndCompressModuleTable[];
ReadTable[LOOPHOLE[@bcd.expOffset]];
ReadTable[LOOPHOLE[@bcd.evOffset]];
ReadTable[LOOPHOLE[@bcd.ftOffset]];
bcdStream.destroy[bcdStream];
END;
ReleaseScrunchedBcd: PROCEDURE [bcd: BcdOps.BcdBase] =
BEGIN OPEN SegmentDefs;
DeleteDataSegment[VMtoDataSegment[bcd]];
END;
END...