BootTool.mesa
last edited by Roy Levin on January 7, 1983 1:14 pm
last edited by Andrew Birrell on February 6, 1983 11:41 am
Last Edited by: Maxwell, February 23, 1983 9:30 am
DIRECTORY
AMBridge USING [TVToCardinal],
AMEventsExtra USING [Boot],
AMTypes USING [--Error,-- Index, IndexToName, NValues, Type, Value],
Ascii USING [SP],
Booting USING [
Boot, Bootee, BootFileNumber, defaultSwitches, GetBootFileNumber, MakeBootable, MicrocodeType, nullMicrocode, Switches],
Buttons USING [Button, ButtonProc, Create, SetDisplayStyle],
Containers USING [ChildXBound, Container, Create],
ConvertUnsafe USING [AppendRope, ToRefText, ToRope],
DCSFileTypes USING [tLeaderPage],
Directory USING [Error, GetNext, ignore, Lookup, VolumeError],
File USING [Capability, GetAttributes, nullCapability, PageNumber],
Heap USING [systemZone],
IO USING [char, EndOf, Error, GetCard, PutFToRope, rope, RIS, STREAM],
Labels USING [Create],
List USING [Compare, Comparison, Reverse, Sort],
Menus USING [AppendMenuEntry, CreateMenu, CreateEntry, Menu, MenuLine, MenuProc],
MessageWindow USING [Append, Blink, Clear, Confirm],
PhysicalVolume USING [
GetAttributes, GetNext, GetNextLogicalVolume, ID, maxNameLength, nullID],
Process USING [Detach],
Rope USING [
Cat, Concat, Equal, Fetch, Find, FromChar, FromRefText, Length, Lower, Map, ROPE, Substr, ToRefText, Upper],
Rules USING [Create, Rule],
Runtime USING[IsBound],
UserProfile USING [Line],
VFonts USING [FontHeight, StringWidth],
ViewerClasses USING [Viewer],
ViewerOps USING [ChangeColumn, EnumerateViewers, EnumProc, OpenIcon, SetMenu, SetOpenHeight],
ViewerSpecs USING [openRightWidth],
ViewerTools USING [
GetContents, InhibitUserEdits, MakeNewTextViewer, SelPosRec, SetContents, SetSelection],
Volume USING [
GetLabelString, GetNext, ID, maxNameLength, NeedsScavenging, nullID, systemID, Unknown],
VolumeExtras USING [OpenVolume];
BootTool: CEDAR MONITOR
IMPORTS
AMBridge, AMEventsExtra, AMTypes, Booting, Buttons, Containers, ConvertUnsafe, Directory, File, Heap, IO, Labels, List, Menus, MessageWindow, PhysicalVolume, Process, Rope, Rules, Runtime, UserProfile, VFonts, ViewerOps, ViewerSpecs, ViewerTools, Volume, VolumeExtras =
BEGIN
entryHeight: NAT = VFonts.FontHeight[] + 2;
entryVSpace: NAT = VFonts.FontHeight[]*2/3;
entryHSpace: NAT = 10;
BootToolHandle: TYPE = REF BootToolHandleRec;
BootToolHandleRec: TYPE = RECORD [
outer: Containers.Container ← NIL,
height: NAT ← 0,
s1Data: S1Data,
s2Data: S2Data
];
SimpleButtonRef: TYPE = REF SimpleButtonRec;
SimpleButtonRec: TYPE = RECORD [
volume: Volume.ID,
switches: Rope.ROPE
];
---- ---- ---- ---- ---- ---- ---- ----
Tool Construction
---- ---- ---- ---- ---- ---- ---- ----
MakeBootTool: ENTRY Buttons.ButtonProc = {
menu: Menus.Menu;
handle: BootToolHandle;
IF mouseButton # blue THEN {MakeBootButtons[]; RETURN};
menu ← Menus.CreateMenu[lines: 2];
handle ← NEW[BootToolHandleRec];
handle.outer ← Containers.Create[info: [
name: "Boot Tool", iconic: TRUE, column: right, scrollable: FALSE],
paint: FALSE];
MakeSimpleButtonsMenu[menu, 0];
Menus.AppendMenuEntry[menu,
Menus.CreateEntry[name: "Boot", proc: DoBoot, clientData: handle, fork: TRUE, guarded: TRUE],
1];
Menus.AppendMenuEntry[menu,
Menus.CreateEntry[name: "Reset", proc: ResetDefaults, clientData: handle, fork: TRUE],
1];
ViewerOps.SetMenu[viewer: handle.outer, menu: menu];
BuildSection1[handle];
BuildSection2[handle];
ResetDefaultsInternal[handle];
ViewerOps.SetOpenHeight[handle.outer, handle.height];
ViewerOps.OpenIcon[icon: handle.outer, bottom: FALSE];
};
buttonsCreated: BOOLEANFALSE;
MakeBootButtons: PROC = {
volID: Volume.ID ← Volume.nullID;
IF buttonsCreated THEN RETURN ELSE buttonsCreated ← TRUE;
UNTIL (volID ← Volume.GetNext[volID, ALL[TRUE]]) = Volume.nullID DO
volName: STRING ← [Volume.maxNameLength];
label, switches: Rope.ROPE;
TRUSTED{ Volume.GetLabelString[volID, volName]; label𡤌onvertUnsafe.ToRope[volName] };
IF (switches ← GetSwitches[label]).Length[] > 0 THEN label ← label.Cat["/", switches];
[] ← Buttons.Create[info: [name: label], proc: SimpleBootProc,
clientData: NEW[SimpleButtonRec ← [volID, switches]], fork: TRUE, guarded: TRUE];
ENDLOOP;
[] ← Buttons.Create[info: [name: "Alto"], proc: AltoBootProc,
clientData: NIL, fork: TRUE, guarded: TRUE];
[] ← Buttons.Create[info: [name: "BB"], proc: BootButtonProc,
clientData: NIL, fork: TRUE, guarded: TRUE];
[] ← Buttons.Create[info: [name: "Basic"], proc: BasicCedarDorado,
clientData: NIL, fork: TRUE, guarded: TRUE];
};
MakeSimpleButtonsMenu: INTERNAL PROC [menu: Menus.Menu, line: Menus.MenuLine] = {
volID: Volume.ID ← Volume.nullID;
UNTIL (volID ← Volume.GetNext[volID, ALL[TRUE]]) = Volume.nullID DO
volName: STRING ← [Volume.maxNameLength];
label, switches: Rope.ROPE;
TRUSTED{ Volume.GetLabelString[volID, volName]; label𡤌onvertUnsafe.ToRope[volName] };
IF (switches ← GetSwitches[label]).Length[] > 0 THEN label ← label.Cat["/", switches];
Menus.AppendMenuEntry[menu,
Menus.CreateEntry[name: label, proc: SimpleBootProc,
clientData: NEW[SimpleButtonRec ← [volID, switches]], fork: TRUE, guarded: TRUE],
line];
ENDLOOP;
Menus.AppendMenuEntry[menu,
Menus.CreateEntry[name: "Alto", proc: AltoBootProc, clientData: NIL, fork: TRUE, guarded: TRUE],
line];
Menus.AppendMenuEntry[menu,
Menus.CreateEntry[name: "BB", proc: BootButtonProc, clientData: NIL, fork: TRUE, guarded: TRUE],
line];
Menus.AppendMenuEntry[menu,
Menus.CreateEntry[name: "Basic", proc: BasicCedarDorado, clientData: NIL, fork: TRUE, guarded: TRUE],
line];
};
GetSwitches: PROC [name: Rope.ROPE] RETURNS [switches: Rope.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.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;
};
ResetDefaultsInternal: INTERNAL PROC [handle: BootToolHandle] = {
ResetSection1[handle];
ResetSection2[handle];
};
---- ---- ---- ---- ---- ---- ---- ----
Section1Data: TYPE = RECORD [
selected: BooteeClass ← volume,
bootees: ARRAY BooteeClass OF Bootee ← ALL[NIL],
microcodeListDone: BOOLFALSE,
microcodeListBuilt: CONDITION ← [timeout: 0]
];
S1Data: TYPE = REF Section1Data;
BooteeClass: TYPE = {volume, file, ucode, physicalVolume};
Bootee: TYPE = REF BooteeObject;
BooteeObject: TYPE = RECORD [
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.ROPE,
value: REF ANY];
VolumeVal: TYPE = REF Volume.ID;
FileVal: TYPE = REF File.Capability;
MicrocodeVal: TYPE = REF Booting.BootFileNumber;
PhysicalVolumeVal: TYPE = REF PhysicalVolume.ID;
BuildSection1: INTERNAL PROC [handle: BootToolHandle] = {
s1Data: S1Data = NEW[Section1Data ← []];
MakeBootee: PROC [
buttonName: Rope.ROPE, buttonProc: Buttons.ButtonProc,
choicesProc: PROC RETURNS [ChoiceList] ← NIL] RETURNS [bootee: Bootee] = {
bootee ← NEW[BooteeObject ← []];
bootee.button ← Buttons.Create[
info: [name: buttonName,
wx: entryHSpace,
wy: handle.height,
wh: entryHeight,   -- specify rather than defaulting so line is uniform
parent: handle.outer, 
border: TRUE
],
proc: buttonProc,
fork: TRUE,
clientData: handle
];
IF choicesProc ~= NIL THEN bootee.choices ← choicesProc[];
bootee.displayedChoice ← ViewerTools.MakeNewTextViewer[info: [
parent: handle.outer,
wx: bootee.button.wx + bootee.button.ww + entryHSpace,
wy: handle.height,
ww: 50, -- ugly, but necessary preparation for Containers.ChildXBound
wh: entryHeight,
data: NIL,
scrollable: FALSE,
border: FALSE
]];
Containers.ChildXBound[container: handle.outer, child: bootee.displayedChoice];
handle.height ← handle.height + entryHeight + entryVSpace;
};
AddSeparatingRule[handle];
MakeCenteredLabel[handle, "Booting Sources"];
s1Data.bootees[volume] ← MakeBootee["Volume", SelectVolumeBootee, BuildVolumeList];
ViewerTools.InhibitUserEdits[s1Data.bootees[volume].displayedChoice];
s1Data.bootees[file] ← MakeBootee["File", SelectFileBootee];
TRUSTED{ Process.Detach[FORK BuildBootFileListEntry[handle]] };
s1Data.bootees[ucode] ← MakeBootee["Microcode", SelectMicrocodeBootee];
s1Data.bootees[physicalVolume] ← MakeBootee["Physical Volume", SelectPhysicalVolumeBootee, BuildPhysicalVolumeList];
ViewerTools.InhibitUserEdits[s1Data.bootees[physicalVolume].displayedChoice];
handle.s1Data ← s1Data;
BuildMicrocodeList[handle];
};
ResetSection1: INTERNAL PROC [handle: BootToolHandle] = {
s1Data: S1Data = handle.s1Data;
SelectBootee[s1Data, volume];
s1Data.bootees[volume].currentChoice ← s1Data.bootees[volume].choices;
SetChoice[s1Data.bootees[volume]];
s1Data.bootees[file].choices ← s1Data.bootees[file].currentChoice ← NIL;
SetChoice[s1Data.bootees[file]];
s1Data.bootees[ucode].currentChoice ← s1Data.bootees[ucode].choices;
SetChoice[s1Data.bootees[ucode]];
s1Data.bootees[physicalVolume].currentChoice ← s1Data.bootees[physicalVolume].choices;
SetChoice[s1Data.bootees[physicalVolume]];
};
SetChoice: PROC [bootee: Bootee, deselect: BOOLFALSE] = {
name: Rope.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: INTERNAL PROC RETURNS [volumes: ChoiceList ← NIL] = {
volume: Volume.ID ← Volume.nullID;
UNTIL (volume ← Volume.GetNext[volume, ALL[TRUE]]) = Volume.nullID DO
IF volume ~= Volume.systemID THEN
volumes ← CONS[VolumeToChoice[volume], volumes];
ENDLOOP;
volumes ← List.Sort[volumes, CompareChoiceNames];
RETURN [CONS[SystemVolumeChoice[], volumes]]
};
SystemVolumeChoice: PROC RETURNS [ChoiceRef] = INLINE
{RETURN [VolumeToChoice[Volume.systemID]]};
VolumeToChoice: PROC [volume: Volume.ID] RETURNS [ChoiceRef] = TRUSTED {
volName: STRING = [Volume.maxNameLength];
volID: VolumeVal = NEW[Volume.ID ← volume];
Volume.GetLabelString[volume, volName];
RETURN [NEW[ChoiceDesc ← [Rope.FromRefText[ConvertUnsafe.ToRefText[volName]], volID]]]
};
CompareChoiceNames: PROC [ref1, ref2: REF ANY] RETURNS [List.Comparison] =
{RETURN[List.Compare[NARROW[ref1, ChoiceRef].name, NARROW[ref2, ChoiceRef].name]]};
BuildBootFileListEntry: ENTRY PROC [handle: BootToolHandle] = {
The catch phrase below is intended to short-circuit catastrophic errors so that at least some minimal form of booting is possible, even if not all booting sources work.
handle.s1Data.bootees[file].choices ← BuildBootFileList[handle ! ANY => CONTINUE]};
BuildBootFileList: INTERNAL PROC [handle: BootToolHandle]
RETURNS [bootFiles: ChoiceList ← NIL] = {
NormalizeFileName: PROC [file: Rope.ROPE] RETURNS [front, back: Rope.ROPENIL] = {
<volume>back, <volume>SysDir>back, volume>back, volume>SysDir>back all yield front = <volume>SysDir>
SysDir>back and back yield front = NIL
sysDir: Rope.ROPE = "SysDir>";
i: INT ← file.Find[sysDir, 0, FALSE];
IF i >= 0 THEN
file ← Rope.Concat[file.Substr[0, i], file.Substr[i+sysDir.Length[]]];
IF (i ← file.Find[">"]) = -1 THEN {back ← file; RETURN};
IF file.Fetch[0] ~= '< THEN front ← Rope.Concat["<", file.Substr[0, i+1]]
ELSE front ← file.Substr[0, i+1];
front ← front.Concat[sysDir];
back ← file.Substr[i+1];
};
GetNextBootFile: PROC [file: Rope.ROPE]
RETURNS [nextFile: Rope.ROPE, nextCap: File.Capability] = TRUSTED {
path, thisFile: Rope.ROPE;
pathS: LONG STRING;
EndsInDotBoot: PROC [file: LONG STRING] RETURNS [BOOL] = TRUSTED {
bootExt: STRING = ".boot"L;
bootLen: NAT = bootExt.length;
len: NAT = IF file = NIL THEN 0 ELSE file.length;
IF len < bootLen THEN RETURN[FALSE];
FOR i: NAT IN [0..bootLen) DO
IF Rope.Lower[file[len-bootLen+i]] ~= bootExt[i] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE]
};
It would be nice if we didn't have to pick some a-priori upper bound for this
string, but the Directory interface gives us no choice.
thisFileS: LONG STRING = [100];
[path, thisFile] ← NormalizeFileName[file];
pathS ← LOOPHOLE[Rope.ToRefText[path]];
ConvertUnsafe.AppendRope[thisFileS, thisFile];
DO
nextFile ← NIL;
nextCap ← Directory.GetNext[
pathName: pathS, currentName: thisFileS, nextName: thisFileS
! Directory.VolumeError--[volume]-- =>
IF type = volumeNotOpen THEN {
VolumeExtras.OpenVolume[volume: volume, readOnly: TRUE
! Volume.Unknown, Volume.NeedsScavenging => EXIT];
RETRY}
ELSE EXIT
];
SELECT TRUE FROM
thisFileS.length = 0 => EXIT;
EndsInDotBoot[thisFileS] => {nextFile ← ConvertUnsafe.ToRope[thisFileS]; EXIT};
ENDCASE;
ENDLOOP;
};
DoBootFilesWithPrefix: PROC [file: Rope.ROPE] = {
DO
cap: File.Capability;
Better error reporting someday, maybe...
[file, cap] ← GetNextBootFile[file
! Directory.Error, Directory.VolumeError => {
PostMessage[handle, "Incorrect file path"]; EXIT}];
IF file = NIL THEN EXIT;
bootFiles ← CONS[NEW[ChoiceDesc ← [file, NEW[File.Capability ← cap]]], bootFiles];
ENDLOOP;
};
front, back: Rope.ROPE;
[front, back] ←
NormalizeFileName[ViewerTools.GetContents[handle.s1Data.bootees[file].displayedChoice]];
IF front.Length[] > 0 AND back.Length[] = 0 THEN DoBootFilesWithPrefix[front]
ELSE
FOR c: ChoiceList ← handle.s1Data.bootees[volume].choices, c.rest UNTIL c = NIL DO
DoBootFilesWithPrefix[Rope.Cat["<", NARROW[c.first, ChoiceRef].name, ">SysDir>"]];
ENDLOOP;
RETURN [List.Reverse[bootFiles]]
};
BuildMicrocodeList: INTERNAL PROC [handle: BootToolHandle] = {
OPEN Booting;
handle.s1Data.bootees[ucode].choices ←
LIST[NEW[ChoiceDesc ← ["(default)", NEW[BootFileNumber ← nullMicrocode]]]];
TRUSTED{ Process.Detach[FORK BuildFullMicrocodeList[handle]] };
};
BuildFullMicrocodeList: PROC [handle: BootToolHandle] = {
OPEN Booting;
InstallMicrocodeList: ENTRY PROC [handle: BootToolHandle, microcode: ChoiceList] = {
handle.s1Data.bootees[ucode].choices ← microcode;
handle.s1Data.microcodeListDone ← TRUE;
NOTIFY handle.s1Data.microcodeListBuilt;
};
microcode: ChoiceList ←
LIST[NEW[ChoiceDesc ← ["(default)", NEW[BootFileNumber ← nullMicrocode]]]];
BEGIN
ENABLE --AMTypes.Error-- ANY => CONTINUE;
Massage: PROC [name: Rope.ROPE] RETURNS [newName: Rope.ROPE] = {
OPEN Rope;
XOR: PROC [a, b: BOOL] RETURNS [BOOL] = INLINE
{RETURN [(a AND b) OR (~a AND ~b)]};
start: INT ← 1;
doingLower: BOOLFALSE;
newName ← FromChar[Upper[Fetch[name, 0]]];
DO
next: INT ← start;
UNTIL next = Length[name] OR XOR[doingLower, ~(Fetch[name, next] IN ['a..'z])] DO
next ← next + 1;
ENDLOOP;
newName ← Concat[newName, Substr[name, start, next - start]];
IF next = Length[name] THEN EXIT ELSE start ← next;
IF doingLower THEN newName ← Concat[newName, FromChar[Ascii.SP]];
doingLower ← ~doingLower;
ENDLOOP;
};
uT: AMTypes.Type = LOOPHOLE[CODE[MicrocodeType]];
FOR i: AMTypes.Index IN [1..AMTypes.NValues[uT]] DO
name: Rope.ROPE = Massage[AMTypes.IndexToName[uT, i]];
ucodeType: MicrocodeType;
TRUSTED{ ucodeType ← LOOPHOLE[AMBridge.TVToCardinal[AMTypes.Value[uT, i]]] };
microcode ← CONS[
NEW[ChoiceDesc ← [name, NEW[BootFileNumber ← GetBootFileNumber[ucodeType]]]],
microcode];
ENDLOOP;
END;
InstallMicrocodeList[handle, List.Reverse[microcode]];
};
BuildPhysicalVolumeList: INTERNAL PROC RETURNS [physicalVolumes: ChoiceList ← NIL] = TRUSTED {
physicalVolume: PhysicalVolume.ID ← PhysicalVolume.nullID;
UNTIL (physicalVolume ← PhysicalVolume.GetNext[physicalVolume]) = PhysicalVolume.nullID DO
IF PhysicalVolume.GetNextLogicalVolume[physicalVolume, Volume.nullID] ~= Volume.nullID THEN
physicalVolumes ← CONS[PhysicalVolumeToChoice[physicalVolume], physicalVolumes];
ENDLOOP;
physicalVolumes ← List.Sort[physicalVolumes, CompareChoiceNames];
};
PhysicalVolumeToChoice: PROC [physicalVolume: PhysicalVolume.ID] RETURNS [ChoiceRef] = TRUSTED {
volName: STRING = [PhysicalVolume.maxNameLength];
volID: PhysicalVolumeVal = NEW[PhysicalVolume.ID ← physicalVolume];
[] ← PhysicalVolume.GetAttributes[physicalVolume, volName];
RETURN [NEW[ChoiceDesc ← [Rope.FromRefText[ConvertUnsafe.ToRefText[volName]], volID]]]
};
---- ---- ---- ---- ---- ---- ---- ----
Section2Data: TYPE = RECORD [
switchesButton: Buttons.Button ← NIL,
switches: ViewerClasses.Viewer ← NIL
];
S2Data: TYPE = REF Section2Data;
BuildSection2: INTERNAL PROC [handle: BootToolHandle] = {
s2Data: S2Data = NEW[Section2Data ← []];
AddSeparatingRule[handle];
MakeCenteredLabel[handle, "Booting Options"];
s2Data.switchesButton ← Buttons.Create[
info: [name: "Switches:",
wx: entryHSpace,
wy: handle.height,
wh: entryHeight,   -- specify rather than defaulting so line is uniform
parent: handle.outer, 
border: FALSE
],
proc: SelectBootSwitches,
fork: TRUE,
clientData: handle
];
s2Data.switches ← ViewerTools.MakeNewTextViewer[info: [
parent: handle.outer,
wx: s2Data.switchesButton.wx + s2Data.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: s2Data.switches];
handle.height ← handle.height + entryHeight + entryVSpace;
handle.s2Data ← s2Data;
};
ResetSection2: INTERNAL PROC [handle: BootToolHandle] = {
s2Data: S2Data = handle.s2Data;
ViewerTools.SetContents[s2Data.switches, UserProfile.Line["Boot.Switches"]];
};
---- ---- ---- ---- ---- ---- ---- ----
Button Action Procedures
---- ---- ---- ---- ---- ---- ---- ----
PostMessage: PROC [handle: BootToolHandle, msg: Rope.ROPE] =
{MessageWindow.Clear[]; MessageWindow.Append[msg]};
SelectVolumeBootee: ENTRY Buttons.ButtonProc = {
handle: BootToolHandle = NARROW[clientData];
s1Data: S1Data = handle.s1Data;
previouslySelected: BOOL = (s1Data.selected = volume);
previousContents: Rope.ROPE =
ViewerTools.GetContents[s1Data.bootees[volume].displayedChoice];
IF ~previouslySelected THEN SelectBootee[s1Data, volume];
Kill pending delete selection elsewhere in tool
ViewerTools.SetSelection[s1Data.bootees[volume].displayedChoice,
NEW[ViewerTools.SelPosRec ← [start: Rope.Length[previousContents], length: 0]]];
IF previouslySelected THEN AdvanceName[s1Data.bootees[volume]];
};
SelectFileBootee: ENTRY Buttons.ButtonProc = {
handle: BootToolHandle = NARROW[clientData];
s1Data: S1Data = handle.s1Data;
previouslySelected: BOOL = (s1Data.selected = file);
IF ~previouslySelected THEN SelectBootee[s1Data, file];
ViewerTools.SetSelection[viewer: s1Data.bootees[file].displayedChoice, selection: NIL];
IF previouslySelected THEN {
IF s1Data.bootees[file].choices = NIL OR (mouseButton ~= red) THEN
s1Data.bootees[file].choices ← BuildBootFileList[handle];
AdvanceName[s1Data.bootees[file]];
};
};
SelectMicrocodeBootee: ENTRY Buttons.ButtonProc = {
handle: BootToolHandle = NARROW[clientData];
s1Data: S1Data = handle.s1Data;
previouslySelected: BOOL = (s1Data.selected = ucode);
IF ~previouslySelected THEN SelectBootee[s1Data, ucode];
ViewerTools.SetSelection[viewer: s1Data.bootees[ucode].displayedChoice, selection: NIL];
IF previouslySelected THEN {
UNTIL handle.s1Data.microcodeListDone DO
WAIT handle.s1Data.microcodeListBuilt;
ENDLOOP;
AdvanceName[s1Data.bootees[ucode]];
};
};
SelectPhysicalVolumeBootee: ENTRY Buttons.ButtonProc = {
handle: BootToolHandle = NARROW[clientData];
s1Data: S1Data = handle.s1Data;
previouslySelected: BOOL = (s1Data.selected = physicalVolume);
previousContents: Rope.ROPE =
ViewerTools.GetContents[s1Data.bootees[physicalVolume].displayedChoice];
IF ~previouslySelected THEN SelectBootee[s1Data, physicalVolume];
Kill pending delete selection elsewhere in tool
ViewerTools.SetSelection[s1Data.bootees[physicalVolume].displayedChoice,
NEW[ViewerTools.SelPosRec ← [start: Rope.Length[previousContents], length: 0]]];
IF previouslySelected THEN AdvanceName[s1Data.bootees[physicalVolume]];
};
SelectBootee: INTERNAL PROC [s1Data: S1Data, booteeClass: BooteeClass] = {
SetButton[s1Data.bootees[s1Data.selected].button, FALSE];
SetButton[s1Data.bootees[booteeClass].button, TRUE];
s1Data.selected ← booteeClass;
};
AdvanceName: INTERNAL 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: ENTRY Buttons.ButtonProc = {
handle: BootToolHandle = NARROW[clientData];
ViewerTools.SetSelection[viewer: handle.s2Data.switches, selection: NIL];
};
SetButton: INTERNAL PROC [button: Buttons.Button, on: BOOL] = {
IF on THEN Buttons.SetDisplayStyle[button: button, style: $WhiteOnBlack]
ELSE Buttons.SetDisplayStyle[button: button, style: $BlackOnWhite];
};
---- ---- ---- ---- ---- ---- ---- ----
Menu Action Procedures
---- ---- ---- ---- ---- ---- ---- ----
ActualBoot: PROC[boot: Booting.Bootee, switches: Booting.Switches ← Booting.defaultSwitches] = TRUSTED {
IF Runtime.IsBound[AMEventsExtra.Boot] THEN
AMEventsExtra.Boot[boot, switches]
ELSE Booting.Boot[boot, switches]
};
SimpleBootProc: Menus.MenuProc = {
data: SimpleButtonRef = NARROW[clientData];
bootSwitches: Booting.Switches;
error: Rope.ROPE;
IF SomethingWorthSaving[] THEN RETURN;
[bootSwitches, error] ← RopeToSwitches[data.switches];
IF error ~= NIL THEN MessageWindow.Append["Illegal boot switches.", TRUE]
ELSE ActualBoot[[logical[data.volume]], bootSwitches];
};
AltoBootProc: Menus.MenuProc = {
IF ~SomethingWorthSaving[] THEN
ActualBoot[[microcode[Booting.GetBootFileNumber[altoMesa]]]];
};
BootButtonProc: Menus.MenuProc = {
IF ~SomethingWorthSaving[] THEN ActualBoot[[bootButton[]]];
};
BasicCedarDorado: Menus.MenuProc = {
cap: File.Capability = DirLookup["BasicCedarDorado.boot"];
IF cap ~= File.nullCapability THEN {
switches: Booting.Switches;
error: Rope.ROPE;
bias: NAT = IF File.GetAttributes[cap].type = DCSFileTypes.tLeaderPage THEN 1 ELSE 0;
[switches, error] ← RopeToSwitches[UserProfile.Line["FileSwitches"]];
IF error = NIL AND ~SomethingWorthSaving[] THEN {
Booting.MakeBootable[[cap, bias]];
ActualBoot[[file[[cap, bias]]], switches];
}
ELSE MessageWindow.Append[error, TRUE];
}
ELSE MessageWindow.Append["BasicCedarDorado.boot not found.", TRUE];
};
DoBoot: Menus.MenuProc = {
CantBoot: ERROR = CODE;
DoBootEntry: ENTRY PROC = --INLINE-- TRUSTED {
This procedure can't be INLINE until Trinity (catch phrase in caller).
ENABLE UNWIND => NULL;
handle: BootToolHandle = NARROW[clientData];
bootee: Bootee = handle.s1Data.bootees[handle.s1Data.selected];
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;
switches: Booting.Switches;
ProcessOptions: PROC = CHECKED {
error: Rope.ROPE;
[switches, error] ← RopeToSwitches[ViewerTools.GetContents[handle.s2Data.switches]];
IF error ~= NIL THEN {PostMessage[handle, error]; ERROR CantBoot};
};
SELECT handle.s1Data.selected FROM
volume => {
volume: Volume.ID;
IF ~isCurrentChoice THEN ERROR;
volume ← NARROW[choiceRef.value, VolumeVal]^;
ProcessOptions[];
ActualBoot[[logical[volume]], switches];
};
file => {
cap: File.Capability;
firstPage: File.PageNumber;
IF isCurrentChoice THEN cap ← NARROW[choiceRef.value, FileVal]^
ELSE {
fileName: Rope.ROPE = ViewerTools.GetContents[bootee.displayedChoice];
IF (cap ← DirLookup[fileName]) = File.nullCapability THEN {
PostMessage[handle,
IO.PutFToRope["Unrecognizable file name '%g'", IO.rope[fileName]]];
ERROR CantBoot;
};
};
firstPage ← IF File.GetAttributes[cap].type = DCSFileTypes.tLeaderPage THEN 1 ELSE 0;
ProcessOptions[];
Booting.MakeBootable[[cap, firstPage]];
ActualBoot[[file[[cap, firstPage]]], switches];
};
ucode => {
bfn: Booting.BootFileNumber;
IF isCurrentChoice THEN
bfn ← NARROW[choiceRef.value, MicrocodeVal]^
ELSE {
rs: IO.STREAM = IO.RIS[ViewerTools.GetContents[bootee.displayedChoice]];
bfnC: LONG CARDINAL = IO.GetCard[rs ! IO.Error => GO TO badBfn];
IF ~rs.EndOf[] OR bfnC > LAST[CARDINAL]--Booting.BootFileNumber-- THEN GO TO badBfn;
bfn ← [bfnC]; -- can't cause a fault
EXITS
badBfn => {
PostMessage[handle, "Bad microcode specification"];
ERROR CantBoot;
};
};
ProcessOptions[];
ActualBoot[[microcode[bfn]]];
};
physicalVolume => {
physicalVolume: PhysicalVolume.ID;
IF ~isCurrentChoice THEN ERROR;
physicalVolume ← NARROW[choiceRef.value, PhysicalVolumeVal]^;
ProcessOptions[];
ActualBoot[
[physical[PhysicalVolume.GetNextLogicalVolume[physicalVolume, Volume.nullID]]],
switches];
};
ENDCASE;
};
IF SomethingWorthSaving[] THEN RETURN;
DoBootEntry[ ! CantBoot => CONTINUE];
};
ResetDefaults: ENTRY Menus.MenuProc = {
ResetDefaultsInternal[NARROW[clientData, BootToolHandle]];
};
DirLookup: PROC [fileName: Rope.ROPE] RETURNS [cap: File.Capability ← File.nullCapability] = TRUSTED {
fileS: LONG STRING ← Heap.systemZone.NEW[StringBody[Rope.Length[fileName]]];
ConvertUnsafe.AppendRope[from: fileName, to: fileS];
cap ← Directory.Lookup[fileName: fileS, permissions: Directory.ignore !
Directory.Error => CONTINUE;
Directory.VolumeError =>
IF type = volumeNotOpen THEN {
VolumeExtras.OpenVolume[volume: volume, readOnly: TRUE]; RETRY}
ELSE CONTINUE];
Heap.systemZone.FREE[@fileS];
};
RopeToSwitches: PROC [textSwitches: Rope.ROPE]
RETURNS [switches: Booting.Switches ← Booting.defaultSwitches, error: Rope.ROPENIL] = {
errorChar: CHAR;
SetBootSwitch: PROC [c: CHAR] RETURNS [stop: BOOL] = {
SELECT Rope.Lower[c] FROM
'a => switches.a ← down;
'b => switches.b ← down;
'c => switches.c ← down;
'd => switches.d ← down;
'e => switches.e ← down;
'f => switches.f ← down;
'g => switches.g ← down;
'h => switches.h ← down;
'i => switches.i ← down;
'j => switches.j ← down;
'k => switches.k ← down;
'l => switches.l ← down;
'm => switches.m ← down;
'n => switches.n ← down;
'o => switches.o ← down;
'p => switches.p ← down;
'q => switches.q ← down;
'r => switches.r ← down;
's => switches.s ← down;
't => switches.t ← down;
'u => switches.u ← down;
'v => switches.v ← down;
'w => switches.w ← down;
'x => switches.x ← down;
'y => switches.y ← down;
'z => switches.z ← down;
'0 => switches.zero ← down;
'1 => switches.one ← down;
'2 => switches.two ← down;
'3 => switches.three ← down;
'4 => switches.four ← down;
'5 => switches.five ← down;
'6 => switches.six ← down;
'7 => switches.seven ← down;
'8 => switches.eight ← down;
'9 => switches.nine ← down;
ENDCASE => {errorChar ← c; RETURN [TRUE]};
RETURN [FALSE]
};
IF Rope.Map[base: textSwitches, action: SetBootSwitch] THEN
error ← IO.PutFToRope["Unrecognizable boot switch '%g'", IO.char[errorChar]];
};
SomethingWorthSaving: PROC RETURNS [BOOLEAN] =
BEGIN
dirty: BOOLEANFALSE;
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];
END;
---- ---- ---- ---- ---- ---- ---- ----
The Main Body
---- ---- ---- ---- ---- ---- ---- ----
[] ← Buttons.Create[info: [name: "Boot"], proc: MakeBootTool, fork: TRUE];
END.
Edited on December 20, 1982 5:39 pm, by Levin
Add forking of Boot file list construction on startup
changes to: BuildSection1 - add FORK, BuildBootFileListEntry - new, BuildBootFileList - made INTERNAL, SimpleButtonRef, SimpleButtonRec, SimpleBootProc - make simple boot switches work, [], MakeSimpleButtonsMenu, AltoBootProc, BootButtonProc, BasicCedarDorado
Edited on January 4, 1983 5:45 pm, by Levin
Add forking of Microcode list construction and bullet-proof it against inability to locate MicrocodeBooting symbols
changes to: BuildSection1, BuildMicrocodeList, BuildFullMicrocodeList, InstallMicrocodeList (local of BuildFullMicrocodeList), Massage (local of BuildFullMicrocodeList), Section1Data, SelectMicrocodeBootee, []
Edited on February 6, 1983 11:48 am, by Birrell
Converted to CEDAR module (because Viewers is now SAFE); added TRUSTED as appropriate.
Edited on February 6, 1983 1:51 pm, by Birrell
Converted uses of MicrocodeBooting and TemporaryBooting to be Booting.
Edited on February 8, 1983 11:37 am, by Levin
Inserted catch phrases for ANY in the following procedures to try to recover gracefully when booting is required to escape out-of-VM. This doesn't guarantee that the tool will work, but it should work more often, and the buttons-only variants should work almost always.
changes to: BuildBootFileListEntry, BuildFullMicrocodeList
Edited on February 23, 1983 9:30 am, by Maxwell
Made left and middle mouse clicks put up system buttons instead of creating a boot tool. SomethingWorthSaving[] now moves dirty viewers onto the screen if they are off the screen.