MBOutput.mesa
Edited by Sandman on 6-Aug-81 15:41:43
Edited by Lewis on 25-Sep-81 15:01:46
Edited by Levin on April 5, 1983 3:21 pm
DIRECTORY
BcdOps USING [MTHandle],
BootFile USING [
Entry, Header, maxEntriesPerHeader, maxEntriesPerTrailer, Trailer, currentVersion],
Environment USING [bytesPerPage, wordsPerPage],
Inline USING [HighHalf, LongMult, LowHalf],
LongString USING [AppendString],
MB USING [BHandle, DoAllModules, DumpSegs, Handle, StartControlLink, Zero],
MBOut USING [CR, FileName, NumberFormat, Char, Line, LongNumber, Number, Spaces, Text],
MBStorage USING [Pages, FreePages],
MBTTY USING [Handle, PutLine, PutString],
MBVM USING [
Base, CodeSeg, CopyRead, CopyWrite, DataSeg, FileSeg, GetPage, ReleaseCodeSegs, Seg, SortSegs, Write],
PageMap USING [Flags, flagsClean, flagsWriteProtected, Value],
PrincOps USING [GlobalFrame],
Segments USING [
FHandle, GetFileLength, NewFile, Read, ReleaseFile, SegmentAddress, SetFileLength, SetFileTimes, SHandle, SwapIn, Unlock, Write],
StartList USING [Base, Entry, Index, StartIndex, SwapUnitIndex, SwapUnitInfo],
Streams USING [
CreateStream, Destroy, Handle, GetBlock, GetIndex, GetLength, PutBlock, PutWord, Read, SetIndex, Write],
Time USING [Packed];
MBOutput: PROGRAM
IMPORTS
Inline, MB, MBOut, MBStorage, MBTTY, MBVM, Segments, Streams, String: LongString
EXPORTS MB =
BEGIN
OPEN MBVM;
wordsPerPage: CARDINAL = Environment.wordsPerPage;
bytesPerPage: CARDINAL = Environment.bytesPerPage;
data: MB.Handle ← NIL;
header: LONG POINTER TO BootFile.Header ← NIL;
trailer: LONG POINTER TO BootFile.Trailer ← NIL;
nEntries, currentEntry: CARDINAL;
filePage: MBVM.Base;
trailerIndex: LONG CARDINAL;
EtherHeader: TYPE = MACHINE DEPENDENT RECORD [
version(0): CARDINAL ← 1,
mustBeZero(1): LONG CARDINAL ← 0,
createTime(3): BCPLTime
the following are implicit
name(5): StringBody,
fill(5+WordsForString[name]): ARRAY [5+WordsForString[name]..wordsPerPage) OF WORD ← ALL[0]
];
BCPLTime: TYPE = MACHINE DEPENDENT RECORD [high, low: CARDINAL];
InitOutput: PUBLIC PROC [h: MB.Handle] = {data ← h};
FinishOutput: PUBLIC PROC = {
IF header # NIL THEN {MBStorage.FreePages[header]; header ← NIL};
IF trailer # NIL THEN {MBStorage.FreePages[trailer]; trailer ← NIL};
IF data.bootStream # NIL THEN {
Streams.SetIndex[data.bootStream, Streams.GetLength[data.bootStream]];
Streams.Destroy[data.bootStream];
data.bootStream ← NIL;
};
MBVM.ReleaseCodeSegs[];
data ← NIL;
};
Boot file (not germ) output
WriteBootFile: PUBLIC PROC = {
tty: MBTTY.Handle = data.ttyHandle;
segs: LONG DESCRIPTOR FOR ARRAY OF MBVM.Seg ← MBVM.SortSegs[];
MBTTY.PutString[tty, "Writing boot file..."L];
InitializeBootFile[];
WriteVM[data.bootStream, segs ! UNWIND => MBStorage.FreePages[BASE[segs]]];
MBStorage.FreePages[BASE[segs]];
FinalizeBootFile[];
MBTTY.PutLine[tty, "finished writing."L];
IF data.etherFormat THEN MakeEtherFile[];
};
InitializeBootFile: PROC = {
name: STRING ← [40];
bootFile: Segments.FHandle;
String.AppendString[name, data.output];
bootFile ← Segments.NewFile[name, Segments.Write];
Segments.SetFileLength[bootFile, Inline.LongMult[data.nFilePages, bytesPerPage]
! UNWIND => Segments.ReleaseFile[bootFile]
];
data.bootStream ← Streams.CreateStream[bootFile, Streams.Write];
Segments.SetFileTimes[file: bootFile, create: data.buildTime];
Streams.SetIndex[data.bootStream, bytesPerPage]; -- skip over header page
data.bootHeader ← header ← MBStorage.Pages[1];
MB.Zero[header, wordsPerPage];
header^ ← BootFile.Header[
creationDate: data.buildTime, pStartListHeader: data.header.table,
inLoadMode: load, continuation: [vp: initial[mdsi: [data.mdsBase/256],
destination: MB.StartControlLink[]]], countData: data.nBootPages, entries:
];
trailer ← NIL;
nEntries ← BootFile.maxEntriesPerHeader;
currentEntry ← 0;
filePage ← 2;
};
FinalizeBootFile: PROC = {
length: LONG CARDINAL = Streams.GetIndex[data.bootStream];
Streams.SetIndex[data.bootStream, 0];
[] ← Streams.PutBlock[data.bootStream, header, wordsPerPage];
Streams.SetIndex[data.bootStream, length];
Streams.Destroy[data.bootStream];
data.bootStream ← NIL;
};
WriteVM: PROC [stream: Streams.Handle, segs: LONG DESCRIPTOR FOR ARRAY OF MBVM.Seg] = {
OPEN MBOut;
tty: MBTTY.Handle = data.ttyHandle;
i: CARDINAL;
seg: MBVM.Seg;
scriptBase: StartList.Base = data.scriptBase;
index: StartList.Index ← StartList.StartIndex;
CR[]; CR[];
Line["BOOT FILE MAP"L];
CR[];
Line[" Bootloaded Memory"L];
Line[" File VM Map Type"L];
Line[" Page Address Flags"L];
Number[1, [8,FALSE,FALSE,6]]; -- header page
Line[" Header Page"L];
MBTTY.PutString[tty, "bootloaded memory..."L];
FOR i IN [0..LENGTH[segs]) DO
seg ← segs[i];
IF ~seg.bootLoaded THEN LOOP;
WITH s: seg SELECT FROM
data => WriteDataSeg[stream, @s];
code => WriteCodeSeg[stream, @s];
file => WriteFileSeg[stream, @s];
ENDCASE;
ENDLOOP;
IF trailer # NIL THEN [] ← WriteTrailerPage[stream];
CR[];
Line[" Nonresident Memory"L];
Line[" File VM Pages Type Source[base,pages]"L];
Line[" Page Address"L];
MBTTY.PutString[tty, "nonresident memory..."L];
FOR i IN [0..LENGTH[segs]) DO
info: StartList.SwapUnitInfo;
seg ← segs[i];
info ← scriptBase[seg.index].info;
WITH s: seg SELECT FROM
data =>
IF info.state # resident THEN AppendDataSeg[stream, @s]
ELSE data.nResidentPages ← data.nResidentPages + 1;
code => AppendCodeSeg[stream, @s];
file =>
IF info.state # resident THEN AppendFileSeg[stream, @s]
ELSE data.nResidentPages ← data.nResidentPages + 1;
ENDCASE;
ENDLOOP;
};
WriteDataSeg: PROC [stream: Streams.Handle, seg: MBVM.DataSeg] = {
lp: LONG POINTER;
v: PageMap.Value ← MapValueForSeg[seg];
FOR page: CARDINAL IN [seg.base..seg.base+seg.pages) DO
IF page = 376B THEN LOOP;
EnterPage[page: page, value: v, stream: stream];
data.nBootLoadedPages ← data.nBootLoadedPages + 1;
BootPageToLoadmap[filePage: filePage, vmPage: page, flags: v.flags, type: "data"L];
lp ← MBVM.GetPage[page];
IF lp = NIL THEN WriteEmptyPage[stream] ELSE WritePage[stream, lp];
ENDLOOP;
};
WriteCodeSeg: PROC [stream: Streams.Handle, seg: MBVM.CodeSeg] = {
page: CARDINAL;
nUnits: CARDINALIF seg.sph = NIL THEN 1 ELSE seg.sph.length;
index: StartList.SwapUnitIndex ← seg.index;
v: PageMap.Value ← MapValueForSeg[seg];
scriptBase: StartList.Base = data.scriptBase;
lp: LONG POINTER;
su: POINTER TO swapUnit StartList.Entry;
lp ← OpenSegForTransfer[seg.segment];
page ← seg.base;
FOR i: CARDINAL IN [0..nUnits) DO
su ← @scriptBase[index];
FOR j: CARDINAL IN [0..su.pages) DO
IF scriptBase[su.parent].bootLoaded THEN {
EnterPage[page: page, value: v, stream: stream];
data.nBootLoadedPages ← data.nBootLoadedPages + 1;
BootPageToLoadmap[filePage: filePage, vmPage: page, flags: v.flags, type: "code"L];
WritePage[stream, lp]};
lp ← lp + wordsPerPage;
page ← page + 1;
ENDLOOP;
index ← index + SIZE[swapUnit StartList.Entry];
ENDLOOP;
CloseSegAfterTransfer[seg.segment];
};
WriteFileSeg: PROC [stream: Streams.Handle, seg: MBVM.FileSeg] = {
v: PageMap.Value ← MapValueForSeg[seg];
lp: LONG POINTER ← OpenSegForTransfer[seg.segment];
FOR page: CARDINAL IN [seg.base..seg.base+seg.pages) DO
EnterPage[page: page, value: v, stream: stream];
data.nBootLoadedPages ← data.nBootLoadedPages + 1;
BootPageToLoadmap[filePage: filePage, vmPage: page, flags: v.flags, type: "file"L];
WritePage[stream, lp];
lp ← lp + wordsPerPage;
ENDLOOP;
CloseSegAfterTransfer[seg.segment];
};
AppendDataSeg: PROC [stream: Streams.Handle, seg: MBVM.DataSeg] = {
lp: LONG POINTER;
SegToLoadmap[seg, filePage, seg.pages];
FOR page: CARDINAL IN [seg.base..seg.base+seg.pages) DO
lp ← MBVM.GetPage[page];
IF lp = NIL THEN WriteEmptyPage[stream] ELSE WritePage[stream, lp];
ENDLOOP;
};
AppendCodeSeg: PROC [stream: Streams.Handle, seg: MBVM.CodeSeg] = {
lp: LONG POINTER;
nUnits: CARDINAL = (IF seg.sph = NIL THEN 1 ELSE seg.sph.length);
index: StartList.SwapUnitIndex;
scriptBase: StartList.Base = data.scriptBase;
su: POINTER TO swapUnit StartList.Entry;
pagesToWrite: CARDINAL ← 0;
first: BOOLTRUE;
index ← seg.index;
FOR i: CARDINAL IN [0..nUnits) DO
su ← @scriptBase[index];
IF su.info.state ~= resident THEN pagesToWrite ← pagesToWrite + su.pages;
index ← index + SIZE[swapUnit StartList.Entry];
ENDLOOP;
IF pagesToWrite = 0 THEN RETURN;
lp ← OpenSegForTransfer[seg.segment];
index ← seg.index;
SegToLoadmap[seg, filePage, pagesToWrite];
FOR i: CARDINAL IN [0..nUnits) DO
su ← @scriptBase[index];
IF data.debug AND su.info.state # resident THEN {
IF first THEN {MBOut.Spaces[26]; MBOut.Text["SwapUnits["L]; first ← FALSE}
ELSE MBOut.Text[", "L];
MBOut.Number[index, [8,FALSE,FALSE,1]]};
FOR j: CARDINAL IN [0..su.pages) DO
IF su.info.state # resident THEN WritePage[stream, lp]
ELSE data.nResidentPages ← data.nResidentPages + 1;
lp ← lp + wordsPerPage;
ENDLOOP;
index ← index + SIZE[swapUnit StartList.Entry];
ENDLOOP;
IF --data.debug AND-- ~first THEN {MBOut.Char[']]; MBOut.CR[]};
CloseSegAfterTransfer[seg.segment];
};
AppendFileSeg: PROC [stream: Streams.Handle, seg: MBVM.FileSeg] = {
lp: LONG POINTER;
SegToLoadmap[seg, filePage, seg.pages];
IF data.debug THEN {
index: StartList.SwapUnitIndex ← seg.index;
MBOut.Spaces[26]; MBOut.Text["SwapUnits["L];
FOR i: CARDINAL IN [0..seg.nUnits) DO
MBOut.Number[index, [8,FALSE,FALSE,1]];
IF i ~= seg.nUnits - 1 THEN MBOut.Text[", "L];
index ← index + SIZE[swapUnit StartList.Entry];
ENDLOOP;
MBOut.Char[']]; MBOut.CR[]};
lp ← OpenSegForTransfer[seg.segment];
FOR i: CARDINAL IN [0..seg.pages) DO
WritePage[stream, lp]; lp ← lp + wordsPerPage;
ENDLOOP;
CloseSegAfterTransfer[seg.segment];
};
Germ file output
GermMDS: CARDINAL = 37000B;
BootXferLocation: POINTER = LOOPHOLE[1376B];
GermPageCountLocation: POINTER = LOOPHOLE[1377B];
WriteGermFile: PUBLIC PROC = {
tty: MBTTY.Handle = data.ttyHandle;
segs: LONG DESCRIPTOR FOR ARRAY OF MBVM.Seg ← MBVM.SortSegs[];
IF data.debug THEN MB.DumpSegs[segs, "WRITING GERM"L];
MBTTY.PutString[tty, "Writing germ file..."L];
InitializeGermFile[];
data.nFilePages ← data.nResidentPages ← data.nBootLoadedPages ←
WriteGerm[data.bootStream, segs ! UNWIND => MBStorage.FreePages[BASE[segs]]];
MBStorage.FreePages[BASE[segs]];
FinalizeGermFile[];
MBTTY.PutLine[tty, "finished writing."L];
IF data.etherFormat THEN MakeEtherFile[];
};
InitializeGermFile: PROC = {
name: STRING ← [40];
bootFile: Segments.FHandle;
String.AppendString[name, data.output];
bootFile ← Segments.NewFile[name, Segments.Write];
data.bootStream ← Streams.CreateStream[bootFile, Streams.Write];
Segments.SetFileTimes[file: bootFile, create: data.buildTime];
filePage ← 1;
};
FinalizeGermFile: PROC = {
Streams.Destroy[data.bootStream];
data.bootStream ← NIL;
};
WriteGerm: PROC [stream: Streams.Handle, segs: LONG DESCRIPTOR FOR ARRAY OF MBVM.Seg]
RETURNS [germPages: CARDINAL] = {
OPEN MBOut;
relocationPages: CARDINAL;
[germPages, relocationPages] ← RelocateGerm[segs];
CR[]; CR[];
Line["GERM FILE MAP"L];
CR[];
Line[" File VM Map Type"L];
Line[" Page Address Flags"L];
FOR i: CARDINAL IN [0..LENGTH[segs]) DO
WITH s: segs[i] SELECT FROM
data => WriteGermData[stream, @s, relocationPages];
code => WriteGermCode[stream, @s, relocationPages];
file => ERROR;
ENDCASE;
ENDLOOP;
};
RelocateGerm: PROC [segs: LONG DESCRIPTOR FOR ARRAY OF MBVM.Seg]
RETURNS [germPages: CARDINAL, relocationPages: CARDINAL] = {
codeRelocation: LONG CARDINAL =
Inline.LongMult[(relocationPages ← GermMDS - data.mdsBase), wordsPerPage];
RelocateOneModule: PROC [loadee: MB.BHandle, mth: BcdOps.MTHandle] RETURNS [BOOL] = {
gf: PrincOps.GlobalFrame;
MBVM.CopyRead[from: loadee.mt[mth.gfi].frame, to: @gf, nwords: SIZE[PrincOps.GlobalFrame]];
gf.code.longbase ← gf.code.longbase + codeRelocation;
MBVM.CopyWrite[to: loadee.mt[mth.gfi].frame, from: @gf, nwords: SIZE[PrincOps.GlobalFrame]];
RETURN[FALSE]
};
[] ← MB.DoAllModules[RelocateOneModule];
germPages ← 0;
FOR i: CARDINAL IN [0..LENGTH[segs]) DO germPages ← germPages + segs[i].pages; ENDLOOP;
MBVM.Write[p: BootXferLocation, v: MB.StartControlLink[]];
MBVM.Write[p: GermPageCountLocation, v: germPages];
};
WriteGermData: PROC [stream: Streams.Handle, seg: MBVM.DataSeg, relocationPages: CARDINAL] = {
lp: LONG POINTER;
v: PageMap.Value ← MapValueForSeg[seg];
FOR page: CARDINAL IN [seg.base..seg.base+seg.pages) DO
BootPageToLoadmap[
filePage: filePage, vmPage: page+relocationPages, flags: v.flags, type: "data"L];
lp ← MBVM.GetPage[page];
IF lp = NIL THEN WriteEmptyPage[stream] ELSE WritePage[stream, lp];
ENDLOOP;
};
WriteGermCode: PROC [stream: Streams.Handle, seg: MBVM.CodeSeg, relocationPages: CARDINAL] = {
v: PageMap.Value ← MapValueForSeg[seg];
lp: LONG POINTER ← OpenSegForTransfer[seg.segment];
SegmentSourceToLoadmap[seg]; MBOut.Char[':]; MBOut.CR[];
FOR page: CARDINAL IN [seg.base..seg.base+seg.pages) DO
BootPageToLoadmap[
filePage: filePage, vmPage: page+relocationPages, flags: v.flags, type: "code"L];
WritePage[stream, lp];
lp ← lp + wordsPerPage;
ENDLOOP;
CloseSegAfterTransfer[seg.segment];
};
Subroutines
MapValueForSeg: PROC [seg: MBVM.Seg] RETURNS [PageMap.Value] = {
flags: PageMap.Flags ←
IF ~seg.info.readOnly THEN PageMap.flagsClean ELSE PageMap.flagsWriteProtected;
reserved = FALSE (0) on a D0 means don't log single bit memory error
RETURN[PageMap.Value[logSingleError: FALSE, flags: flags, realPage: 0]]
};
OpenSegForTransfer: PROC [seg: Segments.SHandle] RETURNS [lp: LONG POINTER] = {
Segments.SwapIn[seg];
lp ← Segments.SegmentAddress[seg];
};
CloseSegAfterTransfer: PROC [seg: Segments.SHandle] = INLINE {Segments.Unlock[seg]};
EnterPage: PROC [page: MBVM.Base, value: PageMap.Value, stream: Streams.Handle] = {
entries: LONG POINTER TO ARRAY [0..0) OF BootFile.Entry;
IF currentEntry = nEntries THEN AddTrailerPage[stream];
entries ←
IF nEntries = BootFile.maxEntriesPerTrailer THEN @trailer.entries ELSE @header.entries;
entries[currentEntry] ← [page: page, value: value];
currentEntry ← currentEntry + 1;
};
AddTrailerPage: PROC [stream: Streams.Handle] = {
IF trailer # NIL THEN trailerIndex ← WriteTrailerPage[stream]
ELSE {
create trailer page
trailer ← MBStorage.Pages[1];
trailerIndex ← Streams.GetIndex[stream];
};
Streams.SetIndex[stream, trailerIndex+bytesPerPage];
MB.Zero[trailer, wordsPerPage];
trailer.version ← BootFile.currentVersion;
nEntries ← BootFile.maxEntriesPerTrailer;
currentEntry ← 0;
MBOut.Number[filePage, [8,FALSE,FALSE,6]];
MBOut.Line[" Trailer Page"L];
filePage ← filePage + 1;
};
WriteTrailerPage: PROC [stream: Streams.Handle] RETURNS [index: LONG CARDINAL] = {
index ← Streams.GetIndex[stream];
Streams.SetIndex[stream, trailerIndex];
[] ← Streams.PutBlock[stream, trailer, wordsPerPage];
Streams.SetIndex[stream, index];
};
WritePage: PROC [s: Streams.Handle, p: LONG POINTER] = {
filePage ← filePage + 1;
IF Streams.PutBlock[s, p, wordsPerPage] # wordsPerPage THEN ERROR;
};
WriteEmptyPage: PROC [s: Streams.Handle] = {
filePage ← filePage + 1;
THROUGH [0..wordsPerPage) DO Streams.PutWord[s, 0] ENDLOOP;
};
BootPageToLoadmap: PROC [filePage, vmPage: MBVM.Base, flags: PageMap.Flags, type: STRING] = {
OPEN MBOut;
Number[filePage, [8,FALSE,FALSE,6]];
Spaces[2];
LongNumber[(LONG[vmPage] * wordsPerPage), [8,FALSE,FALSE,8]];
Spaces[3];
Char[IF flags.writeProtected THEN 'W ELSE ' ];
Char[IF flags.dirty THEN 'D ELSE ' ];
Char[IF flags.referenced THEN 'R ELSE ' ];
Spaces[5];
Text[type];
CR[];
};
SegToLoadmap: PROC [s: MBVM.Seg, backingPage: MBVM.Base, pages: CARDINAL] = {
OPEN MBOut;
octal: NumberFormat = NumberFormat[8,FALSE,FALSE,1];
Number[backingPage, [8,FALSE,FALSE,6]];
Spaces[2];
LongNumber[LONG[s.base]*wordsPerPage, [8,FALSE,FALSE,8]];
Spaces[2];
Number[pages, [8,FALSE,FALSE,4]];
Spaces[4];
WITH seg: s SELECT FROM
data => Text["data"L];
code => {Text["code"L]; Spaces[3]; SegmentSourceToLoadmap[s]};
file => {Text["file"L]; Spaces[3]; SegmentSourceToLoadmap[s]};
ENDCASE;
CR[];
};
SegmentSourceToLoadmap: PROC [seg: MBVM.Seg] = {
OPEN MBOut;
octal: NumberFormat = NumberFormat[8,FALSE,FALSE,1];
file: Segments.FHandle;
fileBase: CARDINAL;
WITH s: seg SELECT FROM
code => {file ← s.file; fileBase ← s.fileBase};
file => {file ← s.file; fileBase ← s.fileBase};
ENDCASE;
FileName[file]; Char['[]; Number[fileBase, octal];
Char[',]; Number[seg.pages, octal]; Char[']];
};
MakeEtherFile: PROC = {
tty: MBTTY.Handle = data.ttyHandle;
name: STRING ← [40];
bufferPages: CARDINAL = 50;
inFile, outFile: Segments.FHandle;
inStream, outStream: Streams.Handle ← NIL;
buffer: LONG POINTER ← MBStorage.Pages[bufferPages];
PutEtherHeader: PROC [s: Streams.Handle] = {
etherHeader: EtherHeader ← [createTime: MesaToBCPLTime[data.buildTime]];
[] ← Streams.PutBlock[s, @etherHeader, SIZE[EtherHeader]];
[] ← Streams.PutBlock[s, data.output, SIZE[StringBody[data.output.maxlength]]];
THROUGH [SIZE[EtherHeader]+SIZE[StringBody[data.output.maxlength]]..wordsPerPage) DO
Streams.PutWord[s, 0];
ENDLOOP;
};
MesaToBCPLTime: PROC [t: Time.Packed] RETURNS [BCPLTime] = INLINE
{RETURN [[high: Inline.HighHalf[t], low: Inline.LowHalf[t]]]};
MBTTY.PutString[tty, "Writing ether "L];
MBTTY.PutString[tty, IF data.germ THEN "germ"L ELSE "boot"L];
MBTTY.PutString[tty, "file..."L];
String.AppendString[name, data.output];
inFile ← Segments.NewFile[name, Segments.Read];
name.length ← 0; String.AppendString[name, data.etherOutput];
outFile ← Segments.NewFile[name, Segments.Write];
{ENABLE UNWIND => {
IF inStream ~= NIL THEN Streams.Destroy[inStream] ELSE Segments.ReleaseFile[inFile];
IF outStream ~= NIL THEN Streams.Destroy[outStream] ELSE Segments.ReleaseFile[outFile];
MBStorage.FreePages[buffer];
};
Segments.SetFileLength[outFile, Segments.GetFileLength[inFile] + bytesPerPage];
inStream ← Streams.CreateStream[inFile, Streams.Read];
outStream ← Streams.CreateStream[outFile, Streams.Write];
PutEtherHeader[outStream];
DO
words: CARDINAL = Streams.GetBlock[inStream, buffer, bufferPages*wordsPerPage];
IF words = 0 THEN EXIT;
IF Streams.PutBlock[outStream, buffer, words] ~= words THEN ERROR;
ENDLOOP;
};
Streams.Destroy[inStream];
Streams.Destroy[outStream];
MBStorage.FreePages[buffer];
MBTTY.PutLine[tty, "finished writing."L];
};
END.