-- 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