BootTool.mesa
last edited by Roy Levin on October 5, 1983 12:20 pm
last edited by Andrew Birrell on February 6, 1983 11:41 am
Last Edited by: Maxwell, February 23, 1983 9:30 am
Last Edited by: Paul Rovner, December 13, 1983 10:40 am
DIRECTORY
AMBridge USING [TVToCardinal],
AMTypes USING [--Error,-- Index, IndexToName, NValues, Type, Value],
Ascii USING [Digit, Letter, Lower, SP, Upper],
BasicTime USING [Now],
Booting USING [
Boot, BootFileNumber, Checkpoint, GetBootFileNumber, MicrocodeType, nullMicrocode, Switches],
Buttons USING [Button, ButtonProc, Create],
Containers USING [ChildXBound, ChildYBound, Container, Create],
Convert USING [CardFromRope, Error],
File USING [
GetVolumeName, Handle, IsDebugger, NextVolume, PageNumber, SystemVolume, Volume, VolumeID],
FS USING [EnumerateForNames, Error, NameProc, Open, OpenFile],
FSBackdoor USING [GetFileHandle],
GermSwap USING [Switch],
IO USING [PutFR, time],
Labels USING [Create],
List USING [Compare, Comparison, Reverse, Sort],
MBQueue USING [Create, CreateButton, Queue, QueueClientAction],
MessageWindow USING [Append, Blink, Confirm],
PhysicalVolume USING [NextPhysical, Physical, PhysicalInfo, PhysicalRC],
Process USING [Detach],
Rope USING [Cat, Concat, Equal, Fetch, FromChar, Length, Map, ROPE, Substr],
Rules USING [Create, Rule],
UserProfile USING [Line],
VFonts USING [FontHeight, StringWidth],
ViewerClasses USING [Viewer],
ViewerOps USING [
AddProp, ChangeColumn, EnumerateViewers, EnumProc, OpenIcon, SetOpenHeight],
ViewerSpecs USING [openRightWidth],
ViewerTools USING [
GetContents, GetSelectedViewer, InhibitUserEdits, MakeNewTextViewer, SelPosRec, SetContents, SetSelection];
BootTool: CEDAR MONITOR
IMPORTS
AMBridge, AMTypes, Ascii, BasicTime, Booting, Buttons, Containers, Convert, File, FS, FSBackdoor, IO, Labels, List, MBQueue, MessageWindow, PhysicalVolume, Process, Rope, Rules, UserProfile, VFonts, ViewerOps, ViewerSpecs, ViewerTools =
BEGIN
ROPE: TYPE = Rope.ROPE;
entryHeight: NAT = VFonts.FontHeight[] + 2;
entryVSpace: NAT = VFonts.FontHeight[]*2/3;
entryHSpace: NAT = 10;
maxFeedbackLines: NAT = 5;
BootToolHandle: TYPE = REF BootToolHandleRec;
BootToolHandleRec: TYPE = RECORD [
outer: Containers.Container ← NIL,
height: NAT ← 0,
q: MBQueue.Queue ← NIL,
bootees: ARRAY BooteeClass OF Bootee ← ALL[NIL],
switchesButton: Buttons.Button ← NIL,
switches: ViewerClasses.Viewer ← NIL,
feedback: ViewerClasses.Viewer ← NIL
];
SimpleButtonRef: TYPE = REF SimpleButtonRec;
SimpleButtonRec: TYPE = RECORD [
volume: File.Volume,
switches: ROPE
];
---- ---- ---- ---- ---- ---- ---- ----
Global Variables (monitored)
---- ---- ---- ---- ---- ---- ---- ----
q: MBQueue.Queue ← MBQueue.Create[];
initialized: BOOLFALSE;
simpleButtonsUp: BOOLFALSE; -- protected by synchronization on 'q'
---- ---- ---- ---- ---- ---- ---- ----
Tool Construction
---- ---- ---- ---- ---- ---- ---- ----
MakeBootTool: ENTRY Buttons.ButtonProc = {
ENABLE UNWIND => NULL;
IF mouseButton = blue THEN {
handle: BootToolHandle = NEW[BootToolHandleRec ← []];
handle.q ← MBQueue.Create[];
handle.q.QueueClientAction[proc: MakeNewBootTool, data: handle];
}
ELSE q.QueueClientAction[proc: MakeSimpleButtons, data: NIL];
};
MakeNewBootTool: PROC [h: REF ANY] = {
handle: BootToolHandle = NARROW[h];
handle.outer ← Containers.Create[info: [
name: "Boot Tool", iconic: TRUE, column: right, scrollable: FALSE],
paint: FALSE -- keeps icon from appearing
];
In order to ensure that some reference to the main data structure exists, we store a reference to it on the property list of the outer container. It would be nicer if containers had a clientData field for this purpose.
ViewerOps.AddProp[handle.outer, $BootToolHandle, handle];
BuildSources[handle];
BuildSwitches[handle];
BuildFeedback[handle];
ViewerOps.SetOpenHeight[handle.outer, handle.height];
ViewerOps.OpenIcon[icon: handle.outer, bottom: FALSE];
};
MakeSimpleButtons: PROC [ignored: REF ANY] = {
volume: File.Volume ← NIL;
IF simpleButtonsUp THEN RETURN;
UNTIL (volume ← File.NextVolume[volume]) = NIL DO
name: ROPE ← File.GetVolumeName[volume];
switches: ROPE = GetSwitches[name];
IF switches.Length[] > 0 THEN name ← name.Cat["/", switches];
[] ← q.CreateButton[
info: [name: name], proc: SimpleBootProc,
clientData: NEW[SimpleButtonRec ← [volume, switches]]
];
ENDLOOP;
[] ← q.CreateButton[info: [name: "Basic"], proc: BasicCedarDorado];
[] ← q.CreateButton[info: [name: "Alto"], proc: AltoBootProc];
[] ← q.CreateButton[info: [name: "BB"], proc: BootButtonProc];
[] ← q.CreateButton[info: [name: "Self"], proc: SelfBootProc];
simpleButtonsUp ← TRUE;
};
GetSwitches: PROC [name: ROPE] RETURNS [switches: ROPE] = {
name ← name.Concat["Switches"];
switches ← UserProfile.Line[name];
IF switches.Length[] = 0 THEN switches ← UserProfile.Line[Rope.Concat["Boot.", name]];
};
AddSeparatingRule: PROC [handle: BootToolHandle] = {
rule: Rules.Rule = Rules.Create[info: [
parent: handle.outer,
wy: handle.height,
ww: handle.outer.cw,
wh: 1
]];
Containers.ChildXBound[handle.outer, rule]; -- constrain rule to be width of parent
handle.height ← handle.height + entryVSpace; -- spacing after rule
};
MakeCenteredLabel: PROC [handle: BootToolHandle, name: ROPE] = {
[] ← Labels.Create[info: [
name: name,
parent: handle.outer,
wx: (ViewerSpecs.openRightWidth - VFonts.StringWidth[name])/2,
wy: handle.height,
wh: entryHeight,
border: FALSE
]];
handle.height ← handle.height + entryHeight + entryVSpace;
};
---- ---- ---- ---- ---- ---- ---- ----
BooteeClass: TYPE = {volume, file, ucode, pv, --self,-- bb};
Bootee: TYPE = REF BooteeObject;
BooteeObject: TYPE = RECORD [
handle: BootToolHandle,
class: BooteeClass,
button: Buttons.Button ← NIL,
choices: ChoiceList ← NIL,
currentChoice: ChoiceList ← NIL,  -- currentChoice.first is what's interesting
displayedChoice: ViewerClasses.Viewer ← NIL
];
ChoiceList: TYPE = LIST OF REF ANY;
ChoiceRef: TYPE = REF ChoiceDesc;
ChoiceDesc: TYPE = RECORD [
name: ROPE,
value: REF ANY];
VolumeVal: TYPE = REF PaintedVolumeVal;
PaintedVolumeVal: TYPE = RECORD[File.Volume];
MicrocodeVal: TYPE = REF Booting.BootFileNumber;
PhysicalVolumeVal: TYPE = REF PaintedPhysicalVolumeVal;
PaintedPhysicalVolumeVal: TYPE = RECORD[File.VolumeID];
BuildSources: PROC [handle: BootToolHandle] = {
MakeBootee: PROC [
class: BooteeClass, buttonName: ROPE, buttonProc: Buttons.ButtonProc,
choicesProc: PROC [BootToolHandle] RETURNS [ChoiceList], readOnly: BOOLFALSE] = {
bootee: Bootee ← NEW[BooteeObject ← [handle: handle, class: class]];
doIt: Buttons.Button = handle.q.CreateButton[
info: [name: "Boot",
wx: entryHSpace,
wy: handle.height,
wh: entryHeight,
parent: handle.outer, 
border: TRUE
],
proc: DoBoot,
clientData: bootee,
guarded: TRUE
];
IF buttonProc ~= NIL THEN
bootee.button ← Buttons.Create[
info: [
name: buttonName,
wx: doIt.wx + doIt.ww,
wy: handle.height,
wh: entryHeight,
parent: handle.outer, 
border: FALSE
],
proc: buttonProc,
fork: FALSE,
clientData: handle
]
ELSE
[] ← Labels.Create[
info: [
name: buttonName,
wx: doIt.wx + doIt.ww,
wy: handle.height,
wh: entryHeight,
parent: handle.outer, 
border: FALSE
]
];
IF choicesProc ~= NIL THEN {
bootee.displayedChoice ← ViewerTools.MakeNewTextViewer[info: [
parent: handle.outer,
wx: bootee.button.wx + bootee.button.ww + entryHSpace,
wy: handle.height,
ww: 9999, -- arbitrary; Containers.ChildXBound will fix.
wh: entryHeight,
data: NIL,
scrollable: FALSE,
border: FALSE
]];
Containers.ChildXBound[container: handle.outer, child: bootee.displayedChoice];
IF readOnly THEN ViewerTools.InhibitUserEdits[bootee.displayedChoice];
bootee.currentChoice ← bootee.choices ← choicesProc[handle];
SetChoice[bootee];
};
handle.bootees[class] ← bootee;
handle.height ← handle.height + entryHeight + entryVSpace;
};
MakeCenteredLabel[handle, "Booting Sources"];
MakeBootee[volume, "Volume:", SelectVolumeBootee, BuildVolumeList, TRUE];
MakeBootee[file, "File:", SelectFileBootee, BuildBootFileList];
MakeBootee[ucode, "Microcode:", SelectMicrocodeBootee, BuildMicrocodeList];
MakeBootee[
pv, "Physical Volume:", SelectPhysicalVolumeBootee, BuildPhysicalVolumeList, TRUE];
MakeBootee[bb, "Button", NIL, NIL];
MakeBootee[self, "Self", NIL, NIL];
};
SetChoice: PROC [bootee: Bootee, deselect: BOOLFALSE] = {
name: ROPE =
IF bootee.currentChoice = NIL THEN NIL
ELSE NARROW[bootee.currentChoice.first, ChoiceRef].name;
ViewerTools.SetContents[bootee.displayedChoice, name];
IF deselect THEN
The following turns off pending delete, and sets the type-in point at the end of the
text if user editing is not inhibited
ViewerTools.SetSelection[bootee.displayedChoice,
NEW[ViewerTools.SelPosRec ← [start: Rope.Length[name], length: 0]]];
};
BuildVolumeList: PROC [handle: BootToolHandle] RETURNS [volumes: ChoiceList ← NIL] = {
system: File.Volume = File.SystemVolume[];
volume: File.Volume ← NIL;
UNTIL (volume ← File.NextVolume[volume]) = NIL DO
IF volume ~= system THEN
volumes ← CONS[VolumeToChoice[volume], volumes];
ENDLOOP;
volumes ← List.Sort[volumes, CompareChoiceNames];
RETURN[CONS[VolumeToChoice[system], volumes]]
};
VolumeToChoice: PROC [volume: File.Volume] RETURNS [ChoiceRef] = {
RETURN[NEW[ChoiceDesc ← [
File.GetVolumeName[volume],
NEW[PaintedVolumeVal ← [volume]]
]]]
};
CompareChoiceNames: PROC [ref1, ref2: REF ANY] RETURNS [List.Comparison] =
{RETURN[List.Compare[NARROW[ref1, ChoiceRef].name, NARROW[ref2, ChoiceRef].name]]};
InstallRequest: TYPE = RECORD [bootee: Bootee, list: ChoiceList];
InstallList: PROC [data: REF ANY] = {
req: REF InstallRequest = NARROW[data];
req.bootee.choices ← req.list;
};
BuildBootFileList: PROC [handle: BootToolHandle] RETURNS [ChoiceList ← NIL] = {
TRUSTED{Process.Detach[FORK BuildBootFileListForked[handle]]};
};
BuildBootFileListForked: PROC [handle: BootToolHandle] = {
handle.q.QueueClientAction[
proc: InstallList,
data: NEW[InstallRequest ← [handle.bootees[file], BuildBootFileListSynchronously[handle]]]
];
};
BuildBootFileListSynchronously: PROC [handle: BootToolHandle]
RETURNS [bootFiles: ChoiceList ← NIL] = {
GetWorkingDir: PROC RETURNS [ROPENIL] = {
r: ROPE = ViewerTools.GetContents[handle.bootees[file].displayedChoice];
IF r.Length[] > 0 THEN
SELECT r.Fetch[r.Length[]-1] FROM
'>, '/ => RETURN[IF r.Fetch[0] = '< THEN Rope.Concat["[]", r] ELSE r];
ENDCASE;
};
DoBootFilesWithPrefix: PROC [wDir: ROPE] = {
DoOneBootFile: FS.NameProc = {
bootFiles ← CONS[NEW[ChoiceDesc ← [fullFName, NIL]], bootFiles];
RETURN[TRUE]
};
FS.EnumerateForNames["*.boot!H", DoOneBootFile, wDir ! FS.Error => CONTINUE];
};
wDir: ROPE = GetWorkingDir[];
IF wDir ~= NIL THEN DoBootFilesWithPrefix[wDir]
ELSE
FOR c: ChoiceList ← handle.bootees[volume].choices, c.rest UNTIL c = NIL DO
DoBootFilesWithPrefix[Rope.Cat["[]<", NARROW[c.first, ChoiceRef].name, ">"]];
ENDLOOP;
RETURN[List.Reverse[bootFiles]]
};
BuildMicrocodeList: PROC [handle: BootToolHandle] RETURNS [ChoiceList] = {
TRUSTED{Process.Detach[FORK BuildMicrocodeListForked[handle]]};
RETURN[LIST[
NEW[ChoiceDesc ← ["(default)", NEW[Booting.BootFileNumber ← Booting.nullMicrocode]]]
]]
};
BuildMicrocodeListForked: PROC [handle: BootToolHandle] = {
handle.q.QueueClientAction[
proc: InstallList,
data: NEW[InstallRequest ← [
handle.bootees[ucode],
BuildMicrocodeListSynchronously[handle]
]]
];
};
BuildMicrocodeListSynchronously: PROC [handle: BootToolHandle]
RETURNS [microcode: ChoiceList] = {
OPEN B: Booting;
microcode ← LIST[NEW[ChoiceDesc ← ["(default)", NEW[B.BootFileNumber ← B.nullMicrocode]]]];
BEGIN
ENABLE --AMTypes.Error-- ANY => CONTINUE;
Massage: PROC [name: ROPE] RETURNS [newName: ROPE] = {
start: INT ← 1;
doingLower: BOOLFALSE;
newName ← Rope.FromChar[Ascii.Upper[name.Fetch[0]]];
DO
next: INT ← start;
UNTIL next = name.Length[] OR (doingLower ~= (name.Fetch[next] IN ['a..'z])) DO
next ← next + 1;
ENDLOOP;
newName ← newName.Concat[name.Substr[start, next - start]];
IF next = name.Length[] THEN EXIT ELSE start ← next;
IF doingLower THEN newName ← newName.Concat[Rope.FromChar[Ascii.SP]];
doingLower ← ~doingLower;
ENDLOOP;
};
uT: AMTypes.Type = LOOPHOLE[CODE[B.MicrocodeType]];
FOR i: AMTypes.Index IN NAT[1..AMTypes.NValues[uT]] DO
name: ROPE = Massage[AMTypes.IndexToName[uT, i]];
ucodeType: B.MicrocodeType;
TRUSTED{ucodeType ← LOOPHOLE[AMBridge.TVToCardinal[AMTypes.Value[uT, i]]]};
microcode ← CONS[
NEW[ChoiceDesc ← [name, NEW[B.BootFileNumber ← B.GetBootFileNumber[ucodeType]]]],
microcode];
ENDLOOP;
END;
RETURN[List.Reverse[microcode]]
};
BuildPhysicalVolumeList: PROC [handle: BootToolHandle]
RETURNS [physicalVolumes: ChoiceList ← NIL] = {
OPEN PV: PhysicalVolume;
physical: PV.Physical ← NIL;
UNTIL (physical ← PV.NextPhysical[physical]) = NIL DO
name: ROPE;
status: PV.PhysicalRC;
id: File.VolumeID;
[rootStatus: status, name: name, id: id] ← PV.PhysicalInfo[physical];
IF status = ok THEN
physicalVolumes ← CONS[
NEW[ChoiceDesc ← [name, NEW[PaintedPhysicalVolumeVal ← [id]]]],
physicalVolumes
];
ENDLOOP;
physicalVolumes ← List.Sort[physicalVolumes, CompareChoiceNames];
};
---- ---- ---- ---- ---- ---- ---- ----
BuildSwitches: PROC [handle: BootToolHandle] = {
help: Buttons.Button;
AddSeparatingRule[handle];
MakeCenteredLabel[handle, "Booting Options"];
help ← handle.q.CreateButton[
info: [name: "Help",
wx: entryHSpace,
wy: handle.height,
wh: entryHeight,   -- specify rather than defaulting so line is uniform
parent: handle.outer, 
border: TRUE
],
proc: SwitchesHelp,
clientData: handle
];
handle.switchesButton ← Buttons.Create[
info: [name: "Switches:",
wx: help.wx + help.ww,
wy: handle.height,
wh: entryHeight,   -- specify rather than defaulting so line is uniform
parent: handle.outer, 
border: FALSE
],
proc: SelectBootSwitches,
fork: TRUE,
clientData: handle
];
handle.switches ← ViewerTools.MakeNewTextViewer[info: [
parent: handle.outer,
wx: handle.switchesButton.wx + handle.switchesButton.ww + entryHSpace,
wy: handle.height,
ww: 50, -- ugly, but necessary preparation for Containers.ChildXBound
wh: entryHeight,
data: NIL, -- initial contents
scrollable: FALSE,
border: FALSE
]];
Containers.ChildXBound[container: handle.outer, child: handle.switches];
ViewerTools.SetContents[handle.switches, UserProfile.Line["Boot.Switches"]];
handle.height ← handle.height + entryHeight + entryVSpace;
};
---- ---- ---- ---- ---- ---- ---- ----
BuildFeedback: PROC [handle: BootToolHandle] = {
feedbackHeight: NAT = maxFeedbackLines*entryHeight;
AddSeparatingRule[handle];
handle.feedback ← ViewerTools.MakeNewTextViewer[info: [
parent: handle.outer,
wx: 0,
wy: handle.height,
ww: 50, -- ugly, but necessary preparation for Containers.ChildXBound
wh: feedbackHeight,
data: NIL, -- initial contents
scrollable: TRUE,
border: FALSE
]];
Containers.ChildXBound[container: handle.outer, child: handle.feedback];
Containers.ChildYBound[container: handle.outer, child: handle.feedback];
ViewerTools.InhibitUserEdits[handle.feedback];
handle.height ← handle.height + feedbackHeight + entryVSpace;
};
---- ---- ---- ---- ---- ---- ---- ----
Button Action Procedures
---- ---- ---- ---- ---- ---- ---- ----
SelectVolumeBootee: Buttons.ButtonProc = {
handle: BootToolHandle = NARROW[clientData];
previousContents: ROPE = ViewerTools.GetContents[handle.bootees[volume].displayedChoice];
Kill pending delete selection elsewhere in tool
ViewerTools.SetSelection[handle.bootees[volume].displayedChoice,
NEW[ViewerTools.SelPosRec ← [start: Rope.Length[previousContents], length: 0]]];
AdvanceName[handle.bootees[volume]];
};
SelectFileBootee: Buttons.ButtonProc = {
handle: BootToolHandle = NARROW[clientData];
previouslySelected: BOOL =
ViewerTools.GetSelectedViewer[] = handle.bootees[file].displayedChoice;
ViewerTools.SetSelection[viewer: handle.bootees[file].displayedChoice, selection: NIL];
IF previouslySelected THEN {
IF handle.bootees[file].choices = NIL OR (mouseButton ~= red) THEN
handle.bootees[file].choices ← BuildBootFileListSynchronously[handle];
AdvanceName[handle.bootees[file]];
};
};
SelectMicrocodeBootee: Buttons.ButtonProc = {
handle: BootToolHandle = NARROW[clientData];
previouslySelected: BOOL =
ViewerTools.GetSelectedViewer[] = handle.bootees[ucode].displayedChoice;
ViewerTools.SetSelection[viewer: handle.bootees[ucode].displayedChoice, selection: NIL];
IF previouslySelected THEN AdvanceName[handle.bootees[ucode]];
};
SelectPhysicalVolumeBootee: Buttons.ButtonProc = {
handle: BootToolHandle = NARROW[clientData];
previousContents: ROPE = ViewerTools.GetContents[handle.bootees[pv].displayedChoice];
Kill pending delete selection elsewhere in tool
ViewerTools.SetSelection[handle.bootees[pv].displayedChoice,
NEW[ViewerTools.SelPosRec ← [start: Rope.Length[previousContents], length: 0]]];
AdvanceName[handle.bootees[pv]];
};
AdvanceName: PROC [bootee: Bootee] = {
choiceRef: ChoiceRef =
IF bootee.currentChoice = NIL THEN NIL ELSE NARROW[bootee.currentChoice.first];
IF bootee.choices = NIL THEN RETURN;
IF choiceRef = NIL OR
~Rope.Equal[choiceRef.name, ViewerTools.GetContents[bootee.displayedChoice], FALSE] OR
(bootee.currentChoice ← bootee.currentChoice.rest) = NIL THEN
bootee.currentChoice ← bootee.choices;
SetChoice[bootee: bootee, deselect: TRUE];
};
SelectBootSwitches: Buttons.ButtonProc = {
handle: BootToolHandle = NARROW[clientData];
ViewerTools.SetSelection[viewer: handle.switches, selection: NIL];
};
SwitchesHelp: Buttons.ButtonProc = {
ToChar: PROC [sw: GermSwap.Switch] RETURNS [ROPE] = {
IF sw IN [zero..nine] THEN RETURN [Rope.FromChar['0+sw.ORD]];
RETURN[Rope.FromChar['A + (sw.ORD - GermSwap.Switch.a.ORD)]]
};
handle: BootToolHandle = NARROW[clientData];
r: ROPE ← "Switch interpretations:\N";
textSwitches: ROPE = ViewerTools.GetContents[handle.switches];
switches: Booting.Switches ← ALL[TRUE];
all: BOOL = textSwitches.Length[] = 0;
IF ~all THEN {
error: ROPE;
[switches, error] ← RopeToSwitches[textSwitches];
IF error.Length[] > 0 THEN {ViewerTools.SetContents[handle.feedback, error]; RETURN};
};
FOR sw: GermSwap.Switch IN GermSwap.Switch DO
IF switches[sw] THEN
IF switchTable[sw].Length[] > 0 THEN
r ← r.Cat[" ", ToChar[sw], " - ", switchTable[sw], "\N"]
ELSE IF ~all THEN
r ← r.Cat[" ", ToChar[sw], " - ", "has no assigned interpretation\N"];
ENDLOOP;
ViewerTools.SetContents[handle.feedback, r];
};
RopeDefaultNil: TYPE = ROPENIL;
switchTable: ARRAY GermSwap.Switch OF RopeDefaultNil = [
zero: "worry-call debugger as early as possible",
one: "call debugger after MesaRuntime initialization",
two: "call debugger after VM initialization",
three: "call debugger after File initialization",
four: "call debugger after finding VM file and swapping in non-initial parts of boot file",
five: "call debugger in LoaderDriver after loading but before starting loadees",
a: "don't start Ethernet1 drivers",
b: "don't start Ethernet2 drivers",
c: "don't start Communication package",
d: "development version: don't try the Cedar directory in LoaderDriver/Installer",
f: "do full booting sequence, even if defaults would imply a rollback",
h: "hang in CPU loop with MP code instead of swapping to debugger",
i: "enable control-swat as early as possible (instead of waiting until after login)",
l: "long (and interactive) installation dialogue",
n: "don't touch the disk during initialization (forces entry to Iago)",
p: "don't read user profile from disk",
r: "rollback if the system volume has a valid checkpoint",
t: "force teledebugging instead of disk world-swap",
v: "perform VM copying for checkpoint (internal use only!)",
w: "for world-swap debugger, assume debuggee outload file is valid/interesting"
];
---- ---- ---- ---- ---- ---- ---- ----
Action Procedures
---- ---- ---- ---- ---- ---- ---- ----
PostMessage: PROC [handle: BootToolHandle, msg: ROPE] = {
IF handle = NIL THEN MessageWindow.Append[msg, TRUE]
ELSE ViewerTools.SetContents[handle.feedback, msg];
};
RollbackButton: Buttons.ButtonProc = {
PostMessage[NIL, Booting.Boot[[self[]], [r: TRUE]]];
};
CheckpointButton: Buttons.ButtonProc = {
okMsg: ROPE = IO.PutFR["Checkpoint created at %t.", IO.time[BasicTime.Now[]]];
msg: ROPE;
PostMessage[NIL, "Creating checkpoint."];
msg ← Booting.Checkpoint[];
PostMessage[NIL, IF msg.Length[] = 0 THEN okMsg ELSE msg];
};
SimpleBootProc: Buttons.ButtonProc = {
data: SimpleButtonRef = NARROW[clientData];
bootSwitches: Booting.Switches;
error: ROPE;
IF SomethingWorthSaving[] THEN RETURN;
[bootSwitches, error] ← RopeToSwitches[data.switches];
IF mouseButton = blue THEN bootSwitches[f] ← TRUE;
IF error = NIL THEN error ← Booting.Boot[[logical[data.volume]], bootSwitches];
PostMessage[NIL, error]
};
SelfBootProc: Buttons.ButtonProc = {
IF ~SomethingWorthSaving[] THEN PostMessage[NIL, Booting.Boot[[self[]], Booting.switches]];
};
AltoBootProc: Buttons.ButtonProc = {
IF ~SomethingWorthSaving[] THEN
PostMessage[NIL, Booting.Boot[[microcode[Booting.GetBootFileNumber[altoMesa]]], []]];
};
BootButtonProc: Buttons.ButtonProc = {
IF ~SomethingWorthSaving[] THEN PostMessage[NIL, Booting.Boot[[bootButton[]], []]];
};
BasicCedarDorado: Buttons.ButtonProc = {
file: File.Handle = DirLookup["BasicCedarDorado.boot"];
IF file ~= NIL THEN {
switches: Booting.Switches;
error: ROPE;
[switches, error] ← RopeToSwitches[UserProfile.Line["FileSwitches"]];
IF error = NIL AND ~SomethingWorthSaving[] THEN
error ← Booting.Boot[[file[file]], switches];
PostMessage[NIL, error];
}
ELSE PostMessage[NIL, "BasicCedarDorado.boot not found."];
};
DoBoot: Buttons.ButtonProc = {
CantBoot: ERROR = CODE;
DoBootInner: PROC = --INLINE-- {
This procedure can't be INLINE until Trinity (catch phrase in caller).
bootee: Bootee = NARROW[clientData];
handle: BootToolHandle = bootee.handle;
isCurrentChoice: BOOL = bootee.currentChoice ~= NIL AND
Rope.Equal[
ViewerTools.GetContents[bootee.displayedChoice],
NARROW[bootee.currentChoice.first, ChoiceRef].name];
choiceRef: ChoiceRef =
IF isCurrentChoice THEN NARROW[bootee.currentChoice.first] ELSE NIL;
ProcessOptions: PROC RETURNS [switches: Booting.Switches] = {
error: ROPE;
[switches, error] ← RopeToSwitches[ViewerTools.GetContents[handle.switches]];
IF error ~= NIL THEN {PostMessage[handle, error]; ERROR CantBoot};
};
SELECT bootee.class FROM
volume => {
IF ~isCurrentChoice THEN ERROR;
PostMessage[handle,
Booting.Boot[[logical[NARROW[choiceRef.value, VolumeVal]^]], ProcessOptions[]]];
};
file => {
file: File.Handle;
name: ROPE =
IF isCurrentChoice THEN choiceRef.name
ELSE ViewerTools.GetContents[bootee.displayedChoice];
IF (file ← DirLookup[name]) = NIL THEN {
PostMessage[handle, Rope.Cat["Unrecognizable file name '", name, "'"]];
ERROR CantBoot;
};
PostMessage[handle, Booting.Boot[[file[file]], ProcessOptions[]]];
};
ucode => {
bfn: Booting.BootFileNumber;
IF isCurrentChoice THEN
bfn ← NARROW[choiceRef.value, MicrocodeVal]^
ELSE {
r: ROPE = ViewerTools.GetContents[bootee.displayedChoice];
bfnC: LONG CARDINAL = Convert.CardFromRope[r ! Convert.Error => GO TO badBfn];
IF bfnC > CARDINAL.LAST--Booting.BootFileNumber-- THEN GO TO badBfn;
bfn ← [bfnC]; -- can't cause a fault
EXITS
badBfn => {
PostMessage[handle, "Bad microcode specification"];
ERROR CantBoot;
};
};
PostMessage[handle, Booting.Boot[[microcode[bfn]], ProcessOptions[]]];
};
pv => {
IF ~isCurrentChoice THEN ERROR;
PostMessage[handle, Booting.Boot[
[physical[NARROW[choiceRef.value, PhysicalVolumeVal]^]], ProcessOptions[]]];
};
bb => {
PostMessage[handle, Booting.Boot[[bootButton[]], ProcessOptions[]]];
};
self => {
PostMessage[handle, Booting.Boot[[self[]], ProcessOptions[]]];
};
ENDCASE;
};
IF SomethingWorthSaving[] THEN RETURN;
DoBootInner[ ! CantBoot => CONTINUE];
};
DirLookup: PROC [fileName: ROPE] RETURNS [file: File.Handle ← NIL] = {
fsFile: FS.OpenFile ← [NIL];
fsFile ← FS.Open[fileName ! FS.Error => CONTINUE];
IF fsFile ~= NIL THEN file ← FSBackdoor.GetFileHandle[fsFile];
};
RopeToSwitches: PROC [textSwitches: ROPE]
RETURNS [switches: Booting.Switches ← [], error: ROPENIL] = {
errors: ROPENIL;
SetBootSwitch: PROC [c: CHAR] RETURNS [stop: BOOL ← FALSE] = {
SELECT TRUE FROM
Ascii.Letter[c] => switches[VAL[GermSwap.Switch.a.ORD+(Ascii.Lower[c]-'a)]] ← TRUE;
Ascii.Digit[c] => switches[VAL[GermSwap.Switch.zero.ORD+(c-'0)]] ← TRUE;
ENDCASE => errors ← errors.Concat[Rope.FromChar[c]];
};
[] ← Rope.Map[base: textSwitches, action: SetBootSwitch];
IF errors.Length[] > 0 THEN error ← Rope.Concat["Unrecognizable boot switch(es): ", errors];
};
SomethingWorthSaving: PROC RETURNS [BOOL] = {
dirty: BOOLFALSE;
CheckDirty: ViewerOps.EnumProc = {
IF v.newVersion OR v.newFile THEN dirty ← TRUE ELSE RETURN;
IF v.offDeskTop THEN ViewerOps.ChangeColumn[v, left];
};
ViewerOps.EnumerateViewers[CheckDirty];
IF dirty THEN {
MessageWindow.Blink[];
IF ~MessageWindow.Confirm["Confirm discard of edits . . . "] THEN {
MessageWindow.Append["boot aborted."];
RETURN[TRUE]
};
};
RETURN[FALSE]
};
---- ---- ---- ---- ---- ---- ---- ----
The Main Body
---- ---- ---- ---- ---- ---- ---- ----
Initialize: ENTRY PROC = {
ENABLE UNWIND => NULL;
IF initialized THEN RETURN;
IF ~File.IsDebugger[File.SystemVolume[]] THEN {
[] ← q.CreateButton[
info: [name: "Checkpoint"], proc: CheckpointButton, fork: TRUE, guarded: TRUE];
[] ← q.CreateButton[
info: [name: "RollBack"], proc: RollbackButton, fork: TRUE, guarded: TRUE];
};
[] ← Buttons.Create[info: [name: "Boot"], proc: MakeBootTool, fork: TRUE];
initialized ← TRUE;
};
Initialize[];
END.