--ILTBootImpl.mesa
--Created by
--   JFung.PASA	  	19-Dec-83 11:35:03

--last edited by
--   JFung.PASA	   	19-Dec-83 13:55:39



DIRECTORY
     CmFile,
     Display,
     Environment,
     Event,
     Exec,
     File,
     FileTypes,
     FileTransfer,
     FormSW,
     Heap,
     Inline,
     LispToolOps,
     OthelloOps,
     Process,
     Put,
     Runtime,
     Space,
     String,
     TemporaryBooting,
     Time,
     Token,
     Tool,
     ToolDriver,
     ToolWindow,
     UserInput,
     Version,
     Volume,
     Window;

ILTBootImpl: PROGRAM
     IMPORTS
          Display, File, FormSW, Heap, Inline, LispToolOps, OthelloOps, 
	  Process, Put, Runtime, Space, String, 
	  TemporaryBooting, Time, Tool, ToolDriver, UserInput, Version, Volume, Window

     EXPORTS LispToolOps =


     BEGIN OPEN ILT: LispToolOps, OthelloOps;

     -- TYPEs
     Indicator: TYPE = {off, left, right};
     VolHints: TYPE = RECORD [names: SEQUENCE length: CARDINAL OF LONG STRING];
     SizeHints: TYPE = RECORD [names: SEQUENCE length: CARDINAL OF CARDINAL];


     DataHandle: TYPE = LONG POINTER TO Data;
     Data: TYPE = MACHINE DEPENDENT RECORD [
          -- Message subwindow stuff
          msgSW(0): Window.Handle ← NIL,
          -- File subwindow stuff
          fileSW(2): Window.Handle ← NIL,

          -- Form subwindow stuff
          -- Note: enumerateds and booleans must be word boundary
          -- aligned as addresses for them must be generated
          --formSW: Window.Handle ← NIL,
          paramSW(4): Window.Handle ← NIL,
          commandSW(6): Window.Handle ← NIL,

          busy(8): BOOLEAN ← FALSE,  -- command is running
          destVolName(9): LONG STRING ← NIL,
          srcVolName(11): LONG STRING ← NIL,
          connection(13): FileTransfer.Connection ← NIL,
          indicator(15): Indicator ← left];


     active: BOOLEAN ← FALSE;
     copyWH: Window.Handle ← NIL;
     debug: BOOLEAN ← FALSE;
     toolData: DataHandle ← NIL;

     formDisplay: ToolWindow.DisplayProcType ← NIL;
     indicatorBox: Window.Box = [[10, 10], [16, 16]];
     volume: Volume.ID ← Volume.nullID;



     BootStuff: PUBLIC PROCEDURE =
          BEGIN
          IF toolData = NIL THEN toolData ← Heap.systemZone.NEW[Data ← []];
          IF debug THEN {
               Put.Line[ILT.toolData.fileSW, "MakeTool...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };

          IF ILT.Confirm[] THEN copyWH ← MakeTool[];
          END;  --BootStuff



     ClearCommandSubwindow: PROCEDURE =
          BEGIN
          item: FormSW.ItemHandle;

          FOR i: CARDINAL ← 0, i + 1 UNTIL
               (item ← FormSW.FindItem[toolData.commandSW, i]) = NIL DO
               item.flags.invisible ← TRUE ENDLOOP;
          FormSW.Display[toolData.commandSW];
          toolData.indicator ← left;
          formDisplay ← Window.GetDisplayProc[toolData.commandSW];
          [] ← Window.SetDisplayProc[toolData.commandSW, DisplayEmpty];
          END;  --ClearCommandSubwindow



     ClearFileSubwindow: PROCEDURE =
          BEGIN
          item: FormSW.ItemHandle;

          FOR i: CARDINAL ← 0, i + 1 UNTIL
               (item ← FormSW.FindItem[toolData.fileSW, i]) = NIL DO
               item.flags.invisible ← TRUE ENDLOOP;
          FormSW.Display[toolData.fileSW];
          --formDisplay ← Window.GetDisplayProc[toolData.fileSW];
          END;  --ClearFileSubwindow


     ClearMsgSubwindow: PROCEDURE =
          BEGIN
          item: FormSW.ItemHandle;

          FOR i: CARDINAL ← 0, i + 1 UNTIL
               (item ← FormSW.FindItem[toolData.msgSW, i]) = NIL DO
               item.flags.invisible ← TRUE ENDLOOP;
          FormSW.Display[toolData.msgSW];
          --formDisplay ← Window.GetDisplayProc[toolData.msgSW];
          END;  --ClearMsgSubwindow



     ClearSubWindows: PROCEDURE =
          BEGIN
          --ClearFileSubwindow;
          ClearMsgSubwindow;
          END;  --ClearSubWindows



     CloseVolume: PUBLIC PROC [vID: Volume.ID] RETURNS [vOpen: BOOLEAN] =
          BEGIN IF vOpen THEN {Volume.Close[vID]; vOpen ← FALSE; } END;


     <<
     ClientTransition: ToolWindow.TransitionProcType =
          -- This procedure is called whenever the system determines that this
          -- Tool's state is undergoing a user invoked transition.
          -- In this Example we demonstrate a technique that minimizes the memory
          -- requirements for a Tool that is inactive.
          BEGIN
          SELECT TRUE FROM
               old = inactive =>
                    BEGIN
                    IF toolData = NIL THEN
                         toolData ← Heap.systemZone.NEW[Data ← []];
                    active ← TRUE;
                    END;
               new = inactive =>
                    BEGIN
                    IF toolData # NIL THEN
                         BEGIN
                         FormSW.Destroy[toolData.paramSW];
                         FormSW.Destroy[toolData.commandSW];
                         Heap.systemZone.FREE[@toolData];
                         END;
                    --ToolDriver.RemoveSWs[tool: "LispTool"L];
                    active ← FALSE;
                    END;
               ENDCASE
          END;  --ClientTransition
	  >>


     DisplayCommandSubwindow: PROCEDURE =
          BEGIN
          item: FormSW.ItemHandle;

          toolData.indicator ← off;
          [] ← Window.SetDisplayProc[toolData.commandSW, formDisplay];
          FormSW.Display[toolData.commandSW];
          FOR i: CARDINAL ← 0, i + 1 UNTIL
               (item ← FormSW.FindItem[toolData.commandSW, i]) = NIL DO
               item.flags.invisible ← FALSE ENDLOOP;

          FormSW.Display[toolData.commandSW];
          END;  --DisplayCommandSubwindow


     DisplayEmpty: ToolWindow.DisplayProcType =
          BEGIN
          ENABLE UNWIND => NULL;
          IF ~toolData.busy THEN RETURN;
          DisplayIndicator[window];
          END;



     DisplayIndicator: ToolWindow.DisplayProcType = {
          pattern: ARRAY [0..1] OF ARRAY [0..8) OF WORD;
          SELECT toolData.indicator FROM
               left => {
                    pattern ← [
                         [
                         111000B, 052000B, 034000B, 177000B, 034000B, 052000B,
                         111000B, 000000B], [
                         000222B, 000124B, 000070B, 000376B, 000070B, 000124B,
                         000222B, 000000B]];
                    Display.Bitmap[
                         window, indicatorBox, [@pattern, , 0], 16,
                         Display.replaceFlags]};
               right => {
                    pattern ← [
                         [
                         000222B, 000124B, 000070B, 000376B, 000070B, 000124B,
                         000222B, 000000B], [
                         111000B, 052000B, 034000B, 177000B, 034000B, 052000B,
                         111000B, 000000B]];
                    Display.Bitmap[
                         window, indicatorBox, [@pattern, , 0], 16,
                         Display.replaceFlags]};
               ENDCASE};  --DisplayIndicator



     DoInstall: UserInput.PeriodicProcType =
          BEGIN
          ClearMsgSubwindow;
          IF debug THEN {
               Put.Line[toolData.fileSW, "Start Install ....."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
	  ClearCommandSubwindow;
          DisplayIndicator[toolData.commandSW];
          toolData.busy ← TRUE;

           IF Retrieve[] THEN
               BEGIN
               Put.Line[toolData.fileSW, "\n Boot destination volume?"L];
               IF ILT.Confirm[] THEN TemporaryBooting.BootButton[];
               END;
	  DisplayCommandSubwindow;
	  toolData.busy ← FALSE;
          END;  --DoInstall




     FormSWBoot: FormSW.ProcType =
          BEGIN
          IF debug THEN {
               Put.Line[toolData.fileSW, "FormSWCopyProc...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          [] ← UserInput.CreatePeriodicNotify[
               proc: DoInstall, window: sw, rate: 0];
          END;  --FormSWBoot


     FormSWQuit: FormSW.ProcType =
          BEGIN

          IF toolData # NIL THEN
               BEGIN Tool.Destroy[copyWH]; Heap.systemZone.FREE[@toolData]; END;
          END;  --FormSWQuit





     FormSWVolHintsProc: FormSW.MenuProcType =
          BEGIN
          RETURN[
               hints: DESCRIPTOR[
               @ILT.toolData.volHints[0], ILT.toolData.volHints.length],
                    freeHintsProc: HintsNoLongerBusy, replace: TRUE];

          END;


     HintsNoLongerBusy: FormSW.FreeHintsProcType = BEGIN END;


     InvertIndicator: PROC [] =
          BEGIN
          ENABLE UNWIND => NULL;
          pattern: ARRAY [0..1] OF ARRAY [0..8) OF WORD;

          IF ~toolData.busy THEN RETURN;
          SELECT toolData.indicator FROM
               left => {
                    pattern ← [
                         [
                         000222B, 000124B, 000070B, 000376B, 000070B, 000124B,
                         000222B, 000000B], [
                         111000B, 052000B, 034000B, 177000B, 034000B, 052000B,
                         111000B, 000000B]];
                    Display.Bitmap[
                         toolData.commandSW, indicatorBox, [@pattern, , 0], 16,
                         Display.replaceFlags];
                    toolData.indicator ← right};
               right => {
                    pattern ← [
                         [
                         111000B, 052000B, 034000B, 177000B, 034000B, 052000B,
                         111000B, 000000B], [
                         000222B, 000124B, 000070B, 000376B, 000070B, 000124B,
                         000222B, 000000B]];
                    Display.Bitmap[
                         toolData.commandSW, indicatorBox, [@pattern, , 0], 16,
                         Display.replaceFlags];
                    toolData.indicator ← left};
               off => toolData.indicator ← left;
               ENDCASE;
          END;


     MakeCommands: FormSW.ClientItemsProcType =
          BEGIN OPEN FormSW;

          tabs: ARRAY [0..2) OF CARDINAL ← [0, 60];
          nItems: CARDINAL = 2;
          items ← AllocateItemDescriptor[nItems];

          items[0] ← CommandItem[tag: "Start"L, place: newLine, proc: FormSWBoot];

          items[1] ← CommandItem[tag: "Quit"L, place: newLine, proc: FormSWQuit];

          SetTagPlaces[items, DESCRIPTOR[tabs], FALSE];
          RETURN[items, TRUE];
          END;  --MakeCommands




     MakeParams: FormSW.ClientItemsProcType =
          BEGIN OPEN FormSW;
          i, nVols: CARDINAL;
          tabs: ARRAY [0..4) OF CARDINAL ← [0, 30, 60, 75];
          nItems: CARDINAL = 2;

          items ← AllocateItemDescriptor[nItems];
          nVols ← ILT.ListLogicalVolumes[];

          String.Copy[toolData.destVolName, ILT.toolData.volHints[0]];
          FOR i IN [0..nVols) DO
               IF String.Equivalent[ILT.toolData.volHints[i], "Lisp"L] THEN
                    BEGIN
                    String.Replace[
                         @toolData.destVolName, ILT.toolData.volHints[i],
                         Heap.systemZone];
                    EXIT;
                    END;
               ENDLOOP;


          String.Copy[toolData.srcVolName, ILT.toolData.volHints[0]];
          FOR i IN [0..nVols) DO
               IF String.Equivalent[ILT.toolData.volHints[i], "Lisp"L] THEN
                    BEGIN
                    String.Replace[
                         @toolData.srcVolName, ILT.toolData.volHints[i],
                         Heap.systemZone];
                    EXIT;
                    END;
               ENDLOOP;

          items[0] ← StringItem[
               tag: "Source Volume"L, place: newLine, 
	       string: @toolData.srcVolName,
               inHeap: TRUE, menuProc: FormSWVolHintsProc];

          items[1] ← StringItem[
               tag: "Dest. Volume"L, string: @toolData.destVolName, inHeap: TRUE,
               menuProc: FormSWVolHintsProc];

          SetTagPlaces[items, DESCRIPTOR[tabs], FALSE];
          RETURN[items, TRUE]
          END;  --MakeParams




     MakeSWs: Tool.MakeSWsProc =
          BEGIN

          addresses: ARRAY [0..4) OF ToolDriver.Address;
          logName: STRING ← [20];
          Tool.UnusedLogName[unused: logName, root: "CopyOption.log"L];
          toolData.msgSW ← Tool.MakeMsgSW[window: window, lines: 1];

          toolData.paramSW ← Tool.MakeFormSW[window: window, formProc: MakeParams];

          toolData.commandSW ← Tool.MakeFormSW[
               window: window, formProc: MakeCommands];

          --note: logName is compulsory, else it bombs     
          toolData.fileSW ← Tool.MakeFileSW[window: window, name: logName];

          --Supervisor.AddDependency[client: agent, implementor: Event.toolWindow];

          -- do the ToolDriver stuff
          addresses ← [
               [name: "msgSW"L, sw: toolData.msgSW],
               --[name: "formSW"L, sw: toolData.formSW],
               [name: "ParamSW"L, sw: toolData.paramSW], [
               name: "CmdSW"L, sw: toolData.commandSW], [
               name: "fileSW"L, sw: toolData.fileSW]];
          ToolDriver.NoteSWs[
               tool: "Boot Options"L, subwindows: DESCRIPTOR[addresses]];

          END;  --MakeSWs




     MakeTool: PROCEDURE RETURNS [wh: Window.Handle] =
          BEGIN
          heraldName: STRING ← [80];

          String.AppendString[heraldName, "XSIS:Xerox Remote Boot Option"L];
          String.AppendString[heraldName, " of "L];
          Time.Append[heraldName, Time.Unpack[Runtime.GetBcdTime[]]];
          heraldName.length ← heraldName.length - 3;
          String.AppendString[heraldName, " on Pilot Version "L];
          Version.Append[heraldName];

          RETURN[
               Tool.Create[
                    makeSWsProc: MakeSWs, initialState: default,
                    --clientTransition: ClientTransition,
                    name: heraldName]];
          --initialBox: [
          --place: sw.BitmapPlace[[10, hisBox.dims.h]],
          --dims: [hisBox.dims.w - 20, 180]]];
          END;  --MakeTool





     Retrieve: PROC[] RETURNS [BOOLEAN] =

          BEGIN OPEN OthelloOps;

          base: LONG POINTER ← NIL;
          bufferPages: Space.PageCount = 100;
          created: BOOLEAN ← FALSE;
          destCap: File.Capability;
          destVolumeID: Volume.ID ← Volume.nullID;
          destVolumeOpen: BOOLEAN ← FALSE;
          firstPage: File.PageNumber;
          freePages: Volume.PageCount;
          pageCount: INTEGER ← 0;
          space: Space.Handle ← Space.nullHandle;
          srcCap: File.Capability;
          srcVolumeID: Volume.ID ← Volume.nullID;
          srcVolumeOpen: BOOLEAN ← FALSE;
          uCodeSize: File.PageCount ← 0;
          volSize: Volume.PageCount ← 0;


          Cleanup: PROC =
               BEGIN
               IF debug THEN {
                    Put.Line[toolData.fileSW, "Enter Cleanup....."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };

               IF space # Space.nullHandle THEN
                    BEGIN
                    IF debug THEN {
                         Put.Line[toolData.fileSW, "space # null handle..."L];
                         Process.Pause[Process.SecondsToTicks[5]];
                         };
                    Space.Delete[space];
                    space ← Space.nullHandle;
                    END;

               --CloseVolume[];
               END;  --Cleanup



          Put.Line[toolData.fileSW,
               "Will copy Source Volume boot file into Dest. Volume and boot Dest. Volume"L];
          Put.Line[toolData.fileSW, "Confirm to continue?"L];
          IF ~ILT.Confirm[] THEN RETURN[FALSE];

          [srcVolumeID, srcVolumeOpen] ← ILT.GetVolumeID[toolData.srcVolName];
          IF srcVolumeID = Volume.nullID THEN RETURN[FALSE];
          [destVolumeID, destVolumeOpen] ← ILT.GetVolumeID[toolData.destVolName];
          IF destVolumeID = Volume.nullID THEN RETURN[FALSE];

          IF String.Equivalent[toolData.srcVolName, toolData.destVolName] THEN
               BEGIN
               Put.Line[toolData.msgSW, " Source Volume equals Destination Volume"];
               RETURN[FALSE];
               END;

          [volSize, freePages] ← Volume.GetAttributes[srcVolumeID];
          Put.Text[toolData.fileSW, " \n Source Volume size = "L];
          Put.LongDecimal[toolData.fileSW, volSize];
          Put.Line[toolData.fileSW, " pages"L];
          Put.Text[toolData.fileSW, " Free pages on source volume = "L];
          Put.LongDecimal[toolData.fileSW, freePages];
          Put.CR[toolData.fileSW];

          [volSize, freePages] ← Volume.GetAttributes[destVolumeID];
          Put.Text[toolData.fileSW, " \n Destination Volume size = "L];
          Put.LongDecimal[toolData.fileSW, volSize];
          Put.Line[toolData.fileSW, " pages"L];
          Put.Text[toolData.fileSW, " Free pages on destination volume = "L];
          Put.LongDecimal[toolData.fileSW, freePages];
          Put.CR[toolData.fileSW];

          [srcCap, firstPage] ← GetVolumeBootFile[srcVolumeID, hardMicrocode];
          [destCap, firstPage] ← GetVolumeBootFile[destVolumeID, hardMicrocode];
          
	  
          IF destCap = File.nullCapability THEN
               BEGIN
               IF debug THEN {
                    Put.Line[ILT.toolData.fileSW, " = nullCapability...."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };
               destCap ← File.Create[destVolumeID, 0, FileTypes.tUntypedFile];
               END
          ELSE
               BEGIN
               IF debug THEN {
                    Put.Line[ILT.toolData.fileSW, " MakeUnbootable.."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };

               MakeUnbootable[
                    destCap, hardMicrocode, firstPage !
                    TemporaryBooting.InvalidParameters =>
                         BEGIN
                         Put.Text[ILT.toolData.msgSW,
                              "Warning: trouble making unbootable"L];
                         CONTINUE;
                         END];
               END;


	  IF srcCap = File.nullCapability THEN uCodeSize ← 0
          ELSE uCodeSize ← File.GetSize[srcCap];

          Put.Text[toolData.fileSW, " Source boot file size = "];
          Put.LongDecimal[toolData.fileSW, uCodeSize];
          Put.Text[toolData.fileSW, " pages; "];
          Put.LongDecimal[toolData.fileSW, uCodeSize*512];
          Put.Line[toolData.fileSW, " bytes."];

          <<File.SetSize[destCap, uCodeSize !
               File.Unknown => {
               Put.Line[toolData.fileSW, " File.Unknown!"L];
 	       Cleanup[];
               GOTO noGood}];

          IF debug THEN {
               Put.Line[toolData.fileSW, "SetSize...."L];
               Process.Pause[Process.SecondsToTicks[10]];
               };

          File.SetSize[destCap, uCodeSize !
               Volume.Unknown => {
               Put.Line[toolData.fileSW, " Volume.Unknown!"L];
 	       Cleanup[];
               GOTO noGood}];>>

          IF debug THEN {
               Put.Line[toolData.fileSW, "SetSize...."L];
               Process.Pause[Process.SecondsToTicks[10]];
               };

          File.SetSize[destCap, uCodeSize !
               Volume.InsufficientSpace => {
               Put.Line[toolData.fileSW, " Not enough room on dest. volume!"L];
 	       Cleanup[];
               GOTO noGood}];

          IF debug THEN {
               Put.Line[toolData.fileSW, "Create...."L];
               Process.Pause[Process.SecondsToTicks[10]];
               };
          space ← Space.Create[bufferPages, Space.virtualMemory];

          IF debug THEN {
               Put.Line[toolData.fileSW, "Map...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          Space.Map[space];

          IF debug THEN {
               Put.Line[toolData.fileSW, "CreateUniformSwapUnits...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };

          Space.CreateUniformSwapUnits[size: 16, parent: space];

          IF debug THEN {
               Put.Line[toolData.fileSW, "LongPointer...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          base ← Space.LongPointer[space];

          FOR windowPage: Space.PageOffset ← 0, windowPage + bufferPages WHILE
               windowPage < Inline.LowHalf[uCodeSize] DO

               Space.CopyIn[space, [srcCap, windowPage]];
               Space.CopyOut[space, [destCap, windowPage]];
               pageCount ← pageCount + bufferPages;
               Put.Text[toolData.msgSW, "Pages transfered: "];
               Put.Decimal[toolData.msgSW, pageCount];
               Put.CR[toolData.msgSW];
               InvertIndicator[];
               ENDLOOP;

          IF debug THEN {
               Put.Line[toolData.fileSW, "Cleanup...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
          Cleanup[];

          IF debug THEN {
               Put.Text[toolData.fileSW, "SetVolumeBootFile..."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };

          SetVolumeBootFile[destCap, hardMicrocode, 0];
          IF debug THEN {
               Put.Text[toolData.fileSW, "MakePermanent..."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };

          File.MakePermanent[destCap];
          IF debug THEN {
               Put.Text[toolData.fileSW, "MakeBootable..."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };

          MakeBootable[
               destCap, hardMicrocode, 0 !
               TemporaryBooting.InvalidParameters =>
                    BEGIN
                    Put.Text[toolData.msgSW, "Warning: trouble making bootable"L];
                    CONTINUE;
                    END];
          Put.Line[toolData.fileSW, " installed."L];

          IF debug THEN {
               Put.Text[toolData.fileSW, "SetPhysicalVolumeBootFile..."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };

          SetPhysicalVolumeBootFile[destCap, hardMicrocode, 0];
          destVolumeOpen ← CloseVolume[destVolumeID];
          srcVolumeOpen ← CloseVolume[srcVolumeID];

          RETURN[TRUE];
          EXITS noGood => RETURN[FALSE];
          END;  --Retrieve

     END...ILTBootImpl.mesa