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 April 5, 1983 2:44 pm
DIRECTORY
BcdDefs
USING [
Base, EPLimit, FTIndex, FTNull, FTSelf, IMPIndex, LinkFrag, NameRecord, SGIndex, SPIndex],
BcdOps
USING [
BcdBase, IMPHandle, MTHandle, NameString, ProcessImports, ProcessSegs, ProcessSpaces, SGHandle, SPHandle],
Environment USING [wordsPerPage],
File USING [Capability],
Inline USING [DIVMOD],
LongString
USING [
AppendString, AppendSubString, EquivalentSubStrings, SubStringDescriptor],
MB USING [Abort, BHandle, BIndex, FileItem, FileObject, Handle, MT, VirtualGlobalFrame],
MBLoaderOps
USING [
AcquireBcd, EnumerateModules, FinishLoaderCore, FinishLoaderExtra, FinishLoadState, GetVirtualLinks, InitLoaderCore, InitLoaderExtra, InitLoadState, ModuleInfo, ReleaseBcd, VirtualLinks],
MBOut USING [Char, CR, Decimal, Line, SP],
MBTTY USING [Handle, PutChar, PutCR, PutDecimal, PutLine, PutString],
MBVM
USING [
AllocCode, Base, Code, CodeSeg, CopyRead, CopyWrite, LongCopyWrite, LongPointerFromSeg, LongRead, LongWrite],
PrincOps
USING [
ControlLink, EPIndex, GFTIndex, GFTNull, GlobalFrame, GlobalFrameHandle, NullLink, UnboundLink],
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
BcdOps, Inline, MB, MBLoaderOps, MBOut, MBTTY, MBVM, Segments, Space, String: LongString
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[];
FreeFileLists[];
data ← NIL;
};
FreeFileLists:
PROC = {
FOR i:
MB.BIndex
IN [0..data.inputBCDs.nBcds)
DO
loadee: MB.BHandle = data.inputBCDs.bcds[i];
DO
f: MB.FileItem ← loadee.files;
IF f = NIL THEN EXIT;
loadee.files ← f.link;
data.zone.FREE[@f];
ENDLOOP
ENDLOOP;
Locate code files for bcd
FileNotFound: PUBLIC SIGNAL [name: STRING] = CODE;
FindFiles:
PUBLIC
PROC [loadee:
MB.BHandle] = {
bcd: BcdOps.BcdBase ← loadee.bcd;
ftb: BcdDefs.Base ← LOOPHOLE[bcd + bcd.ftOffset];
ssb: BcdOps.NameString ← LOOPHOLE[bcd + bcd.ssOffset];
nFilesToFind: CARDINAL ← 0;
invalidFile: BOOL ← FALSE;
CheckAndAddCodeSeg:
PROC [sgh: BcdOps.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 ← data.zone.
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 {
ssd: String.SubStringDescriptor ← [
base: @ssb.string,
offset: ftb[f.fti].name,
length: ssb.size[ftb[f.fti].name]
];
name: STRING ← [40];
String.AppendSubString[name, @ssd];
IF f.ext THEN String.AppendString[name, ".bcd"L];
SIGNAL FileNotFound[name];
};
ENDLOOP;
};
CheckIfInList:
PROC [fp: Segments.FPHandle, name:
--LONG--
STRING]
RETURNS [stop: BOOL] = {
dirName: String.SubStringDescriptor;
bcdExt: String.SubStringDescriptor ← [base: "bcd"L, offset: 0, length: 3];
FillInFileHandle:
PROC [ext:
BOOLEAN]
RETURNS [found:
BOOL ←
FALSE] = {
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 => {
file: String.SubStringDescriptor ← [
base: @ssb.string,
offset: ftb[f.fti].name,
length: ssb.size[ftb[f.fti].name]
];
CheckFileVersion:
PROC = {
referentBcd: BcdOps.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 {
MBTTY.PutCR[data.ttyHandle]; MBTTY.PutChar[data.ttyHandle, '!];
OutName[ftb[f.fti].name, bcd];
MBTTY.PutString[data.ttyHandle, " has incorrect version"L];
invalidFile ← TRUE;
};
Segments.Unlock[seg];
Segments.LockFile[f.handle];
Segments.DeleteSegment[seg];
Segments.UnlockFile[f.handle];
};
lastCharIsDot: BOOLEAN = file.base[file.offset+file.length-1] = '.;
IF lastCharIsDot THEN dirName.length ← dirName.length + 1;
IF ext = f.ext
AND String.EquivalentSubStrings[@file, @dirName]
THEN {
f.handle ← Segments.InsertFile[fp, Segments.Read];
f.create ← Segments.GetFileTimes[f.handle].create;
CheckFileVersion[];
nFilesToFind ← nFilesToFind - 1;
RETURN[TRUE]
};
IF lastCharIsDot THEN dirName.length ← dirName.length - 1;
};
ENDLOOP;
};
FOR i:
CARDINAL
IN [0..name.length)
DO
IF name[i] = '.
THEN {
IF name.length-i # 4 THEN EXIT;
dirName ← [base: name, offset: i+1, length: 3];
IF ~String.EquivalentSubStrings[@dirName, @bcdExt] THEN EXIT;
dirName.offset ← 0; dirName.length ← i;
IF FillInFileHandle[ext: FALSE] THEN RETURN[nFilesToFind = 0];
EXIT
};
ENDLOOP;
try using entire name
dirName ← [base: name, offset: 0, length: name.length-1]; -- ignore dot on end
[] ← 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: BcdOps.BcdBase = loadee.bcd;
mt: MB.MT = loadee.mt;
invalidCode: BOOLEAN ← FALSE;
FindCodeSegment:
PROC [mth: BcdOps.MTHandle]
RETURNS [cseg: MBVM.CodeSeg] = {
sgh: BcdOps.SGHandle = @LOOPHOLE[bcd+bcd.sgOffset, BcdDefs.Base][mth.code.sgi];
file: Segments.FHandle;
pages: CARDINAL;
FindSpace:
PROC [sph: BcdOps.SPHandle, spi: BcdDefs.SPIndex]
RETURNS [
BOOLEAN] = {
RETURN[sph.seg = mth.code.sgi]};
FOR i:
CARDINAL
IN [1..
LENGTH[mt])
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..
LENGTH[mt])
DO
mth: BcdOps.MTHandle = mt[gfi].mth;
cseg: MBVM.CodeSeg;
IF mth.gfi ~= gfi THEN LOOP;
IF mth.altoCode
THEN {
MBTTY.PutCR[data.ttyHandle]; MBTTY.PutChar[data.ttyHandle, '!];
OutName[mth.name, bcd];
MBTTY.PutString[data.ttyHandle, " is compiled for the Alto!"L];
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..
LENGTH[mt])
DO
mth: BcdOps.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 = {
tty: MBTTY.Handle = data.ttyHandle;
anyUnbound, anyUnboundCodeLinks: BOOL ← FALSE;
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.gfi].frame];
IF vgf.gfi = rgfi
THEN {
codeLinks: BOOL = vgf.codelinks;
bcd: BcdOps.BcdBase = bh.bcd;
mth: BcdOps.MTHandle = bh.mt[module.gfi].mth;
vLinks: MBLoaderOps.VirtualLinks = MBLoaderOps.GetVirtualLinks[bh, mth];
first: BOOL ← TRUE;
MapDummy:
PROC [gfi: PrincOps.GFTIndex, ep: PrincOps.EPIndex]
RETURNS [imp: BcdOps.IMPHandle, offset: CARDINAL] = {
CheckImport:
PROC [imph: BcdOps.IMPHandle, impi: BcdDefs.IMPIndex]
RETURNS [BOOL] = {
IF gfi
IN [imph.gfi..imph.gfi+imph.ngfi)
THEN {
imp ← imph;
offset ← (gfi-imph.gfi)*BcdDefs.EPLimit + 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: BcdOps.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 {
MBOut.CR[];
MBOut.Line["Unbound Imports (* = module has code links):"L]; MBOut.CR[];
anyUnbound ← TRUE;
};
IF ~anyUnboundCodeLinks
AND codeLinks
THEN {
MBTTY.PutLine[tty, "!Warning: the following modules have code links and unbound imports:"L];
anyUnboundCodeLinks ← TRUE;
};
IF first
THEN {
MBOut.SP[];
IF codeLinks THEN MBOut.Char['*] ELSE MBOut.SP[];
OutNameToLoadmap[mth.name, bcd];
MBOut.Char[':];
IF codeLinks
THEN {
MBTTY.PutString[tty, " "L];
OutName[mth.name, bcd];
MBTTY.PutChar[tty, ':];
};
first ← FALSE;
}
ELSE {MBOut.Char[',]; IF codeLinks THEN MBTTY.PutChar[tty, ',]};
MBOut.Char[' ]; OutNameToLoadmap[imp.name, bcd];
MBOut.Char['[]; MBOut.Decimal[offset]; MBOut.Char[']];
IF codeLinks
THEN {
MBTTY.PutChar[tty, ' ]; OutName[imp.name, bcd];
MBTTY.PutChar[tty, '[]; MBTTY.PutDecimal[tty, offset]; MBTTY.PutChar[tty, ']];
};
};
ENDCASE;
ENDLOOP;
IF ~first THEN {MBOut.CR[]; IF codeLinks THEN MBTTY.PutCR[tty]};
CloseLinkSpace[];
};
MBLoaderOps.ReleaseBcd[bh];
};
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: BcdOps.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: BcdOps.MTHandle,
links: LONG POINTER TO ARRAY [0..0) OF PrincOps.ControlLink] = {
nLinks: CARDINAL = LinkFragLength[loadee, mth];
OpenLinkSpace[loadee, mth];
IF linkSpace = Space.nullHandle
THEN
-- frame links at ls
MBVM.LongCopyWrite[from: 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] ← Inline.DIVMOD[offsetToLinks+offset, Environment.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] ← Inline.DIVMOD[offsetToLinks+offset, Environment.wordsPerPage];
IF page # currentPage THEN MoveToPage[page];
RETURN[(linkBase+offset)^]
};
LinkFragLength:
PUBLIC
PROC [loadee:
MB.BHandle, mth: BcdOps.MTHandle]
RETURNS [nLinks: CARDINAL] = {
bcd: BcdOps.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: BcdOps.MTHandle]
RETURNS [virtualLinks: MBLoaderOps.VirtualLinks] = {
bcd: BcdOps.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
OutName:
PROC [name: BcdDefs.NameRecord, bcd: BcdOps.BcdBase] = {
ssb: BcdOps.NameString = LOOPHOLE[bcd + bcd.ssOffset];
FOR i:
CARDINAL
IN [name..name+ssb.size[name])
DO
MBTTY.PutChar[data.ttyHandle, ssb.string[i]];
ENDLOOP;
OutNameToLoadmap:
PROC [name: BcdDefs.NameRecord, bcd: BcdOps.BcdBase] = {
ssb: BcdOps.NameString = LOOPHOLE[bcd + bcd.ssOffset];
FOR i:
CARDINAL
IN [name..name+ssb.size[name])
DO
MBOut.Char[ssb.string[i]];
ENDLOOP;