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; 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 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]]; 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, 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 = [ 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, readOnly: FALSE, cylOffset: FALSE]; sealValid: CARDINAL = 125377B; sealNil: CARDINAL = 0; cylinderRestore: CARDINAL = 177777B; cylinderUnknown: CARDINAL = 77777B; 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): BOOL _ FALSE, -- valid only in label of page 0 temporary (0:14..14): BOOL _ FALSE, -- valid only in label of page 0 zeroSize (0:15..15): BOOL _ FALSE ] ], raw => [ lowHalf(0): CARDINAL, highHalf(1): CARDINAL ] ENDCASE ]; 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}; 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]; MuffInput: TYPE = MACHINE DEPENDENT RECORD [ unused (0: 0..14): Filler, bit (0: 15..15): BOOLEAN]; 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]; 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), (177B)}; diskDMuxAddr: [0..7777B] = 2000B; -- DMux address of first disk muffler Filler: TYPE = [0..1] _ 0; 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 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]; globalStateSize: PUBLIC CARDINAL _ 0; nullDeviceHandle: PUBLIC DeviceHandle _ DeviceFromDiskHandle[nullDiskHandle]; operationSize: PUBLIC CARDINAL _ SIZE[DoradoOperation]; csb: CSBPtr = LOOPHOLE[LONG[177520B]]; totalErrors: CARDINAL _ 0; -- = total errors reported by Poll modelTable: ARRAY [0..drives) OF Model _ ALL [nonexistent]; 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; 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; 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; 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]; END; AutoStrobe: PROCEDURE [data: TagCommand] = BEGIN Output[data, diskTag]; THROUGH [0..10) DO NULL; ENDLOOP; END; ReadMuffler: PROCEDURE [addr: MufflerAddress] RETURNS [status: BOOLEAN] = BEGIN RETURN [ DoradoInputOutput.RWMufMan[ [useDMD: FALSE, dMuxAddr: DMuxFromMufAddr[addr]]].dMuxData#0]; END; ClassifyDrive: PROCEDURE [drive: Drive] RETURNS [model: Model] = BEGIN 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 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] _ 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^]; 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: BOOL_ FALSE; lastInProgressInfo: TYPE = RECORD[drive: Drive, pageCount, count: CARDINAL, diskAddress, diskHeader: DiskAddress]; ifLastInProgress: lastInProgressInfo_ [0, 0, 0, nullDiskAddr, nullDiskAddr]; 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; status _ IF iocb.seal=sealValid THEN inProgress ELSE goodCompletion; operationPtr.clientHeader _ iocb.diskAddress; operationPtr.pageCount _ iocb.pageCount; pollCounters.calls_ pollCounters.calls + 1; 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: BOOL_ FALSE; wentOffLine: BOOL_ FALSE; 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 lastStatusWasInProgress_ FALSE; IF iocb.pageCount=0 THEN BEGIN IF iocb.diskAddress.head = modelHeads[modelTable[iocb.drive]] THEN BEGIN iocb.diskAddress.head _ 0; iocb.diskAddress.cylinder _ iocb.diskAddress.cylinder+1; END END ELSE BEGIN 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 combinedStatus.notSelected OR combinedStatus.notOnLine OR combinedStatus.notReady => status _ notReady; combinedStatus.seekInc => status _ seekTimeout; combinedStatus.sectorSearchErr => status _ sectorTimeout; 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; iocb.headerStatus.checkErr => status _ SELECT TRUE FROM iocb.diskHeader.cylinder#iocb.diskAddress.cylinder => wrongCylinder, iocb.diskHeader.head#iocb.diskAddress.head => wrongHead, ENDCASE => wrongSector; PrincOpsUtils.BITAND[iocb.headerStatus, errorStatusMask]#0 => status _ hardwareError; iocb.labelStatus.sectorOvfl OR iocb.labelStatus.eccErr => status _ labelError; iocb.labelStatus.checkErr => status _ labelCheck; PrincOpsUtils.BITAND[iocb.labelStatus, errorStatusMask]#0 => status _ hardwareError; iocb.dataStatus.sectorOvfl OR iocb.dataStatus.eccErr => status _ dataError; ENDCASE => status _ hardwareError; 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; 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 END; 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 Ô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 Data structures shared with disk microcode Remainder of CSB is not used by the microcode IOCB must be odd word aligned Guarantee required odd word alignment, since Operation is 16-word aligned These bits constitute errors: These are just status bits that do not necessarily constitute errors: Microcode implements Pilot file page number layout in labels, but Cedar Face expects INT's Disk hardware definitions Output to DiskControl register, and used as diskCommand in IOCBs Output to DiskMuff register Input from DiskMuff register Output to DiskTag register Muffler addresses for status bits (DskEth-relative) others of interest only to Midas Data structures private to the software 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. Public variables an IOCB for label fixup is not required for the Dorado implementation. Private variables Public procedures Assumes Initialize is called before GetDeviceAttributes Assumes Initialize is called before GetDeviceAttributes Assumes Initialize is called before GetNextDevice Assumes that the disk hardware and microcode are quiescent No need to wait: drive is advertised to return status within 200 ns of its being selected. 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. 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. Select the drive. If it doesn't become selected then it doesn't exist. Try to select a head which does not exist on a T80. This causes a headOvfl error on a T80, not on a T300 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. Chain this IOCB onto CSB for processing by microcode. to make things easier to read 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. Copy things back to client that he might want to monitor while the command is still in progress. 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 Command has completed: successfully if pageCount=0, unsuccessfully otherwise. Successful transfer happened to end at a cylinder boundary. Increment disk address to next cylinder. Error occurred. Must carefully examine DiskStatus for each block to determine what happened and to decide what Status to report to the client. First check for problems that might have caused the operation to malfunction and generated other errors as secondary effects. Check for head overflow: means transfer crossed a cylinder boundary. Look for header problems: Header check error: must discriminate possible causes Would like to distinguish other header errors (e.g., header checksum), but there is no Status value with which to report them!! Look for label problems: Look for data problems: Microcode has been deferring ever since the error occurred. Now zap all deferred operations and allow new ones to be Initiated. 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. WHAT SHOULD THIS DO? Private procedures Ê«˜JšÏc2™2Jšœ™Jšœ6™6Jšœ3™3J™8J™:J˜šÏk ˜ Jšœžœ˜*Jšœ žœ ˜J˜Jšœžœ4˜KJšœžœžœžœ ˜-J˜ J˜—šœž˜Jšžœ0˜7Jšžœ˜$Jšžœ ˜—J˜Jšžœžœ ˜J˜Jšœ*™*J˜Jš œžœžœžœžœžœ˜#š žœžœžœž œžœ˜&J˜Jšœžœ˜Jšœ˜-Jšœžœ#˜;Jšœ.™.J˜J˜—š œžœžœž œžœ˜0šžœžœž˜J˜!J˜7Jšžœ˜ J˜——J˜DJ˜Jšœ™Jš œžœžœžœžœžœ˜>Jš œ žœžœžœžœžœ˜)š žœžœžœž œžœ˜'J˜Jšœ žœ˜J˜Jšœžœ˜J˜J˜J˜Jšœžœžœžœ ˜-Jšœžœžœ˜J˜Jšœžœžœžœ˜/Jšœžœžœ˜J˜Jšœžœžœ˜Jšœžœžœ˜J˜J˜!J˜—Jšœ žœ˜,J˜Jš œžœžœžœžœ˜;š œžœžœž œžœ˜2J˜JšœI™IJšœ žœ˜J˜—š œ žœžœž œžœ˜-Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜!Jšœžœ˜ Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜"Jšœžœ˜#Jšœžœ˜Jšœžœ˜&J˜—Jšœžœ˜)˜Jšœ™Jš œ žœ žœ žœžœ žœ˜RJš œ žœžœžœžœ˜JJš œ žœžœžœ žœ˜FJšœžœ˜JšœE™EJšœ žœ žœ˜#J˜—Jšœ žœ ˜Jšœ žœ˜J˜Jšœžœ ˜$Jšœžœ ˜#J˜JšœZ™Zšœ žœžœž œžœžœžœž˜Jšœ˜Jšœ žœ˜—˜ Jšœžœ˜šœ žœ˜Jšœ8˜VJšœ˜+Jšœžœžœ ˜DJšœžœžœ ˜DJšœžœž˜!J˜—J˜—˜Jšœ žœ˜Jšœ ž˜J˜—Jšž˜Jšœ˜—J˜Jšœ™J˜Jšœ@™@J˜/š œ žœžœž œžœ˜.Jšœžœžœ(˜UJ˜Jšœžœžœ˜*Jšœžœžœ˜%Jšœžœžœ˜)J˜ J˜!J˜ J˜$—Jšœžœžœž œ˜—šœžœžœžœ˜)J˜@J˜J˜—Jšœ™J˜Jšœžœžœ˜%JšœF™FJšœžœ5˜MJšœžœžœžœ˜7J˜J˜Jšœ™J˜Jšœžœžœ ˜&Jšœ žœ"˜=Jšœ žœ žœ žœ˜;J˜J˜Jšœ™J˜Jšœ7™7šÏnœžœž œ˜——Jšžœ˜—šŸ œž œžœ˜@Jšž˜JšœG™Gšœžœ˜?Jšœžœ˜6—Jšžœžœžœ˜5Jšžœžœžœžœ˜8šžœž˜ Jšœ3™3Jšœ4™4Jšœžœ!žœ˜EJšœžœ+˜DJšœžœžœžœ˜4Jšœžœ!žœ˜EJšžœ˜—Jšžœ˜—J˜Jšœ8˜L˜J˜3Jšœ!žœ˜&—JšœH™HJšœ™JšœA™AJšœO™OJšœG™GJšœG™GJšœM™MJšœR™Ršœžœž˜,J˜J˜J˜Jšžœžœ˜—Jš œ žœžœžœžœ žœ˜Ršžœžœž˜%J˜)Jšžœ˜—Jšœ*˜=Jšžœ˜J˜—šŸœžœž œ˜%Jšžœžœ˜J˜ J˜Jšœ žœ˜šž˜J˜šžœž˜˜Jšž˜Jšžœžœžœ˜%J˜J˜ Jšžœ˜—˜ Jšž˜J˜J˜Jšžœ˜—Jšž˜—Jšž˜—Jšžœ˜J˜—šŸœžœž œ˜9Jšž˜Jšœ)žœ˜@J˜-Jšœžœ˜@šœžœž˜;Jšœžœ+˜IJšœžœ+˜JJšœžœ,˜KJšœžœ,˜KJšœžœ+˜IJšœžœ,˜KJšœžœ*˜HJšœžœ*˜IJšœžœ+˜JJšœžœ+˜JJšœžœ*˜HJšœžœ*˜IJšœžœ+˜JJšœžœ+˜JJšœžœ*˜HJšœžœ+˜JJšœžœ)˜GJšœžœ)˜HJšœžœ*˜IJšœžœ*˜IJšœžœ*˜GJšœžœ,˜KJšžœžœ˜—J˜=J˜šžœ/˜5Jš œžœžœžœžœ"˜PJšžœžœ0˜K—Jšžœ ˜J˜˜ J˜'J˜7J˜8J˜NJ˜HJšœ žœžœžœ˜TJ˜)J˜FJ˜#J˜—Jšœ5™5Jšžœ!žœ˜Bšžœ!žœž˜AJ˜—J˜Jšžœ˜J˜—Jšœ žœžœ0žœ˜OJšœ-˜-Jš^˜^J˜Jšœ>˜>Jšœžœžœ˜%šœžœ˜Jšžœ!žœ(˜W—JšœL˜LJ˜Jšœ™JšœžœÅ˜ÛJ˜šœžœžœ˜ JšœL˜L—Jšœ#˜#J˜šœžœžœ˜$JšœZ˜Z—Jšœ+˜+J˜šŸœžœžœ˜>šž˜šžœžœžœž˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ"˜"Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ"˜"Jšœ˜Jšœ&˜&—Jšžœ˜—Jšžœ˜—J˜šŸœžœž œžœ˜NJšž˜Jšœ)žœ˜@J˜-Jšœžœ˜@šŸœžœ˜Jšž˜Jšœžœ˜Jšœ˜Jšœ$˜$Jšœ,˜,Jšœ0˜0Jšœ.˜.Jšžœ˜—šŸ œžœžœžœ˜ šž˜Jšžœ3žœ/žœ'žœ2˜Ê—Jšžœ˜—J˜JšœL™LJšœN™NJšœA™AJšœ žœžœ žœ˜DJ˜JšœJ™JJšœ™J˜-J˜(Jšœ+˜+J˜J™[J™8J˜šžœž˜Jšžœžœ˜4—šž˜Jšžœžœ˜*Jšž˜Jšž˜Jšžœ;ž˜Ašž˜Jšœžœžœ˜!Jšœ žœžœ˜Jšœ-˜-Jš ˜ Jšžœ žœžœžœ˜%š žœžœžœžœ˜3Jšžœžœ˜8šž˜šžœ˜Jšœ žœ4˜B—Jšžœžœžœžœ˜:J˜šžœ˜Jšœ žœ3˜A—Jšžœž˜Jšžœžœžœ˜)——šžœž˜ Jšœ,˜,Jšœ,˜,Jšœ,˜,Jšœ,˜,Jšœ,˜,—Jšžœ˜Jšžœ˜šžœž˜šž˜Jšœ+˜+J˜ Jšœ˜Jšœ˜šžœ žœž˜3Jšžœžœ˜%—Jšœ%"˜G—Jšžœ˜—Jšžœ˜—Jšžœ˜—J˜šžœž˜Jšž˜JšœM™MJšœžœ˜šžœž˜Jšž˜šžœ<ž˜BJšœ;™;Jšœ(™(Jšž˜J˜J˜8Jšž˜—Jšž˜—šžœž˜ JšœN™NJšœ@™@šœ+žœ˜CJšœžœ%˜8—šžœž˜šœ˜Jšœ9˜9JšœA˜AJšœ˜——šž˜šœ?˜?JšœE˜EJšœ8˜8——J˜šžœžœž˜Jšœ@™@Jšœ<™<šœžœž˜9J˜-—J˜/J˜9J˜JšœD™D˜šžœ<ž˜BJšž˜J˜J˜8Jšžœ ˜ Jšž˜—Jšžœ˜—Jšœ™˜Jšœ5™5šœ žœžœž˜J˜DJ˜8Jšžœ˜——JšœF™FJšœ8™8JšœžœA˜UJ˜Jšœ™Jšœžœ0˜NJ˜1Jšœžœ@˜TJ˜Jšœ™Jšœžœ.˜KJšžœ˜"J˜—Jšœ;™;JšœC™CJ˜ J˜šžœžœ"˜8Jšžœ'˜+—šž˜˜ Jšž˜J˜HJ˜J˜J˜Jšžœ˜——Jšžœ˜J˜—Jšœ(™(Jšœ*™*JšœP™PJšžœžœ)˜HJ˜$J˜*Jšžœ˜—J˜Jšžœ˜šžœ/˜4Jšž˜Jš œžœžœžœžœ"˜PJšžœžœ-˜HJšžœ ˜—Jšžœ˜J˜—šŸ œžœž œ˜6Jšžœ!žœ˜*J˜—šŸœžœž œ˜0Jšž˜Jšœ™Jšžœ˜—J˜J˜Jšœ™J˜šŸœž œžœž˜TJšžœžœžœ žœ˜$J˜—šŸœž œžœž˜RJšžœžœžœ žœ˜"J˜—šŸœž œ˜3Jšžœ ž˜-Jš žœžœžœ žœžœ˜;J˜—Jšžœ˜Jšž˜Jšœžœ/˜LJšœžœ$˜BJšœžœ=˜\JšœžœT˜sJšœžœ>˜\Jšœžœžœ>˜xJšœžœH˜hJšœžœZ˜yJšœžœ’˜±Jšœžœ¡žœ˜ÕJšœžœA˜bJšœžœC˜cJšœžœ)˜FJšœžœH˜eJšœžœ˜9JšœKžœ˜QJ˜SJ˜rJ˜BJ˜TJ˜J˜J˜J˜—…—YØ„W