-- DiskHeadDorado.mesa
-- Last Edited by: Taft, May 22, 1983 1:55 pm
DIRECTORY
DeviceCleanup USING [Await, Item, Reason],
DoradoInputOutput USING [IOAddress, DMuxAddr, Output, ResetDisk, RWMufMan],
Environment USING [Base],
HeadStartChain USING [Start],
Inline USING [BITAND, BITOR, LowHalf],
PilotDisk USING [Handle, Label],
SA4000Face,
SA4000FaceExtras USING [www];
DiskHeadDorado: PROGRAM
IMPORTS DeviceCleanup, DoradoInputOutput, RemainingHeads: HeadStartChain, Inline
EXPORTS HeadStartChain, SA4000Face
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 = Environment.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 PilotDisk.Label,
labelECC (20B): LONG CARDINAL,
labelStatus (22B): DiskStatus,
dataPtr (23B): LONG POINTER,
dataECC (25B): LONG CARDINAL,
dataStatus (27B): DiskStatus,
diskLabel (30B): PilotDisk.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;
-- 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): BOOLEAN ← FALSE, -- in IOCB only; not defined in hardware
unused1 (0: 1..4): Filler,
clearEnableRun (0: 5..5): BOOLEAN ← FALSE,
debugMode (0: 6..6): BOOLEAN ← FALSE,
blockTilIndex (0: 7..7): BOOLEAN ← FALSE,
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): BOOLEAN ← FALSE,
setChecksumErr (0: 3..3): BOOLEAN ← FALSE,
clearIndexTW (0: 4..4): BOOLEAN ← FALSE,
clearSectorTW (0: 5..5): BOOLEAN ← FALSE,
clearSeekTagTW (0: 6..6): BOOLEAN ← FALSE,
clearErrors (0: 7..7): BOOLEAN ← FALSE,
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): BOOLEAN ← FALSE,
cylinderTag (0: 1..1): BOOLEAN ← FALSE,
headTag (0: 2..2): BOOLEAN ← FALSE,
controlTag (0: 3..3): BOOLEAN ← FALSE,
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): BOOLEAN ← FALSE,
select (0: 11..11): BOOLEAN ← FALSE,
driveNumber (0: 12..15): Drive],
cylinder => [
cylinder (0: 4..15): [0..7777B]],
head => [
unused (0: 4..7): Filler,
offset (0: 8..8): BOOLEAN ← FALSE,
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): BOOLEAN ← FALSE,
strobeEarly (0: 7..7): BOOLEAN ← FALSE,
write (0: 8..8): BOOLEAN ← FALSE,
read (0: 9..9): BOOLEAN ← FALSE,
addressMark (0: 10..10): BOOLEAN ← FALSE,
headReset (0: 11..11): BOOLEAN ← FALSE,
deviceCheckReset (0: 12..12): BOOLEAN ← FALSE,
headSelect (0: 13..13): BOOLEAN ← FALSE,
rezero (0: 14..14): BOOLEAN ← FALSE,
headAdvance (0: 15..15): BOOLEAN ← FALSE],
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 always assumed to be a T-80 that is
-- addressed in a funny way. t80 and t300 refer to drives other than drive 0,
-- which are addressed in the normal way.
Model: TYPE = {nonexistent, system, t80, t300};
modelCylinders: ARRAY Model OF CARDINAL = [
nonexistent: 0, system: 815*5, t80: 815, t300: 815];
modelHeads: ARRAY Model OF CARDINAL = [
nonexistent: 0, system: 1, t80: 5, t300: 19];
modelSectors: ARRAY Model OF CARDINAL = [
nonexistent: 0, system: 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 CARDINAL ← SIZE[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 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[Inline.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 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];
modelTable[0] ← system; -- drive 0 presumed to exist always
-- 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.
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[Inline.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],
SA4000FaceExtras.www =>
[incrementDataPtr: FALSE, header: write, label: write, data: write],
ENDCASE => ERROR;
diskCommand.incrementDataPtr ← operationPtr.incrementDataPtr;
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;
Poll: PUBLIC PROCEDURE [operationPtr: OperationPtr] RETURNS [status: Status] =
BEGIN
doradoOperationPtr: DoradoOperationPtr = LOOPHOLE[operationPtr];
iocb: IOCBLongPtr = @doradoOperationPtr.iocb;
iocbShort: IOCBShortPtr = LOOPHOLE[Inline.LowHalf[iocb]];
-- 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;
IF status#inProgress THEN
BEGIN
-- Command has completed: successfully if pageCount=0, unsuccessfully otherwise.
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 = Inline.BITOR[iocb.headerStatus,
Inline.BITOR[iocb.labelStatus, iocb.dataStatus]];
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!!
Inline.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;
Inline.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;
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;
END;
Recalibrate: PUBLIC PROCEDURE [device: DeviceHandle] =
BEGIN csb.cylinder ← cylinderRestore; END;
Reset: PUBLIC PROCEDURE [device: DeviceHandle] =
BEGIN
-- WHAT SHOULD THIS DO?
END;
Start: PUBLIC PROCEDURE = -- exported to HeadStartChain
BEGIN RemainingHeads.Start[]; 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
December 6, 1980 2:56 PM Taft Convert for Dorado
February 13, 1983 1:24 pm Taft Add support for multiple drives and T-300s
May 22, 1983 1:53 pm Taft Add www command