-- PromOpsImpl.mesa -- edited by: Masinter.pa on: 23-Aug-84 11:36:52 -- edited by: Curbow on: 20-Jun-84 16:43:16 DIRECTORY AccessFloppy USING [ Attributes, AttributesRecord, Close, Error, ErrorType, leaderLength, LookUp, maxDataSize, Open], Device USING [PilotDisk, nullType, Type], DeviceTypes USING [ cdc9730, q2000, q2010, q2020, q2030, q2040, q2080, sa1000, sa1004, sa4000, sa4008, sa800, t300, t80], Environment USING [PageCount], File USING [ Create, Delete, GetAttributes, File, MakePermanent, nullFile, PageCount, PageNumber, SetSize, Type, Unknown], Floppy USING [ CopyToPilotFile, DataError, Error, ErrorType, FileHandle, GetAttributes, nullFileID, nullVolumeHandle, PageCount, PageNumber, Read, VolumeHandle], FloppyChannel USING [Drive, Error, ErrorType, GetHandle, Handle, Nop], FormatPilotDisk, FormatPilotDiskExtras, Heap USING [systemZone], NSString USING [String, StringFromMesaString], OthelloOps USING [ BadSwitches, BootFileType, DecodeSwitches, DeleteTempFiles, GetDriveSize, GetPhysicalVolumeBootFile, GetSwitches, GetVolumeBootFile, MakeBootable, MakeUnbootable, SetDebugger, SetDebuggerSuccess, SetGetSwitchesSuccess, SetPhysicalVolumeBootFile, SetSwitches, SetVolumeBootFile, VoidPhysicalVolumeBootFile, VoidVolumeBootFile], OthelloDefs USING [LeaderPage, leaderPages, lpVersion, lpNoteLength], PhysicalVolume USING [ AssertNotAPilotVolume, AssertPilotVolume, CreatePhysicalVolume, Error, ErrorType, FinishWithNonPilotVolume, GetAttributes, GetContainingPhysicalVolume, GetHandle, GetHints, GetNext, GetNextBadPage, GetNextDrive, GetNextLogicalVolume, Handle, ID, InterpretHandle, NeedsScavenging, MarkPageBad, maxNameLength, maxSubvolumesOnPhysicalVolume, noProblems, nullBadPage, nullDeviceIndex, nullID, Offline, PageNumber, RepairType, Scavenge, ScavengerStatus], Process USING [MsecToTicks, Pause, SecondsToTicks], PromCommand USING [ AbortOption, CommandTable, CommandTableRecord, workingDriveIndex], PromIO USING [ ttyHandle, Filter, FormatError, HardError, InputError, ReadLine, ReadName, ReadNumber, ReadShortNumber, ReadYes, SetIOFilter, WriteChar, WriteCR, WriteFixedWidthNumber, WriteLine, WriteLongNumber, WriteString], PromScript USING [AllowStateToBeSaved, GetRootFile, SaveState], PromTime USING [RestoreTimeZone], Scavenger USING [Scavenge, Error, ErrorType], Space USING [Interval, Map, ScratchMap, Unmap], String USING [ AppendChar, AppendCharAndGrow, AppendLongNumber, AppendString, Equivalent, FreeString], System USING [defaultSwitches, PowerOff, Switches], TemporaryBooting USING [BootButton, BootFromVolume, InvalidParameters], TextInput USING [GetYesNo], Time USING [Append, Unpack], UserTerminal USING [Beep], Volume USING [ Close, Create, Erase, GetAttributes, GetLabelString, GetStatus, GetType, ID, InsufficientSpace, nullID, Open, PageCount, Type]; PromOpsImpl: PROGRAM IMPORTS AccessFloppy, File, Floppy, FloppyChannel, FormatPilotDisk, Heap, NSString, OthelloOps, PhysicalVolume, Process, PromCommand, PromIO, PromScript, PromTime, Scavenger, Space, String, System, TemporaryBooting, TextInput, Time, UserTerminal, Volume EXPORTS PromCommand SHARES File, FormatPilotDisk = BEGIN OPEN PromIO, OthelloOps; z: UNCOUNTED ZONE ← Heap.systemZone; -- Keep command table alphabetical commandTable: ARRAY [0..32) OF PromCommand.CommandTableRecord ← [ ["--", CodeComment], ["Boot", BootBoot], ["Check Drive", CheckDrive], [ "Close", CloseCmd], ["Comment", WriteComment], ["Confirm", Confirm], [ "Create Physical Volume", CreateVolume], ["Debug", ShowDebugInfo], [ "Delete Boot File", DeleteBootFile], [ "Delete Temporary Files", DeleteTempFilesUser], [ "Diagnostic Microcode Fetch", FetchDiagnosticMicrocode], ["Erase", Erase], [ "Fetch", FetchBoot], ["Format", Format], ["Germ Fetch", FetchGerm], [ "Initial Microcode Fetch", FetchInitialMicrocode], [ "Logical Volume Scavenge", LVScavenge], ["Make Page Bad", MakeBad], [ "Offline", Offline], ["Online", Online], ["Pause", Pause], [ "Physical Volume Scavenge", PVScavenge], [ "Pilot Microcode Fetch", FetchPilotMicrocode], ["Power Off", PowerOff], [ "Quit", Quit], ["Request Floppy", RequestFloppy], [ "Root Fetch", FetchRootFile], ["Save", Save], [ "Set Boot File Default Switches", SetBootFileSwitches], [ "Set Debugger Pointers", SetDebuggerUser], [ "Set Physical Boot Files", SetPvBoot], [ "Specify Default Volume Sizes", SpecifyDefaultVolumeSizes]]; logicalVolumeTypeString: ARRAY Volume.Type OF STRING ← [ "normal", "debugger", "debuggerDebugger", "nonPilot"]; DefaultLVSizeData: TYPE = RECORD [ volumeName: StringBody ← [maxlength: maxNameLength, length: 0, text:], txt: PACKED ARRAY [0..maxNameLength) OF CHARACTER ← NULL, sizes: ARRAY [0..nSupportedDiskTypes) OF TypeAndSize]; DiskPageNumber: TYPE = PhysicalVolume.PageNumber; Handle: TYPE = PhysicalVolume.Handle; TypeAndSize: TYPE = RECORD [ type: Device.Type ← Device.nullType, size: LONG CARDINAL]; bufferPtr: LONG POINTER = Space.ScratchMap[1]; badTableSize: CARDINAL = 200; carryVolumeOpen: BOOLEAN ← FALSE; defaultLVSizeData: DefaultLVSizeData; lastCarryName: STRING ← [10 + maxNameLength]; lastDriveName: STRING ← [10]; lastLvName: STRING ← [10 + maxNameLength]; lastPvName: STRING ← [10 + maxNameLength]; -- switches: LONG STRING ← NIL; logicalVolumeOverhead: CARDINAL = 1; maxNameLength: CARDINAL = PhysicalVolume.maxNameLength; minLogicalVolumeSize: CARDINAL = 50; -- fudge + 1+1+6; nSupportedDiskTypes: CARDINAL = 5; fileName: STRING ← [100]; buffer: ARRAY [1..AccessFloppy.maxDataSize + SIZE[AccessFloppy.AttributesRecord]] OF WORD ← ALL[0]; attributes: AccessFloppy.Attributes ← LOOPHOLE[LONG[@buffer]]; -- 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; 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]; VolumeNotFound: ERROR = CODE; BadSeal: ERROR = CODE; WrongVersion: ERROR = CODE; WrongIdOnMarkerPage: ERROR = CODE; MarkerPageReadFailed: ERROR = CODE; MarkerPageWriteFailed: ERROR = CODE; GetCommandTable: PUBLIC PROC RETURNS [PromCommand.CommandTable] = { RETURN[DESCRIPTOR[commandTable]]}; ---- ---- ---- ---- ---- ---- ---- ---- ---- -- individual commands. BootBoot: PROC = BEGIN lvID: Volume.ID; ts: System.Switches; switches: LONG STRING ← [100]; lvID ← ReadLvID[].lvID; GetSetBootFileSwitches[ get, lvID ! File.Unknown => InputError["(can't get default switches)"L]]; ReadLine["switches: "L, switches]; ts ← DecodeSwitches[switches ! BadSwitches => {InputError["Bad Switches"L]}]; PromScript.SaveState[]; TemporaryBooting.BootFromVolume[lvID, ts]; END; -- of BootBoot CheckDrive: PROC = BEGIN << Othello version found in OthelloDeviceImplD0DLion.mesa >> badSpots: CARDINAL; badSpotArray: ARRAY [0..badTableSize) OF DiskPageNumber; couldDo: BOOLEAN; h: Handle; p: PhysicalVolume.ID ← PhysicalVolume.nullID; wasOnLine: BOOLEAN ← FALSE; [couldDo, badSpots, h] ← FormatCheckDrive[@badSpotArray, check]; IF ~couldDo THEN {InputError["Can't check this device."L]; RETURN} ELSE IF badSpots = 0 THEN {WriteLine["No bad pages found."L]; 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; -- this code depends upon Pilot's bad spot table being smaller than ours, -- and an error being raised when Pilot's table is full. FOR i: CARDINAL IN [0..badSpots) DO PhysicalVolume.MarkPageBad[ p, badSpotArray[i] ! PhysicalVolume.Error => IF error = badSpotTableFull THEN { HardError["Too many bad spots on the System Disk. Cannot proceed."L]; EXIT}]; ENDLOOP; IF ~wasOnLine THEN PhysicalVolume.Offline[p]; WriteLine["Consider scavenging some volumes."L]; --should scavenger be called automatically? What do we tell the user? END; -- of CheckDrive CloseCmd: PROC = BEGIN AccessFloppy.Close[ ! AccessFloppy.Error, Floppy.Error => CONTINUE]; WriteLine["closed"L]; END; CodeComment: PROC = BEGIN PromIO.ReadLine[NIL, NIL]; END; Confirm: PROC = BEGIN prompt: LONG STRING ← [80]; PromIO.ReadLine[NIL, prompt]; InternalConfirm[prompt]; END; -- of Confirm InternalConfirm: PROC [prompt: LONG STRING] = BEGIN defaultPrompt: LONG STRING = "OK to proceed? (Y/N)"L; IF prompt = NIL THEN prompt ← defaultPrompt; IF TextInput.GetYesNo[PromIO.ttyHandle, NSString.StringFromMesaString[prompt]] # yes THEN ERROR PromCommand.AbortOption[]; END; -- of InternalConfirm CreateVolume: PROC = BEGIN << Othello version is found in OthelloDeviceImplD0DLion.mesa >> h: Handle = ReadDrive[]; badTable: ARRAY [0..badTableSize) OF PhysicalVolume.PageNumber; bad: CARDINAL ← 0; broughtOnLine: BOOLEAN ← FALSE; driveSize: LONG CARDINAL; driveType: Device.Type; nSubVols: CARDINAL; lvID: Volume.ID; lvsize: Volume.PageCount; lvTable: ARRAY [0..10) OF LvRecord ← ALL[[]]; pvID: PhysicalVolume.ID ← PhysicalVolume.nullID; pvName: STRING ← [maxNameLength]; retryCount: CARDINAL ← 0; 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 ← ReadYes["Shall I try to find an old bad page Table? "L]; << If the physical volume needs Scavenging, just ignore it. We will be creating a new volume on top, and so there's no reason to waste time scavenging it. This is what Othello does. >> IF volumeFound THEN BEGIN broughtOnLine ← TRUE; pvID ← PhysicalVolume.AssertPilotVolume[ h ! PhysicalVolume.Error, PhysicalVolume.NeedsScavenging => { 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; ReadName["New physical volume name: "L, pvName]; nSubVols ← ReadShortNumber[ "Number of logical volumes: "L, 1, PhysicalVolume.maxSubvolumesOnPhysicalVolume, 3]; driveType ← SELECT PhysicalVolume.InterpretHandle[h].type FROM DeviceTypes.sa1004, DeviceTypes.sa1000 => DeviceTypes.sa1004, DeviceTypes.sa4008, DeviceTypes.sa4000 => DeviceTypes.sa4008, DeviceTypes.q2040, DeviceTypes.q2000 => DeviceTypes.q2040, ENDCASE => PhysicalVolume.InterpretHandle[h].type; driveSize ← GetDriveSize[h] - (physicalVolumeOverhead + nSubVols * logicalVolumeOverhead + PagesRippedOff[] + LastCylinder[h]); FOR i: CARDINAL IN [0..nSubVols) DO OPEN lvTable[i]; defaultVolumeSize: LONG CARDINAL ← 0; WriteString["Logical volume "L]; WriteLongNumber[LONG[i]]; WriteCR[]; DO duplicate: BOOLEAN ← FALSE; ReadName[" Name: "L, @name]; FOR j: CARDINAL IN [0..i) WHILE ~duplicate DO duplicate ← String.Equivalent[@name, @lvTable[j].name]; ENDLOOP; IF ~duplicate THEN EXIT; InputError["Name is already in use; please choose another."L]; ENDLOOP; --determine default size for this volume. defaultVolumeSize ← driveSize / (nSubVols - i); IF String.Equivalent[@name, @defaultLVSizeData.volumeName] THEN FOR i: CARDINAL IN [0..nSupportedDiskTypes) DO IF driveType = defaultLVSizeData.sizes[i].type THEN defaultVolumeSize ← defaultLVSizeData.sizes[i].size; ENDLOOP; size ← ReadNumber[ " Pages: "L, minLogicalVolumeSize, driveSize - ((nSubVols - (i + 1)) * minLogicalVolumeSize), defaultVolumeSize]; driveSize ← driveSize - size; type ← ReadLvType[" Type: "L]; ENDLOOP; IF broughtOnLine THEN PhysicalVolume.Offline[pvID]; 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 ← Volume.Create[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]; WriteCR[]; END; ENDLOOP; PromTime.RestoreTimeZone[pvID]; END; -- of CreateVolume DeleteBootFile: PROC = BEGIN lvID: Volume.ID = ReadLvID[].lvID; pvID: PhysicalVolume.ID = PhysicalVolume.GetContainingPhysicalVolume[lvID]; FOR t: BootFileType IN [hardMicrocode..pilot] DO bootFile: File.File; [bootFile] ← GetVolumeBootFile[lvID, t]; IF bootFile = File.nullFile THEN LOOP; Volume.Open[lvID]; BEGIN ENABLE File.Unknown => CONTINUE; File.Delete[bootFile]; END; VoidVolumeBootFile[lvID, t]; IF GetPhysicalVolumeBootFile[pvID, t].file = bootFile THEN VoidPhysicalVolumeBootFile[pvID, t]; Volume.Close[lvID]; ENDLOOP; END; -- of DeleteBootFile DeleteTempFilesUser: PROC = {DeleteTempFiles[ReadLvID[].lvID]}; Erase: PROC = BEGIN lvID: Volume.ID ← ReadLvID[].lvID; Volume.Close[lvID]; WriteString["Erase...."L]; Volume.Erase[lvID]; WriteLine["complete"L]; END; -- of Erase FetchBoot: PROC = {Fetch[pilot, "Boot file name: "L]}; FetchGerm: PROC = {Fetch[germ, "Germ file name: "L]}; FetchPilotMicrocode: PROC = { Fetch[softMicrocode, "Pilot microcode file name: "L]}; FetchDiagnosticMicrocode: PROC = { Fetch[hardMicrocode, "Diagnostic microcode file name: "L]}; Fetch: PROC [type: OthelloOps.BootFileType, prompt: STRING] = BEGIN attr: AccessFloppy.Attributes ← attributes; created: BOOLEAN; bootFile: File.File; fileHandle: Floppy.FileHandle; lvID: Volume.ID; firstPage: File.PageNumber; FetchInternal: PROC = BEGIN fileHandle ← AccessFloppy.LookUp[ NSString.StringFromMesaString[fileName], attr]; -- IF a boot file exists on this logical volume already, and -- not of the same TYPE then delete it. -- Also remove it as the physical volume boot file as necessary. IF bootFile # File.nullFile THEN BEGIN ENABLE File.Unknown => {bootFile ← File.nullFile; CONTINUE}; oldType: File.Type; [oldType] ← File.GetAttributes[bootFile]; IF oldType # attr.type THEN BEGIN pvID: PhysicalVolume.ID = PhysicalVolume.GetContainingPhysicalVolume[ lvID]; File.Delete[bootFile]; OthelloOps.VoidVolumeBootFile[lvID, type]; IF OthelloOps.GetPhysicalVolumeBootFile[pvID, type].file = bootFile THEN OthelloOps.VoidPhysicalVolumeBootFile[pvID, type]; bootFile ← File.nullFile; END END; -- Retrieve the specified floppy file -- NOTE: Allow OthelloDefs.leaderPages at front of file to contain -- info about this file, i.e. filename, dates, size, etc. IF (created ← (bootFile = File.nullFile)) THEN bootFile ← File.Create[ lvID, attr.totalSize + OthelloDefs.leaderPages, attr.type] ELSE BEGIN OthelloOps.MakeUnbootable[ bootFile, type, firstPage ! TemporaryBooting.InvalidParameters => { PromIO.WriteLine["Warning: trouble making unbootable."L]; CONTINUE}]; File.SetSize[ bootFile, attr.totalSize + OthelloDefs.leaderPages ! Volume.InsufficientSpace => { PromIO.WriteLine["Volume Full"L]; ERROR PromCommand.AbortOption[]}]; END; PromIO.WriteString["Fetching..."L]; Floppy.CopyToPilotFile[ floppyFile: fileHandle, pilotFile: bootFile, firstFloppyPage: AccessFloppy.leaderLength, firstPilotPage: attr.offset + OthelloDefs.leaderPages, count: attr.size]; END; --of FetchInternal lvID ← ReadLvID[].lvID; Volume.Open[lvID]; PromIO.ReadName[prompt, fileName]; [bootFile, firstPage] ← OthelloOps.GetVolumeBootFile[lvID, type]; fileHandle ← [Floppy.nullVolumeHandle, Floppy.nullFileID]; FetchInternal[ ! AccessFloppy.Error => { SELECT type FROM volumeNotOpen => PromIO.InputError["Floppy not open."L]; fileNotFound => PromIO.InputError["Floppy file not found."L]; ENDCASE => PromIO.InputError["AccessFloppy Error"L]}; Floppy.Error => IF fileHandle.file = Floppy.nullFileID THEN { SELECT error FROM fileNotFound => PromIO.InputError["Floppy file not found."L]; ENDCASE => PromIO.InputError["Floppy Lookup Error"]; GO TO Return} ELSE SELECT error FROM fileNotFound, endOfFile => {GetNewFloppy[fileHandle.volume]; RETRY}; ENDCASE; Floppy.DataError => {GetNewFloppy[fileHandle.volume]; RETRY}]; PromIO.WriteString["Installing..."L]; OthelloOps.SetVolumeBootFile[bootFile, type, OthelloDefs.leaderPages]; File.MakePermanent[bootFile]; IF attr.offset + attr.size = attr.totalSize THEN BEGIN << at end of multiple piece file, or only single piece file>> lp: LONG POINTER TO OthelloDefs.LeaderPage ← Space.Map[ [bootFile, 0, OthelloDefs.leaderPages]].pointer; note: LONG STRING ← [OthelloDefs.lpNoteLength]; note.length ← 0; -- just to be sure. String.AppendString[to: note, from: fileName]; String.AppendString[to: note, from: "("L]; Time.Append[ s: note, unpacked: Time.Unpack[time: attr.createDate], zone: TRUE]; String.AppendString[to: note, from: ")"L]; lp.version ← OthelloDefs.lpVersion; lp.length ← MIN[note.length, OthelloDefs.lpNoteLength]; FOR i: CARDINAL IN [0..lp.length) DO lp.note[i] ← note[i]; ENDLOOP; [] ← Space.Unmap[lp]; OthelloOps.MakeBootable[ bootFile, type, OthelloDefs.leaderPages ! TemporaryBooting.InvalidParameters => BEGIN Volume.Close[lvID]; --what happens here if we are fetching the bootfile in pieces? PromIO.InputError[ IF type # pilot THEN "Can't make file bootable."L ELSE "Warning: File not complete."L]; GOTO Return; END]; END; PromIO.WriteLine["done"L]; IF type IN [hardMicrocode..germ] AND PromIO.ReadYes["Shall I also use this for the Physical Volume? "L] THEN OthelloOps.SetPhysicalVolumeBootFile[ bootFile, type, OthelloDefs.leaderPages]; Volume.Close[lvID]; EXITS Return => NULL; END; -- of Fetch FetchInitialMicrocode: PROC = BEGIN OPEN DeviceTypes; << Othello version found in OthelloDeviceImplD0DLion.mesa This version very different from Othello one >> h: Handle = ReadDrive[]; t: Device.Type = GetDriveType[h]; mapped: BOOLEAN ← FALSE; attr: AccessFloppy.Attributes ← attributes; fileHandle: Floppy.FileHandle; -- Read file name and verify that it exists on Floppy ReadName["File name: "L, fileName]; fileHandle ← AccessFloppy.LookUp[ NSString.StringFromMesaString[fileName], attr ! AccessFloppy.Error => { SELECT type FROM volumeNotOpen => PromIO.InputError["Floppy not open."L]; fileNotFound => PromIO.InputError["Floppy file not found."L]; ENDCASE => PromIO.InputError["AccessFloppy Error"L]; GO TO Return}; Floppy.Error => { SELECT error FROM fileNotFound => InputError["Floppy file not found."L]; ENDCASE => InputError["Floppy Lookup Error"]; GO TO Return}]; SELECT t FROM q2000, q2040, q2080, sa1000, sa1004, sa4000, sa4008, t80, t300 => BEGIN OPEN FSa: FormatPilotDisk; currentPage: File.PageNumber ← AccessFloppy.leaderLength; GetPage: PROC RETURNS [LONG POINTER] = BEGIN IF currentPage > attr.size THEN RETURN[NIL]; Floppy.Read[fileHandle, currentPage, 1, bufferPtr]; currentPage ← currentPage + 1; RETURN[bufferPtr]; END; -- end of GetPage wasOnline: BOOLEAN = ForceOffline[h]; PhysicalVolume.AssertNotAPilotVolume[h]; << FormatBootMicrocodeArea may raise the NotAPilotDisk ERROR. >> { ENABLE UNWIND => PhysicalVolume.FinishWithNonPilotVolume[h]; FSa.FormatBootMicrocodeArea[ h: h, passes: 1, retries: 0 ! FSa.NotAPilotDisk => GOTO BadDevice; FSa.BadPage => BEGIN WriteString["Warning: page "L]; WriteLongNumber[p]; WriteLine[" is bad (will be skipped). "L]; RESUME ; END]; WriteString["Fetching..."L]; FSa.InstallBootMicrocode[h, GetPage]} << end ENABLE UNWIND>> ; PhysicalVolume.FinishWithNonPilotVolume[h]; IF wasOnline THEN [] ← PhysicalVolume.AssertPilotVolume[h]; WriteLine["Done"L]; END; ENDCASE => GOTO BadDevice; EXITS BadDevice => InputError["Cannot install microcode on that device."L]; Return => NULL; END; -- of FetchInitialMicrocode FetchRootFile: PROC = BEGIN attr: AccessFloppy.Attributes ← attributes; fileHandle: Floppy.FileHandle; rootFile: File.File ← File.nullFile; rootFileVolumeID: Volume.ID; rootFileVolumeID ← ReadLvID[].lvID; ReadName["Rootfile Name: ", fileName]; fileHandle ← AccessFloppy.LookUp[ NSString.StringFromMesaString[fileName], attr ! AccessFloppy.Error => { SELECT type FROM volumeNotOpen => PromIO.InputError["Floppy not open."L]; fileNotFound => PromIO.InputError["Floppy file not found."L]; ENDCASE => PromIO.InputError["AccessFloppy Error"L]; GO TO Return}; Floppy.Error => { SELECT error FROM fileNotFound => InputError["Floppy file not found."L]; ENDCASE => InputError["Floppy Lookup Error"]; GO TO Return}]; Volume.Open[rootFileVolumeID]; rootFile ← PromScript.GetRootFile[rootFileVolumeID, attr.type, attr.totalSize]; Floppy.CopyToPilotFile[ floppyFile: fileHandle, pilotFile: rootFile, firstFloppyPage: AccessFloppy.leaderLength, firstPilotPage: attr.offset, count: attr.size]; Volume.Close[rootFileVolumeID]; EXITS Return => NULL; END; -- of FetchRootFile Format: PROC = BEGIN << Othello version found in OthelloDeviceImplD0DLion.mesa >> badSpots: CARDINAL; badSpotArray: ARRAY [0..badTableSize) OF DiskPageNumber; couldDo: BOOLEAN; h: Handle; p: PhysicalVolume.ID; [couldDo, badSpots, h] ← FormatCheckDrive[@badSpotArray, format]; IF ~couldDo THEN {InputError["Cannot format this device."L]; RETURN}; FOR i: CARDINAL IN [0..MIN[badSpots, LENGTH[badSpotArray]]) DO IF badSpotArray[i] = 0 THEN { HardError["Critical System Disk pages bad. Cannot proceed"L]; RETURN}; ENDLOOP; WriteCR[]; WriteLine["Creating Pilot volume named ""Empty"" to hold bad spot table"L]; p ← PhysicalVolume.CreatePhysicalVolume[h, "Empty"L]; FOR i: CARDINAL IN [0..badSpots) DO PhysicalVolume.MarkPageBad[ p, badSpotArray[i] ! PhysicalVolume.Error => IF error = badSpotTableFull THEN GOTO fullBadSpotTable]; ENDLOOP; PromTime.RestoreTimeZone[p]; PhysicalVolume.Offline[p]; EXITS fullBadSpotTable => HardError["Too many bad spots on the disk. Cannot proceed."L]; END; -- of Format 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]}}; -- end of NoteBad 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; WriteCR[]}}; 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; WriteCR[]} ELSE column ← column + 1}; badSpots ← 0; h ← ReadDrive[]; 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 ← CARDINAL[ReadNumber["Number of passes: "L, 1, 200, 10]]; retries ← CARDINAL[ ReadNumber[ "Number of retries: "L, FPD.noRetries, FPD.retryLimit, FPD.noRetries]]; [] ← 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; 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]}; -- end of FormatCheckDrive 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}; GetNewFloppy: PROC [floppyHandle: Floppy.VolumeHandle] = BEGIN desiredFloppyName: STRING ← [maxNameLength]; [] ← Floppy.GetAttributes[floppyHandle, desiredFloppyName]; AccessFloppy.Close[ ! AccessFloppy.Error, Floppy.Error => CONTINUE]; UserTerminal.Beep[]; WriteCR[always]; WriteLine["Trouble reading this Floppy"L, always]; WriteString["Insert another Floppy Disk labeled """L, always]; WriteString[desiredFloppyName, always]; WriteLine[""" in Floppy Disk Drive."L, always]; UNTIL TextInput.GetYesNo[ PromIO.ttyHandle, NSString.StringFromMesaString[ "Indicate when Floppy Disk is ready"L]] = yes DO ENDLOOP; RequestFloppyInternal[desiredFloppyName]; END; LVScavenge: PROC = BEGIN lvID: Volume.ID ← ReadLvID[].lvID; scavIfInconsistent: BOOLEAN ← ReadYes["Only if inconsistent? "L]; Volume.Close[lvID ! ANY => CONTINUE]; IF scavIfInconsistent AND Volume.GetStatus[lvID] # closedAndInconsistent THEN RETURN; WriteString["Scavenging...."L]; [] ← Scavenger.Scavenge[ lvID, lvID, safeRepair, FALSE ! Scavenger.Error => SELECT error FROM needsConversion => { WriteLine[ "Needs conversion to current format. Conversion may not be reversible."L]; InternalConfirm[NIL]; [] ← Scavenger.Scavenge[lvID, lvID, safeRepair, TRUE]}; needsRiskyRepair => { WriteLine["Needs risky repair. Results are not guarenteed."L]; InternalConfirm[NIL]; [] ← Scavenger.Scavenge[lvID, lvID, riskyRepair, FALSE]}; cannotWriteLog => WriteLine["cannotWriteLog"L]; noSuchPage => WriteLine["noSuchPage"L]; orphanNotFound => WriteLine["orphanNotFound"L]; volumeOpen => WriteLine["volumeOpen"L]; diskHardwareError => WriteLine["diskHardwareError"L]; diskNotReady => WriteLine["diskNotReady"L]; ENDCASE => WriteLine["UNKNOWN ERROR"L]; ]; WriteLine["complete"L]; END; -- of LVScavenge MakeBad: PROC = BEGIN handle: PhysicalVolume.Handle; id: PhysicalVolume.ID; page: PhysicalVolume.PageNumber; [id, handle] ← ReadPvID[]; page ← ReadNumber["Decimal Page Number: "L, 0, GetDriveSize[handle] - 1]; PhysicalVolume.MarkPageBad[id, page]; --consider scavenging some volumes END; -- of MakeBad Offline: PROC = BEGIN id: PhysicalVolume.ID; handle: Handle; [id, handle] ← ReadPvID[]; PhysicalVolume.Offline[id]; END; -- of Offline Online: PROC = BEGIN handle: Handle = ReadDrive[]; [] ← PhysicalVolume.AssertPilotVolume[ handle ! PhysicalVolume.Error => SELECT error FROM alreadyAsserted => CONTINUE; diskReadError => {FormatError["not Pilot Volume"L]; CONTINUE}; ENDCASE; ]; END; -- of Online Pause: PROC = BEGIN Process.Pause[ Process.SecondsToTicks[ReadShortNumber["Seconds: "L, 1, 120, 5]]]; END; -- Pause PVScavenge: PROC = BEGIN OPEN PV: PhysicalVolume; s: PV.ScavengerStatus; h: PhysicalVolume.Handle; verbose: PromIO.Filter; repair: PV.RepairType; p: PhysicalVolume.ID ← PhysicalVolume.nullID; h ← ReadDrive[]; verbose ← IF ReadYes["Verbose? "L] THEN always ELSE debug; repair ← IF ~ReadYes["Repair? "L] THEN checkOnly ELSE IF ReadYes["Risky repair? "L] THEN riskyRepair ELSE safeRepair; DO IF (p ← PhysicalVolume.GetNext[p]) = PhysicalVolume.nullID THEN EXIT; IF h = PhysicalVolume.GetAttributes[p].instance THEN { PhysicalVolume.Offline[p]; EXIT}; ENDLOOP; WriteString["Scavenging...."L]; s ← PV.Scavenge[h, repair, TRUE]; <<okayToConvert>> WriteLine["Complete"L]; IF s = PV.noProblems THEN {WriteLine["No problems detected"L]; RETURN}; SELECT s.internalStructures FROM damaged => WriteLine["Critical disk data structures appear to be damaged"L, always]; repaired => WriteLine["Critical disk data structures repaired"L, always]; ENDCASE; SELECT s.badPageList FROM damaged => WriteLine["Bad disk page list appears to be damaged"L, always]; lost => WriteLine["Bad disk page list was lost"L, always]; ENDCASE; IF s.germ = damaged OR s.softMicrocode = damaged OR s.hardMicrocode = damaged OR s.bootFile = damaged THEN WriteLine["Installed software appears to be damaged"L, verbose] ELSE IF s.germ = lost OR s.softMicrocode = lost OR s.hardMicrocode = lost OR s.bootFile = lost THEN WriteLine["Installed software was lost"L, verbose]; IF s.internalStructures = damaged OR s.badPageList # okay THEN { WriteLine["Cannot Proceed"L, always]; PromCommand.AbortOption[]; }; PromTime.RestoreTimeZone[]; END; -- of PVScavenge PowerOff: PROC = {CloseCmd[]; System.PowerOff[]}; Quit: PROC = BEGIN CloseCmd[]; PromScript.SaveState[]; TemporaryBooting.BootButton[]; --BootFromPhysicalVolume? END; -- of Quit RequestFloppy: PROC = BEGIN desiredFloppyName: STRING ← [maxNameLength]; ReadLine["Label: "L, desiredFloppyName]; RequestFloppyInternal[desiredFloppyName]; END; -- of RequestFloppy RequestFloppyInternal: PROC [desiredFloppyName: STRING] = BEGIN actualFloppyName: STRING ← [maxNameLength]; floppyHandle: Floppy.VolumeHandle; AwaitFloppyChange: PROC = BEGIN OPEN FloppyChannel; --if the floppy is ready, wait until it goes notReady first UNTIL Nop[ GetHandle[0] ! Error => IF type = invalidHandle THEN RETRY].notReady DO Process.Pause[Process.MsecToTicks[3000]]; ENDLOOP; UNTIL ~Nop[ FloppyChannel.GetHandle[0] ! Error => IF type = invalidHandle THEN RETRY].notReady DO Process.Pause[Process.MsecToTicks[3000]]; ENDLOOP; END; -- of AwaitFloppyChange CheckFloppy: PROC = BEGIN floppyHandle ← AccessFloppy.Open[]; [] ← Floppy.GetAttributes[floppyHandle, actualFloppyName]; RestoreBlanks[actualFloppyName]; IF NOT String.Equivalent[desiredFloppyName, actualFloppyName] THEN BEGIN AccessFloppy.Close[ ! AccessFloppy.Error => CONTINUE]; ERROR Floppy.Error[volumeNotOpen]; END; END; -- of CheckFloppy RestoreBlanks[desiredFloppyName]; CheckFloppy[ ! Floppy.Error => BEGIN SELECT error FROM invalidFormat => WriteLine["This Floppy Disk has invalid format."L, always]; needsScavenging => WriteLine["This Floppy Disk is not well formed."L, always]; noSuchDrive => WriteLine["No Floppy Disk drive is known."L, always]; notReady => WriteLine["Floppy Disk is not placed in drive properly."L, always]; volumeNotOpen => BEGIN WriteString["This Floppy Disk is labeled """L, always]; WriteString[actualFloppyName, always]; WriteLine[""".", always]; END; ENDCASE => REJECT; UserTerminal.Beep[]; WriteString["Insert Floppy Disk labeled """L, always]; WriteString[desiredFloppyName, always]; WriteLine[""" in Floppy Disk Drive."L, always]; AwaitFloppyChange[]; RETRY; END]; END; -- of RequestFloppyInternal RestoreBlanks: PROC [s: STRING] = --Replace arrows with blanks. --Arrows are there to make the name one word. BEGIN FOR i: CARDINAL IN [0..s.length) DO IF s[i] = '← THEN s[i] ← ' ; ENDLOOP; END; -- of RestoreBlanks Save: PROC = {PromScript.AllowStateToBeSaved[ReadLvID[].lvID]; }; SetBootFileSwitches: PROC = BEGIN ts: System.Switches; lvID: Volume.ID; switches: STRING ← [100]; lvID ← ReadLvID[].lvID; GetSetBootFileSwitches[get, lvID]; -- volume.needsScav (caught higher up) DO ReadName["switches: "L, switches]; ts ← DecodeSwitches[switches ! BadSwitches => {InputError["bad switches"L]}]; EXIT; ENDLOOP; GetSetBootFileSwitches[set, lvID, ts]; END; SetDebuggerUser: PROC = BEGIN bootFile: File.File; firstPage: File.PageNumber; lvID: Volume.ID = ReadLvID["for debuggee Logical Volume: "L].lvID; dLvID: Volume.ID; dPvID: PhysicalVolume.ID; dH: Handle; [bootFile, firstPage] ← GetVolumeBootFile[lvID, pilot]; IF bootFile = File.nullFile THEN {InputError["No boot file found."]; RETURN}; [dPvID, dLvID, dH] ← ReadLvID["for debugger Logical Volume: "L]; Volume.Open[lvID]; BEGIN ENABLE UNWIND => Volume.Close[lvID]; SELECT (SetDebugger[ debuggeeFile: bootFile, debuggeeFirstPage: firstPage, debugger: dLvID, debuggerType: GetDriveType[dH], debuggerOrdinal: GetDriveNumber[dH]]) FROM success => NULL; nullBootFile, cantWriteBootFile, notInitialBootFile => InputError["Boot file broken."L]; cantFindStartListHeader, startListHeaderHasBadVersion => InputError["Boot file header broken."L]; noDebugger => InputError["No debugger installed."L]; ENDCASE; END; Volume.Close[lvID]; END; SetPvBoot: PROC = BEGIN lvID: Volume.ID = ReadLvID["Logical Volume Name: "L].lvID; pvID: PhysicalVolume.ID = PhysicalVolume.GetContainingPhysicalVolume[lvID]; set: ARRAY BootFileType OF BOOLEAN ← ALL[FALSE]; found, changed: BOOLEAN ← FALSE; Smash: PROC [s: STRING, t: BootFileType] = BEGIN IF GetVolumeBootFile[lvID, t].file = File.nullFile THEN RETURN; found ← TRUE; WriteString["Set physical volume "L]; WriteString[s]; IF (set[t] ← ReadYes[" from this logical volume? "L]) THEN changed ← TRUE; END; Smash["boot file"L, pilot]; Smash["pilot microcode"L, softMicrocode]; Smash["germ"L, germ]; Smash["diagnostic microcode"L, hardMicrocode]; IF ~found THEN {WriteLine["Logical volume has null boot files"L]; RETURN}; IF ~changed THEN RETURN; Volume.Open[lvID]; FOR t: BootFileType IN [hardMicrocode..pilot] DO IF set[t] THEN BEGIN bootFile: File.File; firstPage: File.PageNumber; [bootFile, firstPage] ← GetVolumeBootFile[lvID, t]; SetPhysicalVolumeBootFile[bootFile, t, firstPage] END; ENDLOOP; Volume.Close[lvID]; END; ShowDebugInfo: PROC = BEGIN PromIO.SetIOFilter[debug]; END; SpecifyDefaultVolumeSizes: PROC = BEGIN ReadName[" volume name: "L, @defaultLVSizeData.volumeName]; defaultLVSizeData.sizes ← [ [ DeviceTypes.sa1004, ReadNumber[ prompt: " size if sa1004: "L, min: 0, max: 37777777777B, default: 0]], [ DeviceTypes.sa4008, ReadNumber[ prompt: " size if sa4008: "L, min: 0, max: 37777777777B, default: 0]], [ DeviceTypes.t300, ReadNumber[ prompt: " size if t300: "L, min: 0, max: 37777777777B, default: 0]], [ DeviceTypes.t80, ReadNumber[ prompt: " size if t80: "L, min: 0, max: 37777777777B, default: 0]], [ DeviceTypes.q2040, ReadNumber[ prompt: " size if q2040: "L, min: 0, max: 37777777777B, default: 0]]] END; WriteComment: PROC = BEGIN s: STRING ← [80]; PromIO.ReadLine[NIL, s]; PromIO.WriteLine[s, always]; END; -- Volume Init Supporting Procedures unknown: STRING = "Unknown"; GetSetBootFileSwitches: PROC [ getSet: {get, set}, lvID: Volume.ID, ts: System.Switches ← System.defaultSwitches] = BEGIN outcome: SetGetSwitchesSuccess; bootFile: File.File; firstPage: File.PageNumber; switches: LONG STRING ← NIL; -- ***** Think this can be deleted. String.FreeString[z, switches]; Volume.Open[lvID]; [bootFile, firstPage] ← GetVolumeBootFile[lvID, pilot]; IF bootFile = File.nullFile THEN InputError["No boot file found."L]; IF getSet = get THEN [outcome, ts] ← GetSwitches[bootFile, firstPage] ELSE outcome ← SetSwitches[bootFile, firstPage, ts]; Volume.Close[lvID]; WriteSetDebuggerSuccess[outcome]; IF getSet = set THEN RETURN; FOR c: CHARACTER IN [0C..377C] DO IF ts[c] = up THEN LOOP; SELECT c FROM '~, '-, '\\, '', '" => NULL; IN ['a..'z], IN ['A..'Z], IN (' ..'?] => { String.AppendCharAndGrow[@switches, c, z]; LOOP}; ENDCASE => NULL; String.AppendCharAndGrow[@switches, '\\, z]; IF c > 77C THEN String.AppendCharAndGrow[@switches, (c - 0C) / 64 + '0, z]; IF c > 7C THEN String.AppendCharAndGrow[@switches, ((c - 0C) / 8 MOD 8) + '0, z]; String.AppendCharAndGrow[@switches, ((c - 0C) MOD 8) + '0, z]; String.AppendCharAndGrow[@switches, '\\, z] ENDLOOP; END; -- end of GetSetBootFileSwitches LastCylinder: PROC [h: Handle] RETURNS [LONG CARDINAL] = BEGIN OPEN FormatPilotDisk, FormatPilotDiskExtras, DeviceTypes; RETURN[ SELECT PhysicalVolume.InterpretHandle[h].type FROM sa1000, sa1004 => SA1004pagesPerCylinder, q2000 => Q2040pagesPerCylinder, q2010 => Q2010pagesPerCylinder, q2020 => Q2020pagesPerCylinder, q2030 => Q2030pagesPerCylinder, q2040 => Q2040pagesPerCylinder, q2080 => Q2080pagesPerCylinder, sa4000, sa4008 => SA4008pagesPerCylinder, t80 => t80pagesPerCylinder, t300 => t300pagesPerCylinder, ENDCASE => 0]; END; --The "ReadMumble" procedures acquire some kind of object from the character stream. Characters are read until the name of a Mumble object is found, and converted to an object with a more usable Mesa type than STRING. ReadDrive: PROC RETURNS [h: Handle] = BEGIN inString: STRING ← [20]; DO index: CARDINAL ← PhysicalVolume.nullDeviceIndex; ReadName["Drive Name: "L, inString, lastDriveName]; FOR i: CARDINAL IN [0..inString.length) DO lastDriveName[i] ← inString[i]; ENDLOOP; lastDriveName.length ← inString.length; IF inString[inString.length - 1] = ': THEN inString.length ← inString.length - 1; DO index ← PhysicalVolume.GetNextDrive[index]; IF index = PhysicalVolume.nullDeviceIndex THEN EXIT; h ← PhysicalVolume.GetHandle[index]; IF String.Equivalent[GetDriveStringName[h], inString] THEN RETURN; ENDLOOP; FormatError["Drive not found!"L]; --running on the wrong hardware!! ENDLOOP; END; ReadLvID: PROC [prompt: STRING ← NIL] RETURNS [pvID: PhysicalVolume.ID, lvID: Volume.ID, drive: Handle] = BEGIN inString: STRING ← [10 + maxNameLength]; fullName: STRING ← [10 + maxNameLength]; s: LONG STRING ← [maxNameLength]; driveString: STRING; matches: CARDINAL; tempLvID: Volume.ID; tempPvID: PhysicalVolume.ID; IF prompt = NIL THEN prompt ← "Logical Volume Name: "L; DO ReadName[prompt, inString, lastLvName]; FOR i: CARDINAL IN [0..inString.length) DO lastLvName[i] ← inString[i]; ENDLOOP; lastLvName.length ← inString.length; matches ← 0; tempPvID ← PhysicalVolume.nullID; DO tempPvID ← PhysicalVolume.GetNext[tempPvID]; IF tempPvID = PhysicalVolume.nullID THEN EXIT; driveString ← GetDriveStringName[ PhysicalVolume.GetAttributes[tempPvID].instance]; -- Form string Drive:LogicalName FOR i: CARDINAL IN [0..driveString.length) DO fullName[i] ← driveString[i]; ENDLOOP; fullName[driveString.length] ← ':; tempLvID ← Volume.nullID; DO match: BOOLEAN; tempLvID ← PhysicalVolume.GetNextLogicalVolume[tempPvID, tempLvID]; IF tempLvID = Volume.nullID THEN EXIT; GetLogicalVolumeName[tempLvID, s]; FOR i: CARDINAL IN [0..s.length) DO fullName[i + driveString.length + 1] ← s[i]; ENDLOOP; fullName.length ← driveString.length + s.length + 1; match ← String.Equivalent[s, inString] OR String.Equivalent[fullName, inString]; IF match THEN {matches ← matches + 1; lvID ← tempLvID; pvID ← tempPvID}; ENDLOOP; ENDLOOP; SELECT matches FROM 0 => FormatError["Not found"L]; 1 => {drive ← PhysicalVolume.GetAttributes[pvID].instance; RETURN}; ENDCASE => FormatError["Ambigous; please specify Device:LogicalName"L]; ENDLOOP; END; ReadLvType: PROC [prompt: STRING] RETURNS [t: Volume.Type] = BEGIN inString: STRING ← [30]; DO ReadName[prompt, inString]; FOR t IN [normal..nonPilot] DO IF String.Equivalent[logicalVolumeTypeString[t], inString] THEN RETURN; ENDLOOP; InputError["Illegal type"L]; ENDLOOP; END; -- Accept string of Form LogicalVolumeName OR -- Drive:LogicalVolumeName ReadPvID: PROC RETURNS [id: PhysicalVolume.ID, drive: Handle] = BEGIN h: Handle; inString: STRING ← [10 + maxNameLength]; fullName: STRING ← [10 + maxNameLength]; s: STRING ← [maxNameLength]; driveString: STRING; matches: CARDINAL; tmpID: PhysicalVolume.ID; DO ReadName["Physical Volume Name: "L, inString, lastPvName]; FOR i: CARDINAL IN [0..inString.length) DO lastPvName[i] ← inString[i]; ENDLOOP; lastPvName.length ← inString.length; matches ← 0; tmpID ← PhysicalVolume.nullID; DO match: BOOLEAN; tmpID ← PhysicalVolume.GetNext[tmpID]; IF tmpID = PhysicalVolume.nullID THEN EXIT; h ← PhysicalVolume.GetAttributes[tmpID, s].instance; driveString ← GetDriveStringName[h]; -- Form string Drive:PhysicalName FOR i: CARDINAL IN [0..driveString.length) DO fullName[i] ← driveString[i]; ENDLOOP; fullName[driveString.length] ← ':; FOR i: CARDINAL IN [0..s.length) DO fullName[i + driveString.length + 1] ← s[i]; ENDLOOP; fullName.length ← driveString.length + s.length + 1; match ← String.Equivalent[s, inString] OR String.Equivalent[fullName, inString] OR String.Equivalent[driveString, inString]; IF match THEN {matches ← matches + 1; id ← tmpID; drive ← h}; ENDLOOP; SELECT matches FROM 0 => FormatError["Not Found"L]; 1 => RETURN; ENDCASE => FormatError["Ambigous; please specify Device:PhysicalName"L]; ENDLOOP; END; --The GetMumble routines take one kind of name for an object and convert it to another kind of name for that same object. GetDriveNumber: PROC [h: Handle] RETURNS [CARDINAL] = { RETURN[PhysicalVolume.InterpretHandle[h].index]}; GetDriveStringName: PROC [h: Handle] RETURNS [s: STRING] = BEGIN OPEN DeviceTypes; s ← SELECT GetDriveType[h] FROM sa800 => "Fp?", q2000, q2010, q2030, q2040, q2080, sa1000, sa4000, sa1004, sa4008, t80, t300 => "Rd?", cdc9730 => "Cd?", ENDCASE => "UnknownType?"; s[s.length - 1] ← GetDriveNumber[h] + '0; END; GetDriveType: PROC [h: Handle] RETURNS [Device.Type] = { RETURN[PhysicalVolume.InterpretHandle[h].type]}; GetLogicalVolumeName: PROC [vid: Volume.ID, s: LONG STRING] = BEGIN s.length ← 0; Volume.GetLabelString[vid, s ! ANY => GOTO signal]; EXITS signal => BEGIN f: POINTER TO ARRAY [0..4) OF WORD = LOOPHOLE[@vid]; s.length ← 0; String.AppendChar[s, '[]; FOR i: CARDINAL IN [0..4) DO String.AppendLongNumber[s, LONG[f[i]], 8]; String.AppendChar[s, ',]; String.AppendChar[s, ' ]; ENDLOOP; String.AppendChar[s, ']]; END END; GetLogicalVolumeTypeName: PROC [vid: Volume.ID] RETURNS [STRING] = BEGIN t: Volume.Type; t ← Volume.GetType[vid ! ANY => GOTO signal]; RETURN[logicalVolumeTypeString[t]]; EXITS signal => RETURN[unknown]; END; MinPilotPage: PROC [h: Handle] RETURNS [DiskPageNumber] = BEGIN OPEN FSa: FormatPilotDisk, DeviceTypes; dn: CARDINAL = GetDriveNumber[h]; SELECT GetDriveType[h] FROM q2000, q2010, q2020, q2030, q2040, q2080 => RETURN[FSa.FirstQ2000PageForPilot]; sa1000, sa1004 => RETURN[FSa.FirstSA1000PageForPilot]; sa4000, sa4008 => RETURN[FSa.SA4000FirstPageForPilot[0]]; t80 => RETURN[FSa.Firstt80PageForPilot]; t300 => RETURN[FSa.Firstt300PageForPilot]; ENDCASE => RETURN[0]; END; WriteSetDebuggerSuccess: PROC [outcome: SetDebuggerSuccess] = { SELECT outcome FROM success => NULL; nullBootFile, cantWriteBootFile, notInitialBootFile => InputError["Boot file broken."L]; cantFindStartListHeader, startListHeaderHasBadVersion => InputError["file built by incompatible version of StartPilot"L]; noDebugger => InputError["No debugger installed."L]; ENDCASE => ERROR}; Init: PROC = BEGIN driveString: STRING; pvID: PhysicalVolume.ID; handle: PhysicalVolume.Handle ← PhysicalVolume.GetHandle[ PromCommand.workingDriveIndex]; lastDriveName ← GetDriveStringName[handle]; pvID ← PhysicalVolume.GetHints[handle ! PhysicalVolume.Error => CONTINUE].pvID; << A Physical Volume Name may be (1) drive name only, (2) physical volume name only, or (3) both 1 and 2 seperated by a colon. Since the drive name is unique by itself, just use that. >> driveString ← GetDriveStringName[handle]; FOR i: CARDINAL IN [0..driveString.length) DO lastPvName[i] ← driveString[i]; ENDLOOP; lastPvName.length ← driveString.length; lastLvName.length ← 0; END; -- of Init <<Main>> Init[]; END. LOG [Time - Person - Action] 6-Apr-83 16:47:36 - Thorup - Update to Sierra 6-Sep-83 16:21:22 - Curbow - Converted to Klamath 6-Sep-83 16:21:25 - Curbow - Deleted ChangeVolumeType 27-Sep-83 10:16:41 - Curbow - Replaced FormatCheckDrive from Othello>OthelloDeviceImplD0DLion 18-Oct-83 14:48:28 - Curbow - Cleanup Klamath conversion code 1-Nov-83 10:46:27 - Curbow - Catch errors from PhysicalVolume and handle. 19-Dec-83 13:12:32 - Curbow - Convert to Services 8.0. Replace NSCommand with TextInput 13-Feb-84 16:51:45 - Curbow - Add catchphrase for AccessFloppy calls. 13-May-84 15:19:19 - Curbow - Fixed AccessFloppy.AttributesRecord being stomped (ar #6614). 12-Jun-84 12:31:55 - Curbow - Changed all Floppy.Close calls to AccessFloppy.Close so that CACHE in AccessFloppy would work correctly. 19-Jun-84 17:33:38 - Curbow - Fixed AR 7455 (Doesn't preserve create dates of installed files) 20-Jun-84 16:35:58 - Curbow - Fixed AR 8382 - Handle multiple drives correctly. 20-Jun-84 16:42:59 - Curbow - Purge log of Pre-1983 info. 28-Jun-84 14:25:45 - Curbow - Update CreateVolume to handle PhysicalVolume.NeedsScavenging the same way as Othello does. 28-Jun-84 14:25:49 - Curbow - Modify fix for AR 7455 to record the same info as Othello per Davirro.PA 28-Jun-84 17:19:52 - Curbow - Make sure that all AccessFloppy calls catch Floppy.Error in addition to AccessFloppy.Error AR #9217 23-Aug-84 11:36:28 - Masinter - add q2080