-- FormatSA1000andSA4000Impl.mesa
-- Last Edited by: Taft, May 22, 1983 4:47 pm
-- this must be generalized to also do sa1000's. The general theory is the same, but the interface has to be a little different since SA1000's only do cylinders, and don't know about initial microcode yet.
-- do something about event counts???
DIRECTORY
DeviceTypes USING [sa1000, sa4000],
DiskChannel USING [
Address, Create, Delete, Drive, DiskPageNumber, GetDriveAttributes,
GetPageAddress, GetPageNumber, Handle, PVHandle],
Environment USING [Base, first64K, wordsPerPage],
FormatSA1000andSA4000,
Inline USING [BITNOT, LowHalf, LongCOPY],
MicrocodeFile,
PhysicalVolume USING [], -- exports Handle
PilotDisk USING [Label, NextLabel, nullLabel],
Process USING [Yield],
RandomCard USING [Init, Next],
ResidentHeap USING [MakeNode],
SA4000Face USING [
Command, DeviceHandle, DiskAddress, Initiate, Operation, OperationPtr,
operationSize, Poll, Recalibrate, Status],
Space USING [
Create, defaultWindow, Handle, LongPointer, Map, Pointer, Unmap, mds],
String USING [AppendString],
System USING [GreenwichMeanTime],
Zone USING [Status];
FormatSA1000andSA4000Impl: PROGRAM -- should be a monitor since globals shared
IMPORTS
DiskChannel, Inline, MicrocodeFile, PilotDisk, Process, RandomCard, ResidentHeap,
SA4000Face, Space, String
EXPORTS FormatSA1000andSA4000, PhysicalVolume
SHARES DiskChannel = PUBLIC
BEGIN OPEN Inline;
Handle: PUBLIC TYPE = DiskChannel.PVHandle;
DiskPageNumber: PRIVATE TYPE = FormatSA1000andSA4000.DiskPageNumber;
RetryLimit: PRIVATE TYPE = [0..10);
retryLimit: PRIVATE RetryLimit = LAST[RetryLimit];
space: PRIVATE Space.Handle = Space.Create[
size: 1, parent: Space.mds];
op: PRIVATE SA4000Face.OperationPtr = MakeOperation[];
BadPage: SIGNAL [p: DiskPageNumber] = CODE;
PassStarting: PUBLIC SIGNAL [pass: CARDINAL, operation: FormatSA1000andSA4000.Operation] = CODE;
MicrocodeInstallFailure: SIGNAL [m: FormatSA1000andSA4000.FailureType]
= CODE;
NotSA1000orSA4000: ERROR = CODE;
SA1000FormatTrackError, SA1000FormatCylinderError: ERROR = CODE;
Format: PROC [h: Handle, firstPage: DiskPageNumber, count: LONG CARDINAL,
passes: CARDINAL] =
BEGIN
ckData: LONG POINTER = Space.LongPointer[space];
cnl: DiskChannel.Handle;
label: PilotDisk.Label;
formatCount: LONG CARDINAL = AssertSA1000orSA4000[h, firstPage, count];
SetLabel: PROC [value: CARDINAL] =
BEGIN
SetBlock[@label, SIZE[PilotDisk.Label], value];
label.pad1 ← 0;
IF label.filePageLo#0 OR label.filePageHi#0 THEN
label.immutable ← label.temporary ← label.zeroSize ← FALSE;
END;
Space.Map[space, Space.defaultWindow];
cnl ← DiskChannel.Create[h.drive, [LONG[0]]];
BEGIN ENABLE UNWIND => {Space.Unmap[space]; DiskChannel.Delete[cnl]};
SetBlock[@label, SIZE[PilotDisk.Label], 52525B];
FOR countDone: LONG CARDINAL ← 0, countDone + formatCount
WHILE countDone < count DO
Transfer[h, cnl, firstPage+countDone, formatCount, w, @label, ckData];
ENDLOOP;
[] ← RandomCard.Init[-1];
FOR pass: CARDINAL IN [0..passes) DO
labelValue: CARDINAL ← (IF pass MOD 2 = 0 THEN RandomCard.Next[] ELSE BITNOT[labelValue]);
SIGNAL PassStarting[pass+1, write];
SetLabel[labelValue];
FOR i: CARDINAL IN [0..Environment.wordsPerPage) DO
(ckData+i)↑ ← (IF pass MOD 2 = 0 THEN RandomCard.Next[] ELSE BITNOT[(ckData+i)↑]);
ENDLOOP;
Transfer[h, cnl, firstPage, count, vww, @label, ckData];
SIGNAL PassStarting[pass+1, read];
SetLabel[labelValue];
Transfer[h, cnl, firstPage, count, vvv, @label, ckData, 1];
ENDLOOP;
END;
Space.Unmap[space];
DiskChannel.Delete[cnl];
END;
IdentifyInitialMicrocode: PUBLIC PROCEDURE [h: Handle, s: STRING]
RETURNS [microcodeInstalled: BOOLEAN ← FALSE, time: System.GreenwichMeanTime] =
BEGIN
label: PilotDisk.Label ← PilotDisk.nullLabel;
page: DiskPageNumber;
cnl: DiskChannel.Handle;
header: POINTER TO MicrocodeFile.Header;
[] ← AssertSA1000orSA4000[h];
cnl ← DiskChannel.Create[h.drive, [LONG[0]]];
page ← IF DiskChannel.GetDriveAttributes[h.drive].deviceType = DeviceTypes.sa4000
THEN FormatSA1000andSA4000.SA4000startOfMicrocode
ELSE FormatSA1000andSA4000.SA1000startOfMicrocode;
Space.Map[space, Space.defaultWindow];
BEGIN
Transfer[h: h, channel: cnl, firstPage: page, count: 1,
cmd: vvr, label: @label, data: Space.LongPointer[space] !
BadPage => GOTO error];
header ← Space.Pointer[space];
IF header.name.length IN [1..header.name.maxlength] AND
header.name.maxlength<=MicrocodeFile.maxNameLength AND
header.name.length<=s.maxlength THEN
BEGIN
time ← MicrocodeFile.GMTFromBCPLTime[header.createDate];
s.length ← 0;
String.AppendString[s, @header.name];
microcodeInstalled ← TRUE;
END;
EXITS
error => NULL;
END;
Space.Unmap[space];
DiskChannel.Delete[cnl];
END;
InstallBootMicrocode: PROC [
h: Handle, getPage: PROC RETURNS [LONG POINTER]] =
BEGIN
cnl: DiskChannel.Handle;
[] ← AssertSA1000orSA4000[h];
cnl ← DiskChannel.Create[h.drive, [LONG[0]]];
Space.Map[space, Space.defaultWindow];
InstallBootMicrocode1[
h, cnl, Space.LongPointer[space], getPage
! UNWIND => {Space.Unmap[space]; DiskChannel.Delete[cnl]}];
Space.Unmap[space];
DiskChannel.Delete[cnl];
END;
Scan: PROC [h: Handle, firstPage: DiskPageNumber, count: LONG CARDINAL,
passes: CARDINAL] =
BEGIN
label: PilotDisk.Label;
cnl: DiskChannel.Handle;
[] ← AssertSA1000orSA4000[h];
cnl ← DiskChannel.Create[h.drive, [LONG[0]]];
Space.Map[space, Space.defaultWindow];
FOR pass: CARDINAL IN [0..passes) DO
SIGNAL PassStarting[pass+1, read];
Transfer[
h, cnl, firstPage, count, vrr, @label, Space.LongPointer[space]
! UNWIND => {Space.Unmap[space]; DiskChannel.Delete[cnl]}];
ENDLOOP;
Space.Unmap[space];
DiskChannel.Delete[cnl];
END;
-- Private Procedures
AssertSA1000orSA4000: PRIVATE PROC [
h: Handle, first: DiskPageNumber ← 0, count: LONG CARDINAL ← 0]
RETURNS [countFormat: LONG CARDINAL] =
BEGIN
SELECT DiskChannel.GetDriveAttributes[h.drive].deviceType FROM
DeviceTypes.sa4000 => RETURN[count];
DeviceTypes.sa1000 =>
BEGIN
p: CARDINAL = FormatSA1000andSA4000.SA1000pagesPerTrack;
IF (first MOD p) # 0 OR (count MOD p) # 0 THEN
ERROR SA1000FormatTrackError;
RETURN[FormatSA1000andSA4000.SA1000pagesPerTrack]
END;
ENDCASE => ERROR NotSA1000orSA4000
END;
CopyPage: PRIVATE PROC [from, to: LONG POINTER] = INLINE
{LongCOPY[from: from, to: to, nwords: Environment.wordsPerPage]; };
GetDiskAddress: PRIVATE PROC [h: DiskChannel.Handle, p: DiskPageNumber]
RETURNS [SA4000Face.DiskAddress] =
BEGIN
x: DiskChannel.Address = DiskChannel.GetPageAddress[h, p];
RETURN[[cylinder: x.cylinder, head: x.head, sector: x.sector]];
END;
GetPageNumber: PRIVATE PROC [
d: DiskChannel.Drive, a: SA4000Face.DiskAddress]
RETURNS [DiskPageNumber] =
BEGIN
RETURN[DiskChannel.GetPageNumber[
d, [cylinder: a.cylinder, head: a.head, sector: a.sector]]]
END;
-- h has been asserted to be SA4000
InstallBootMicrocode1: PRIVATE PROC [
h: Handle, cnl: DiskChannel.Handle, myPage: LONG POINTER,
getPage: PROC RETURNS [LONG POINTER]] =
BEGIN
bits: LONG POINTER;
device: SA4000Face.DeviceHandle = DiskChannel.GetDriveAttributes[
h.drive].deviceHandle;
thisPage, prevPage: DiskPageNumber ← 0;
stopDa: SA4000Face.DiskAddress = LOOPHOLE[LONG[-1]];
offset, lastPage: DiskPageNumber;
GetDA: PROC [p: DiskPageNumber] RETURNS [SA4000Face.DiskAddress] = INLINE
{RETURN[GetDiskAddress[cnl, p + offset]]};
IO: PROC [
command: SA4000Face.Command, thisPage, link: SA4000Face.DiskAddress]
RETURNS [success: BOOLEAN] =
BEGIN
label: PilotDisk.Label ← PilotDisk.nullLabel;
rCount: RetryLimit ← 0;
status: SA4000Face.Status;
op↑ ← [
command: command, device: device, clientHeader: thisPage,
pageCount: 1,
labelPtr: @label, dataPtr: myPage, incrementDataPtr: FALSE];
label.bootChainLink ← LOOPHOLE[link];
SA4000Face.Initiate[op];
DO
SELECT status ← SA4000Face.Poll[op] FROM
inProgress => {Process.Yield[]; LOOP; };
goodCompletion => EXIT;
ENDCASE =>
BEGIN
SELECT rCount FROM
>= retryLimit => RETURN[FALSE];
= retryLimit/2 => SA4000Face.Recalibrate[device];
ENDCASE => NULL;
rCount ← rCount + 1;
op.pageCount ← 1;
SA4000Face.Initiate[op];
END
ENDLOOP;
RETURN[IF command = vww THEN IO[vvv, thisPage, link] ELSE TRUE];
END;
IF DiskChannel.GetDriveAttributes[h.drive].deviceType
= DeviceTypes.sa4000 THEN
BEGIN
offset ← FormatSA1000andSA4000.SA4000startOfMicrocode;
lastPage ← FormatSA1000andSA4000.SA4000lastPageOfMicrocode
END
ELSE
BEGIN
offset ← FormatSA1000andSA4000.SA1000startOfMicrocode;
lastPage ← FormatSA1000andSA4000.SA1000lastPageOfMicrocode;
END;
bits ← getPage[];
IF bits = NIL THEN GOTO emptyFile;
CopyPage[from: bits, to: myPage];
DO
bits ← getPage[];
DO
IF thisPage + offset > lastPage THEN GOTO tooBig;
IF IO[
vww, GetDA[thisPage],
IF bits = NIL THEN stopDa ELSE GetDA[thisPage + 1]] THEN EXIT;
IF thisPage = 0 THEN GOTO firstPageBad;
thisPage ← thisPage + 1;
ENDLOOP;
IF thisPage # prevPage + 1 AND thisPage # 0 THEN
-- have to fix up previous link if we had an error
BEGIN
IF ~IO[vvr, GetDA[prevPage], GetDA[prevPage + 1]] THEN GOTO flakey;
IF ~IO[vww, GetDA[prevPage], GetDA[thisPage]] THEN GOTO flakey;
END;
IF bits = NIL THEN EXIT;
-- we are done and have written a zero link in the last page
CopyPage[from: bits, to: myPage];
prevPage ← thisPage;
thisPage ← thisPage + 1;
ENDLOOP;
EXITS
emptyFile => SIGNAL MicrocodeInstallFailure[emptyFile];
firstPageBad => SIGNAL MicrocodeInstallFailure[firstPageBad];
flakey => SIGNAL MicrocodeInstallFailure[flakeyPageFound];
tooBig => SIGNAL MicrocodeInstallFailure[microcodeTooBig];
END;
MakeOperation: PRIVATE PROC RETURNS [SA4000Face.OperationPtr] =
BEGIN
rp: Environment.Base RELATIVE POINTER TO SA4000Face.Operation;
NoWay: ERROR = CODE;
status: Zone.Status;
[rp, status]
← ResidentHeap.MakeNode[n: SA4000Face.operationSize, alignment: a16];
IF status # okay THEN ERROR NoWay;
RETURN[@Environment.first64K[rp]]
END;
SetBlock: PRIVATE PROC [blk: LONG POINTER, length, v: CARDINAL] = INLINE
{blk↑ ← v; LongCOPY[from: blk, to: blk + 1, nwords: length - 1]; };
-- SA4000-ness has been asserted
Transfer: PRIVATE PROC [
h: Handle, channel: DiskChannel.Handle, firstPage: DiskPageNumber,
count: LONG CARDINAL, cmd: SA4000Face.Command,
label: POINTER TO PilotDisk.Label, data: LONG POINTER,
retries: RetryLimit ← retryLimit] =
BEGIN
device: SA4000Face.DeviceHandle = DiskChannel.GetDriveAttributes[
h.drive].deviceHandle;
rCount: RetryLimit ← 0;
status: SA4000Face.Status;
runStartAddr: SA4000Face.DiskAddress ← GetDiskAddress[channel, firstPage];
Ooops: PROC RETURNS [notDoneYet: BOOLEAN] =
BEGIN
rCount ← IF op.clientHeader = runStartAddr THEN rCount + 1 ELSE 0;
SELECT TRUE FROM
rCount >= retries =>
BEGIN
page: DiskPageNumber = GetPageNumber[h.drive, op.clientHeader];
SIGNAL BadPage[page];
op.clientHeader ← GetDiskAddress[channel, page + 1];
op.pageCount ← op.pageCount - 1;
PilotDisk.NextLabel[label]; -- Fix up the label to the next page
rCount ← 0;
END;
rCount = retryLimit/2 OR (status IN [wrongSector..wrongHead] AND rCount = retries/2) =>
SA4000Face.Recalibrate[device];
ENDCASE => NULL;
runStartAddr ← op.clientHeader;
IF (notDoneYet ← op.pageCount > 0) THEN SA4000Face.Initiate[op];
END;
op↑ ← [
device: device, clientHeader: runStartAddr, pageCount: LowHalf[count],
labelPtr: label, dataPtr: data, incrementDataPtr: FALSE,
command: cmd];
IF count > LAST[CARDINAL] THEN ERROR;
SA4000Face.Initiate[op];
DO
SELECT status ← SA4000Face.Poll[op] FROM
inProgress => {Process.Yield[]; LOOP};
goodCompletion => EXIT;
ENDCASE => IF Ooops[] THEN LOOP ELSE EXIT;
ENDLOOP;
END;
END....
LOG
Time: June 1, 1980 1:23 AM By: Forrest Action: Changed to use sanitized interface. Added limit checking on InstallMicrocode. Trimmed log.
Time: July 28, 1980 8:22 AM By: Forrest Action: Retrofited McJones changes for new SA4000face. Converted to SA1000and4000.
Time: September 30, 1980 5:31 PM By: Luniewski Action: Increment File Page numbers in labels after finding a bad page in Transfer.
17-Jun-82 17:46:14 Taft ~= => # for Cedar compiler
21-Jun-82 18:21:10 Taft Add IdentifyInitialMicrocode
May 22, 1983 4:28 pm Taft Change Format to do multiple passes with random data.