PrinterIagoCommands1Impl.mesa - PrinterIago commands implementation
Copyright © 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Willie-Sue, August 13, 1984 3:29:51 pm PDT
Russ Atkinson (RRA) May 2, 1986 6:14:14 pm PDT
Hal Murray, January 20, 1986 5:49:09 pm PST
Doug Wyatt, June 10, 1986 4:23:56 pm PDT
Tim Diebert: July 8, 1986 10:04:04 am PDT
DIRECTORY
Basics USING [ BITNOT, BYTE, BytePair, DivMod, LongDivMod, LowHalf ],
BasicTime USING [ FromPupTime, GetClockPulses, GMT, nullGMT, OutOfRange ],
BootFile USING [MemorySizeToFileSize],
Booting USING [ Boot, Switches, switches ],
DebuggerSwap USING [EnableSwatWatcher],
Disk USING [Channel, DriveAttributes, GetDeviceFromChannel, Label, NextChannel, PageCount, PageNumber],
DiskFace USING [DeviceHandle, GetDeviceAttributes, GetTrueDeviceAttributes],
File USING [Error, FindVolumeFromName, FP, GetVolumeID, GetVolumeName, Handle, Info, LogicalInfo, NextVolume, nullFP, Open, RC, SetSize, SystemVolume, Volume, VolumeID, VolumeFile, wordsPerPage],
FileBackdoor USING [CreateLogicalVolume, CreatePhysicalVolume, EraseVolume, GetRoot, GetVolumePages, IsDebugger, MarkDebugger, PhysicalPageBad, SetRoot],
FormatDisk USING [BadPage, IdentifyInitialMicrocode, InstallInitialMicrocode, MicrocodeInstallFailure, Operation, Sweep],
FS USING [ Close, ComponentPositions, Copy, Delete, EnumerateForNames, Error, ExpandName, FileInfo, NameProc, Open, OpenFile, SetByteCountAndCreatedTime, StreamOpen ],
FSBackdoor USING [ CloseVolume, CreateVMBacking, FNameFromHandle, GetFileHandle ],
FSRemoteFile USING [Retrieve],
GermSwap USING [ Switch ],
IO USING [ Close, EndOfStream, Error, GetChar, GetLength, PutChar, PutF, PutF1, PutRope, RopeFromROS, RIS, ROS, SetIndex, STREAM ],
PhysicalVolume USING [ GetSubVolumes, NextPhysical, Physical, PhysicalInfo, PhysicalRC, SetPhysicalRoot, SubVolumes ],
PrinterDefaultRemoteNames USING [Get],
PrinterIagoCommands,
PrinterIagoOps --USING everything--,
Rope USING [ Cat, Concat, Find, ROPE, Substr ],
SystemVersion USING [ machineType, uCodeCedar, uCodeDate, uCodeFloatingPoint, uCodeVersion ],
ThisMachine USING [ Name, ProcessorID ],
UserCredentials USING [ChangeState],
VM USING [AddressForPageNumber, Allocate, Free, Interval, PagesForBytes, PagesForWords, Pin, wordsPerPage],
VMSideDoor USING [rmPages];
PrinterIagoCommands1Impl: CEDAR PROGRAM
IMPORTS Basics, BasicTime, BootFile, Booting, DebuggerSwap, Disk, DiskFace, File, FileBackdoor, FormatDisk, FS, FSBackdoor, FSRemoteFile, IO, PhysicalVolume, PrinterDefaultRemoteNames, PrinterIagoCommands, PrinterIagoOps, Rope, SystemVersion, ThisMachine, UserCredentials, VM, VMSideDoor
EXPORTS PrinterIagoCommands = {
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
******** The command subroutines (alphabetic order) ********
Attach: PUBLIC PROC [in, out: STREAM] = {
from: ROPE = PrinterIagoOps.GetFile[
in: in,
out: out,
prompt: "From ",
check: TRUE];
to: ROPE = PrinterIagoOps.GetFile[
in: in,
out: out,
prompt: "To "];
IO.PutRope[out, " ... "];
[] ← FS.Copy[from: from, to: to, remoteCheck: FALSE, attach: TRUE];
IO.PutRope[out, "done"];
};
BootLogical: PUBLIC PROC [in, out: STREAM] = {
v: File.Volume = PrinterIagoOps.GetLogical[in, out];
reject: ROPE = Booting.Boot[[logical[v]], PrinterIagoOps.GetSwitches[in, out]];
IO.PutF1[out, " ... couldn't: %g\n", [rope[reject]] ];
};
Copy: PUBLIC PROC [in, out: STREAM] = {
from: ROPE = PrinterIagoOps.GetFile[
in: in,
out: out,
prompt: "From ",
check: TRUE];
to: ROPE = PrinterIagoOps.GetFile[
in: in,
out: out,
prompt: "To "];
IO.PutRope[out, " ... "];
[] ← FS.Copy[from: from, to: to];
IO.PutRope[out, "done"];
};
CreateLogical: PUBLIC PROC [in, out: STREAM] = {
name: ROPEIF File.FindVolumeFromName[PrinterIagoOps.clientVolName] = NIL
THEN PrinterIagoOps.clientVolName
ELSE NIL;
size: INT;
p: PhysicalVolume.Physical;
free: INT;
HelpCreation: PROC = {
IO.PutRope[out,
"? type a string which is not the same as the name of any existing volume"];
};
DO
name ← PrinterIagoOps.GetArg[
in: in,
out: out,
prompt: "\nNew volume name: ",
default: name,
help: HelpCreation];
IF File.FindVolumeFromName[name] # NIL
THEN IO.PutRope[out, " ... already exists"]
ELSE EXIT
ENDLOOP;
p ← PrinterIagoOps.GetPhysical[in, out];
PrinterIagoOps.ReservePages[in, out, p];
free ← PhysicalVolume.PhysicalInfo[p].free;
size ← PrinterIagoOps.GetSize[in: in, out: out, default: free, max: free];
IO.PutRope[out, "\nAre you sure? "];
IF PrinterIagoOps.Confirm[in, out] THEN {
new: File.Volume ← NIL;
IO.PutRope[out, " ... "];
new ← FileBackdoor.CreateLogicalVolume[
id: PrinterIagoOps.NewID[], where: LIST[p], size: size, name: name];
IO.PutRope[out, " done"];
PrinterIagoOps.clientVolName ← name;
IO.PutRope[out,
"\nDo you want to use this as the debugger volume? "];
IF PrinterIagoOps.Confirm[in, out] THEN FileBackdoor.MarkDebugger[new, TRUE];
};
};
CloseFSVolumes: PROC = {
FOR v: File.Volume ← File.NextVolume[NIL], File.NextVolume[v] UNTIL v = NIL DO
FSBackdoor.CloseVolume[v];
ENDLOOP;
};
CreatePhysical: PUBLIC PROC [in, out: STREAM] RETURNS[done: BOOLFALSE] = {
HelpCreation: PROC = {
IO.PutRope[out, "? type a name not the same as that of any existing volume"];
};
d: Disk.Channel = PrinterIagoOps.GetDrive[in, out];
alto: BOOL = Disk.DriveAttributes[d].ordinal = 0 AND PrinterIagoOps.CheckAltoRegions[in, out];
name: ROPE = PrinterIagoOps.GetArg[
in: in,
out: out,
prompt: "\nNew volume name: ",
default: ThisMachine.Name[],
help: HelpCreation];
IF PrinterIagoOps.ConfirmDestruction[in, out, Rope.Concat[
"the entire disk drive", IF alto THEN " (excluding Alto regions)" ELSE NIL ] ]
THEN {
new: PhysicalVolume.Physical;
CloseFSVolumes[];
new ← FileBackdoor.CreatePhysicalVolume[where: d, name: name, id: PrinterIagoOps.NewID[]];
PrinterIagoOps.ReservePages[in, out, new];
done ← TRUE;
PrinterIagoCommands.InstallCredentials[in, out];
DebuggerSwap.EnableSwatWatcher[TRUE];
};
};
CreateUserWorld: PUBLIC PROC [in, out: STREAM] = {
Install: PROC [where: File.Volume, which: File.VolumeFile[microcode..bootFile]] = {
wDir: ROPE = Rope.Cat["[]<", File.GetVolumeName[where], ">"];
remote: ROPE = PrinterIagoOps.RemoteRootFileName[which];
local: ROPE = PrinterIagoOps.LocalRootFileName[which];
fsFile: FS.OpenFile;
file: File.Handle;
createTime: BasicTime.GMT;
IO.PutF[out, "\nInstalling %g as %g%g ... ", [rope[remote]], [rope[wDir]], [rope[local]] ];
[] ← FS.Copy[from: remote, to: local, wDir: wDir];
createTime ← FS.FileInfo[name: local, wDir: wDir].created;
fsFile ← FS.Open[name: local, lock: write, wDir: wDir];
file ← FSBackdoor.GetFileHandle[fsFile];
FileBackdoor.SetRoot[which, file];
FS.SetByteCountAndCreatedTime[file: fsFile, created: createTime];
FS.Close[fsFile];
IO.PutRope[out, "done"];
};
format, protect: BOOL;
altoText: ROPENIL;
myName: ROPE = ThisMachine.Name[];
currentSystem: ROPE = PrinterDefaultRemoteNames.Get[].currentSystem;
d: Disk.Channel = Disk.NextChannel[NIL];
newP: PhysicalVolume.Physical;
newC: File.Volume;
memImagePages: INT;
Room for a physical memory image plus possible junk
cedarVMPages: INT ← 0;
TRUSTED {
memImagePages ← BootFile.MemorySizeToFileSize[VMSideDoor.rmPages]+24;
Has to be trusted because BootFile.MemorySizeToFileSize is misdeclared!
};
IO.PutChar[out, '\n];
PrinterIagoOps.ReserveAltoRegions[in, out];
IF PrinterIagoOps.CheckAltoRegions[in, out] THEN
altoText ← " (excluding the Alto disk regions)";
IO.PutRope[out, "\n\nHas your disk already been formatted? (Confirm if the disk has previously been used for Cedar or Pilot, AND you have not reduced the amount of disk reserved for Alto volumes) "];
format ← NOT PrinterIagoOps.Confirm[in, out];
IO.PutRope[out, "\n\nDo you want to password-protect this disk? "];
protect ← PrinterIagoOps.Confirm[in, out];
WHILE cedarVMPages < 8000 DO
cedarVMPages ← PrinterIagoOps.GetNumber[
in: in, out: out, default: DefaultVMSize[], max: 64000,
prompt: "\n\nSpecify VM size (in pages): ",
help: "? give number of VM pages in decimal (max: 64000)"
];
ENDLOOP;
IO.PutRope[out, "\n\nI intend to perform the following operations:"];
IF format THEN
IO.PutF1[out, "\n - format the entire disk%g;", [rope[altoText]] ];
IO.PutF1[out, "\n - create a new physical volume named \"%g\";", [rope[myName]] ];
IO.PutF[out,
"\n - create a client volume named \"Cedar\" with a %g page VM;\n - install microcode, germ and boot files(s) from %g;\n - make the \"Cedar\" volume be the physical boot volume;\n - boot the volume(s) to initialize the software.\n",
[integer[cedarVMPages]], [rope[currentSystem]] ];
IF NOT PrinterIagoOps.ConfirmDestruction[
in, out, Rope.Concat["the entire disk on drive RD0", altoText]]
THEN RETURN;
CloseFSVolumes[];
IO.PutChar[out, '\n];
IF format THEN TRUSTED {
DoFormatOrScan[in: in, out: out, d: d, format: TRUE, passes: 1] };
IO.PutRope[out, "\nCreating physical volume ... "];
newP ← FileBackdoor.CreatePhysicalVolume[where: d, name: myName, id: PrinterIagoOps.NewID[]];
PrinterIagoOps.ReservePages[in, out, newP];
[] ← UserCredentials.ChangeState[IF protect THEN name ELSE nameHint];
IO.PutRope[out, "done"];
IO.PutRope[out, "\nCreating logical volume \"Cedar\" ... "];
newC ← FileBackdoor.CreateLogicalVolume[
id: PrinterIagoOps.NewID[],
where: LIST[newP],
size: PhysicalVolume.PhysicalInfo[newP].free,
name: "Cedar"];
IO.PutRope[out, "done. Erasing it ... "];
FileBackdoor.EraseVolume[newC];
IO.PutRope[out, "done"];
CreateVMFile[out, newC, cedarVMPages];
Install[where: newC, which: microcode];
Install[where: newC, which: germ];
Install[where: newC, which: bootFile];
PhysicalVolume.SetPhysicalRoot[newC, microcode];
PhysicalVolume.SetPhysicalRoot[newC, germ];
PhysicalVolume.SetPhysicalRoot[newC, bootFile];
{
init: ROPE = PrinterIagoOps.InitialMicrocodeFileName[];
IO.PutRope[out, "\nInstalling "];
IO.PutRope[out, init];
IF DoInitialMicrocodeInstallation[out, d, init, "[]<Cedar>Temp>Initial.Microcode"] THEN
[] ← Booting.Boot[
[logical[newC]],
[d: Booting.switches[d]] ];
};
};
CreateVM: PUBLIC PROC [in, out: STREAM] = {
v: File.Volume = PrinterIagoOps.GetLogical[in, out, "On "];
size: INT = PrinterIagoOps.GetSize[
in: in, out: out, default: DefaultVMSize[], max: FileBackdoor.GetVolumePages[v].size];
oldFP: File.FP = FileBackdoor.GetRoot[v, VM].fp;
oldFile: File.Handle ← NIL;
IF oldFP # File.nullFP THEN oldFile ← File.Open[v, oldFP
! File.Error => IF why = unknownFile THEN CONTINUE];
IF oldFile # NIL
THEN {
IO.PutF[out,
"\nThat volume already has a VM backing file of size %g. Do you want me to set its size to %g? ", [integer[File.Info[oldFile].size]], [integer[size]] ];
IF PrinterIagoOps.Confirm[in, out] THEN {
IO.PutRope[out, " ... "]; File.SetSize[oldFile, size]; IO.PutRope[out, "done"] };
}
ELSE CreateVMFile[out, v, size]; -- Create the file ourselves
};
DefaultVMSize: PROC RETURNS [pages: INT ← 16000] = {
SELECT VMSideDoor.rmPages FROM
<= 4*1024 => {};
<= 8*1024 => pages ← 20000;
<= 12*1024 => pages ← 24000;
ENDCASE => pages ← 32000;
IF SystemVersion.machineType = dandelion THEN pages ← MIN[pages, 16000];
};
CreateVMFile: PROC [out: STREAM, v: File.Volume, size: INT] = {
newFile: FS.OpenFile;
volName: ROPE = File.GetVolumeName[v];
vmName: ROPE = Rope.Cat["[]<", volName, ">", PrinterIagoOps.LocalRootFileName[VM]];
IO.PutF1[out, "\nCreating %g ... ", [rope[vmName]] ];
newFile ← FSBackdoor.CreateVMBacking[name: vmName, pages: size];
FileBackdoor.SetRoot[VM, FSBackdoor.GetFileHandle[newFile]];
FS.Close[newFile];
IO.PutRope[out, "done"];
[] ← PrinterIagoOps.WriteRemoteNames[volName, out];
};
Delete: PUBLIC PROC [in, out: STREAM] = {
count: INT ← 0;
DoName: FS.NameProc = {
count ← count+1;
IO.PutF1[out, "\nDeleting %g ... ", [rope[fullFName]]];
{
ENABLE FS.Error => { IO.PutRope[out, error.explanation]; CONTINUE };
FS.Delete[fullFName];
IO.PutRope[out, "done"];
};
RETURN[TRUE];
};
pattern: ROPE ← PrinterIagoOps.GetFile[in: in, out: out, pattern: TRUE];
IF Rope.Find[pattern, "!"] < 0 THEN pattern ← pattern.Concat["!L"];
IO.PutRope[out, " ... "];
FS.EnumerateForNames[pattern, DoName];
IF count = 0
THEN IO.PutRope[out, "not found"]
ELSE IO.PutF1[out, "\n%g files", [integer[count]] ];
};
DescribeDrives: PUBLIC PROC [in, out: STREAM] = {
FOR c: Disk.Channel ← Disk.NextChannel[NIL], Disk.NextChannel[c] UNTIL c = NIL DO
ordinal: INT;
nPages: INT;
[ordinal: ordinal, nPages: nPages] ← Disk.DriveAttributes[c];
IO.PutF[out, "\nRD%g, size: %g pages", [integer[ordinal]], [integer[nPages]] ];
ENDLOOP;
};
DescribeLV: PUBLIC PROC [in, out: STREAM] = {
FOR v: File.Volume ← File.NextVolume[NIL], File.NextVolume[v] UNTIL v = NIL DO {
ENABLE File.Error => IO.PutRope[out, "\nUnexpected File.Error"]--and let it propagate--;
id: File.VolumeID;
rootStatus, vamStatus: File.RC;
name: ROPE;
size, free, freeboard: INT;
[id: id, size: size, rootStatus: rootStatus, name: name, vamStatus: vamStatus, free: free, freeboard: freeboard] ← File.LogicalInfo[v];
IO.PutRope[out, "\n\nID: "];
PrinterIagoOps.PutID[out, File.GetVolumeID[v]];
IF v = File.SystemVolume[] THEN IO.PutRope[out, " (the system volume)"];
SELECT rootStatus FROM
ok, nonCedarVolume => NULL;
wentOffline => {
IO.PutRope[out, "\nVolume went offline"];
LOOP;
};
inconsistent => {
IO.PutRope[out, "\nRoot page is inconsistent (wrong version?)"];
LOOP;
};
software => {
IO.PutRope[out, "\nCan't find the volume root page (label-check)"];
LOOP;
};
hardware => {
IO.PutRope[out, "\nHard disk error reading the volume root page"];
LOOP;
};
ENDCASE => ERROR File.Error[rootStatus];
IO.PutF1[out, "\nName: %g", [rope[name]] ];
IF FileBackdoor.IsDebugger[v] THEN IO.PutRope[out, "\nThis is a debugger volume"];
IO.PutF1[out, "\nSize: %g", [integer[size]] ];
IF rootStatus = nonCedarVolume THEN { IO.PutRope[out, "\nNot a Cedar volume"]; LOOP };
SELECT vamStatus FROM
ok => IO.PutF1[out, ", free pages: %g", [integer[free]] ];
wentOffline => IO.PutRope[out, "\nVolume went offline during VAM operation"];
inconsistent => IO.PutRope[out, "\nDon't understand the VAM"];
software => IO.PutRope[out, "\nCan't find the VAM (label-check)"];
hardware => IO.PutRope[out, "\nHard disk error reading the VAM"];
unknownFile => IO.PutRope[out, "\nCan't find the VAM (unknown-file)"];
unknownPage => IO.PutRope[out, "\nCan't find the VAM (unknown-page)"];
ENDCASE => ERROR File.Error[vamStatus];
FOR vf: File.VolumeFile IN File.VolumeFile DO
PutVolumeFile[out, v, vf];
ENDLOOP;
};
ENDLOOP;
};
PutVolumeFile: PROC [out: STREAM, v: File.Volume, vf: File.VolumeFile] = {
fp: File.FP ← FileBackdoor.GetRoot[v, vf].fp;
file: File.Handle;
name: ROPE;
IF fp = File.nullFP THEN RETURN;
IO.PutF[out, "\n%g fp: [id:%bB,da:%bB]",
[rope[SELECT vf FROM
checkpoint => "Checkpoint",
microcode => "Microcode",
germ => "Germ",
bootFile => "BootFile",
debugger => "Debugger",
debuggee => "Debuggee",
VM => "VM",
VAM => "VAM",
client => "FS-root",
alpine => "Alpine-root",
ENDCASE => "Other-root"]],
[cardinal[LOOPHOLE[fp.id]]],
[cardinal[LOOPHOLE[fp.da]]]
];
file ← File.Open[v, fp ! File.Error => {
IO.PutRope[out, " ... "];
IO.PutRope[out, PrinterIagoOps.FileError[why]];
file ← NIL;
CONTINUE } ];
IF file # NIL THEN {
ENABLE FS.Error => {
IO.PutRope[out, " ... "];
IF error.code = $invalidPropertyPage
THEN IO.PutRope[out, "not an FS file"]
ELSE IO.PutRope[out, error.explanation];
CONTINUE
};
IO.PutF1[out, ", size: %g", [integer[File.Info[file].size]] ];
name ← FSBackdoor.FNameFromHandle[file];
IO.PutRope[out, ", name: "]; IO.PutRope[out, name];
};
};
DescribeMachine: PUBLIC PROC [in, out: STREAM] = {
IO.PutF[out,
"\nThis processor is a %g, id = %g, %g, %g, available real memory: %g pages",
[rope[SELECT SystemVersion.machineType FROM
dolphin => "Dolphin",
dorado => "Dorado",
dandelion => "Dandelion",
dicentra => "Dicentra",
ENDCASE => "{unknown processor type!}"]],
[rope[ThisMachine.ProcessorID[$Octal]]],
[rope[ThisMachine.ProcessorID[$ProductSoftware]]],
[rope[ThisMachine.ProcessorID[$Hex]]],
[integer[VMSideDoor.rmPages]] ];
IO.PutF1[out, "\nThe microcode is version %g of ", [integer[SystemVersion.uCodeVersion]] ];
{
ENABLE BasicTime.OutOfRange => {
IO.PutRope[out, "unknown date."];
CONTINUE;
};
IO.PutF1[out, "%g.", [time[BasicTime.FromPupTime[SystemVersion.uCodeDate]]] ];
};
IF NOT SystemVersion.uCodeCedar
THEN IO.PutRope[out, " No Cedar microcode."];
IF NOT SystemVersion.uCodeFloatingPoint
THEN IO.PutRope[out, " No floating-point microcode."];
};
DescribePV: PUBLIC PROC [in, out: STREAM] = {
FOR p: PhysicalVolume.Physical ← PhysicalVolume.NextPhysical[NIL], PhysicalVolume.NextPhysical[p] UNTIL p = NIL DO
PrinterIagoOps.ReservePages[in, out, p];
ENDLOOP;
FOR p: PhysicalVolume.Physical ← PhysicalVolume.NextPhysical[NIL], PhysicalVolume.NextPhysical[p] UNTIL p = NIL DO
channel: Disk.Channel;
rootStatus: PhysicalVolume.PhysicalRC;
id: File.VolumeID;
name: ROPE;
size: Disk.PageCount;
free: Disk.PageCount;
[channel: channel, rootStatus: rootStatus, id: id, name: name, size: size, free: free] ←
PhysicalVolume.PhysicalInfo[p];
IO.PutRope[out, "\n\nID: "];
PrinterIagoOps.PutID[out, id];
IO.PutF1[out, "\nOn drive RD%g", [integer[Disk.DriveAttributes[channel].ordinal]] ];
SELECT rootStatus FROM
ok => NULL--drop through to after ENDCASE--;
wentOffline => {
IO.PutRope[out, "\nVolume went offline"];
LOOP };
inconsistent => {
IO.PutRope[out, "\nRoot page is inconsistent (wrong version?)"];
LOOP };
software => {
IO.PutRope[out, "\nCan't find the volume root page (label-check)"];
LOOP };
hardware => {
IO.PutRope[out, "\nHard disk error reading the volume root page"];
LOOP };
ENDCASE => ERROR File.Error[rootStatus];
TRUSTED {
microcodeInstalled: BOOL;
time: BasicTime.GMT;
name: ROPE;
[microcodeInstalled, time, name] ← FormatDisk.IdentifyInitialMicrocode[channel];
IF microcodeInstalled THEN
IO.PutF[out, "\nContains initial microcode %g of %g",
[rope[name]], [time[time]] ];
};
IO.PutF[out, "\nName: %g\nSize: %g, free pages: %g\nLogical sub-volumes:",
[rope[name]],
[integer[size]],
[integer[free]]
];
{
sv: PhysicalVolume.SubVolumes = PhysicalVolume.GetSubVolumes[p];
FOR i: CARDINAL IN [0..sv.count) DO
IO.PutF1[out, "\n At physical page %g, logical id: ", [integer[sv[i].address]] ];
PrinterIagoOps.PutID[out, sv[i].id];
IO.PutF[out, ", pages [%g..%g)",
[integer[sv[i].start]],
[integer[sv[i].start+sv[i].size]]
];
ENDLOOP;
};
ENDLOOP;
};
EraseLogical: PUBLIC PROC [in, out: STREAM] = {
v: File.Volume = PrinterIagoOps.GetLogical[in, out];
IF PrinterIagoOps.ConfirmDestruction[in, out, "the entire logical volume"] THEN {
IO.PutRope[out, "\nErasing ... "];
FileBackdoor.EraseVolume[v];
IO.PutRope[out, "done"];
FSBackdoor.CloseVolume[v];
};
};
FormatOrScan: PUBLIC PROC [in, out: STREAM, format: BOOL] = TRUSTED {
d: Disk.Channel = PrinterIagoOps.GetDrive[in, out];
alto: BOOL = Disk.DriveAttributes[d].ordinal = 0 AND PrinterIagoOps.CheckAltoRegions[in, out];
passes: INT = PrinterIagoOps.GetNumber[in, out, 10, 999,
"\nHow many check passes should I make? ",
"? Type how often I should make transfers over the entire disk trying to find bad spots"];
IF NOT format
OR PrinterIagoOps.ConfirmDestruction[in, out, Rope.Concat[
"the entire disk drive",
IF alto THEN " (excluding Alto regions)" ELSE NIL ] ]
THEN DoFormatOrScan[in, out, d, format, passes];
};
DoFormatOrScan: UNSAFE PROC [in, out: STREAM, d: Disk.Channel, format: BOOL, passes: INT] = UNCHECKED {
initLabel: Disk.Label;
pageSpace: VM.Interval = VM.Allocate[VM.PagesForWords[File.wordsPerPage]];
data: LONG POINTER = VM.AddressForPageNumber[pageSpace.page];
soft, hard: INT ← 0;
BadList: TYPE = LIST OF RECORD[
page: Disk.PageNumber, correctable: BOOL, dontReport: BOOLFALSE];
badPages: BadList ← NIL;
errorsThisPass: BOOL;
SweepDisk: UNSAFE PROC [out: STREAM, d: Disk.Channel, operation: FormatDisk.Operation] = UNCHECKED {
origin: Disk.PageNumber ← [0];
label: Disk.Label ← initLabel;
IO.PutRope[out, SELECT operation FROM
format => "\nFormatting ... ",
write => "\nWriting ... ",
read => "\nReading ... ",
verify => "\nVerifying ... "
ENDCASE => ERROR];
errorsThisPass ← FALSE;
DO firstPage: Disk.PageNumber; pageCount: Disk.PageCount;
[firstPage, pageCount] ← PrinterIagoOps.NextRun[d, origin];
IF pageCount < 0 THEN EXIT;
FormatDisk.Sweep[d, operation, firstPage, pageCount, @label, data];
origin ← [firstPage+pageCount];
ENDLOOP;
IF NOT errorsThisPass THEN IO.PutRope[out, "no errors"];
};
IF format THEN CloseFSVolumes[];
{
ENABLE {
UNWIND => VM.Free[pageSpace];
FormatDisk.BadPage => {
[page: Disk.PageNumber, correctable: BOOL]
b: BadList;
FOR b ← badPages, b.rest UNTIL b = NIL DO
IF b.first.page = page THEN {
IF b.first.correctable
THEN {
IF NOT correctable
THEN { b.first.correctable ← FALSE; soft ← soft-1; hard ← hard+1 };
};
EXIT
};
REPEAT FINISHED => {
badPages ← b ←
CONS[first: [page: page, correctable: correctable], rest: badPages];
IF correctable THEN soft ← soft+1 ELSE hard ← hard+1;
}
ENDLOOP;
IF hard > 100 THEN {
IO.PutRope[out, "\nThere are over 100 hard disk errors. Continue? "];
IF NOT PrinterIagoOps.Confirm[in, out] THEN CONTINUE;
hard ← 0;
};
IF NOT b.first.dontReport THEN {
logicalDiskCyl: CARDINAL;
physicalDiskCyl, physicalDiskTrack, physicalDiskSector: CARDINAL;
cylinder, track, sector, temp: CARDINAL;
device: DiskFace.DeviceHandle ← Disk.GetDeviceFromChannel[d];
IO.PutRope[out, IF errorsThisPass THEN ", " ELSE "\nDisk error(s): "];
errorsThisPass ← TRUE;
[cylinders: physicalDiskCyl, movingHeads: physicalDiskTrack, sectorsPerTrack: physicalDiskSector] ← DiskFace.GetTrueDeviceAttributes[device];
[cylinders: logicalDiskCyl] ← DiskFace.GetDeviceAttributes[device];
IF logicalDiskCyl # physicalDiskCyl
THEN {
Abnormal cyl/track/sect numbering on a Dorado for Alto compatability
[quotient: temp, remainder: sector] ← Basics.LongDivMod[
num: page, den: physicalDiskSector ];
[quotient: track, remainder: cylinder] ← Basics.DivMod[
num: temp, den: physicalDiskCyl];
}
ELSE {
Normal cyl/track/sect numbering
[quotient: cylinder, remainder: temp] ← Basics.LongDivMod[
num: page, den: physicalDiskSector * physicalDiskTrack];
[quotient: track, remainder: sector] ← Basics.DivMod[
num: temp, den: physicalDiskSector];
};
IO.PutF[out, "%bB[c:%g, t:%g, s:%g](%g)",
[integer[page]], [cardinal[cylinder]], [cardinal[track]], [cardinal[sector]],
[rope[IF correctable THEN "soft" ELSE "hard"]] ];
b.first.dontReport ← NOT correctable; -- suppress messages on subsequent passes
};
RESUME
};
};
VM.Pin[pageSpace];
IF format
THEN {
init: BOOLTRUE;
Randomize: UNSAFE PROC = UNCHECKED {
RandomizeOne[LOOPHOLE[LONG[@initLabel]], SIZE[Disk.Label]];
RandomizeOne[data, File.wordsPerPage];
init ← NOT init;
};
RandomizeOne: UNSAFE PROC [where: LONG POINTER, nwords: CARDINAL] = UNCHECKED {
array: LONG POINTER TO ARRAY OF CARDINAL = LOOPHOLE[where];
FOR i: CARDINAL IN [0..nwords) DO
array[i] ← IF init
THEN Basics.LowHalf[BasicTime.GetClockPulses[]]
ELSE Basics.BITNOT[array[i]];
ENDLOOP;
};
Randomize[];
SweepDisk[out, d, format];
SweepDisk[out, d, verify];
THROUGH [1..passes]
DO
First, set label and data to random values
Randomize[];
SweepDisk[out, d, write];
SweepDisk[out, d, verify];
ENDLOOP;
}
ELSE THROUGH [1..passes] DO SweepDisk[out, d, read] ENDLOOP;
Record any bad pages
{
p: PhysicalVolume.Physical ← NIL;
IF NOT format THEN
FOR p ← PhysicalVolume.NextPhysical[NIL], PhysicalVolume.NextPhysical[p] DO
IF p = NIL OR PhysicalVolume.PhysicalInfo[p].channel = d THEN EXIT;
ENDLOOP;
IF p = NIL THEN {
IO.PutRope[out, "\nCreating a physical volume to hold the bad page table ..."];
p ← FileBackdoor.CreatePhysicalVolume[
where: d, name: "JustForTheBadPageTable", id: PrinterIagoOps.NewID[]];
IO.PutRope[out, "done"];
};
IF badPages # NIL THEN {
rc: PhysicalVolume.PhysicalRC ← ok;
IO.PutRope[out, "\nRecording bad pages in physical bad page table ... "];
FOR b: BadList ← badPages, b.rest UNTIL b = NIL OR rc # ok DO
full: BOOLFALSE;
IF NOT b.first.correctable THEN {
[full, rc] ← FileBackdoor.PhysicalPageBad[p, b.first.page];
IF full THEN {
IO.PutRope[out, "\nOops, bad page table full. Aborting."];
GO TO tableFull;
};
};
ENDLOOP;
IF rc = ok THEN IO.PutRope[out, " done" ] ELSE ERROR File.Error[rc];
EXITS tableFull => {};
};
};
};
VM.Free[pageSpace];
};
InstallInitial: PUBLIC PROC [in, out: STREAM] = {
d: Disk.Channel = PrinterIagoOps.GetDrive[in, out];
name: ROPE = PrinterIagoOps.GetFile[
in: in, out: out,
extension: ".eb",
default: PrinterIagoOps.InitialMicrocodeFileName[],
check: TRUE
];
IO.PutRope[out, " ... installing"];
[] ← DoInitialMicrocodeInstallation[out, d, name];
};
DoInitialMicrocodeInstallation: PROC [out: STREAM, d: Disk.Channel, name: ROPE, temp: ROPENIL] RETURNS [done: BOOLFALSE] = {
cp: FS.ComponentPositions;
fullFName: ROPE;
pages: INT ← 0;
strm: STREAM;
ProduceStream: PROC [fullGName: ROPE, bytes: INT, created: BasicTime.GMT] RETURNS [STREAM] = {
pages← VM.PagesForBytes[bytes];
RETURN[strm← IO.ROS[]];
};
{
[fullFName, cp, ]← FS.ExpandName[name];
IF cp.server.length # 0
THEN {
FSRemoteFile.Retrieve[fullFName.Substr[cp.server.start, cp.server.length], fullFName.Substr[cp.dir.start-1], BasicTime.nullGMT, ProduceStream
! FS.Error => { out.PutChar['\n]; out.PutRope[error.explanation]; GO TO finish}];
IF strm = NIL THEN GOTO noFile;
strm← IO.RIS[IO.RopeFromROS[strm], strm];
}
ELSE {
strm ← FS.StreamOpen[fullFName
! FS.Error => { out.PutChar['\n]; out.PutRope[error.explanation]; GO TO finish}];
pages← VM.PagesForBytes[strm.GetLength[]];
};
strm.SetIndex[0];
TRUSTED {
VMReader: UNSAFE PROC [ ptr: LONG POINTER]RETURNS [ok: BOOLTRUE] = {
index: INT;
{
IF pages <= 0 THEN RETURN[FALSE];
FOR i: INT IN [0 .. VM.wordsPerPage) DO
ENABLE IO.EndOfStream => {index← i; GOTO eos};
val: Basics.BytePair;
val.high← LOOPHOLE[strm.GetChar[], Basics.BYTE];
val.low← LOOPHOLE[strm.GetChar[], Basics.BYTE];
(ptr + i)^← LOOPHOLE[val, WORD];
ENDLOOP;
pages ← pages-1;
EXITS
eos => {
FOR j: INT IN [index .. VM.wordsPerPage) DO (ptr + j)^← 0; ENDLOOP;
pages← 0;
};
};
};
FormatDisk.InstallInitialMicrocode[d, VMReader
! FormatDisk.MicrocodeInstallFailure => {
IO.PutRope[out, SELECT why FROM
emptyFile => "empty microcode file",
firstPageBad => "first page of microcode area of disk is bad",
flakeyPageFound => "bad page in microcode area of disk",
microcodeTooBig => "microcode too big for fixed area of disk",
other => "other error during installation",
ENDCASE => "unknown error during installation"];
GO TO finish;
} ];
};
done ← TRUE;
IO.PutRope[out, " done."];
GO TO finish;
EXITS
noFile => {};
finish => {IF strm # NIL THEN strm.Close[ ! IO.Error => CONTINUE]};
};
};
}.