-- OthelloDeviceImplD0DLion.mesa
-- Contains Othello commands and operations that are specific to disks connected to D0s and Dandelions.
-- Last Edited by: Taft, May 22, 1983 4:47 pm

DIRECTORY
  Device,
  DeviceTypes USING [sa1000, sa4000, sa800],
  FloppyChannel USING [Status],
  FormatSA1000andSA4000,
  FormatSA800,
  OthelloDefs,
  OthelloDevice,
  OthelloOps,
  PhysicalVolume,
  String USING [EquivalentStrings],
  System USING [GreenwichMeanTime],
  Volume;

OthelloDeviceImplD0DLion: PROGRAM
  IMPORTS FormatSA1000andSA4000, FormatSA800, OthelloDefs, OthelloOps,
  PhysicalVolume, String, Volume
  EXPORTS OthelloDevice =
  
BEGIN OPEN OthelloDefs, OthelloOps, OthelloDevice;

printNameSA4000: PUBLIC STRING ← "Shugart 4000";

logicalVolumeOverhead: CARDINAL  = 1;
minLogicalVolumeSize:  CARDINAL  = 50; -- fudge + 1+1+6;
maxNameLength:         CARDINAL = PhysicalVolume.maxNameLength;

 -- BUG! The following statement is wrong!  physicalVolumeOverhead should be set based upon device type.  This will be necessary once Pilot knows how to create bad page tables based upon device types.  For the time bbeing, we just KNOW that all bad page tables are one page long, and that as a result there are always two pages of physical volume overhead.
physicalVolumeOverhead: CARDINAL  = 2;

sa4000Flavor: PACKED ARRAY [0..8) OF [0..377B] ← ALL[377B];

LvRecord: TYPE = MACHINE DEPENDENT RECORD[
  size: Volume.PageCount ← NULL,
  type: Volume.Type ← NULL,
  fill: [0..16000] ← 0,
  name: StringBody ← [maxlength: maxNameLength, length:  0, text: ],
  txt: PACKED ARRAY [0..maxNameLength) OF CHARACTER ← NULL];

CallCommandProc: PUBLIC PROC [proc: PROC] =
  BEGIN
  proc[
    -- All D0/DLion device-specific SIGNALs are caught here, remaining ones by caller.
    ! FormatSA1000andSA4000.MicrocodeInstallFailure =>
        BEGIN
        NewLine[];
        ReportError[SELECT m FROM
          emptyFile => "\rThat remote file is empty!"L,
          firstPageBad => "\rFirst microcode page of this disk is bad."L,
          flakeyPageFound => "\rIntermittent page in microcode area."L,
          microcodeTooBig => "\rMicrocode too large."L,
          ENDCASE => "\rUnknown Install microcode error."L];
        RESUME;
        END;
      FormatSA800.BadSector, FormatSA1000andSA4000.BadPage => 
        {WriteString["\rBadPage?"L]; DebugAsk[]; CONTINUE};
      FormatSA1000andSA4000.NotSA1000orSA4000 => 
        {WriteString["\rNotSAx000?"L]; DebugAsk[]; CONTINUE};
      FormatSA1000andSA4000.SA1000FormatCylinderError => 
        {WriteString["\rSA1000CylinderBoundry?"L]; DebugAsk[]; CONTINUE}
    ];
  END;

CreateVolume: PUBLIC PROC=
  BEGIN
  h: PhysicalVolume.Handle = GetDriveFromUser[];
  badTable: ARRAY [0..badTableSize) OF PhysicalVolume.PageNumber;
  bad: CARDINAL ← 0;
  broughtOnLine: BOOLEAN ← FALSE;
  driveSize: LONG CARDINAL;
  nSubVols: CARDINAL;
  lvID: Volume.ID;
  lvsize: Volume.PageCount;
  lvTable: ARRAY [0..10) OF LvRecord ← ALL[[]];
  pvID: PhysicalVolume.ID ← PhysicalVolume.nullID;
  pvName: STRING ← [maxNameLength];
  PagesRippedOff: PROC RETURNS [p: LONG CARDINAL] =  --we don't really lose pg 0
    INLINE {p ← MinPilotPage[h]; IF p # 0 THEN p ← p - 1};
  volumeFound: BOOLEAN ← TRUE;

  DO
    pvID ← PhysicalVolume.GetNext[pvID];
    IF pvID=PhysicalVolume.nullID THEN
      BEGIN
      volumeFound ← Yes["Shall I try to find an old bad page Table? "L];
      IF volumeFound THEN
        BEGIN
        broughtOnLine ← TRUE;
        pvID ← PhysicalVolume.AssertPilotVolume[h
        ! PhysicalVolume.Error, PhysicalVolume.CanNotScavenge => 
            {volumeFound ← broughtOnLine ← FALSE; CONTINUE}]
        END;
      EXIT;
      END;
    IF h=PhysicalVolume.GetAttributes[pvID].instance THEN EXIT;
    ENDLOOP;
  IF volumeFound THEN
    BEGIN OPEN PhysicalVolume;
    badTable[bad] ← GetNextBadPage[pvID, nullBadPage];
    WHILE badTable[bad]#nullBadPage DO
      badTable[bad+1] ← GetNextBadPage[pvID, badTable[bad]];
      bad ← bad+1;
      ENDLOOP;
    volumeFound ← PhysicalVolume.GetNextLogicalVolume[
      pvID, Volume.nullID]#Volume.nullID;
    END;
  GetName["New physical volume name: "L, pvName];
  nSubVols ← ReadShortNumber[
    "Number of logical volumes: "L, 1,
    PhysicalVolume.maxSubvolumesOnPhysicalVolume, 3];
  driveSize ← GetDriveSize[h] - (physicalVolumeOverhead
      + nSubVols*logicalVolumeOverhead + PagesRippedOff[]);
  FOR i: CARDINAL IN [0..nSubVols) DO OPEN lvTable[i];
    WriteString["Logical volume "L];
    WriteLongNumber[LONG[i]];
    NewLine[];
    DO
      duplicate: BOOLEAN ← FALSE;
      GetName["  Name: "L, @name];
      FOR j: CARDINAL IN [0..i) WHILE ~duplicate DO
        duplicate ← String.EquivalentStrings[@name, @lvTable[j].name];
        ENDLOOP;
      IF ~duplicate THEN EXIT;
      ReportError["Name is already in use; please choose another"L];
      ENDLOOP;
    size ← ReadNumber["  Pages: "L, minLogicalVolumeSize,
	driveSize-((nSubVols-(i+1)) * minLogicalVolumeSize),
	driveSize/(nSubVols-i)];
    driveSize ← driveSize - size;
    type ← GetLvTypeFromUser["  Type: "L];
    ENDLOOP;
  IF broughtOnLine THEN PhysicalVolume.Offline[pvID];
  Confirm[IF volumeFound THEN twice ELSE once];
  PhysicalVolume.Offline[pvID ! ANY => CONTINUE];
  pvID ← PhysicalVolume.CreatePhysicalVolume[h, pvName];
  FOR i: CARDINAL IN [0..bad) DO
    PhysicalVolume.MarkPageBad[pvID, badTable[i]]; ENDLOOP;
  FOR i: CARDINAL IN [0..nSubVols) DO OPEN lvTable[i];
    lvID ← PhysicalVolume.CreateLogicalVolume[
      pvID, size, @name, type, MinPilotPage[h]];
    IF (lvsize←Volume.GetAttributes[lvID].volumeSize)#size THEN
      BEGIN
      WriteString[@name];
      WriteString["'s size decreased (because of bad pages) to "L];
      WriteLongNumber[lvsize];
      NewLine[];
      END;
    ENDLOOP;
  InstallUserCredentials[];
  END;

FetchInitialMicrocode: PUBLIC PROC =
  BEGIN
  h: PhysicalVolume.Handle ← GetDriveFromUser[];
  msg: STRING ← [100];
  t: Device.Type;
  IF ~ftpOpen THEN {ReportError["Please open a connection first"L]; RETURN};
  GetName["File name: "L, file];
  Confirm[];
  SELECT (t ← GetDriveType[h]) FROM
    DeviceTypes.sa1000, DeviceTypes.sa4000 =>
      BEGIN OPEN FSa: FormatSA1000andSA4000;
      startOfMicrocode: FSa.DiskPageNumber;
      cnt: LONG CARDINAL;
      P: PROC [getPage: PROC RETURNS[LONG POINTER]] =
	{FSa.InstallBootMicrocode[h, getPage]};
      IF t=DeviceTypes.sa1000 THEN
        BEGIN
        startOfMicrocode ← FSa.SA1000startOfMicrocode;
        cnt ← 1+FSa.SA1000lastPageOfMicrocode-FSa.SA1000startOfMicrocode;
        END
      ELSE -- SA4000
        BEGIN
        startOfMicrocode ← FSa.SA4000startOfMicrocode;
        cnt ← 1+FSa.SA4000lastPageOfMicrocode-FSa.SA4000startOfMicrocode;
        END;
      FSa.Format[h, startOfMicrocode, cnt, 1 !
      FSa.BadPage => 
          BEGIN
          WriteString["Warning: page "L];
          WriteLongNumber[p];
          WriteLine[" is bad (will be skipped). "L];
          RESUME;
          END;
      FSa.PassStarting => RESUME];
      WriteString["Fetching..."L];
      [] ← Retrieve[remoteFile: file, msg: msg, destination: [rawWrite[P]] !
        CredentialError => { PromptForCredentials[ftpError, message]; RESUME }];
      WriteLine["Done"L];
      END;
    ENDCASE => ReportError["Othello can't install microcode on that device."L];
  IF msg.length#0 THEN ReportError[msg];
  END;

FormatCheckDrive: PUBLIC PROC [
  bs: POINTER TO ARRAY[0..badTableSize) OF PhysicalVolume.PageNumber,
  op: FormatCheckOp]
  RETURNS [couldDo: BOOLEAN, badSpots: CARDINAL, h: PhysicalVolume.Handle] =
  BEGIN
  t: Device.Type;
  badSpotsThisPass: CARDINAL ← 0;
  passes: CARDINAL;
  NoteBad: PROC [p: PhysicalVolume.PageNumber] =
    BEGIN
    FOR i: CARDINAL IN [0..badSpots) DO
      IF p = bs[i] THEN RETURN;
      ENDLOOP;
    IF badSpotsThisPass=0 THEN WriteString["  Bad pages found:"L];
    IF (badSpotsThisPass MOD 6)=0 THEN NewLine[];
    WriteFixedWidthNumber[p, 11];
    IF badSpots IN [0..LENGTH[bs↑]) THEN bs[badSpots] ← p;
    badSpotsThisPass ← badSpotsThisPass + 1;
    badSpots ← badSpots+1;
    END;
  NotePassStarting: PROC [pass: CARDINAL, operation: FormatSA1000andSA4000.Operation] =
    BEGIN
    WriteString["\rPass "L];
    WriteFixedWidthNumber[pass, 3];
    WriteString[IF operation=write THEN ", writing."L ELSE ", reading."L];
    badSpotsThisPass ← 0;
    END;
  FormatConfirm: PROC = 
    BEGIN
    IF op=check THEN Confirm[once]
    ELSE -- op=format
      BEGIN
      p: PhysicalVolume.ID ← PhysicalVolume.nullID;
      Confirm[twice];
      DO
        p ← PhysicalVolume.GetNext[p];
        IF p=PhysicalVolume.nullID THEN EXIT;
        IF h = PhysicalVolume.GetAttributes[p].instance THEN
          {PhysicalVolume.Offline[p]; EXIT};
        ENDLOOP;
      END;
    END;

  h ← GetDriveFromUser[];
  badSpots ← 0;
  SELECT (t ← GetDriveType[h]) FROM
    DeviceTypes.sa1000, DeviceTypes.sa4000 =>
      BEGIN OPEN FSa: FormatSA1000andSA4000;
      pilotStart: PhysicalVolume.PageNumber = MinPilotPage[h];
      ucodeSize: LONG CARDINAL = (
        IF t=DeviceTypes.sa1000 THEN FSa.SA1000lastPageOfMicrocode+1
        ELSE FSa.SA4000lastPageOfMicrocode+1);
      proc: PROC[h: PhysicalVolume.Handle, firstPage: PhysicalVolume.PageNumber,
        count: LONG CARDINAL, passes: CARDINAL] = IF op=format THEN FSa.Format ELSE FSa.Scan;
      passes ← ReadShortNumber["Number of passes: "L, 1, 1000, (IF op=format THEN 10 ELSE 1)];
      FormatConfirm[];
      proc[h, 0, ucodeSize, passes !
        FSa.BadPage => {NoteBad[p]; RESUME};
        FSa.PassStarting => RESUME];
      proc[h, pilotStart, GetDriveSize[h]-pilotStart, passes !
        FSa.BadPage => {NoteBad[p]; RESUME};
        FSa.PassStarting => {NotePassStarting[pass, operation]; RESUME}];
      RETURN[TRUE, badSpots, h];
      END;
    DeviceTypes.sa800 =>
      BEGIN OPEN FormatSA800;
      result: PilotResult;
      status: FloppyChannel.Status;   -- currently for debug purposes
      FormatConfirm[];
      IF op=format THEN
        BEGIN
        density: Density = IF Yes["Double density? "L] THEN double ELSE single;
        name: STRING ← "PilotV"L;
        GetName["Track 0 Name"L, name];
        [status, result] ← FormatPilotDisk[
          h, name, density ! BadSector => {NoteBad[p]; RESUME}]
        END
      ELSE [status, result] ← ScanPilotDisk[h ! BadSector => {NoteBad[p]; RESUME}];
      SELECT result FROM
        goodDisk       => RETURN[TRUE, badSpots, h];
        cantSetContext => ReportError["Can't set context"L];
        badTrack0      => ReportError["Track 0 bad; disk unusable"L];
        badLabelArea   => ReportError["Label area bad; disk unusable"L];
        ENDCASE        => ReportError["Unknown floppy format error"L];
      END;
    ENDCASE;
  RETURN[FALSE, badSpots, h];
  END;
  
IdentifyInitialMicrocode: PUBLIC PROCEDURE [h: PhysicalVolume.Handle, s: STRING]
  RETURNS [microcodeInstalled: BOOLEAN ← FALSE, time: System.GreenwichMeanTime] =
  BEGIN
  SELECT GetDriveType[h] FROM
    DeviceTypes.sa1000, DeviceTypes.sa4000 =>
      [microcodeInstalled, time] ← FormatSA1000andSA4000.IdentifyInitialMicrocode[h, s];
    ENDCASE => NULL;
  END;

IndicateAltoness: PUBLIC PROC=
  BEGIN
  h: PhysicalVolume.Handle = GetDriveFromUser[];
  IF GetDriveType[h] # DeviceTypes.sa4000  THEN ReportError["Command is only for SA4000's."L]
  ELSE IndicateAltoness1[h];
  END;

IndicateAltoness1: PROC[h: PhysicalVolume.Handle]=
  BEGIN
  dn: CARDINAL = GetDriveNumber[h];
  sa4000Flavor[dn] ← 377B;
  IF ~Yes["Reserve space for alto volume? "L] THEN {sa4000Flavor[dn] ← 0; RETURN};
  sa4000Flavor[dn] ← ReadShortNumber[
    "Number of Model 44's:"L, 1,
    LAST[FormatSA1000andSA4000.SA4000Model44Count], 1];
  END;

MinPilotPage: PROC [h: PhysicalVolume.Handle] RETURNS[PhysicalVolume.PageNumber] =
  BEGIN OPEN FSa: FormatSA1000andSA4000;
  dn: CARDINAL = GetDriveNumber[h];
  SELECT GetDriveType[h] FROM
    DeviceTypes.sa1000 => RETURN[FSa.FirstSA1000PageForPilot];
    DeviceTypes.sa4000 =>
      DO
      IF sa4000Flavor[dn] IN  FSa.SA4000Model44Count THEN
        RETURN[FSa.SA4000FirstPageForPilot[sa4000Flavor[dn]]];
      IndicateAltoness1[h]
      ENDLOOP;
    ENDCASE => RETURN[0];
  END;

PartitionCmd: PUBLIC PROC = {ReportError["Command not applicable to Dolphin."L]};
END.


11-Jun-81 10:56:57  Taft  Created file using excerpts from VolumeInitImplA.mesa
 4-Jun-82  8:23:39  Taft  Handle CredentialError from Retrieve
21-Jun-82 18:03:57  Taft  Add IdentifyInitialMicrocode
September 8, 1982 10:51 am  Taft  Rename to OthelloDeviceImplD0DLion.mesa
September 10, 1982 11:34 am  Taft  Mods for new Cedar Login
May 22, 1983 4:36 pm  Taft  Modify FormatCheckDrive for multi-pass Format and Scan.