<> <> <> <> DIRECTORY Ascii USING [CR, NUL], Commander USING [CommandProc, Register], Exec USING [AddCommand, GetChar, status, w], File USING [Capability], Heap USING [Create, Delete], Inline USING [LongCOPY], IO USING [EndOf, GetChar, RIS, STREAM], LongString USING [AppendString, EquivalentString, InvalidNumber, StringToDecimal], MB USING [ Abort, BCount, BHandle, BIndex, 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, ProcessInput, ReportFrameAllocation, TurnOffStartTrap, WriteBootFile, WriteGermFile], MBCommandUtil USING [ CommandObject, CommandPtr, CopyString, Create, Destroy, Failed, FreePairList, FreeString, GetNthPair, ListLength, PairList, Parse, SetExtension], MBOut USING [Char, CR, OpenLoadmap, Spaces, Text, Time], MBTTY USING [ Handle, PutChar, PutCR, PutDecimal, PutLine, PutLongDecimal, PutLongNumber, PutString, UserAbort], PrincOps USING [GFTIndex], PSB USING [PsbIndex], Runtime USING [CallDebugger, GetBcdTime, GlobalFrame, IsBound], RuntimeInternal USING [Codebase], Segments USING [FHandle, FileNameProblem, FPFromFile, GetFileTimes, NewFile, ReleaseFile], Space USING [GetAttributes, GetHandle, GetWindow, Handle, PageFromLongPointer], Time USING [Append, Current, Packed, Unpack], TTYIO USING [CreateTTYHandleFromStreams], Volume USING [InsufficientSpace]; MBDriver: MONITOR IMPORTS Commander, Exec, Heap, Inline, IO, MB, MBCommandUtil, MBOut, MBTTY, Runtime, RuntimeInternal, Segments, Space, String: LongString, Time, TTYIO, Volume EXPORTS MB SHARES File = BEGIN useCommander: BOOL = Runtime.IsBound[Commander.Register]; -- *** temporary data: MB.Handle _ NIL; tty: MBTTY.Handle; commandStream: IO.STREAM; Outcome: TYPE = Exec.status; RunFromCommander: Commander.CommandProc = TRUSTED { commandStream _ IO.RIS[cmd.commandLine]; tty _ TTYIO.CreateTTYHandleFromStreams[in: cmd.in, out: cmd.out]; [] _ DoRun[]; }; RunFromExec: PROC = { tty _ Exec.w; [] _ DoRun[]; }; DoRun: ENTRY PROC RETURNS [outcome: Outcome] = { ENABLE UNWIND => NULL; WriteHerald[Time.Current[]]; DO -- until no commands remain start: LONG CARDINAL _ Time.Current[]; nFilePages, nModules, nGFIs, nResidentPages, nBootLoadedPages: CARDINAL; outcome _ abort; Initialize[]; outcome _ DoWork[ ! UNWIND => Finalize[]; MB.Abort, ABORTED => CONTINUE; Volume.InsufficientSpace => {MBTTY.PutLine[tty, "!Disk full"L]; CONTINUE}; Segments.FileNameProblem[] => { MBTTY.PutString[tty, "!Can't find "L]; MBTTY.PutLine[tty, name]; CONTINUE } ]; SELECT outcome FROM normal => { nFilePages _ data.nFilePages; nModules _ data.nModules; nGFIs _ data.nGFIs; nResidentPages _ data.nResidentPages; nBootLoadedPages _ data.nBootLoadedPages; }; abort => {MBTTY.PutLine[tty, "...MakeBoot aborted"L]; EXIT}; spare1 --no commands remain-- => {outcome _ normal; EXIT}; ENDCASE --warning or error-- => EXIT; Finalize[]; ReportStats[ nFilePages: nFilePages, nModules: nModules, nGFIs: nGFIs, nResidentPages: nResidentPages, nBootLoadedPages: nBootLoadedPages, elapsedTime: (LOOPHOLE[Time.Current[], LONG CARDINAL] - start)]; ENDLOOP; Finalize[]; }; WriteHerald: PROC [now: Time.Packed] = { herald: STRING _ [80]; MakeBootVersion[herald]; MBTTY.PutLine[tty, herald]; herald.length _ 0; Time.Append[herald, Time.Unpack[[now]]]; herald.length _ herald.length-3; -- remove seconds MBTTY.PutLine[tty, herald]; }; Initialize: PROC = { <> InitDriver[]; <> 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 = { <> MB.FinishOutput[]; MB.FinishLoader[]; <> MB.FinishVM[]; MB.FinishUtilities[]; MB.FinishScript[]; MB.FinishParse[]; MB.FinishMain[]; MB.FinishLoadmap[]; MB.FinishDebug[]; MB.FinishCache[]; <> FinishDriver[]; }; InitDriver: PROC = { zone: UNCOUNTED ZONE _ Heap.Create[initial: 4, swapUnit: 1]; data _ zone.NEW[MB.Object]; Zero[data, SIZE[MB.Object]]; data.zone _ zone; data.buildTime _ Time.Current[]; data.ttyHandle _ tty; data.inputBCDs _ data.zone.NEW[MB.InputBCDs[10] _ [nBcds: 1]]; <> data.inputBCDs.bcds[0] _ data.zone.NEW[MB.BObject _ [ name: NIL, bcd: NIL, bcdSeg: NIL, bcdSegment: NIL, mt: DESCRIPTOR[NIL, 0], gfiOffset: 0, files: NIL]]; }; FinishDriver: PROC = { zone: UNCOUNTED ZONE _ data.zone; [] _ MBCommandUtil.FreeString[data.input]; [] _ MBCommandUtil.FreeString[data.output]; [] _ MBCommandUtil.FreeString[data.etherOutput]; [] _ MBCommandUtil.FreeString[data.loadmap]; FOR i: MB.BIndex IN [0..data.inputBCDs.nBcds) DO IF data.inputBCDs.bcds[i] ~= NIL THEN { [] _ MBCommandUtil.FreeString[data.inputBCDs.bcds[i].name]; data.zone.FREE[@data.inputBCDs.bcds[i]]; }; ENDLOOP; zone.FREE[@data.inputBCDs]; zone.FREE[@data]; Heap.Delete[zone]; }; DoWork: PROC RETURNS [outcome: Outcome] = { CheckAbort: PROC = {IF MBTTY.UserAbort[tty] THEN ERROR MB.Abort}; CheckAbort[]; IF (outcome _ ProcessCmds[]) ~= normal THEN RETURN; MBOut.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] }; ProcessCmds: PROC RETURNS [outcome: Outcome] = { args, results: MBCommandUtil.PairList; switches, sourceName: LONG STRING _ NIL; commandPtr: MBCommandUtil.CommandPtr _ MBCommandUtil.Create[GetChar]; GetChar: PROC RETURNS [char: CHARACTER] = { IF useCommander THEN { -- *** temporary IF commandStream.EndOf[] THEN RETURN [Ascii.NUL]; char _ commandStream.GetChar[]; IF char = Ascii.CR THEN char _ Ascii.NUL; } ELSE RETURN[Exec.GetChar[]]}; CleanupParse: PROC = { IF sourceName ~= NIL THEN { [] _ MBCommandUtil.FreeString[sourceName]; [] _ MBCommandUtil.FreePairList[args]; [] _ MBCommandUtil.FreePairList[results]; [] _ MBCommandUtil.FreeString[switches]; }; MBCommandUtil.Destroy[commandPtr]; }; BEGIN [sourceName, args, results, switches] _ MBCommandUtil.Parse[ cmd: commandPtr, opX: 2+("bcd"L).length, resultX: 2+("boot"L).length ! MBCommandUtil.Failed => GOTO badCommands]; IF sourceName = NIL THEN {CleanupParse[]; RETURN[outcome: spare1]}; -- no commands remain EXITS badCommands => { MBTTY.PutLine[tty, "!Bad MakeBoot command line"L]; CleanupParse[]; RETURN[outcome: error] } END; IF switches # NIL THEN { sense: BOOLEAN _ TRUE; FOR i: CARDINAL IN [0..switches.length) DO SELECT switches[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}; '! => Runtime.CallDebugger["Called from MakeBoot"L]; ENDCASE; ENDLOOP; }; data.input _ MBCommandUtil.CopyString[s: sourceName, extra: (".bcd"L).length]; data.input _ MBCommandUtil.SetExtension[root: data.input, defaultExt: "bcd"L]; data.inputBCDs.bcds[0].name _ MBCommandUtil.CopyString[data.input]; ProcessResults[results ! UNWIND => CleanupParse[]]; EchoCommand[]; ProcessArgs[args ! UNWIND => CleanupParse[]]; CleanupParse[]; RETURN[outcome: normal] }; EchoCommand: PROC = { MBTTY.PutString[tty, "Building "L]; MBTTY.PutString[tty, IF data.germ THEN "germ"L ELSE "boot"L]; MBTTY.PutString[tty, " file "L]; MBTTY.PutString[tty, data.output]; IF data.etherFormat THEN { MBTTY.PutString[tty, " and ether "L]; MBTTY.PutString[tty, IF data.germ THEN "germ"L ELSE "boot"L]; MBTTY.PutString[tty, " file "L]; MBTTY.PutString[tty, data.etherOutput]; }; MBTTY.PutCR[tty]; MBTTY.PutString[tty, "Writing load map to "L]; MBTTY.PutString[tty, data.loadmap]; MBTTY.PutCR[tty]; }; EchoBCDs: PROC = { MBOut.Text["Input BCD"L]; IF data.inputBCDs.nBcds ~= 0 THEN MBOut.Char['s]; MBOut.Char[':]; MBOut.CR[]; MBOut.CR[]; FOR i: CARDINAL IN [0..data.inputBCDs.nBcds) DO file: STRING = [40]; String.AppendString[file, data.inputBCDs.bcds[i].name]; MBOut.Spaces[2]; MBOut.Text[file, 39]; MBOut.Spaces[2]; {ENABLE Segments.FileNameProblem[] => CONTINUE; MBOut.Time[Segments.GetFileTimes[Segments.NewFile[file]].create]; }; MBOut.CR[]; ENDLOOP; MBOut.CR[]; }; ProcessResults: PROC [results: MBCommandUtil.PairList] = { key, value: LONG STRING; bootFileExt: LONG STRING = IF data.germ THEN "germ"L ELSE "boot"L; loadMapExt: LONG STRING = "loadmap"L; data.output _ data.etherOutput _ data.loadmap _ NIL; FOR i: CARDINAL IN [0..MBCommandUtil.ListLength[results]) DO [key: key, value: value] _ MBCommandUtil.GetNthPair[results, i]; SELECT TRUE FROM key = NIL => data.output _ MBCommandUtil.CopyString[s: value, extra: bootFileExt.length+1]; String.EquivalentString["bootFile"L, key] => IF data.output = NIL THEN data.output _ MBCommandUtil.CopyString[s: value, extra: bootFileExt.length+1] ELSE MB.Error["Duplicate result keyword 'bootFile'"L]; String.EquivalentString["loadMap"L, key] => IF data.loadmap = NIL THEN data.loadmap _ MBCommandUtil.CopyString[s: value, extra: loadMapExt.length+1] ELSE MB.Error["Duplicate result keyword 'loadMap'"L]; ENDCASE => MB.Error["Unrecognized result keyword"L]; ENDLOOP; IF data.output = NIL THEN StripExtension[data.output _ MBCommandUtil.CopyString[data.input]]; data.output _ MBCommandUtil.SetExtension[root: data.output, defaultExt: bootFileExt]; IF data.etherFormat THEN { etherExt: STRING = IF data.germ THEN "eg"L ELSE "pb"L; data.etherOutput _ MBCommandUtil.CopyString[s: data.output, extra: etherExt.length+1]; StripExtension[data.etherOutput]; data.etherOutput _ MBCommandUtil.SetExtension[root: data.etherOutput, defaultExt: etherExt]; }; EnsureNotCurrentBootFile[]; IF data.loadmap = NIL THEN StripExtension[data.loadmap _ MBCommandUtil.CopyString[data.output]]; data.loadmap _ MBCommandUtil.SetExtension[root: data.loadmap, defaultExt: loadMapExt]; }; StripExtension: PROC [s: LONG STRING] = { FOR i: CARDINAL IN [0..s.length) DO IF s[i] = '. THEN {s.length _ i; RETURN}; ENDLOOP; }; EnsureNotCurrentBootFile: PROC = { name: STRING _ [40]; cap: File.Capability; someBootFileCode: Space.Handle _ Space.GetHandle[Space.PageFromLongPointer[ RuntimeInternal.Codebase[Runtime.GlobalFrame[Space.GetHandle]]]]; file: Segments.FHandle; String.AppendString[name, data.output]; file _ Segments.NewFile[name ! Segments.FileNameProblem[] => GO TO ok]; Segments.FPFromFile[file, @cap]; Segments.ReleaseFile[file]; DO parent: Space.Handle; mapped: BOOL; [parent: parent, mapped: mapped] _ Space.GetAttributes[someBootFileCode]; IF mapped THEN EXIT; someBootFileCode _ parent; ENDLOOP; IF Space.GetWindow[someBootFileCode].file.fID = cap.fID THEN MB.Error["I refuse to overwrite the currently executing boot file."L]; EXITS ok => NULL; }; ProcessArgs: PROC [args: MBCommandUtil.PairList] = { key, value: LONG STRING; nProcesses, gftLength: CARDINAL _ LAST[CARDINAL]; FOR i: CARDINAL IN [0..MBCommandUtil.ListLength[args]) DO [key: key, value: value] _ MBCommandUtil.GetNthPair[args, i]; SELECT TRUE FROM String.EquivalentString["parm"L, key] => { parmFile: LONG STRING _ MBCommandUtil.CopyString[s: value, extra: (".bootmesa"L).length]; parmFile _ MBCommandUtil.SetExtension[root: parmFile, defaultExt: "bootmesa"L]; MBTTY.PutString[tty, "Processing parameter file "L]; MBTTY.PutString[tty, parmFile]; MB.ProcessInput[parmFile ! UNWIND => [] _ MBCommandUtil.FreeString[parmFile]]; parmFile _ MBCommandUtil.FreeString[parmFile]; MBTTY.PutCR[tty]; }; String.EquivalentString["bcd"L, key] => { bH: MB.BHandle; IF data.inputBCDs.nBcds = LAST[MB.BCount] - 1 --save slot for NullConfig-- THEN MB.Error["Too many input BCDs"L]; IF data.inputBCDs.nBcds = data.inputBCDs.length THEN { newLength: MB.BCount = MIN[2*data.inputBCDs.length, LAST[MB.BCount]]; newInputBCDs: LONG POINTER TO MB.InputBCDs _ data.zone.NEW[MB.InputBCDs[newLength] _ [nBcds: data.inputBCDs.nBcds]]; Inline.LongCOPY[ from: @data.inputBCDs.bcds[0], to: @newInputBCDs.bcds[0], nwords: data.inputBCDs.nBcds*SIZE[MB.BHandle]]; data.zone.FREE[@data.inputBCDs]; data.inputBCDs _ newInputBCDs}; bH _ data.zone.NEW[MB.BObject _ [ name: NIL, bcd: NIL, bcdSeg: NIL, bcdSegment: NIL, mt: DESCRIPTOR[NIL, 0], gfiOffset: 0, files: NIL]]; bH.name _ MBCommandUtil.CopyString[s: value, extra: (".bcd"L).length]; bH.name _ MBCommandUtil.SetExtension[root: bH.name, defaultExt: "bcd"L]; data.inputBCDs.bcds[data.inputBCDs.nBcds] _ bH; data.inputBCDs.nBcds _ data.inputBCDs.nBcds+1; }; String.EquivalentString["nProcesses"L, key] => { nProcesses _ String.StringToDecimal[value ! String.InvalidNumber => GO TO bogus]; IF ~(nProcesses-1 IN PSB.PsbIndex) THEN GO TO bogus; EXITS bogus => MB.Error["Invalid nProcesses"L]; }; String.EquivalentString["gftLength"L, key] => { gftLength _ String.StringToDecimal[value ! String.InvalidNumber => GO TO bogus]; IF ~(gftLength-1 IN PrincOps.GFTIndex) THEN GO TO bogus; EXITS bogus => MB.Error["Invalid gftLength"L]; }; ENDCASE => MB.Error["Unrecognized parameter keyword"L]; ENDLOOP; IF nProcesses ~= LAST[CARDINAL] THEN { data.nProcesses _ nProcesses; MBTTY.PutString[tty, "Number of processes set to "L]; MBTTY.PutDecimal[tty, nProcesses]; MBTTY.PutCR[tty]; }; IF gftLength ~= LAST[CARDINAL] THEN { data.gftLength _ gftLength; MBTTY.PutString[tty, "GFT length set to "L]; MBTTY.PutDecimal[tty, gftLength]; MBTTY.PutCR[tty]; }; }; ReportStats: PROC [ nFilePages, nModules, nGFIs, nResidentPages, nBootLoadedPages: CARDINAL, elapsedTime: LONG CARDINAL] = { OPEN MBTTY; PutString[tty, "Boot file pages: "L]; PutDecimal[tty, nFilePages]; PutString[tty, " ("L]; PutDecimal[tty, nBootLoadedPages]; PutString[tty, " bootloaded, of which "L]; PutDecimal[tty, nResidentPages]; PutLine[tty, " are resident)"L]; PutString[tty, "Modules: "L]; PutDecimal[tty, nModules]; PutString[tty, " (requiring "L]; PutDecimal[tty, nGFIs]; PutLine[tty, " GFIs)"L]; PutString[tty, "Time: "L]; IF elapsedTime > 60 THEN { PutLongDecimal[tty, elapsedTime/60]; PutChar[tty, ':]; elapsedTime _ elapsedTime MOD 60; }; PutLongNumber[tty, elapsedTime, [base: 10, zerofill: TRUE, unsigned: TRUE, columns: 2]]; PutCR[tty]; }; <> Abort: PUBLIC SIGNAL = CODE; Error: PUBLIC PROC [msg: STRING] = { MBTTY.PutCR[data.ttyHandle]; MBTTY.PutString[data.ttyHandle, "MakeBoot Error: "L]; MBTTY.PutLine[data.ttyHandle, msg]; SIGNAL MB.Abort; }; Zero: PUBLIC PROC [p: LONG POINTER, n: CARDINAL] = { IF n # 0 THEN {p^ _ 0; Inline.LongCOPY[from: p, to: p+1, nwords: n-1]}; }; MakeBootVersion: PUBLIC PROC [s: STRING] = { s.length _ 0; String.AppendString[s, "Cedar MakeBoot of "L]; Time.Append[s, Time.Unpack[Runtime.GetBcdTime[]]]; s.length _ s.length-3; -- remove seconds }; Init: PROC = { IF useCommander THEN -- *** temporary Commander.Register[key: "MakeBoot", proc: RunFromCommander, doc: NIL] ELSE Exec.AddCommand["MakeBoot.~"L, RunFromExec]; }; Init[]; END.