MBDriver.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Sandman on 6-Aug-81 15:40:03
Lewis on 28-Sep-81 16:46:51
Levin on January 16, 1984 11:39 am
Russ Atkinson (RRA) March 8, 1985 5:02:23 pm PST
DIRECTORY
Ascii USING [CR, SP, TAB],
BasicTime USING [GMT, Now, Period],
Commander USING [CommandProc, Register],
Convert USING [CardFromRope, Error],
DebuggerSwap USING [CallDebugger],
FS USING [Error, FileInfo],
IO USING [bool, BreakProc, card, EndOfStream, GetTokenRope, PutChar, PutF, PutFR, PutRope, RIS, rope, STREAM, time],
IOUtils USING [CopyPFProcs, PFCodeProc, PFProcs, SetPFCodeProc, SetPFProcs],
List USING [Map, Reverse],
Loader USING [BCDBuildTime],
MB USING [Abort, BCount, BHandle, BObject, DumpBootHeader, DumpFrames, DumpInputBcds, DumpStartList, EchoInput, Error, FinishCache, FinishDebug, FinishLoader, FinishLoadmap, FinishMain, FinishOutput, FinishParse, FinishScript, FinishUtilities, FinishVM, Handle, InitCache, InitDebug, InitLoader, InitLoadmap, InitMain, InitMemory, InitOutput, InitParse, InitScript, InitUtilities, InitVM, InputBCDs, Load, MakeScript, Object, OpenLoadmap, ProcessInput, ReportFrameAllocation, TurnOffStartTrap, WriteBootFile, WriteGermFile],
PrincOps USING [GFTIndex, PsbIndex],
Process USING [CheckForAbort],
Rope USING [Cat, Equal, Fetch, Find, Index, Length, ROPE, Substr];
MBDriver:
CEDAR
MONITOR
IMPORTS BasicTime, Commander, Convert, DebuggerSwap, FS, IO, IOUtils, List, Loader, MB, Process, Rope
EXPORTS MB = BEGIN
ROPE: TYPE = Rope.ROPE;
data: MB.Handle ← NIL;
typescript: IO.STREAM;
commandStream: IO.STREAM;
Outcome: TYPE = {normal, error, abort, noMoreCommands};
RunFromCommander: Commander.CommandProc = {
commandStream ← IO.RIS[cmd.commandLine];
typescript ← cmd.out;
[] ← DoWork[];
commandStream ← typescript ← NIL;
};
DoWork:
ENTRY
PROC
RETURNS [outcome: Outcome] = {
ENABLE UNWIND => NULL;
WriteHerald: PROC = {typescript.PutF["%g\N%t\N", IO.rope[MakeBootVersion[]], IO.time[]]};
ReportStats:
PROC [
nFilePages, nModules, nGFIs, nResidentPages, nBootLoadedPages: CARDINAL,
elapsedTime: LONG CARDINAL] = {
typescript.PutF["Boot file pages: %d (%d bootloaded, of which %d are resident)\N",
IO.card[nFilePages], IO.card[nBootLoadedPages], IO.card[nResidentPages]];
typescript.PutF["Modules: %d (requiring %d GFIs)\N", IO.card[nModules], IO.card[nGFIs]];
typescript.PutF["Time: %r\N", IO.card[elapsedTime]];
};
Initialize:
PROC = {
the following must be first
InitDriver[];
The order of the following is arbitrary.
MB.InitCache[data];
MB.InitDebug[data];
MB.InitLoader[data];
MB.InitLoadmap[data];
MB.InitMain[data];
MB.InitOutput[data];
MB.InitParse[data];
MB.InitScript[data];
MB.InitUtilities[data];
MB.InitVM[data];
};
Finalize:
PROC = {
The ordering of the following is significant.
MB.FinishOutput[];
MB.FinishLoader[];
The ordering of the following is arbitrary.
MB.FinishVM[];
MB.FinishUtilities[];
MB.FinishScript[];
MB.FinishParse[];
MB.FinishMain[];
MB.FinishLoadmap[];
MB.FinishDebug[];
MB.FinishCache[];
the following must be last
FinishDriver[];
};
DoRealWork:
PROC
RETURNS [outcome: Outcome] = {
CheckAbort: PROC = {Process.CheckForAbort[]};
CheckAbort[];
IF (outcome ← ProcessCmds[]) ~= $normal THEN RETURN;
MB.OpenLoadmap[];
MB.InitMemory[];
CheckAbort[];
EchoBCDs[];
MB.EchoInput[];
CheckAbort[];
MB.Load[];
CheckAbort[];
MB.TurnOffStartTrap[];
MB.ReportFrameAllocation[];
IF data.debug THEN {MB.DumpInputBcds[]; MB.DumpFrames[]};
CheckAbort[];
IF data.germ THEN MB.WriteGermFile[]
ELSE {
MB.MakeScript[];
CheckAbort[];
MB.WriteBootFile[];
};
CheckAbort[];
IF data.debug AND ~data.germ THEN {MB.DumpBootHeader[]; MB.DumpStartList[]};
RETURN[normal]
};
WriteHerald[];
DO
-- until no commands remain
start: BasicTime.GMT ← BasicTime.Now[];
nFilePages, nModules, nGFIs, nResidentPages, nBootLoadedPages: CARDINAL;
outcome ← $abort;
Initialize[];
outcome ← DoRealWork[
!
UNWIND => Finalize[];
MB.Abort, ABORTED => CONTINUE;
FS.Error => {
IF error.group ~= $user THEN typescript.PutRope["\NUnexpected error from FS: "];
typescript.PutF["%g\N", IO.rope[error.explanation]];
CONTINUE
};
];
SELECT outcome
FROM
$normal => {
nFilePages ← data.nFilePages;
nModules ← data.nModules;
nGFIs ← data.nGFIs;
nResidentPages ← data.nResidentPages;
nBootLoadedPages ← data.nBootLoadedPages;
};
$abort => {typescript.PutRope["...MakeBoot aborted\N"]; EXIT};
$noMoreCommands => {outcome ← $normal; EXIT};
ENDCASE --$warning or $error-- => EXIT;
Finalize[];
ReportStats[
nFilePages: nFilePages, nModules: nModules, nGFIs: nGFIs,
nResidentPages: nResidentPages, nBootLoadedPages: nBootLoadedPages,
elapsedTime: BasicTime.Period[from: start, to: BasicTime.Now[]]
];
ENDLOOP;
Finalize[];
};
oldTSPFProcs: IOUtils.PFProcs;
InitDriver:
PROC = {
tsPFProcs: IOUtils.PFProcs ← IOUtils.CopyPFProcs[typescript];
[] ← IOUtils.SetPFCodeProc[tsPFProcs, 'y, GermOrBoot];
oldTSPFProcs ← IOUtils.SetPFProcs[typescript, tsPFProcs];
data ←
NEW[
MB.Object ← [
buildTime: BasicTime.Now[],
typescript: typescript,
inputBCDs: NEW[MB.InputBCDs[10] ← [nBcds: 1]]
]];
create BObject describing main input Bcd, used by ProcessCmds
data.inputBCDs.bcds[0] ←
NEW[
MB.BObject ← [
name: NIL,
bcd: NIL, pages: 0, bcdSeg: NIL,
mt: NIL, gfiOffset: 0,
files: NIL]];
FinishDriver:
PROC = {
[] ← IOUtils.SetPFProcs[typescript, oldTSPFProcs];
data ← NIL; -- drop the whole structure on the floor
};
GermOrBoot: IOUtils.PFCodeProc = {
%y: Argument is Boolean
typescript.PutRope[IF data.germ THEN "germ" ELSE "boot"]};
Command Line Parsing and Analysis
ProcessCmds:
PROC
RETURNS [outcome: Outcome] = {
args, results: PairList;
switches, sourceName: ROPE ← NIL;
BEGIN
[sourceName, args, results, switches] ← Parse[commandStream ! ParseFailed => GOTO bogus];
IF sourceName = NIL THEN RETURN[outcome: $noMoreCommands];
EXITS
bogus => {
typescript.PutRope["! Bad MakeBoot command line\N"];
RETURN[outcome: $error]
};
END;
IF switches ~=
NIL
THEN {
sense: BOOL ← TRUE;
FOR i:
INT
IN [0..switches.Length[])
DO
SELECT switches.Fetch[i]
FROM
'-, '~ => sense ← ~sense;
'd, 'D => {data.debug ← sense; sense ← TRUE};
'e, 'E => {data.etherFormat ← sense; sense ← TRUE};
'g, 'G => {data.germ ← sense; sense ← TRUE};
'h, 'H => {data.hexLoadmap ← sense; sense ← TRUE};
'! => DebuggerSwap.CallDebugger["Called from MakeBoot"L];
ENDCASE;
ENDLOOP;
};
data.inputBCDs.bcds[0].name ← data.input ← SetExtension[sourceName, "bcd"];
ProcessResults[results];
EchoCommand[];
ProcessArgs[args];
RETURN[outcome: $normal]
};
EchoCommand:
PROC = {
typescript.PutF["Building %y file %g", IO.bool[data.germ], IO.rope[data.output]];
IF data.etherFormat
THEN
typescript.PutF[" and ether %y file %g\N", IO.bool[data.germ], IO.rope[data.etherOutput]]
ELSE typescript.PutChar['\N];
typescript.PutF["Writing load map to %g\N", IO.rope[data.loadmapName]];
};
EchoBCDs:
PROC = {
data.loadmap.PutRope["Input BCD"];
IF data.inputBCDs.nBcds ~= 0 THEN data.loadmap.PutChar['s];
data.loadmap.PutRope[":\N\N"];
FOR i:
CARDINAL
IN [0..data.inputBCDs.nBcds)
DO
ENABLE FS.Error => IF error.group = $user THEN {data.loadmap.PutChar['\N]; CONTINUE};
data.loadmap.PutF[" %39j %t\N",
IO.rope[data.inputBCDs.bcds[i].name],
IO.time[FS.FileInfo[data.inputBCDs.bcds[i].name].created]
];
ENDLOOP;
data.loadmap.PutChar['\N];
};
ProcessResults:
PROC [results: PairList] = {
DoOnePair:
PROC [p:
REF
ANY, rest:
LIST
OF
REF
ANY] = {
key, value: ROPE;
[key, value] ← NARROW[p, REF Pair]^;
SELECT
TRUE
FROM
key = NIL => data.output ← value;
key.Equal["bootFile",
FALSE] =>
IF data.output = NIL THEN data.output ← value
ELSE MB.Error["Duplicate result keyword 'bootFile'"];
key.Equal["loadMap",
FALSE] =>
IF data.loadmapName = NIL THEN data.loadmapName ← value
ELSE MB.Error["Duplicate result keyword 'loadMap'"];
ENDCASE => MB.Error["Unrecognized result keyword"];
};
data.output ← data.etherOutput ← data.loadmapName ← NIL;
List.Map[results, DoOnePair];
IF data.output = NIL THEN data.output ← StripExtension[data.input];
data.output ← SetExtension[data.output, IF data.germ THEN "germ" ELSE "boot"];
IF data.etherFormat
THEN
data.etherOutput ←
SetExtension[StripExtension[data.output], IF data.germ THEN "eg" ELSE "pb"];
IF data.loadmapName = NIL THEN data.loadmapName ← StripExtension[data.output];
data.loadmapName ← SetExtension[data.loadmapName, "loadmap"];
};
SetExtension:
PROC [r, ext:
ROPE]
RETURNS [
ROPE] = {
RETURN[IF r.Find["."] = -1 THEN r.Cat[".", ext] ELSE r]};
StripExtension: PROC [r: ROPE] RETURNS [ROPE] = {RETURN[r.Substr[len: r.Index[s2: "."]]]};
ProcessArgs:
PROC [args: PairList] = {
nProcesses, gftLength: CARDINAL ← LAST[CARDINAL];
RopeToNumber:
PROC [r:
ROPE]
RETURNS [ok:
BOOL ←
TRUE, v:
LONG
CARDINAL] = {
v ← Convert.CardFromRope[r ! Convert.Error => {ok ← FALSE; CONTINUE}];
};
DoOnePair:
PROC [p:
REF
ANY, rest:
LIST
OF
REF
ANY] = {
key, value: ROPE;
[key, value] ← NARROW[p, REF Pair]^;
SELECT
TRUE
FROM
key.Equal["parm",
FALSE] => {
parmFile: ROPE = SetExtension[value, "bootmesa"];
typescript.PutF["Processing parameter file %g", IO.rope[parmFile]];
MB.ProcessInput[parmFile];
typescript.PutChar[Ascii.CR];
};
key.Equal["bcd",
FALSE] => {
IF data.inputBCDs.nBcds =
LAST[
MB.BCount] - 1
--save slot for NullConfig--
THEN
MB.Error["Too many input BCDs"];
IF data.inputBCDs.nBcds = data.inputBCDs.length
THEN {
newLength: MB.BCount = MIN[2*data.inputBCDs.length, LAST[MB.BCount]];
newInputBCDs:
REF
MB.InputBCDs ←
NEW[MB.InputBCDs[newLength] ← [nBcds: data.inputBCDs.nBcds]];
FOR i:
CARDINAL
IN [0..data.inputBCDs.nBcds)
DO
newInputBCDs.bcds[i] ← data.inputBCDs.bcds[i];
ENDLOOP;
data.inputBCDs ← newInputBCDs;
};
data.inputBCDs.bcds[data.inputBCDs.nBcds] ←
NEW[
MB.BObject ← [
name: SetExtension[value, "bcd"],
bcd: NIL, pages: 0, bcdSeg: NIL,
mt: NIL, gfiOffset: 0,
files: NIL]];
data.inputBCDs.nBcds ← data.inputBCDs.nBcds+1;
};
key.Equal["nProcesses",
FALSE] => {
ok: BOOL;
[ok, nProcesses] ← RopeToNumber[value];
IF ~ok OR ~(nProcesses-1 IN PrincOps.PsbIndex) THEN MB.Error["Invalid nProcesses"];
};
key.Equal["gftLength",
FALSE] => {
ok: BOOL;
[ok, gftLength] ← RopeToNumber[value];
IF ~ok OR ~(gftLength-1 IN PrincOps.GFTIndex) THEN MB.Error["Invalid gftLength"];
};
ENDCASE => MB.Error["Unrecognized parameter keyword"];
};
List.Map[args, DoOnePair];
IF nProcesses ~=
LAST[
CARDINAL]
THEN {
data.nProcesses ← nProcesses;
typescript.PutF["Number of processes set to %d\N", IO.card[nProcesses]];
};
IF gftLength ~=
LAST[
CARDINAL]
THEN {
data.gftLength ← gftLength;
typescript.PutF["GFT length set to %d\N", IO.card[gftLength]];
};
Command Line Parser
PairList: TYPE = LIST OF REF --Pair-- ANY;
Pair: TYPE = RECORD [key, value: ROPE];
ParserState:
TYPE = [0..17];
0:
1: id
2: id ←
3: [ ?{ARG},
4: [ ?{ARG}, id
5: [ ?{ARG}, id :
6: [ ?{ARG}, id : id
7: [ ?{ARG}, ]
8: [ ?{ARG}, ] ←
9: LHS ← id
10: ?(LHS ←) id [ ?{ARG},
11: ?(LHS ←) id [ ?{ARG}, id
12: ?(LHS ←) id [ ?{ARG}, id :
13: ?(LHS ←) id [ ?{ARG}, id : id
14: ?(LHS ←) id RHS
15: ?(LHS ←) id ?RHS /
16: ?(?(LHS ←) id ?RHS) / id
17: ?(?(LHS ←) id ?RHS) ?(/ ?id) (;|eom) | eom
where LHS = id | [ ?{ARG}, ]
RHS = [ ?{ARG}, ]
ARG = id : id | id (if allowNoTagParm)
ParseFailed: ERROR = CODE;
dontAdvance: BOOL ← FALSE;
token: ROPE ← NIL;
Parse:
PROC [cmd:
IO.
STREAM, allowNoTagParm:
BOOL ←
FALSE]
RETURNS [operator: ROPE ← NIL, argList, resultList: PairList ← NIL, switches: ROPE ← NIL] = {
state: ParserState ← 0;
pair0, pair1: ROPE ← NIL;
DontAdvance: PROC = INLINE {dontAdvance ← TRUE};
NextToken:
IO.BreakProc = {
SELECT char
FROM
Ascii.SP, Ascii.CR, Ascii.TAB => RETURN[sepr];
IN ['a..'z], IN ['A..'Z], IN ['0..'9], '<, '>, '., '+, '-, '~, '!, '$ => RETURN[other];
';, '←, '[, ',, '], ':, '/ => RETURN[break];
ENDCASE;
ERROR ParseFailed;
PushArg:
PROC =
INLINE {
argList ← CONS[NEW[Pair ← [key: pair0, value: pair1]], argList]};
PushResult:
PROC =
INLINE {
resultList ← CONS[NEW[Pair ← [key: pair0, value: pair1]], resultList]};
UNTIL state =
LAST[ParserState]
DO
IF dontAdvance THEN dontAdvance ← FALSE
ELSE
token ← cmd.GetTokenRope[NextToken
! IO.EndOfStream => {token ← NIL; CONTINUE}].token;
IF token =
NIL
THEN
-- end of command stream
SELECT state
FROM
0, 1, 9, 14, 15, 16 => state ← 17;
ENDCASE => ERROR ParseFailed
ELSE
SELECT Rope.Fetch[token]
FROM
'← =>
SELECT state
FROM
1 => {
pair0 ← NIL; pair1 ← operator; operator ← NIL;
PushResult[]; state ← 2};
7 => state ← 8;
ENDCASE => ERROR ParseFailed;
'[ =>
SELECT state
FROM
0 => state ← 3;
1, 9 => state ← 10;
14 => {DontAdvance[]; state ← 17};
ENDCASE => ERROR ParseFailed;
'] =>
SELECT state
FROM
3, 6 => state ← 7;
4 =>
IF allowNoTagParm
THEN {
pair1 ← pair0; pair0 ← NIL;
PushArg[]; state ← 7}
ELSE ERROR ParseFailed;
10, 13 => state ← 14;
11 =>
IF allowNoTagParm
THEN {
pair1 ← pair0; pair0 ← NIL;
PushArg[]; state ← 14}
ELSE ERROR ParseFailed;
ENDCASE => ERROR ParseFailed;
': =>
SELECT state
FROM
4 => state ← 5;
11 => state ← 12;
ENDCASE => ERROR ParseFailed;
', =>
SELECT state
FROM
4 =>
IF allowNoTagParm
THEN {
pair1 ← pair0; pair0 ← NIL;
PushArg[]; state ← 3}
ELSE ERROR ParseFailed;
6 => state ← 3;
11 =>
IF allowNoTagParm
THEN {
pair1 ← pair0; pair0 ← NIL;
PushArg[]; state ← 10}
ELSE ERROR ParseFailed;
13 => state ← 10;
ENDCASE => ERROR ParseFailed;
'/ =>
SELECT state
FROM
0, 1, 9, 14 => state ← 15;
ENDCASE => ERROR ParseFailed;
'; =>
SELECT state
FROM
1, 9, 14, 15, 16 => state ← 17;
ENDCASE => ERROR ParseFailed;
ENDCASE =>
-- id--
SELECT state
FROM
0 => {operator ← token; state ← 1};
2, 8 => {operator ← token; state ← 9};
3 => {pair0 ← token; state ← 4};
5 => {pair1 ← token; PushResult[]; state ← 6};
10 => {pair0 ← token; state ← 11};
12 => {pair1 ← token; PushArg[]; state ← 13};
15 => {switches ← token; state ← 16};
1, 9, 14, 16 => {DontAdvance[]; state ← 17};
ENDCASE => ERROR ParseFailed;
ENDLOOP;
resultList ← List.Reverse[resultList];
argList ← List.Reverse[argList];
};
Miscellaneous Exported Utilities
Abort: PUBLIC SIGNAL = CODE;
Error:
PUBLIC
PROC [msg: Rope.
ROPE] = {
typescript.PutF["\NError: %g\N", IO.rope[msg]];
SIGNAL MB.Abort;
};
MakeBootVersion:
PUBLIC
PROC
RETURNS [r:
ROPE] = {
r ← IO.PutFR["Cedar MakeBoot of %t", IO.time[Loader.BCDBuildTime[MakeBootVersion]]];
};
Initialization
Commander.Register[key: "MakeBoot", proc: RunFromCommander, doc: NIL]