MBLoader.mesa
Edited by Sandman on 6-Aug-81 15:40:23
Edited by Lewis on 25-Sep-81 13:33:58
Edited by Levin on September 30, 1983 11:59 am
DIRECTORY
BcdDefs USING [
Base, BcdBase, FTIndex, FTNull, FTSelf, IMPHandle, IMPIndex, LinkFrag, MTHandle, NameRecord, NameString, ProcLimit, SGHandle, SGIndex, SPHandle, SPIndex],
BcdOps USING [ProcessImports, ProcessSegs, ProcessSpaces],
ConvertUnsafe USING [ToRope],
File USING [Capability],
IO USING [card, PutChar, PutF, PutRope, rope, STREAM],
MB USING [Abort, BHandle, FileItem, FileObject, Handle, MT, VirtualGlobalFrame],
MBLoaderOps USING [
AcquireBcd, EnumerateModules, FinishLoaderCore, FinishLoaderExtra, FinishLoadState, GetVirtualLinks, InitLoaderCore, InitLoaderExtra, InitLoadState, ModuleInfo, RealLinks, VirtualLinks],
MBVM USING [
AllocCode, Base, Code, CodeSeg, CopyRead, CopyWrite, LongCopyWrite, LongPointerFromSeg, LongRead, LongWrite],
PrincOps USING [
ControlLink, EPIndex, GFTIndex, GFTNull, GlobalFrame, GlobalFrameHandle, NullLink, UnboundLink, wordsPerPage],
Basics USING [DIVMOD],
Rope USING [Concat, Equal, Fetch, Find, FromProc, Length, ROPE, Substr],
Segments USING [
BaseFromSegment, DeleteSegment, EnumerateDirectory, FileFromSegment, FHandle, FPFromFile, FPHandle, GetFileTimes, InsertFile, LockFile, NewSegment, Read, SegmentAddress, SHandle, SwapIn, Unlock, UnlockFile],
Space USING [
Create, Delete, Handle, LongPointer, Map, nullHandle, Unmap, virtualMemory],
Time USING [Packed];
MBLoader: PROGRAM
IMPORTS
Basics, BcdOps, ConvertUnsafe, IO, MB, MBLoaderOps, MBVM, Rope, Segments, Space
EXPORTS MB, MBLoaderOps =
BEGIN
data: MB.Handle ← NIL;
InitLoader: PUBLIC PROC [h: MB.Handle] = {
data ← h;
MBLoaderOps.InitLoaderCore[h];
MBLoaderOps.InitLoaderExtra[h];
MBLoaderOps.InitLoadState[h];
};
FinishLoader: PUBLIC PROC = {
MBLoaderOps.FinishLoadState[];
MBLoaderOps.FinishLoaderExtra[];
MBLoaderOps.FinishLoaderCore[];
data ← NIL;
};
Locate code files for bcd
FileNotFound: PUBLIC SIGNAL [name: Rope.ROPE] = CODE;
FindFiles: PUBLIC PROC [loadee: MB.BHandle] = {
bcd: BcdDefs.BcdBase ← loadee.bcd;
ftb: BcdDefs.Base ← LOOPHOLE[bcd + bcd.ftOffset];
ssb: BcdDefs.NameString ← LOOPHOLE[bcd + bcd.ssOffset];
nFilesToFind: CARDINAL ← 0;
invalidFile: BOOLFALSE;
CheckAndAddCodeSeg: PROC [sgh: BcdDefs.SGHandle, sgi: BcdDefs.SGIndex]
RETURNS [BOOL] = {
IF sgh.class = code THEN {
f: MB.FileItem;
FOR f ← loadee.files, f.link UNTIL f = NIL DO
IF sgh.file = f.fti THEN RETURN[FALSE];
ENDLOOP;
f ← NEW[MB.FileObject ← [
link: loadee.files,
fti: sgh.file,
handle: NIL,
create: Time.Packed[0],
ext: FALSE]
];
loadee.files ← f;
SELECT sgh.file FROM
BcdDefs.FTSelf => f.handle ← Segments.FileFromSegment[loadee.bcdSegment];
BcdDefs.FTNull => NULL;
ENDCASE => { -- a file name that we must look up
offset: CARDINAL = ftb[sgh.file].name;
length : CARDINAL = ssb.size[ftb[sgh.file].name];
FOR i: CARDINAL IN [offset..offset+length) DO
IF ssb.string.text[i] = '. THEN {f.ext ← TRUE; EXIT};
ENDLOOP;
nFilesToFind ← nFilesToFind + 1;
};
};
RETURN[FALSE]
};
LookupCodeFiles: PROC = {
Segments.EnumerateDirectory[CheckIfInList];
complain about code files that were not found
FOR f: MB.FileItem ← loadee.files, f.link UNTIL f = NIL DO
IF f.handle = NIL AND f.fti ~= BcdDefs.FTNull THEN {
i: CARDINAL ← ftb[f.fti].name;
GetFileChar: SAFE PROC RETURNS [char: CHAR] = TRUSTED {
char ← ssb.string[i]; i ← i + 1};
name: Rope.ROPE ← Rope.FromProc[len: ssb.size[ftb[f.fti].name], p: GetFileChar];
IF f.ext THEN name ← name.Concat[".bcd"];
SIGNAL FileNotFound[name];
};
ENDLOOP;
};
CheckIfInList: PROC [fp: Segments.FPHandle, name: STRING] RETURNS [stop: BOOL] = {
dirName: Rope.ROPE ← ConvertUnsafe.ToRope[name];
bcdExt: Rope.ROPE = ".bcd";
FillInFileHandle: PROC [ext: BOOL] RETURNS [found: BOOLFALSE] = {
FOR f: MB.FileItem ← loadee.files, f.link UNTIL f = NIL DO
SELECT f.fti FROM
BcdDefs.FTSelf => f.create ← Segments.GetFileTimes[f.handle].create;
BcdDefs.FTNull => NULL;
ENDCASE => {
i: CARDINAL ← ftb[f.fti].name;
GetFileChar: SAFE PROC RETURNS [char: CHAR] = TRUSTED {
char ← ssb.string[i]; i ← i + 1};
file: Rope.ROPE = Rope.FromProc[len: ssb.size[ftb[f.fti].name], p: GetFileChar];
nameFromDirectory: Rope.ROPE ← dirName;
IF file.Fetch[file.Length[]-1] = '. THEN
nameFromDirectory ← nameFromDirectory.Concat["."];
IF ext = f.ext AND Rope.Equal[file, nameFromDirectory, FALSE] THEN {
CheckFileVersion: PROC = {
referentBcd: BcdDefs.BcdBase;
seg: Segments.SHandle = Segments.NewSegment[f.handle, 1, 1, Segments.Read];
Segments.SwapIn[seg];
referentBcd ← Segments.SegmentAddress[seg];
IF referentBcd.version ~= ftb[f.fti].version THEN {
data.typescript.PutF[
"\N! %g has incorrect version",
IO.rope[NameToRope[ftb[f.fti].name, bcd]]];
invalidFile ← TRUE;
};
Segments.Unlock[seg];
Segments.LockFile[f.handle];
Segments.DeleteSegment[seg];
Segments.UnlockFile[f.handle];
};
f.handle ← Segments.InsertFile[fp, Segments.Read];
f.create ← Segments.GetFileTimes[f.handle].create;
CheckFileVersion[];
nFilesToFind ← nFilesToFind - 1;
RETURN[TRUE]
};
};
ENDLOOP;
};
extIndex: INT;
IF dirName.Fetch[dirName.Length[]-1] = '. THEN -- ignore dot on end
dirName ← dirName.Substr[len: dirName.Length[] - 1];
extIndex ← dirName.Find[bcdExt, 0, FALSE];
IF extIndex ~= -1 AND extIndex = dirName.Length[] - bcdExt.Length[] THEN {
dirName ← dirName.Substr[len: extIndex];
IF FillInFileHandle[ext: FALSE] THEN RETURN[nFilesToFind = 0];
};
try using entire name
[] ← FillInFileHandle[ext: TRUE];
RETURN[nFilesToFind = 0]
};
[] ← BcdOps.ProcessSegs[bcd, CheckAndAddCodeSeg];
LookupCodeFiles[];
IF invalidFile THEN ERROR MB.Abort;
};
Load bcd's code segments into MBVM
FindCode: PUBLIC PROC [loadee: MB.BHandle] = {
bcd: BcdDefs.BcdBase = loadee.bcd;
mt: MB.MT = loadee.mt;
invalidCode: BOOLEANFALSE;
FindCodeSegment: PROC [mth: BcdDefs.MTHandle]
RETURNS [cseg: MBVM.CodeSeg] = {
sgh: BcdDefs.SGHandle = @LOOPHOLE[bcd+bcd.sgOffset, BcdDefs.Base][mth.code.sgi];
file: Segments.FHandle;
pages: CARDINAL;
FindSpace: PROC [sph: BcdDefs.SPHandle, spi: BcdDefs.SPIndex] RETURNS [BOOLEAN] = {
RETURN[sph.seg = mth.code.sgi]};
FOR i: CARDINAL IN [1..mt.length) DO
IF mt[i].mth.code.sgi = mth.code.sgi AND (cseg ← mt[i].code) ~= NIL THEN {
cseg.shared ← TRUE; -- code is shared and was already loaded
RETURN[cseg]
};
ENDLOOP;
file ← HandleForFile[sgh.file];
pages ← sgh.pages + sgh.extraPages;
cseg ← MBVM.AllocCode[
file: file, base: MBVM.Code, fileBase: sgh.base, pages: pages,
sph: BcdOps.ProcessSpaces[bcd, FindSpace].sph];
cseg.segment ← Segments.NewSegment[file, sgh.base, pages, Segments.Read];
};
HandleForFile: PROC [fti: BcdDefs.FTIndex] RETURNS [file: Segments.FHandle] = {
FOR f: MB.FileItem ← loadee.files, f.link UNTIL f = NIL DO
IF f.fti = fti THEN RETURN[f.handle];
ENDLOOP;
RETURN[NIL]
};
FOR gfi: PrincOps.GFTIndex IN [1..mt.length) DO
mth: BcdDefs.MTHandle = mt[gfi].mth;
cseg: MBVM.CodeSeg;
IF mth.gfi ~= gfi THEN LOOP;
IF mth.altoCode THEN {
data.typescript.PutF[
"\N! %g is compiled for the Alto!",
IO.rope[NameToRope[mth.name, bcd]]];
invalidCode ← TRUE;
LOOP
};
cseg ← FindCodeSegment[mth];
FOR i: CARDINAL IN [gfi..gfi+mth.ngfi) DO mt[i].code ← cseg; ENDLOOP;
ENDLOOP;
IF invalidCode THEN ERROR MB.Abort;
FOR gfi: PrincOps.GFTIndex IN [1..mt.length) DO
mth: BcdDefs.MTHandle;
frame: PrincOps.GlobalFrameHandle;
gf: PrincOps.GlobalFrame;
cseg: MBVM.CodeSeg;
[mth: mth, frame: frame, code: cseg] ← mt[gfi];
IF mth.gfi ~= gfi THEN LOOP;
MBVM.CopyRead[to: @gf, from: frame, nwords: SIZE[PrincOps.GlobalFrame]];
gf.code.longbase ← MBVM.LongPointerFromSeg[cseg] + mth.code.offset;
gf.code.out ← TRUE;
gf.shared ← cseg.shared;
MBVM.CopyWrite[from: @gf, to: frame, nwords: SIZE[PrincOps.GlobalFrame]];
ENDLOOP;
};
Unbound import processing
ProcessUnboundImports: PUBLIC PROC = {
typescript: IO.STREAM = data.typescript;
loadmap: IO.STREAM = data.loadmap;
anyUnbound, anyUnboundCodeLinks: BOOLFALSE;
CheckIfUnresolved: PROC [rgfi: PrincOps.GFTIndex, module: MBLoaderOps.ModuleInfo]
RETURNS [BOOL] = {
IF ~module.resolved THEN {
bh: MB.BHandle = MBLoaderOps.AcquireBcd[module.config];
vgf: PrincOps.GlobalFrameHandle = MB.VirtualGlobalFrame[bh.mt[module.module].frame];
IF vgf.gfi = rgfi THEN {
codeLinks: BOOL = vgf.codelinks;
bcd: BcdDefs.BcdBase = bh.bcd;
mth: BcdDefs.MTHandle = bh.mt[module.module].mth;
vLinks: MBLoaderOps.VirtualLinks = MBLoaderOps.GetVirtualLinks[bh, mth];
first: BOOLTRUE;
MapDummy: PROC [gfi: PrincOps.GFTIndex, ep: PrincOps.EPIndex]
RETURNS [imp: BcdDefs.IMPHandle, offset: CARDINAL] = {
CheckImport: PROC [imph: BcdDefs.IMPHandle, impi: BcdDefs.IMPIndex]
RETURNS [BOOL] = {
IF gfi IN [imph.gfi..imph.gfi+imph.ngfi) THEN {
imp ← imph;
offset ← (gfi-imph.gfi)*BcdDefs.ProcLimit + ep;
RETURN[TRUE]
};
RETURN[FALSE]
};
[] ← BcdOps.ProcessImports[bcd, CheckImport];
};
OpenLinkSpace[bh, mth];
FOR i: CARDINAL IN [0..LENGTH[vLinks]) DO
SELECT ReadLink[i] FROM
PrincOps.NullLink, PrincOps.UnboundLink => {
Things are quite confusing here. An unbound imported procedure or
variable will show up as a virtual link of type 'proc0' or 'proc1'.
An unbound imported program module will show up as a virtual link
of type 'var'. An unbound imported type will show up as a virtual
link of type 'proc0' or 'proc1', while a bound, imported type will
have type 'type'.
imp: BcdDefs.IMPHandle;
offset: CARDINAL;
SELECT vLinks[i].vtag FROM
type => LOOP;
var => {
IF vLinks[i].vgfi = PrincOps.GFTNull THEN LOOP; -- link was unbindable
[imp, offset] ← MapDummy[vLinks[i].vgfi, vLinks[i].var --should be 0--];
};
ENDCASE --proc-- => {
IF vLinks[i].gfi = PrincOps.GFTNull THEN LOOP; -- link was unbindable
[imp, offset] ← MapDummy[vLinks[i].gfi, vLinks[i].ep];
};
IF ~anyUnbound THEN {
loadmap.PutRope["\NUnbound Imports:\N"];
typescript.PutRope["! Warning: the following modules have unbound imports:\N"];
anyUnbound ← TRUE;
};
IF first THEN {
loadmap.PutF[" %g: ", IO.rope[NameToRope[mth.name, bcd]]];
typescript.PutF[" %g: ", IO.rope[NameToRope[mth.name, bcd]]];
first ← FALSE;
}
ELSE {loadmap.PutChar[',]; typescript.PutChar[',]};
loadmap.PutF[
" %g[%d]",
IO.rope[NameToRope[imp.name, bcd]], IO.card[offset]
];
typescript.PutF[
" %g[%d]",
IO.rope[NameToRope[imp.name, bcd]], IO.card[offset]
];
};
ENDCASE;
ENDLOOP;
IF ~first THEN {
loadmap.PutChar['\N];
typescript.PutChar['\N];
};
CloseLinkSpace[];
};
};
RETURN[FALSE]
};
[] ← MBLoaderOps.EnumerateModules[CheckIfUnresolved];
};
Link management
linkBase: PCL;
PCL: TYPE = LONG POINTER TO PrincOps.ControlLink;
linkFile: File.Capability;
linkSpace: Space.Handle;
linkSpaceBasePage, currentPage, offsetToLinks: CARDINAL;
OpenLinkSpace: PUBLIC PROC [loadee: MB.BHandle, mth: BcdDefs.MTHandle] = {
frame: PrincOps.GlobalFrameHandle = loadee.mt[mth.gfi].frame;
nLinks: CARDINAL = LinkFragLength[loadee, mth];
IF MB.VirtualGlobalFrame[frame].codelinks THEN {
cseg: Segments.SHandle = loadee.mt[mth.gfi].code.segment;
Segments.FPFromFile[Segments.FileFromSegment[cseg], @linkFile];
offsetToLinks ← mth.code.offset - nLinks;
linkSpaceBasePage ← Segments.BaseFromSegment[cseg];
IF File.GetAttributes[linkFile].type ~= DCSFileTypes.tLeaderPage THEN
linkSpaceBasePage ← linkSpaceBasePage - 1;
linkSpace ← Space.Create[size: 1, parent: Space.virtualMemory];
linkBase ← Space.LongPointer[linkSpace];
currentPage ← LAST[CARDINAL];
}
ELSE { -- frame links
linkSpace ← Space.nullHandle;
linkBase ← LOOPHOLE[LONG[frame - nLinks], PCL];
};
};
CloseLinkSpace: PUBLIC PROC = {
IF linkSpace ~= Space.nullHandle THEN {
Space.Delete[linkSpace]; linkSpace ← Space.nullHandle};
};
MoveToPage: PROC [page: CARDINAL] = {
IF currentPage ~= LAST[CARDINAL] THEN Space.Unmap[linkSpace];
Space.Map[linkSpace, [linkFile, linkSpaceBasePage+page]];
currentPage ← page;
};
WriteLinks: PUBLIC PROC [
loadee: MB.BHandle, mth: BcdDefs.MTHandle, links: MBLoaderOps.RealLinks] = {
nLinks: CARDINAL = LinkFragLength[loadee, mth];
OpenLinkSpace[loadee, mth];
IF linkSpace = Space.nullHandle THEN -- frame links at ls
MBVM.LongCopyWrite[from: LOOPHOLE[links], to: linkBase, nwords: nLinks]
ELSE {
FOR i: CARDINAL IN [0..nLinks) DO
WriteLink[offset: i, link: links[i]];
ENDLOOP;
CloseLinkSpace[];
};
};
WriteLink: PUBLIC PROC [offset: CARDINAL, link: PrincOps.ControlLink] = {
page: CARDINAL;
IF linkSpace = Space.nullHandle THEN {MBVM.LongWrite[linkBase+offset, link]; RETURN};
[page, offset] ← Basics.DIVMOD[offsetToLinks+offset, PrincOps.wordsPerPage];
IF page ~= currentPage THEN MoveToPage[page];
(linkBase+offset)^ ← link;
};
ReadLink: PUBLIC PROC [offset: CARDINAL] RETURNS [link: PrincOps.ControlLink] = {
page: CARDINAL;
IF linkSpace = Space.nullHandle THEN RETURN[MBVM.LongRead[linkBase+offset]];
[page, offset] ← Basics.DIVMOD[offsetToLinks+offset, PrincOps.wordsPerPage];
IF page ~= currentPage THEN MoveToPage[page];
RETURN[(linkBase+offset)^]
};
LinkFragLength: PUBLIC PROC [loadee: MB.BHandle, mth: BcdDefs.MTHandle]
RETURNS [nLinks: CARDINAL] = {
bcd: BcdDefs.BcdBase = loadee.bcd;
lfTable: BcdDefs.Base = LOOPHOLE[bcd + bcd.lfOffset];
WITH mth: mth SELECT FROM
direct => RETURN[mth.length];
indirect => RETURN[lfTable[mth.links].length];
multiple => RETURN[lfTable[mth.links].length];
ENDCASE;
};
GetVirtualLinks: PUBLIC PROC [loadee: MB.BHandle, mth: BcdDefs.MTHandle]
RETURNS [virtualLinks: MBLoaderOps.VirtualLinks] = {
bcd: BcdDefs.BcdBase = loadee.bcd;
linkFrag: LONG POINTER TO BcdDefs.LinkFrag;
lfTable: BcdDefs.Base = LOOPHOLE[bcd + bcd.lfOffset];
WITH mth: mth SELECT FROM
direct => RETURN[DESCRIPTOR[@mth.frag, mth.length]];
indirect => linkFrag ← @lfTable[mth.links];
multiple => linkFrag ← @lfTable[mth.links];
ENDCASE;
RETURN[DESCRIPTOR[linkFrag.frag]]
};
Utilities
NameToRope: PROC [name: BcdDefs.NameRecord, bcd: BcdDefs.BcdBase]
RETURNS [Rope.ROPE] = {
ssb: BcdDefs.NameString = LOOPHOLE[bcd + bcd.ssOffset];
i: CARDINAL ← name;
Proc: SAFE PROC RETURNS [char: CHAR] = TRUSTED {char ← ssb.string[i]; i ← i + 1};
RETURN[Rope.FromProc[len: ssb.size[name], p: Proc]]
};
END.