-- HelloDevImplDLion.Mesa -- Contains Hello commands specific to DLion disks -- Masinter 22 Aug 84 from OthelloDeviceImplD0DLion -- Glassman 17-Jul-81 16:43:40 -- Linda 10-Mar-82 16:17:10 -- Forrest 7-Jan-82 17:40:00 -- Johnsson 8-Nov-83 9:24:59 -- there is a seperate version at least for dorados. DIRECTORY Device, DeviceTypes, FormatPilotDisk, FormatPilotDiskExtras, OthelloDefs, OthelloOps, PhysicalVolume, String USING [EquivalentStrings], Volume; HelloDevImplDLion: PROGRAM IMPORTS FormatPilotDisk, OthelloDefs, OthelloOps, PhysicalVolume, String, Volume = BEGIN OPEN OthelloDefs, OthelloOps; commandProcessor: CommandProcessor ← [DiskCommands]; DiskCommands: PROC [index: CARDINAL] = { SELECT index FROM 0 => CheckDrive[]; 1 => CreateVolume[]; 2 => Format[]; -- => IndicateAltoness[]; 3 => FetchInitialMicrocode[]; ENDCASE => IndexTooLarge}; badTableSize: CARDINAL = 200; 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 being, 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]; << IndicateAltoness: PUBLIC PROC = { h: PhysicalVolume.Handle; t: Device.Type; MyNameIs[ myNameIs: "Reserve Alto Volume"L, myHelpIs: "Reserve space for simulated Alto-mode partitions"L]; IF (t ← GetDriveType[(h ← GetDriveFromUser[])]) # DeviceTypes.sa4000 AND t # DeviceTypes.sa4008 THEN AbortingCommand["Command is only for SA4000's."L] ELSE IndicateAltoness1[h]}; >> CheckDrive: PROC = BEGIN badSpots: CARDINAL; badSpotArray: ARRAY [0..badTableSize) OF PhysicalVolume.PageNumber; couldDo: BOOLEAN; h: PhysicalVolume.Handle; p: PhysicalVolume.ID ← PhysicalVolume.nullID; wasOnLine: BOOLEAN ← FALSE; badTableOverflow: BOOLEAN; MyNameIs[ myNameIs: "Check Drive"L, myHelpIs: "Scan drive for unreadable pages"L]; [couldDo, badSpots, h] ← FormatCheckDrive[@badSpotArray, check]; IF ~couldDo THEN AbortingCommand["Command can't be done for this device."L] ELSE IF badSpots = 0 THEN {WriteLine["No bad pages found."L]; RETURN} ELSE IF ~Wizard[] OR ~Yes["\rShall I record these pages in the bad table? "L] THEN RETURN; -- See if was on line/put on line DO p ← PhysicalVolume.GetNext[p]; IF p = PhysicalVolume.nullID THEN { p ← PhysicalVolume.AssertPilotVolume[h]; EXIT} ELSE IF h = PhysicalVolume.GetAttributes[p].instance THEN { wasOnLine ← TRUE; EXIT}; ENDLOOP; badTableOverflow ← FillBadTable[@badSpotArray, badSpots, p]; IF ~wasOnLine THEN PhysicalVolume.Offline[p]; IF badTableOverflow THEN AbortingCommand["Too many bad spots."L] ELSE WriteLine["Consider scavenging some volumes."L]; END; Format: PROC = BEGIN badSpots: CARDINAL; badSpotArray: ARRAY [0..badTableSize) OF PhysicalVolume.PageNumber; couldDo: BOOLEAN; h: PhysicalVolume.Handle; p: PhysicalVolume.ID; badTableOverflow: BOOLEAN; IF ~Wizard[] THEN RETURN; MyNameIs[myNameIs: "Format"L, myHelpIs: "Format a disk drive"L]; [couldDo, badSpots, h] ← FormatCheckDrive[@badSpotArray, format]; IF ~couldDo THEN AbortingCommand["Command can't be done for this device."L]; FOR i: CARDINAL IN [0..MIN[badSpots, LENGTH[badSpotArray]]) DO IF badSpotArray[i] = 0 THEN {WriteLine["Physical page zero bad"L]; RETURN} ENDLOOP; WriteLine["\rCreating Pilot volume named ""Empty"" to hold bad spot table"L]; p ← PhysicalVolume.CreatePhysicalVolume[h, "Empty"L]; badTableOverflow ← FillBadTable[@badSpotArray, badSpots, p]; PhysicalVolume.Offline[p]; IF badTableOverflow THEN AbortingCommand["Too many bad spots."L]; END; lvTable: ARRAY [0..10) OF RECORD [ size: Volume.PageCount, type: Volume.Type, name: LONG STRING] ← ALL[[0, normal, NIL]]; newPvName: LONG STRING ← NIL; CreateVolume: PROC = BEGIN h: PhysicalVolume.Handle; badTable: ARRAY [0..badTableSize) OF PhysicalVolume.PageNumber; bad: CARDINAL ← 0; broughtOnLine: BOOLEAN ← FALSE; driveSize: LONG CARDINAL; nSubVols: CARDINAL; lvID: Volume.ID; lvsize: Volume.PageCount; pvID: PhysicalVolume.ID ← PhysicalVolume.nullID; PagesRippedOff: PROC RETURNS [p: LONG CARDINAL] = INLINE { --we don't really lose physicalVolumeOverhead p ← MinPilotPage[h]; IF p # 0 THEN p ← p - physicalVolumeOverhead}; volumeFound: BOOLEAN ← TRUE; MyNameIs[ myNameIs: "Create Physical Volume"L, myHelpIs: "Format physical volume into logical volumes (old contents lost)"L]; h ← GetDriveFromUser[]; DO pvID ← PhysicalVolume.GetNext[pvID]; IF pvID = PhysicalVolume.nullID THEN { volumeFound ← TRUE; -- Yes["Shall I try to find an old bad page Table? "L]; IF volumeFound THEN { broughtOnLine ← TRUE; pvID ← PhysicalVolume.AssertPilotVolume[h ! PhysicalVolume.Error, PhysicalVolume.NeedsScavenging => { volumeFound ← broughtOnLine ← FALSE; CONTINUE}]}; EXIT}; IF h = PhysicalVolume.GetAttributes[pvID].instance THEN EXIT; ENDLOOP; IF volumeFound THEN { 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}; GetName["New physical volume name: "L, @newPvName]; nSubVols ← ReadShortNumber[ "Number of logical volumes: "L, 1, PhysicalVolume.maxSubvolumesOnPhysicalVolume, 3]; driveSize ← GetDriveSize[h] - (physicalVolumeOverhead + nSubVols*logicalVolumeOverhead + PagesRippedOff[]); driveSize ← driveSize - ReserveLastCylinderForDiag[h]; 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, @lvTable[i].name]; FOR j: CARDINAL IN [0..i) WHILE ~duplicate DO duplicate ← String.EquivalentStrings[ lvTable[i].name, lvTable[j].name] ENDLOOP; IF ~duplicate THEN EXIT; WriteLine["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, type]; ENDLOOP; IF broughtOnLine THEN PhysicalVolume.Offline[pvID]; Confirm[IF volumeFound THEN twice ELSE once]; PhysicalVolume.Offline[pvID ! ANY => CONTINUE]; pvID ← PhysicalVolume.CreatePhysicalVolume[h, newPvName]; FOR i: CARDINAL IN [0..bad) DO PhysicalVolume.MarkPageBad[pvID, badTable[i]] ENDLOOP; FOR i: CARDINAL IN [0..nSubVols) DO OPEN lvTable[i]; lvID ← Volume.Create[pvID, size, name, type, MinPilotPage[h]]; IF (lvsize ← Volume.GetAttributes[lvID].volumeSize) # size THEN { WriteString[name]; WriteString["'s size decreased (because of bad pages) to "L]; WriteLongNumber[lvsize]; NewLine[]}; ENDLOOP; END; FetchInitialMicrocode: PROC = { h: PhysicalVolume.Handle; InstallProc: PROC [getPage: PROC RETURNS [LONG POINTER]] = { WriteString["Formatting..."L]; PhysicalVolume.AssertNotAPilotVolume[h]; {ENABLE UNWIND => PhysicalVolume.FinishWithNonPilotVolume[h]; FormatPilotDisk.FormatBootMicrocodeArea[h: h, passes: 1, retries: 0 ! FormatPilotDisk.BadPage => { WriteString["Warning: page "L]; WriteLongNumber[p]; WriteLine[" is bad (will be skipped). "L]; RESUME}]; FormatPilotDisk.InstallBootMicrocode[h, getPage]}; -- ENABLE PhysicalVolume.FinishWithNonPilotVolume[h]}; MyNameIs[ myNameIs: "Initial Microcode Fetch"L, myHelpIs: "Fetch and install initial microcode"L]; h ← GetDriveFromUser[]; SELECT TRUE FROM LOOPHOLE[GetDriveType[h], CARDINAL] IN Device.PilotDisk => BEGIN wasOnline: BOOLEAN = ForceOffline[h]; OthelloDefs.FetchInitialMicrocode[InstallProc ! FormatPilotDisk.MicrocodeInstallFailure => SELECT m FROM emptyFile => AbortingCommand["That remote file is empty!"L]; firstPageBad => AbortingCommand["First microcode page of this disk is bad."L]; flakeyPageFound => { WriteLine["Intermittent page in microcode area."L]; RESUME}; microcodeTooBig => AbortingCommand["Microcode too large."L]; ENDCASE => AbortingCommand["Unknown Install microcode error."L]; FormatPilotDisk.CantInstallUCodeOnThisDevice => GOTO no]; IF wasOnline THEN [] ← PhysicalVolume.AssertPilotVolume[h]; WriteLine["Done"L] END; ENDCASE => GOTO no; EXITS no => AbortingCommand["microcode can't be installed on this disk"L]}; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- support Procs --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- this code depends upon Pilot's bad spot table being smaller than ours, -- and an error being raised when Pilot's table is full. FillBadTable: PROC [ badSpotArray: POINTER TO ARRAY [0..badTableSize) OF PhysicalVolume.PageNumber, badSpots: CARDINAL, p: PhysicalVolume.ID] RETURNS [badSpotTableFull: BOOLEAN ← FALSE] = { FOR i: CARDINAL IN [0..badSpots) DO PhysicalVolume.MarkPageBad[p, badSpotArray[i] ! PhysicalVolume.Error => IF error = badSpotTableFull THEN {badSpotTableFull ← TRUE; EXIT}] ENDLOOP}; ForceOffline: PROC [h: PhysicalVolume.Handle] RETURNS [wasOnline: BOOLEAN ← FALSE] = { p: PhysicalVolume.ID ← PhysicalVolume.nullID; DO p ← PhysicalVolume.GetNext[p]; IF p = PhysicalVolume.nullID THEN EXIT; IF h = PhysicalVolume.GetAttributes[p].instance THEN { wasOnline ← TRUE; PhysicalVolume.Offline[p]; EXIT}; ENDLOOP}; FormatCheckDrive: PUBLIC PROC [ bs: POINTER TO ARRAY [0..badTableSize) OF PhysicalVolume.PageNumber, op: {format, check}] RETURNS [couldDo: BOOLEAN, badSpots: CARDINAL, h: PhysicalVolume.Handle] = { -- leaves pv offline for op = format; restores previous state for op = check cbs: ARRAY [0..badTableSize) OF CARDINAL ← ALL[0]; tooManyMsg: BOOLEAN ← FALSE; passes: CARDINAL ← 1; NoteBad: PROC [p: PhysicalVolume.PageNumber] = { FOR i: CARDINAL IN [0..badSpots) DO IF bs[i] = p THEN {cbs[i] ← cbs[i] + 1; WriteBadSpot[p, cbs[i]]; RETURN} ENDLOOP; IF badSpots < LENGTH[bs↑] THEN { bs[badSpots] ← p; cbs[badSpots] ← cbs[badSpots] + 1; badSpots ← badSpots + 1; WriteBadSpot[p, 1]} ELSE { IF ~tooManyMsg THEN { WriteLine["Too many bad pages"L]; tooManyMsg ← TRUE; column ← 0}; WriteBadSpot[p, 1]}; CheckUserAbort[! UNWIND => FormatSummary[]]}; FormatSummary: PROC = { IF badSpots > 0 THEN { column ← 0; WriteLine["\rSummary of bad pages: badPage(countTimesBad)"L]; FOR i: CARDINAL IN [0..badSpots) DO WriteBadSpot[bs[i], cbs[i]] ENDLOOP; NewLine[]}}; column: CARDINAL ← 0; WriteBadSpot: PROC [p: PhysicalVolume.PageNumber, cnt: CARDINAL] = { WriteFixedWidthNumber[p, 8]; WriteChar['(]; WriteFixedWidthNumber[ cnt, SELECT passes FROM IN [0..9] => 1, IN [10..99] => 2, ENDCASE => 3]; WriteChar[')]; IF (column MOD 5) = 4 THEN {column ← 0; NewLine[]} ELSE column ← column+1}; badSpots ← 0; h ← GetDriveFromUser[]; SELECT TRUE FROM -- dam compiler won't coerce [CARDINAL] into CARDINAL LOOPHOLE[GetDriveType[h], CARDINAL] IN Device.PilotDisk => { OPEN FPD: FormatPilotDisk; pilotStart: PhysicalVolume.PageNumber = MinPilotPage[h]; retries: FormatPilotDisk.RetryLimit ← 0; -- number of retries on bad page cylSize: CARDINAL = CylinderSize[h]; IF op = format THEN { passes ← ReadShortNumber["Number of passes: "L, 1, 200, 10]; retries ← ReadShortNumber[ "Number of retries: "L, FPD.noRetries, FPD.retryLimit, FPD.noRetries]; Confirm[twice]; [] ← ForceOffline[h]; -- format zero'th cylinder separately PhysicalVolume.AssertNotAPilotVolume[h]; {ENABLE UNWIND => PhysicalVolume.FinishWithNonPilotVolume[h]; FPD.Format[h, 0, cylSize, passes, retries ! FPD.BadPage => {NoteBad[p]; RESUME}]; -- format rest of disk possibly allowing for alto-type volume -- or other device dependent dreck FPD.Format[h, pilotStart, GetDriveSize[h] - pilotStart, passes, retries ! FPD.BadPage => {NoteBad[p]; RESUME}]}; -- ENABLE PhysicalVolume.FinishWithNonPilotVolume[h]} ELSE { -- op=scan wasOnline: BOOLEAN; Confirm[once]; wasOnline ← ForceOffline[h]; -- scan zero'th cylinder seperatly FPD.Scan[h, 0, cylSize ! FPD.BadPage => {NoteBad[p]; RESUME}]; -- scan rest of disk possibly allowing for alto-type volume -- or other device dependent dreck FPD.Scan[h, pilotStart, GetDriveSize[h] - pilotStart ! FPD.BadPage => {NoteBad[p]; RESUME}]; IF wasOnline THEN [] ← PhysicalVolume.AssertPilotVolume[h]}; FormatSummary[]; RETURN[TRUE, badSpots, h]}; ENDCASE => RETURN[FALSE, badSpots, h]}; << IndicateAltoness1: PROC [h: PhysicalVolume.Handle] = { 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[FormatPilotDisk.SA4000Model44Count], 1]}; >> MinPilotPage: PROC [h: PhysicalVolume.Handle] RETURNS [PhysicalVolume.PageNumber] = { OPEN FPD: FormatPilotDisk; SELECT GetDriveType[h] FROM DeviceTypes.sa1000, DeviceTypes.sa1004 => RETURN[FPD.FirstSA1000PageForPilot]; DeviceTypes.q2000, DeviceTypes.q2010, DeviceTypes.q2020, DeviceTypes.q2030, DeviceTypes.q2040, DeviceTypes.q2080 => RETURN[FPD.FirstQ2000PageForPilot]; DeviceTypes.sa4000, DeviceTypes.sa4008 => DO dn: CARDINAL = GetDriveNumber[h]; IF sa4000Flavor[dn] IN FPD.SA4000Model44Count THEN RETURN[FPD.SA4000FirstPageForPilot[sa4000Flavor[dn]]]; -- IndicateAltoness1[h] ENDLOOP; DeviceTypes.t80 => RETURN[FPD.Firstt80PageForPilot]; DeviceTypes.t300 => RETURN[FPD.Firstt300PageForPilot]; ENDCASE => RETURN[0]}; UnknownCylSize: ERROR = CODE; CylinderSize: PROC [h: PhysicalVolume.Handle] RETURNS [cylSize: CARDINAL] = { OPEN FPD: FormatPilotDisk, FPDx: FormatPilotDiskExtras; SELECT GetDriveType[h] FROM DeviceTypes.sa1000, DeviceTypes.sa1004 => cylSize ← FPD.SA1004pagesPerCylinder; DeviceTypes.q2000 => cylSize ← FPD.Q2040pagesPerCylinder; DeviceTypes.q2010 => cylSize ← FPD.Q2010pagesPerCylinder; DeviceTypes.q2020 => cylSize ← FPD.Q2020pagesPerCylinder; DeviceTypes.q2030 => cylSize ← FPD.Q2030pagesPerCylinder; DeviceTypes.q2040 => cylSize ← FPD.Q2040pagesPerCylinder; DeviceTypes.q2080 => cylSize ← FPDx.Q2080pagesPerCylinder; DeviceTypes.sa4000, DeviceTypes.sa4008 => cylSize ← FPD.SA4008pagesPerCylinder; DeviceTypes.t80 => cylSize ← FPD.t80pagesPerCylinder; DeviceTypes.t300 => cylSize ← FPD.t300pagesPerCylinder; ENDCASE => ERROR UnknownCylSize}; ReserveLastCylinderForDiag: PROC [h: PhysicalVolume.Handle] RETURNS [ripOff: CARDINAL] = { ripOff ← CylinderSize[h ! UnknownCylSize => GOTO out]; IF Wizard[] AND ~Yes["Reserve last cylinder for diagnostics? "L] THEN ripOff ← 0; EXITS out => RETURN[0]}; RegisterCommandProc[@commandProcessor]; END..... 11-Jun-81 10:56:57 Taft Created file using excerpts from VolumeInitImplA.mesa 17-Jun-81 16:39:45 Glassman Action: Prompt for passes and retries for formatting disk, summary after all passes done 17-Jul-81 16:44:52 Glassman Action: Change name of file and print more summary information, OthelloDevice merged into OthelloDefs11-Jun-81 10:56:57 Taft Created file using excerpts from VolumeInitImplA.mesa 26-Aug-81 18:34:22 Forrest 8.0c build 14-Oct-81 20:17:53 Forrest othello reorg/add trident stuff/add diag cylinder stuff 13-Nov-81 16:24:41 Forrest 8.0e build 23-Nov-81 19:10:16 Forrest add stuff for t80, t300 10-Dec-81 16:15:15 Forrest add Quantum support; fix a couple of awful bugs in install ucode 7-Jan-82 17:39:36 Forrest Change for new FormatPilotDisk.CantInstallUCodeOnThisDevice 10-Mar-82 16:17:20 Linda Formating ==> formatting 22-Aug-84 13:06:35 Masinter remove Alto format command stuff