-- File: OthelloLisp.mesa
-- Created  9-Jul-84 15:06:38 Lichtenberg

DIRECTORY
  CommonSoftwareFileTypes USING [tCarryVolumeDirectory],
  Environment USING [wordsPerPage, Byte],
  File USING [Type, 
    Create, Delete, File, MakePermanent, nullFile, PageNumber, PageCount, GetSize, SetSize, Unknown ],
  FileTypes USING [tUntypedFile],
  Floppy USING [Open, Close, FileHandle, nullFileID,GetNextFile, ErrorType, Error, 
  		VolumeHandle, Read, PageCount, PageNumber],
  Inline USING [LongCOPY],
  OthelloDefs,
  OthelloOps USING [
    BootFileType, 
    MakeBootable, MakeUnbootable, 
    GetPhysicalVolumeBootFile, VoidPhysicalVolumeBootFile,
    SetVolumeBootFile, GetVolumeBootFile, VoidVolumeBootFile
    ],
  PhysicalVolume USING [ID],
  System USING [GreenwichMeanTime, gmtEpoch],
  Space USING [Map, CopyIn, Unmap, Window],
  TemporaryBooting USING [InvalidParameters],
  Time USING [Unpacked,Unpack,Append],
  Volume USING [Close, ID, GetAttributes, nullID, InsufficientSpace, Open ];

-- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

OthelloLisp: PROGRAM
  IMPORTS
   File, OthelloDefs, OthelloOps, Floppy, 
   Space, TemporaryBooting, Time, Volume, Inline =
 
  BEGIN
  

-- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
-- These definitions stolen from AccessFloppy
-- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 
Attributes: TYPE = LONG POINTER TO AttributesRecord;
AttributesRecord: TYPE = MACHINE DEPENDENT RECORD[ -- used by directory or leader page.
   
      -- identity attributes
   seal: WORD ← sealValue, -- used to check consistency of a file.
   version:  CARDINAL, -- version of attributes record type.
   type: File.Type, -- file type of containing file.
    
         -- activity attributes
   createDate: FloppyTime, -- the creation data of the disk file.
   lastWrittenDate: FloppyTime, -- the date the disk file was last modified prior to copying to the floppy.
   
      -- file attributes
   size: Floppy.PageCount ← 0, -- number of pages in the floppy file not including the leader page.
   offset: Floppy.PageNumber ← 0, -- page number in the disk file correspoinding to the first page in the floppy file piece.
   totalSize: Floppy.PageCount ← 0, -- number of pages in the disk file.
   totalSizeInBytes: LengthInBytes ← 0, -- the number of bytes in the disk file

   -- name attributes
   length: CARDINAL ← 0,
   maxLength: CARDINAL ← maxNameLength, -- so that @length is STRING.
   name: PACKED ARRAY [0..maxNameLength) OF Environment.Byte,
   
     -- client attributes
   clientDataLength: CARDINAL ← 0 ,-- number of component in client's private data.
   clientData: SEQUENCE maxlength: CARDINAL OF UNSPECIFIED
  ];
   
 

LengthInBytes: TYPE = LONG CARDINAL;

FloppyTime: TYPE = System.GreenwichMeanTime;

tFloppyLeaderPage: File.Type = CommonSoftwareFileTypes.tCarryVolumeDirectory;

   -- Constants
    
leaderLength: CARDINAL = 1;
  -- leaderLength*Environment.wordsPerPage must be greater than SIZE[AttributesRecord]

maxDataSize: CARDINAL = leaderLength*Environment.wordsPerPage - SIZE[AttributesRecord];

maxNameLength: CARDINAL = 100;
  
  -- includes words field.

nullTime: FloppyTime = System.gmtEpoch;

sealValue: WORD = 125252B; -- 1010101010101010 bits pattern


   
-- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

  
  CopyVM: PROC = BEGIN
    fromVolume, toVolume: Volume.ID;
    fromFile, toFile: File.File;
    pvID: PhysicalVolume.ID;
    error: BOOLEAN ← FALSE;
    currentBase: File.PageNumber;
    nextCount: File.PageCount;
    fromWindow,toWindow: Space.Window;
    toPointer: LONG POINTER;
    fromFileSize: File.PageCount;
    tries: CARDINAL;
    toFileSize: File.PageCount;
    minFileSize: File.PageCount;
    
    OthelloDefs.MyNameIs[
      myNameIs: "Copy Lisp From Another Volume"L,
      myHelpIs: "Copies lisp from one volume to another"L];
      
      [pvID, fromVolume] ← OthelloDefs.GetLvIDFromUser[prompt:"Volume to copy from: "L];
      IF fromVolume = Volume.nullID THEN OthelloDefs.AbortingCommand["Invalid volume."L];
      toVolume ← OthelloDefs.GetLvIDFromUser[prompt:"Volume to copy to:  "L].lvID;
      IF toVolume = Volume.nullID THEN OthelloDefs.AbortingCommand["Invalid volume."L];
      
      -- Open up the volumes
      Volume.Open[fromVolume];
      Volume.Open[toVolume];
      
      -- Is there a source file?
      fromFile ← OthelloOps.GetVolumeBootFile[fromVolume,hardMicrocode].file;
      IF fromFile = File.nullFile THEN {
      		Volume.Close[fromVolume];
      		Volume.Close[toVolume];
      		OthelloDefs.AbortingCommand["No Lisp sysout on source volume"L];
		};
      
      -- Is there a dest file? Delete it if yes.
      
      toFile ← OthelloOps.GetVolumeBootFile[toVolume,hardMicrocode].file;
      IF toFile # File.nullFile THEN {
      	   OthelloOps.VoidVolumeBootFile[toVolume,hardMicrocode];
	   File.Delete[toFile];
	   IF OthelloOps.GetPhysicalVolumeBootFile[pvID,hardMicrocode].file = toFile
	   THEN OthelloOps.VoidPhysicalVolumeBootFile[pvID,hardMicrocode];
	   };
      
      -- Create the dest file
      
      fromFileSize ← File.GetSize[fromFile];
      toFileSize ← fromFileSize;
      tries ← 20;
      toFile ← File.Create[toVolume, toFileSize, FileTypes.tUntypedFile
      			! Volume.InsufficientSpace => 
			    { tries ← tries - 1;
			      toFileSize ← toFileSize - 100;
			      OthelloDefs.WriteString["."L];
			      IF tries = 0 OR toFileSize < 0 THEN {error ← TRUE;
			    		CONTINUE}
			      ELSE RETRY;};];
						    
      IF error THEN {Volume.Close[fromVolume];
      		     Volume.Close[toVolume];
      		     OthelloDefs.AbortingCommand["Destination volume does not contain sufficient space!"L];};
      File.MakePermanent[toFile];
      
      -- Create windows onto the file

      OthelloDefs.WriteString["Copying..."L];
      currentBase ← 0;
      nextCount ← 100;
      
      minFileSize ← fromFileSize;
      IF toFileSize < fromFileSize THEN minFileSize ← toFileSize;
      
      DO -- until file has been copied
       
         fromWindow ← [file: fromFile,
      		       base: currentBase,
		       count: nextCount];

         toWindow ← [file: toFile,
      		     base: currentBase,
		     count: nextCount];
     
         toPointer ← Space.Map[toWindow].pointer;
	 nextCount ← Space.CopyIn[toPointer,fromWindow];
	 toPointer ← Space.Unmap[toPointer];
	 
	 currentBase ← currentBase + nextCount;
	 
	 OthelloDefs.WriteString["."L];

         IF currentBase >= minFileSize THEN EXIT; 
	 
      ENDLOOP;
      
      OthelloDefs.WriteString["Installing..."L];
      OthelloOps.MakeBootable[toFile, hardMicrocode, OthelloDefs.leaderPages
           ! TemporaryBooting.InvalidParameters => {
             OthelloDefs.WriteLine["Warning, trouble making bootable"L]; CONTINUE}];

      OthelloOps.SetVolumeBootFile[toFile,hardMicrocode,OthelloDefs.leaderPages];

      OthelloDefs.WriteLine["done"L];
          
      Volume.Close[fromVolume];
      Volume.Close[toVolume];
      
      END;
      
-- UGH.. This kludge sets the file's size to be as big as Pilot will let it be, on 100 page increments.

  SetBootFileSize: PRIVATE PROC[file: File.File, lvID: Volume.ID] = BEGIN
    tries: CARDINAL; 
    oldSize: File.PageCount ← File.GetSize[file];
    newSize: File.PageCount ← oldSize + Volume.GetAttributes[lvID].freePageCount;
    tries ← 20;
    OthelloOps.MakeUnbootable[file, hardMicrocode, OthelloDefs.leaderPages];
    File.SetSize[file, newSize 
	  ! File.Unknown => {OthelloDefs.WriteLine["Warning: Trouble making file fill volume - File.Unknown"L];
	    CONTINUE};
	    Volume.InsufficientSpace => {OthelloDefs.WriteString["."L]; newSize ← newSize - 100;
	    tries ← tries - 1; 
	    IF tries = 0 OR newSize < oldSize THEN {OthelloDefs.WriteLine["Warning: Expanding volume failed!"]; CONTINUE;} ELSE RETRY};];
    OthelloOps.MakeBootable[file,hardMicrocode, OthelloDefs.leaderPages];
    END;
    
    
    ExpandVM: PROC = BEGIN
      pvID: PhysicalVolume.ID;
      exVolume: Volume.ID;
      exFile: File.File;
      
      OthelloDefs.MyNameIs[
         myNameIs: "Expand Vmem File"L,
	 myHelpIs: "Expand the lisp virtual mem file on a volume"L];
      
      [pvID, exVolume] ← OthelloDefs.GetLvIDFromUser[prompt:"Volume to expand: "L];
      IF exVolume = Volume.nullID THEN OthelloDefs.AbortingCommand["Invalid volume."L];
      Volume.Open[exVolume];
      exFile ← OthelloOps.GetVolumeBootFile[exVolume, hardMicrocode].file;
      IF exFile = File.nullFile THEN OthelloDefs.AbortingCommand["No sysout on volume."L];
      OthelloDefs.WriteString["Expanding file..."L];
      SetBootFileSize[exFile,exVolume];
      OthelloDefs.WriteLine["Done."L];
      Volume.Close[exVolume];
      END;


 
    
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- FLOPPY commands
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  ListFloppyFiles: PROC = BEGIN
  
     floppyHandle: Floppy.VolumeHandle;
     attRec: AttributesRecord;
     floppyFileAttributes: Attributes ← @attRec;
     currentFloppyFile: Floppy.FileHandle ← [floppyHandle,Floppy.nullFileID];
     i: CARDINAL;
     timeTemp: STRING;
     unpackedTime: Time.Unpacked;
     
     floppyFileAttributes ← @attRec;
  
     -- Open floppy
     OthelloDefs.MyNameIs[myNameIs: "List Floppy Files"L,
     	myHelpIs: "List files on pilot floppy"L];
     floppyHandle ← Floppy.Open[
     	! Floppy.Error => {PrintNames: PROC [x: Floppy.ErrorType] =
		{ShowFloppyError[x]};};];
     
     -- Enumerate files
     DO
       currentFloppyFile ← Floppy.GetNextFile[currentFloppyFile];
       IF currentFloppyFile.file = Floppy.nullFileID THEN EXIT;
       ReadLeaderPage[currentFloppyFile, floppyFileAttributes];
       FOR i IN [0..floppyFileAttributes.length) DO 
          OthelloDefs.WriteChar[LOOPHOLE[floppyFileAttributes.name[i]]];
	  ENDLOOP;
       THROUGH [0..(60-floppyFileAttributes.length)) DO 
       	  OthelloDefs.WriteChar[' ];
	  ENDLOOP;
       OthelloDefs.WriteLongNumber[floppyFileAttributes.size];
       OthelloDefs.WriteString["   "L];
       unpackedTime ← Time.Unpack[time: floppyFileAttributes.createDate];
       timeTemp ← "    ";
       Time.Append[timeTemp,unpackedTime];
       OthelloDefs.WriteLine[timeTemp];
     ENDLOOP;
     Floppy.Close[floppyHandle];
     
   END;   
     
     
     
     
  ShowFloppyError:  PROC [error:Floppy.ErrorType] =  {
       errString: ARRAY Floppy.ErrorType OF STRING = [
        notReady: "Floppy drive not ready."L,
        noSuchDrive: "noSuchDrive"L,
        invalidFormat: "Floppy not in pilot format"L,
        needsScavenging: "Floppy needs scavenging"L,
        invalidVolumeHandle: "invalidVolumeHandle"L,
	volumeNotOpen: "volumeNotOpen"L,
	fileNotFound: "File Not Found"L,
	endOfFile: "End of File"L,
	writeInhibited: "Floppy is write-protected"L,
        hardwareError: "Floppy hardware error"L,
	incompatibleSizes: "incompatibleSizes"L,
	insufficientSpace: "Floppy volume is full."L,
	zeroSizeFile: "Floppy file is zero-size!"L,
	fileListFull: "Floppy file list is full."L,
	invalidPageNumber: "invalidPageNumber"L,
	badDisk: "badDisk"L,
	badSectors: "badSectors"L,
	onlyOneSide: "onlyOneSide"L,
	onlySingleDensity: "onlySingleDensity"L,
	initialMicrocodeSpaceNotAvailable: "initialMicrocodeSpaceNotAvailable"L,
	stringTooShort: "stringTooShort"L,
	fileListLengthTooShort: "fileListLengthTooShort"L,
	floppyImageInvalid: "floppyImageInvalid"L,
	floppySpaceTooSmall: "floppySpaceTooSmall"L];
      OthelloDefs.WriteString["Unexpected floppy problem: "L];
      OthelloDefs.WriteString[errString[error]];
      OthelloDefs.AbortingCommand["Floppy command aborted."L];
      };
     
     
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- The following procs were stolen (more or less) from AccessFloppyImpl
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  ReadLeaderPage: PROC [
    file: Floppy.FileHandle, attributes: Attributes] =
    BEGIN
    -- make the buffer for the AttributesRecord in full pages.
    buffer: ARRAY [1..maxDataSize + SIZE[AttributesRecord]] OF WORD ← ALL[0];
    length: CARDINAL ← 0;
    localAttributes: Attributes ← LOOPHOLE[LONG[@buffer]];

    Floppy.Read[file, 0, leaderLength, localAttributes];
    IF localAttributes.clientDataLength > attributes.maxlength THEN
      localAttributes.clientDataLength ← 0;
    length ← SIZE[AttributesRecord] + localAttributes.clientDataLength;
    [] ← Inline.LongCOPY[to: attributes, nwords: length, from: localAttributes];
  END;  -- ReadLeaderPage.



  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- Central commands
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  commandProcessor: OthelloDefs.CommandProcessor ← [LispCommands];

  LispCommands: PROC [index: CARDINAL] = {
    SELECT index FROM
     0 => CopyVM[];
     1 => ExpandVM[];
     2 => ListFloppyFiles[];
    ENDCASE => OthelloDefs.IndexTooLarge};
     
  
  -- Initialize
  OthelloDefs.RegisterCommandProc[@commandProcessor];


END...