Cedar Nucleus: Dorado implementation of SA4000Face
DiskHeadDorado.mesa
Last Edited by: Taft, January 28, 1984 11:40:33 am PST
Last Edited by: Andrew Birrell, May 5, 1983 2:48 pm
Last Edited by: Willie-Sue, July 27, 1984 9:31:57 am PDT
Last Edited by: Bob Hagmann, March 12, 1984 2:58:03 pm PST
DIRECTORY
DeviceCleanup USING [Await, Item, Reason],
DiskFace USING [Label],
SA4000FaceExtras,
DoradoInputOutput USING [IOAddress, DMuxAddr, Output, ResetDisk, RWMufMan],
PrincOpsUtils USING [BITAND, BITOR, LowHalf],
SA4000Face;
DiskHeadDorado: PROGRAM
IMPORTS DeviceCleanup, DoradoInputOutput, PrincOpsUtils
EXPORTS SA4000Face, SA4000FaceExtras
SHARES SA4000Face =
BEGIN OPEN SA4000Face;
Data structures shared with disk microcode
CSBPtr: TYPE = LONG POINTER TO CSB;
CSB: TYPE = MACHINE DEPENDENT RECORD [
head (0): IOCBQueueHead,
interruptMask (1): WORD,
drive (2): Drive, -- drive currently selected
cylinder (3): CARDINAL, -- negative => need to restore disk
Remainder of CSB is not used by the microcode
tail (4): IOCBLongPtr];
IOCBQueueHead: TYPE = MACHINE DEPENDENT RECORD [
SELECT OVERLAID * FROM
untagged => [iocb: IOCBShortPtr],
tagged => [highBits: [0..77777B], tag: {idle, active}],
ENDCASE];
nilIOCBQueueHead: IOCBQueueHead = [untagged[iocb: nilIOCBShortPtr]];
IOCB must be odd word aligned
IOCBShortPtr: TYPE = SA4000Face.Base RELATIVE POINTER TO IOCB;
IOCBLongPtr: TYPE = LONG POINTER TO IOCB;
IOCB: TYPE = MACHINE DEPENDENT RECORD [
next (0B): IOCBShortPtr,
seal (1B): CARDINAL,
drive (2B): Drive,
pageCount (3B): CARDINAL,
command (4B): DiskCommand,
diskAddress (5B): DiskAddress,
diskHeader (7B): DiskAddress,
headerPtr (11B): LONG POINTER TO DiskAddress,
headerECC (13B): LONG CARDINAL,
headerStatus (15B): DiskStatus,
labelPtr (16B): LONG POINTER TO DiskFace.Label,
labelECC (20B): LONG CARDINAL,
labelStatus (22B): DiskStatus,
dataPtr (23B): LONG POINTER,
dataECC (25B): LONG CARDINAL,
dataStatus (27B): DiskStatus,
diskLabel (30B): DiskFace.Label];
nilIOCBShortPtr: IOCBShortPtr = LOOPHOLE[0];
DoradoOperationPtr: TYPE = LONG POINTER TO DoradoOperation;
DoradoOperation: TYPE = MACHINE DEPENDENT RECORD [
operation (0): Operation,
Guarantee required odd word alignment, since Operation is 16-word aligned
iocb (15B): IOCB];
DiskStatus: TYPE = MACHINE DEPENDENT RECORD [
seekInc (0: 0..0): BOOLEAN,
headOvfl (0: 1..1): BOOLEAN,
devCheck (0: 2..2): BOOLEAN,
notSelected (0: 3..3): BOOLEAN,
notOnLine (0: 4..4): BOOLEAN,
notReady (0: 5..5): BOOLEAN,
sectorOvfl (0: 6..6): BOOLEAN,
fifoUnderflow (0: 7..7): BOOLEAN,
fifoOverflow (0: 8..8): BOOLEAN,
checkErr (0: 9..9): BOOLEAN,
readOnly (0: 10..10): BOOLEAN,
cylOffset (0: 11..11): BOOLEAN,
iobParityErr (0: 12..12): BOOLEAN,
fifoParityErr (0: 13..13): BOOLEAN,
eccErr (0: 14..14): BOOLEAN,
sectorSearchErr (0: 15..15): BOOLEAN];
nullDiskStatus: DiskStatus = LOOPHOLE[0];
errorStatusMask: DiskStatus = [
These bits constitute errors:
seekInc: TRUE, headOvfl: TRUE, devCheck: TRUE, notSelected: TRUE, notOnLine: TRUE,
notReady: TRUE, sectorOvfl: TRUE, fifoUnderflow: TRUE, fifoOverflow: TRUE,
checkErr: TRUE, iobParityErr: TRUE, fifoParityErr: TRUE, eccErr: TRUE,
sectorSearchErr: TRUE,
These are just status bits that do not necessarily constitute errors:
readOnly: FALSE, cylOffset: FALSE];
sealValid: CARDINAL = 125377B;
sealNil: CARDINAL = 0;
cylinderRestore: CARDINAL = 177777B;
cylinderUnknown: CARDINAL = 77777B;
Microcode implements Pilot file page number layout in labels, but Cedar Face expects INT's
FilePageRep: TYPE = MACHINE DEPENDENT RECORD[ n(0): SELECT OVERLAID * FROM
cedar =>
[cedar(0): INT],
pilot => [
filePageLo(0): CARDINAL,
pilotHi(1): RECORD[
filePageHi(0:0..6): [0..128), -- restricts to 23-bit page numbers (32-bit byte counts)
pad1 (0:7..12): [0..64) ← 0, -- always zero
immutable (0:13..13): BOOLFALSE, -- valid only in label of page 0
temporary (0:14..14): BOOLFALSE, -- valid only in label of page 0
zeroSize (0:15..15): BOOLFALSE
]
],
raw => [
lowHalf(0): CARDINAL,
highHalf(1): CARDINAL
]
ENDCASE
];
Disk hardware definitions
Output to DiskControl register, and used as diskCommand in IOCBs
diskControl: DoradoInputOutput.IOAddress = 10B;
DiskCommand: TYPE = MACHINE DEPENDENT RECORD [
incrementDataPtr (0: 0..0): BOOLEANFALSE, -- in IOCB only; not defined in hardware
unused1 (0: 1..4): Filler,
clearEnableRun (0: 5..5): BOOLEANFALSE,
debugMode (0: 6..6): BOOLEANFALSE,
blockTilIndex (0: 7..7): BOOLEANFALSE,
header (0: 8..9): Action ← none,
label (0: 10..11): Action ← none,
data (0: 12..13): Action ← none,
unused2 (0: 14..15): Action ← none];
Action: TYPE = MACHINE DEPENDENT {none, write, check, read};
Output to DiskMuff register
diskMuff: DoradoInputOutput.IOAddress = 11B;
MuffCommand: TYPE = MACHINE DEPENDENT RECORD [
unused (0: 0..1): Filler,
clearCompareErr (0: 2..2): BOOLEANFALSE,
setChecksumErr (0: 3..3): BOOLEANFALSE,
clearIndexTW (0: 4..4): BOOLEANFALSE,
clearSectorTW (0: 5..5): BOOLEANFALSE,
clearSeekTagTW (0: 6..6): BOOLEANFALSE,
clearErrors (0: 7..7): BOOLEANFALSE,
muffAddr (0: 8..15): MufflerAddress ← tempSense];
Input from DiskMuff register
MuffInput: TYPE = MACHINE DEPENDENT RECORD [
unused (0: 0..14): Filler,
bit (0: 15..15): BOOLEAN];
Output to DiskTag register
diskTag: DoradoInputOutput.IOAddress = 14B;
TagCommand: TYPE = MACHINE DEPENDENT RECORD [
driveTag (0: 0..0): BOOLEANFALSE,
cylinderTag (0: 1..1): BOOLEANFALSE,
headTag (0: 2..2): BOOLEANFALSE,
controlTag (0: 3..3): BOOLEANFALSE,
bus (0: 4..15): SELECT OVERLAID * FROM
drive => [
unused (0: 4..5): Filler,
subSectorCount (0: 6..9): [0..17B] ← 0, -- sub-sectors/sector - 1
loadSubSector (0: 10..10): BOOLEANFALSE,
select (0: 11..11): BOOLEANFALSE,
driveNumber (0: 12..15): Drive],
cylinder => [
cylinder (0: 4..15): [0..7777B]],
head => [
unused (0: 4..7): Filler,
offset (0: 8..8): BOOLEANFALSE,
offsetDirection (0: 9..9): {out(0), in(1)} ← out,
headNumber (0: 10..15): [0..77B]],
control => [
syncPattern (0: 4..4): {s201, s001} ← s201, -- s001 is Alto-compatible
unused (0: 5..5): Filler,
strobeLate (0: 6..6): BOOLEANFALSE,
strobeEarly (0: 7..7): BOOLEANFALSE,
write (0: 8..8): BOOLEANFALSE,
read (0: 9..9): BOOLEANFALSE,
addressMark (0: 10..10): BOOLEANFALSE,
headReset (0: 11..11): BOOLEANFALSE,
deviceCheckReset (0: 12..12): BOOLEANFALSE,
headSelect (0: 13..13): BOOLEANFALSE,
rezero (0: 14..14): BOOLEANFALSE,
headAdvance (0: 15..15): BOOLEANFALSE],
ENDCASE];
Muffler addresses for status bits (DskEth-relative)
MufflerAddress: TYPE = MACHINE DEPENDENT {
tempSense(0), indexTW(1), sectorTW(2), seekTagTW(3), rdFifoTW(4), wrFifoTW(5),
readData(6), writeData(7), enableRun(10B), debugMode(11B), notRdOnlyBlock(12B),
notWriteBlock(13B), notCheckBlock(14B), active(15B), select0(16B), select1(17B),
seekInc(20B), headOvfl(21B), devCheck(22B), notSelected(23B), notOnLine(24B),
notReady(25B), sectorOvfl(26B), fifoUnderflow(27B), fifoOverflow(30B),
readDataErr(31B), readOnly(32B), cylOffset(33B), iobParityErr(34B),
fifoParityErr(35B), writeError(36B), readError(37B),
others of interest only to Midas
(177B)};
diskDMuxAddr: [0..7777B] = 2000B; -- DMux address of first disk muffler
Filler: TYPE = [0..1] ← 0;
Data structures private to the software
DiskHandle: TYPE = MACHINE DEPENDENT RECORD [
unused (0: 0..7): Filler,
drive (0: 8..15): Drive];
nullDiskHandle: DiskHandle = LOOPHOLE [177777B];
Drive: TYPE = [0..drives);
drives: CARDINAL = 16; -- maximum number of drives per controller
The "system" drive is drive 0, which is a T-80 or T-300 (actually AMS-315) that is addressed in a funny way (for compatibility with Alto emulators), with cylinders incrementing before heads. t80 and t300 refer to drives other than drive 0, which are addressed in the conventional way. An AMS-315 is always treated the same as a T-300; that is, the extra 8 cylinders of an AMS-315 are not used.
Model: TYPE = {nonexistent, system80, system300, t80, t300};
modelCylinders: ARRAY Model OF CARDINAL = [
nonexistent: 0, system80: 815*5, system300: 815*19, t80: 815, t300: 815];
modelTrueCylinders: ARRAY Model OF CARDINAL = [
nonexistent: 0, system80: 815, system300: 815, t80: 815, t300: 815];
modelHeads: ARRAY Model OF CARDINAL = [
nonexistent: 0, system80: 1, system300: 1, t80: 5, t300: 19];
modelTrueHeads: ARRAY Model OF CARDINAL = [
nonexistent: 0, system80: 5, system300: 19, t80: 5, t300: 19];
modelSectors: ARRAY Model OF CARDINAL = [
nonexistent: 0, system80: 28, system300: 28, t80: 28, t300: 28];
Public variables
globalStateSize: PUBLIC CARDINAL ← 0;
an IOCB for label fixup is not required for the Dorado implementation.
nullDeviceHandle: PUBLIC DeviceHandle ← DeviceFromDiskHandle[nullDiskHandle];
operationSize: PUBLIC CARDINALSIZE[DoradoOperation];
Private variables
csb: CSBPtr = LOOPHOLE[LONG[177520B]];
totalErrors: CARDINAL ← 0; -- = total errors reported by Poll
modelTable: ARRAY [0..drives) OF Model ← ALL [nonexistent];
Public procedures
Assumes Initialize is called before GetDeviceAttributes
GetDeviceAttributes: PUBLIC PROCEDURE [device: DeviceHandle]
RETURNS [cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL] =
BEGIN
diskHandle: DiskHandle = DiskFromDeviceHandle[device];
model: Model = modelTable[diskHandle.drive];
RETURN [
cylinders: modelCylinders[model], movingHeads: modelHeads[model],
fixedHeads: 0, sectorsPerTrack: modelSectors[model]];
END;

Assumes Initialize is called before GetDeviceAttributes
GetTrueDeviceAttributes: PUBLIC PROCEDURE [device: DeviceHandle]
RETURNS [cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL] =
BEGIN
diskHandle: DiskHandle = DiskFromDeviceHandle[device];
model: Model = modelTable[diskHandle.drive];
RETURN [
cylinders: modelTrueCylinders[model], movingHeads: modelTrueHeads[model],
fixedHeads: 0, sectorsPerTrack: modelSectors[model]];
END;
Assumes Initialize is called before GetNextDevice
GetNextDevice: PUBLIC PROCEDURE [device: DeviceHandle] RETURNS [DeviceHandle] =
BEGIN
disk: DiskHandle = DiskFromDeviceHandle[device];
IF disk=nullDiskHandle THEN -- drive 0 presumed to exist always
RETURN [DeviceFromDiskHandle[[drive: 0]]];
FOR drive: Drive IN (disk.drive..drives) DO
IF modelTable[drive]#nonexistent THEN RETURN [DeviceFromDiskHandle[[drive: drive]]];
ENDLOOP;
RETURN [nullDeviceHandle];
END;
Assumes that the disk hardware and microcode are quiescent
Initialize: PUBLIC PROCEDURE [t: WORD, globalState: GlobalStatePtr] =
BEGIN OPEN DoradoInputOutput;
ManualStrobe: PROCEDURE [tag, data: TagCommand] =
BEGIN
Output[data, diskTag];
Output[PrincOpsUtils.BITOR[tag, data], diskTag];
Output[data, diskTag];
No need to wait: drive is advertised to return status within 200 ns
of its being selected.
END;
AutoStrobe: PROCEDURE [data: TagCommand] =
BEGIN
Output[data, diskTag];
Need to wait at least 1.2 microseconds for strobe sequence to complete,
plus an unspecified amount of time (probably less than a microsecond)
for the drive to return up-to-date status.
THROUGH [0..10) DO NULL; ENDLOOP;
END;
ReadMuffler: PROCEDURE [addr: MufflerAddress] RETURNS [status: BOOLEAN] =
BEGIN
Reads through system DMux rather than through DskEth muffler interface, because
it's impossible for the disk task to dismiss wakeups without clobbering the
muffler address.
RETURN [
DoradoInputOutput.RWMufMan[
[useDMD: FALSE, dMuxAddr: DMuxFromMufAddr[addr]]].dMuxData#0];
END;
ClassifyDrive: PROCEDURE [drive: Drive] RETURNS [model: Model] =
BEGIN
Select the drive. If it doesn't become selected then it doesn't exist.
ManualStrobe[tag: [driveTag: TRUE, bus: drive[driveNumber: 0]],
data: [bus: drive[select: TRUE, driveNumber: drive]]];
IF ReadMuffler[notSelected] THEN RETURN [nonexistent]
ELSE IF ReadMuffler[notOnLine] THEN RETURN [nonexistent]
ELSE BEGIN
Try to select a head which does not exist on a T80.
This causes a headOvfl error on a T80, not on a T300
AutoStrobe[[controlTag: TRUE, bus: control[deviceCheckReset: TRUE]]];
AutoStrobe[[headTag: TRUE, bus: head[headNumber: modelHeads[t80]]]];
model ← IF ReadMuffler[headOvfl] THEN t80 ELSE t300;
AutoStrobe[[controlTag: TRUE, bus: control[deviceCheckReset: TRUE]]];
END;
END;
lastDrive: Drive;
ResetDisk[disable]; -- disable disk task so it won't run and confuse matters
csb^ ← [
head: nilIOCBQueueHead, interruptMask: t, drive: 0,
cylinder: cylinderUnknown, tail: NIL];
Drives 0-2 are connected directly to the controller, and drive 3 also if
no multiplexor is present.
If a multiplexor is present, it is connected in place of drive 3.
Drive 17B is presumed not to exist. Try selecting drive 17B. If this succeeds
then what really happened is that no multiplexor is present and drive 3
got selected instead. If this fails then either there is a multiplexor
or there is no multiplexor and no drive 3 is present; it is safe to test each
of drives 4-16 since in this case they will select only if there is a multiplexor.
modelTable[0] ← SELECT ClassifyDrive[0] FROM
nonexistent => nonexistent,
t80 => system80,
t300 => system300,
ENDCASE => ERROR;
lastDrive ← (IF ClassifyDrive[LAST[Drive]]=nonexistent THEN LAST[Drive]-1 ELSE 3);
FOR drive: Drive IN [1..lastDrive] DO
modelTable[drive] ← ClassifyDrive[drive];
ENDLOOP;
ResetDisk[normal]; -- put disk microcode back in normal state
END;
InitializeCleanup: PUBLIC PROCEDURE =
BEGIN OPEN DeviceCleanup;
item: Item;
reason: Reason;
savedCSB: CSB;
DO
reason ← Await[@item];
SELECT reason FROM
turnOff, kill =>
BEGIN
UNTIL csb.head.tag = idle DO ENDLOOP;
savedCSB ← csb^;
csb.head.iocb ← nilIOCBShortPtr;
END;
turnOn =>
BEGIN
csb^ ← savedCSB;
csb.cylinder ← cylinderUnknown;
END;
ENDCASE
ENDLOOP
END;
Initiate: PUBLIC PROCEDURE [operationPtr: OperationPtr] =
BEGIN
doradoOperationPtr: DoradoOperationPtr = LOOPHOLE[operationPtr];
iocb: IOCBLongPtr = @doradoOperationPtr.iocb;
iocbShort: IOCBShortPtr = LOOPHOLE[PrincOpsUtils.LowHalf[iocb]];
diskCommand: DiskCommand ← SELECT operationPtr.command FROM
vv => [incrementDataPtr: FALSE, header: check, label: check, data: none],
vvr => [incrementDataPtr: FALSE, header: check, label: check, data: read],
vvw => [incrementDataPtr: FALSE, header: check, label: check, data: write],
vvv => [incrementDataPtr: FALSE, header: check, label: check, data: check],
vw => [incrementDataPtr: FALSE, header: check, label: write, data: none],
vww => [incrementDataPtr: FALSE, header: check, label: write, data: write],
vr => [incrementDataPtr: FALSE, header: check, label: read, data: none],
vrr => [incrementDataPtr: FALSE, header: check, label: read, data: read],
vrw => [incrementDataPtr: FALSE, header: check, label: read, data: write],
vrv => [incrementDataPtr: FALSE, header: check, label: read, data: check],
rv => [incrementDataPtr: FALSE, header: read, label: check, data: none],
rvr => [incrementDataPtr: FALSE, header: read, label: check, data: read],
rvw => [incrementDataPtr: FALSE, header: read, label: check, data: write],
rvv => [incrementDataPtr: FALSE, header: read, label: check, data: check],
rw => [incrementDataPtr: FALSE, header: read, label: write, data: none],
rww => [incrementDataPtr: FALSE, header: read, label: write, data: write],
rr => [incrementDataPtr: FALSE, header: read, label: read, data: none],
rrr => [incrementDataPtr: FALSE, header: read, label: read, data: read],
rrw => [incrementDataPtr: FALSE, header: read, label: read, data: write],
rrv => [incrementDataPtr: FALSE, header: read, label: read, data: check],
w => [incrementDataPtr: FALSE, header: write, label: none, data: none],
www => [incrementDataPtr: FALSE, header: write, label: write, data: write],
ENDCASE => ERROR;
diskCommand.incrementDataPtr ← operationPtr.incrementDataPtr;
BEGIN -- Fix up label file page number representation
kludge: LONG POINTER TO FilePageRep = LOOPHOLE[@operationPtr.labelPtr.filePage];
IF kludge.highHalf # 0 THEN kludge.pilotHi ← [filePageHi: kludge.highHalf];
END; -- Fix up
iocb^ ← [
next: nilIOCBShortPtr, seal: sealValid,
drive: DiskFromDeviceHandle[operationPtr.device].drive,
pageCount: operationPtr.pageCount, command: diskCommand,
diskAddress: operationPtr.clientHeader, diskHeader: operationPtr.clientHeader,
headerPtr: @iocb.diskHeader, headerECC: 0, headerStatus: nullDiskStatus,
labelPtr: IF diskCommand.label=read THEN operationPtr.labelPtr ELSE @iocb.diskLabel,
labelECC: 0, labelStatus: nullDiskStatus,
dataPtr: operationPtr.dataPtr, dataECC: 0, dataStatus: nullDiskStatus,
diskLabel: operationPtr.labelPtr^];
Chain this IOCB onto CSB for processing by microcode.
IF csb.head.iocb # nilIOCBShortPtr THEN csb.tail.next ← iocbShort;
IF csb.head.iocb = nilIOCBShortPtr AND iocb.seal = sealValid THEN
csb.head.iocb ← iocbShort;
csb.tail ← iocb;
END;
PollCounter: TYPE = RECORD[calls, inProg, nr0, nr1, nr2, nr3, nr4, reset: INT];
pollCounters: PollCounter← [0,0,0,0,0,0,0,0];
-- keep track of whether last command was inProgress, and if it seems to hang, reset the disk
nullDiskAddr: DiskAddress = [cylinder: 0, head: 0, sector: 0];
lastStatusWasInProgress: BOOLFALSE;
lastInProgressInfo: TYPE =
RECORD[drive: Drive, pageCount, count: CARDINAL, diskAddress, diskHeader: DiskAddress];
ifLastInProgress: lastInProgressInfo← [0, 0, 0, nullDiskAddr, nullDiskAddr];
to make things easier to read
DoradoDiskStatus: TYPE = {ok, seekInc, headOvfl, devCheck, notSelected, notOnLine, notReady, sectorOvfl, fifoUnderflow, fifoOverflow, checkErr, readOnly, cylOffset, iobParityErr, fifoParityErr, eccErr, sectorSearchErr};
LastErrorStatus0: TYPE = RECORD[
reported: Status, header, label, data: DoradoDiskStatus, addr: DiskAddress];
lastErrorStatus0: LastErrorStatus0;
LastErrorStatusOther: TYPE = RECORD[
drive: Drive, reported: Status, header, label, data: DoradoDiskStatus, addr: DiskAddress];
lastErrorStatusOther: LastErrorStatusOther;
Convert: PROC[ds: DiskStatus] RETURNS[dds: DoradoDiskStatus] =
BEGIN
RETURN[SELECT TRUE FROM
ds.seekInc => seekInc,
ds.headOvfl => headOvfl,
ds.devCheck => devCheck,
ds.notSelected => notSelected,
ds.notOnLine => notOnLine,
ds.notOnLine => notOnLine,
ds.notReady => notReady,
ds.sectorOvfl => sectorOvfl,
ds.fifoUnderflow => fifoUnderflow,
ds.fifoOverflow => fifoOverflow,
ds.checkErr => checkErr,
ds.readOnly => readOnly,
ds.cylOffset => cylOffset,
ds.iobParityErr => iobParityErr,
ds.fifoParityErr => fifoParityErr,
ds.eccErr => eccErr,
ds.sectorSearchErr => sectorSearchErr,
ENDCASE => ok];
END;
Poll: PUBLIC PROCEDURE [operationPtr: OperationPtr] RETURNS [status: Status] =
BEGIN
doradoOperationPtr: DoradoOperationPtr = LOOPHOLE[operationPtr];
iocb: IOCBLongPtr = @doradoOperationPtr.iocb;
iocbShort: IOCBShortPtr = LOOPHOLE[PrincOpsUtils.LowHalf[iocb]];
CaptureInProgress: PROC =
BEGIN
lastStatusWasInProgress← TRUE;
ifLastInProgress.count← 1;
ifLastInProgress.drive← iocb.drive;
ifLastInProgress.pageCount← iocb.pageCount;
ifLastInProgress.diskAddress← iocb.diskAddress;
ifLastInProgress.diskHeader← iocb.diskHeader;
END;
SameCommand: PROC RETURNS[BOOL]=
BEGIN
RETURN[(ifLastInProgress.diskAddress = iocb.diskAddress) AND
(ifLastInProgress.pageCount = iocb.pageCount) AND (ifLastInProgress.drive = iocb.drive)
AND (ifLastInProgress.diskHeader = iocb.diskHeader)];
END;
Capture in-progress/done state here and use it for all subsequent decisions.
If the command finishes during the code below, we will still report inProgress
to the client, who will Poll again and get the real ending state.
status ← IF iocb.seal=sealValid THEN inProgress ELSE goodCompletion;
Copy things back to client that he might want to monitor while the command
is still in progress.
operationPtr.clientHeader ← iocb.diskAddress;
operationPtr.pageCount ← iocb.pageCount;
pollCounters.calls← pollCounters.calls + 1;
Here we check for the possibility that the disk microcode is hung, trying to access a drive
that is OnLine but NotReady and hence getting no WakeUps
IF status=inProgress THEN
IF ~lastStatusWasInProgress THEN CaptureInProgress[]
ELSE
IF ~SameCommand[] THEN CaptureInProgress[]
ELSE
BEGIN
IF (ifLastInProgress.count← ifLastInProgress.count + 1) >= 5 THEN
BEGIN
diskNeedsRestarting: BOOLFALSE;
wentOffLine: BOOLFALSE;
pollCounters.inProg← pollCounters.inProg + 1;
-- wait a while before checking
THROUGH [0..30000) DO NULL; ENDLOOP;
FOR i: INTEGER IN [0..5) DO  -- check several times
IF (iocbShort = csb.head.iocb) AND (iocb.seal=sealValid)
THEN
IF (DoradoInputOutput.RWMufMan[
[useDMD: FALSE, dMuxAddr: DMuxFromMufAddr[notOnLine]]].dMuxData#0)
THEN {diskNeedsRestarting← TRUE; wentOffLine← TRUE; EXIT};
IF (DoradoInputOutput.RWMufMan[
[useDMD: FALSE, dMuxAddr: DMuxFromMufAddr[notReady]]].dMuxData#0)
THEN diskNeedsRestarting← TRUE
ELSE { diskNeedsRestarting← FALSE; EXIT};
SELECT i FROM
0 => pollCounters.nr0← pollCounters.nr0 + 1;
1 => pollCounters.nr1← pollCounters.nr1 + 1;
2 => pollCounters.nr2← pollCounters.nr2 + 1;
3 => pollCounters.nr3← pollCounters.nr3 + 1;
4 => pollCounters.nr4← pollCounters.nr4 + 1;
ENDCASE;
ENDLOOP;
IF diskNeedsRestarting THEN
BEGIN
pollCounters.reset← pollCounters.reset + 1;
csb.head.iocb ← nilIOCBShortPtr;
iocb.seal← sealNil;
status← goodCompletion;
IF wentOffLine THEN iocb.dataStatus.notOnLine← TRUE
ELSE iocb.dataStatus.notReady← TRUE;
DoradoInputOutput.ResetDisk[normal]; -- disk microcode in normal state
END;
END;
END;
IF status#inProgress THEN
BEGIN
Command has completed: successfully if pageCount=0, unsuccessfully otherwise.
lastStatusWasInProgress← FALSE;
IF iocb.pageCount=0 THEN
BEGIN
IF iocb.diskAddress.head = modelHeads[modelTable[iocb.drive]] THEN
Successful transfer happened to end at a cylinder boundary.
Increment disk address to next cylinder.
BEGIN
iocb.diskAddress.head ← 0;
iocb.diskAddress.cylinder ← iocb.diskAddress.cylinder+1;
END
END
ELSE BEGIN
Error occurred. Must carefully examine DiskStatus for each block to determine
what happened and to decide what Status to report to the client.
combinedStatus: DiskStatus = PrincOpsUtils.BITOR[iocb.headerStatus,
PrincOpsUtils.BITOR[iocb.labelStatus, iocb.dataStatus]];
IF iocb.drive = 0 THEN
lastErrorStatus0← [
reported: inProgress, header: Convert[iocb.headerStatus],
label: Convert[iocb.labelStatus], data: Convert[iocb.dataStatus],
addr: iocb.diskAddress]
ELSE
lastErrorStatusOther← [reported: inProgress, drive: iocb.drive,
header: Convert[iocb.headerStatus], label: Convert[iocb.labelStatus],
data: Convert[iocb.dataStatus], addr: iocb.diskAddress];
SELECT TRUE FROM
First check for problems that might have caused the operation to
malfunction and generated other errors as secondary effects.
combinedStatus.notSelected OR combinedStatus.notOnLine OR
combinedStatus.notReady => status ← notReady;
combinedStatus.seekInc => status ← seekTimeout;
combinedStatus.sectorSearchErr => status ← sectorTimeout;
Check for head overflow: means transfer crossed a cylinder boundary.
combinedStatus.headOvfl =>
IF iocb.diskAddress.head = modelHeads[modelTable[iocb.drive]] THEN
BEGIN
iocb.diskAddress.head ← 0;
iocb.diskAddress.cylinder ← iocb.diskAddress.cylinder+1;
GOTO restart;
END
ELSE status ← hardwareError;
Look for header problems:
iocb.headerStatus.checkErr =>
Header check error: must discriminate possible causes
status ← SELECT TRUE FROM
iocb.diskHeader.cylinder#iocb.diskAddress.cylinder => wrongCylinder,
iocb.diskHeader.head#iocb.diskAddress.head => wrongHead,
ENDCASE => wrongSector;
Would like to distinguish other header errors (e.g., header checksum),
but there is no Status value with which to report them!!
PrincOpsUtils.BITAND[iocb.headerStatus, errorStatusMask]#0 => status ← hardwareError;
Look for label problems:
iocb.labelStatus.sectorOvfl OR iocb.labelStatus.eccErr => status ← labelError;
iocb.labelStatus.checkErr => status ← labelCheck;
PrincOpsUtils.BITAND[iocb.labelStatus, errorStatusMask]#0 => status ← hardwareError;
Look for data problems:
iocb.dataStatus.sectorOvfl OR iocb.dataStatus.eccErr => status ← dataError;
ENDCASE => status ← hardwareError;
Microcode has been deferring ever since the error occurred.
Now zap all deferred operations and allow new ones to be Initiated.
csb.head.iocb ← nilIOCBShortPtr;
totalErrors ← totalErrors+1;
IF iocb.drive = 0 THEN lastErrorStatus0.reported← status
ELSE lastErrorStatusOther.reported← status;
EXITS
restart =>
BEGIN
iocb.headerStatus ← iocb.labelStatus ← iocb.dataStatus ← nullDiskStatus;
iocb.seal ← sealValid;
csb.head.iocb ← iocbShort;
status ← inProgress;
END;
END;
Copy updated information back to client.
We already did clientHeader and pageCount.
Note: if reading label, it was read directly to client so don't clobber it here.
IF iocb.command.label#read THEN operationPtr.labelPtr^ ← iocb.diskLabel;
operationPtr.dataPtr ← iocb.dataPtr;
operationPtr.diskHeader ← iocb.diskHeader;
END;
IF status # inProgress
THEN -- Fix up label file page number representation
BEGIN
kludge: LONG POINTER TO FilePageRep = LOOPHOLE[@operationPtr.labelPtr.filePage];
IF kludge.highHalf # 0 THEN kludge.highHalf ← kludge.pilotHi.filePageHi;
END; -- Fix up
END;
Recalibrate: PUBLIC PROCEDURE [device: DeviceHandle] =
BEGIN csb.cylinder ← cylinderRestore; END;
Reset: PUBLIC PROCEDURE [device: DeviceHandle] =
BEGIN
WHAT SHOULD THIS DO?
END;
Private procedures
DiskFromDeviceHandle: PROCEDURE [device: DeviceHandle] RETURNS [DiskHandle] = INLINE
BEGIN RETURN[LOOPHOLE[device]]; END;
DeviceFromDiskHandle: PROCEDURE [disk: DiskHandle] RETURNS [DeviceHandle] = INLINE
BEGIN RETURN[LOOPHOLE[disk]]; END;
DMuxFromMufAddr: PROCEDURE [mufAdr: MufflerAddress]
RETURNS [DoradoInputOutput.DMuxAddr] = INLINE
BEGIN RETURN[diskDMuxAddr+LOOPHOLE[mufAdr, CARDINAL]]; END;
END.
LOG
Time: August 8, 1979 1:09 AM By: Redell Action: Add log to file from Jarvis
Time: August 17, 1979 1:27 PM By: Gobbel Action: Pilot formatting
Time: August 22, 1979 12:40 PM By: Gobbel Action: Bring up to date with revised device face
Time: October 2, 1979 11:38 PM By: Redell Action: Further cleanup, esp for bootchains (partial label verification)
Time: October 8, 1979 9:22 AM By: McJones Action: Restart controller bug, add hardwareState
Time: October 10, 1979 3:53 PM By: McJones Action: INLINE procedures for speed; labelCheck is cue to call StartLabelFix
Time: October 30, 1979 11:04 AM By: McJones AR2643: Label fix didn't reset iocb.label from user's label
Time: November 1, 1979 1:20 PM By: Frandeen Action: Fix recalibrate after header error; bug in SynchronizeWithController
Time: November 7, 1979 1:17 PM By: McJones AR2739: Label fix up confused by spurious labelCheck status returned by read label command on sector with missing (label?) synch byte
Time: November 9, 1979 8:59 AM By: Frandeen Action: Implement runs of pages. Change interface to Poll. Change LabelFix procedures. Change interface to StartB since we no longer need an extra IOCB for label fixup.
Time: December 13, 1979 11:29 AM By: Gobbel Action: Change name from SA4000D0Head to SA4000HeadD0
Time: January 30, 1980 12:48 PM By: McJones Action: Update to match new face; add StartChain logic
Time: June 25, 1980 10:34 AM By: McJones Action: 48-bit processor ids
Time: July 24, 1980 11:23 AM By: McJones Action: Label fixup must update iocb.operation.clientHeader
Time: December 6, 1980 2:56 PM Taft Convert for Dorado
Time: February 13, 1983 1:24 pm Taft Add support for multiple drives and T-300s
Time: May 5, 1983 2:49 pm By: Andrew Birrell Action: convert to new Cedar nucleus
Time: February 14, 1984 12:38:58 pm By: Willie-Sue Action: handle the case of drives that are OnLine but NotReady
Time: March 7, 1984 3:59:04 pm By Willie-Sue Action: poll counter
Time: May 9, 1984 5:45:03 pm PDT By Bob Hagmann Action: add GetTrueDeviceAttributes