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

--last edited by
--   JFung.PASA	   	 4-Sep-84 15:44:53


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

LTCopyVMemImpl: PROGRAM
     IMPORTS
          Display, File, FormSW, Heap, Inline, LispToolOps, 
	  OthelloDefs, 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};
     Graphic: TYPE = ARRAY[0..16) OF WORD;

     
     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;
     buffer: LONG POINTER ← NIL;
     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;

     -- taken from FloppyFileToo
     yinLeft: Graphic = [001600B, 006140B, 014020B, 034410B, 074004B, 076004B, 177402B, 177702B, 
     		177742B, 077744B, 077744B, 037350B, 017760B, 007740B, 001600B, 000000B];
     yangRight: Graphic = [001600B, 007740B, 017760B, 027370B, 047774B, 047774B, 103776B, 100776B, 
     		100176B, 040074B, 040074B, 020470B, 010060B, 006140B, 001600B, 000000B];
  

     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]];
               };

          copyWH ← MakeTool[];
          END;  



     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;
	  pattern: Graphic;
          SELECT toolData.indicator FROM
               left => {
                    pattern ← yinLeft;
                    Display.Bitmap[
                         window, indicatorBox, [@pattern, , 0], 16,
                         Display.replaceFlags]};
               right => {
                    pattern ← yangRight;
                    Display.Bitmap[
                         window, indicatorBox, [@pattern, , 0], 16,
                         Display.replaceFlags]};
               ENDCASE};  --DisplayIndicator


     DoInstall: UserInput.PeriodicProcType =
          BEGIN
                    IF debug THEN {
               Put.Line[toolData.fileSW, "Start Install ....."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
	       
	  ClearMsgSubwindow;
	  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


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


     FormSWQuit: FormSW.ProcType =
          BEGIN

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



     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: Graphic ← yinLeft;

          IF ~toolData.busy THEN RETURN;
          SELECT toolData.indicator FROM
               left => {
                    toolData.indicator ← right;
	            pattern ← yangRight;
		    };
		    
               off, right => {
                    toolData.indicator ← left;
	            pattern ← yinLeft;
		    };
               ENDCASE;
	       
	       Display.Bitmap[
                         toolData.commandSW, indicatorBox, [@pattern, , 0], 16,
                         Display.replaceFlags];
               
	       <<Display.Bitmap[
		window: data.commandSW, box: indicatorBox, 
		address: [word: @pattern, bit: 0],
		bitmapBitWidth: 16, flags: Display.replaceFlags];>>

           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: FormCopyVMem];
          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; 



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

          String.AppendString[heraldName, "System Tools: Copy VMem"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: [[512, 400], [512, 400]], tinyName1: "Copy"L,
                    tinyName2: "VMem"L]]
          END;  



     Retrieve: PROC[] RETURNS [BOOLEAN] =
          BEGIN OPEN OthelloOps;

	  access: Space.Access ← readWrite;
          base: LONG POINTER ← NIL;
          bufferPages: Space.PageCount = 100;
          created: BOOLEAN ← FALSE;
          destCap: File.File;
          destVolumeID: Volume.ID ← Volume.nullID;
          destVolumeOpen: BOOLEAN ← FALSE;
          srcFirstPage: File.PageNumber;
	  destFirstPage: File.PageNumber;

          freePages: Volume.PageCount;
	  pagesIn: Environment.PageCount;
	  pagesOut: Environment.PageCount;
          pageCount: INTEGER ← 0;
<<          space: Space.Interval ← Space.nullInterval;
	  spaceBase: File.PageNumber ← 0;
	  spaceInterval: Space.Interval ← Space.nullInterval;
	  spacePtr: LONG POINTER;
	  srcSpaceWindow: Space.Window;
	  desSpaceWindow: Space.Window;

	  --swapUnitSize: Space.SwapUnitSize = 10;
>>


          srcCap: File.File;
          srcVolumeID: Volume.ID ← Volume.nullID;
          srcVolumeOpen: BOOLEAN ← FALSE;
	  srcUCodeSize: File.PageCount ← 0;
          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 buffer # NIL THEN buffer ← Space.Unmap[buffer];
               --CloseVolume[];
               END;  --Cleanup


	  --spacePtr ← space.pointer;

          Put.Line[toolData.fileSW,
               "Will copy virtual memory from source volume into destination volume and then prompt to boot destination volume"L];
          Put.Line[toolData.fileSW, "Click left-button to continue, right-button to skip"L];
          IF ~ILT.Confirm[] THEN RETURN[FALSE];


	  IF debug THEN {
                    Put.Line[toolData.fileSW, "GetSourceVolumeID..."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };
     
          [srcVolumeID, srcVolumeOpen] ← ILT.GetVolumeID[toolData.srcVolName];
          IF srcVolumeID = Volume.nullID THEN RETURN[FALSE];
	  
	  IF debug THEN {
                    Put.Line[toolData.fileSW, "GetDestVolumeID..."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };

          [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, "Error: Source volume equals destination volume"];
               RETURN[FALSE];
               END;
	       
	  IF debug THEN {
                    Put.Line[toolData.fileSW, "GetVolumeBootFile..."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };
     
          [srcCap, srcFirstPage] ← GetVolumeBootFile[srcVolumeID, hardMicrocode];
	  
          [destCap, destFirstPage] ← GetVolumeBootFile[destVolumeID, hardMicrocode];
          
          IF destCap = File.nullFile THEN
               BEGIN
               IF debug THEN {
                    Put.Line[toolData.fileSW, " = nullFile...."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };
               destCap ← File.Create[destVolumeID, 1, FileTypes.tUntypedFile];
               END
          ELSE
               BEGIN
               IF debug THEN {
                    Put.Line[ILT.toolData.fileSW, " MakeUnbootable.."L];
                    Process.Pause[Process.SecondsToTicks[5]];
                    };
               MakeUnbootable[
                    destCap, hardMicrocode, destFirstPage !
                    TemporaryBooting.InvalidParameters =>
                         BEGIN
                         Put.Text[ILT.toolData.msgSW,
                              "Warning: trouble making unbootable"L];
                         CONTINUE;
                         END];
               END;

          Put.Text[toolData.fileSW, " srcFirstPage = "];
          Put.LongDecimal[toolData.fileSW, srcFirstPage];
	  
	  Put.Text[toolData.fileSW, " destFirstPage = "];
          Put.LongDecimal[toolData.fileSW, destFirstPage];
	  

	  IF srcCap = File.nullFile THEN srcUCodeSize ← 0
          ELSE srcUCodeSize ← File.GetSize[srcCap];

          Put.Text[toolData.fileSW, " Source VMem size = "];
          Put.LongDecimal[toolData.fileSW, srcUCodeSize];
          Put.Text[toolData.fileSW, " pages; "];

          <<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}];
	       >>
	       
	  uCodeSize ← srcUCodeSize; 
	     
	  File.SetSize[destCap, uCodeSize !
               Volume.InsufficientSpace => {
               Put.Text[toolData.fileSW, " Not enough room on dest. volume for "L];
	       Put.LongDecimal[toolData.fileSW, uCodeSize];
               Put.Line[toolData.fileSW, " pages; "];
 	       uCodeSize ← uCodeSize - 100;
               RETRY;}];

          IF debug THEN {
               Put.Line[toolData.fileSW, "Create...."L];
               Process.Pause[Process.SecondsToTicks[10]];
               };
<<	
	  srcSpaceWindow ← [srcCap, spaceBase, bufferPages];
	  desSpaceWindow ← [destCap, spaceBase, bufferPages];
          spaceInterval ← Space.Map[window: srcSpaceWindow,
	       access: access,
	       swapUnits: [uniform[swapUnitSize]]!
               Space.InsufficientSpace => {
                    Put.Line[ILT.toolData.msgSW, "+++ Insufficient free pages"L];
                    Process.Pause[Process.SecondsToTicks[10]];
                    Cleanup[];
                    GOTO noGood}; ];
>>

          IF debug THEN {
               Put.Line[toolData.fileSW, "Map...."L];
               Process.Pause[Process.SecondsToTicks[5]];
               };
	       
	   
	   buffer ← Space.Map[
	  	window: [file: File.nullFile, base: NULL, count: bufferPages], 
      		class: data, swapUnits: [uniform[4]]].pointer;
    
          FOR windowPage: Space.PageOffset ← OthelloDefs.leaderPages, windowPage + bufferPages WHILE
               windowPage < Inline.LowHalf[uCodeSize] DO

               pagesIn ← Space.CopyIn[buffer, [srcCap, windowPage, bufferPages]];
               pagesOut ← Space.CopyOut[buffer, [destCap, windowPage, bufferPages]];
               pageCount ← pageCount + bufferPages;
	       --pageCount ← pageCount + swapUnitSize;
               Put.Text[toolData.msgSW, "Pages transferred: "];
               Put.Decimal[toolData.msgSW, pageCount];
               Put.CR[toolData.msgSW];
               InvertIndicator[];
               ENDLOOP;
	       
 <<         File.SetSize[destCap, srcUCodeSize !
               Volume.InsufficientSpace => {
	       Put.Line[toolData.fileSW, "srcUCodeSize!"L];
               Put.Line[toolData.fileSW, "Not enough room on dest. volume for "L];
	       Put.LongDecimal[toolData.fileSW, srcUCodeSize];
	       Put.Line[toolData.fileSW, " pages. "L];
 	       Cleanup[];
               GOTO noGood}];
	       >>
	       
          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, OthelloDefs.leaderPages];
          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, OthelloDefs.leaderPages !
               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, OthelloDefs.leaderPages];
          destVolumeOpen ← CloseVolume[destVolumeID];
          srcVolumeOpen ← CloseVolume[srcVolumeID];

          RETURN[TRUE];
          END;  --Retrieve

     END.