-- DicentraDiskDriverImpl.mesa -- Driver for Century Data AMS-315 disk drive and Xylogics SMD controller -- Jim Gasbarro 16-Mar-84 10:11:40 -- -- ************** W A R N I N G ************** -- On the Dicentra, the left byte is even and the right byte is odd. Bits are numbered 0..7 -- from left to right. These conventions are opposite of those found in the Xylogics-450 -- controller manual. -- ******************************************* DIRECTORY ColorVersatecUtils, DicentraDiskDriver, DicentraInputOutput, Inline, OthelloDefs, Process USING [Pause, SecondsToTicks], ProcessorFace USING [SetMP]; DicentraDiskDriverImpl: MONITOR IMPORTS ColorVersatecUtils, DicentraInputOutput, Inline, OthelloDefs, Process, ProcessorFace EXPORTS DicentraDiskDriver = BEGIN Head: TYPE = DicentraDiskDriver.Head; Sector: TYPE = DicentraDiskDriver.Sector; Cylinder: TYPE = DicentraDiskDriver.Cylinder; Unit: TYPE = DicentraDiskDriver.Unit; tracksPerCylinder: CARDINAL = DicentraDiskDriver.tracksPerCylinder; sectorsPerTrack: CARDINAL = DicentraDiskDriver.sectorsPerTrack; cylindersPerSpindle: CARDINAL = DicentraDiskDriver.cylindersPerSpindle; wordsPerSector: CARDINAL = DicentraDiskDriver.wordsPerSector; -- Disk Controller Registers DiskControllerRegister: TYPE = { iOPBRelocHigh, iOPBRelocLow, iOPBAddHigh, iOPBAddLow, controllerResetAndUpdate, -- read to reset controller, write to update IOPB controlStatus }; -- controlStatus register bit definitions goBusy: CARDINAL = 80H; generalError: CARDINAL = 40H; doubleError: CARDINAL = 20H; intPending: CARDINAL = 10H; addMode: CARDINAL = 08H; attnReq: CARDINAL = 04H; attnAck: CARDINAL = 02H; driveReady: CARDINAL = 01H; -- IOPB Definition IOPB: TYPE = MACHINE DEPENDENT RECORD [ intMode: IntMode, diskCommand: DiskCommand, statusByte2: StatusByte2 ← success, statusByte1: StatusByte1, driveTypeAndSelect: DriveTypeAndSelect, throttle: Throttle, sectorAdd: [0..256) ← 0, headAdd: [0..256) ← 0, cylAddHigh: [0..256) ← 0, cylAddLow: [0..256) ← 0, sectorCountHigh: [0..256) ← 0, sectorCountLow: SectorCountLow ← [writeSectorCountLow[0]], dataAddHigh: [0..256) ← 0, dataAddLow: [0..256) ← 0, dataRelocHigh: [0..256) ← 0, -- can't address above 24 bits dataRelocLow: [0..256) ← 0, reserved: [0..256) ← 0, headOffset: [0..256) ← 0, nextIOPBAddHigh: [0..256) ← 0, nextIOPBAddLow: [0..256) ← 0, eCCMaskPatternLow: [0..256) ← 0, eCCMaskPatternHigh: [0..256) ← 0, eCCBitAddHigh: [0..256) ← 0, eCCBitAddLow: [0..256) ← 0 ]; DiskCommand: TYPE = MACHINE DEPENDENT RECORD [ autoUpdate (0: 0..0): BOOLEAN ← FALSE, relocate (0: 1..1): BOOLEAN ← FALSE, chainEnable (0: 2..2): BOOLEAN ← FALSE, intEnable (0: 3..3): BOOLEAN ← FALSE, command (0: 4..7): Command ← noOp ]; Command: TYPE = MACHINE DEPENDENT {noOp(0), write(1), read(2), writeHeaders(3), readHeaders(4), seek(5), driveReset(6), writeFormat(7), readHeaderDataECC(8), readDriveStatus(9), writeHeaderDataECC(10), setDriveSize(11), selfTest(12)}; IntMode: TYPE = MACHINE DEPENDENT RECORD [ reserved (0: 0..0): BOOLEAN ← FALSE, intOnEachIOPB (0: 1..1): BOOLEAN ← FALSE, intOnError (0: 2..2): BOOLEAN ← FALSE, holdDualPort (0: 3..3): BOOLEAN ← FALSE, autoSeekRetry (0: 4..4): BOOLEAN ← FALSE, enableExtendedFunc (0: 5..5): BOOLEAN ← FALSE, eCCMode (0: 6..7): ECCMode ← correct ]; ECCMode: TYPE = MACHINE DEPENDENT {patternAndOffset(0), noFlagOrCorrect(1), correct(2), flagAndNoCorrect(3)}; StatusByte1: TYPE = MACHINE DEPENDENT RECORD [ errorSummary (0: 0..0): BOOLEAN ← FALSE, reserved (0: 1..2): BOOLEAN ← FALSE, controllerType (0: 3..5): ControllerType ← type440Controller, reserved2 (0: 6..6): BOOLEAN ← FALSE, done (0: 7..7): BOOLEAN ← FALSE ]; ControllerType: TYPE = MACHINE DEPENDENT {type440Controller(0), type450Controller(1), type472Controller(2), nonExistent(3)}; StatusByte2: TYPE = MACHINE DEPENDENT { success(0), intPending(1), busyConflict(3), operationTimeOut(4), headerNotFound(5), hardECCError(6), illegalCylAdd(7), sectorSlipError(9), illegalSectorAdd(0AH), lastSectorTooSmall(0DH), slaveACKError(0EH), cylAndHeadHeaderError(12H), seekRetry(13H), writeProtectError(14H), unimplementedCommand(15H), driveNotReady(16H), sectorCountZero(17H), driveFaulted(18H), illegalSectorSize(19H), selfTestA(1AH), selfTestB(1BH), selfTestC(1CH), softECCError(1EH), softECCErrorRecovered(1FH), illegalHeadError(20H), diskSequencerError(21H), seekError(25H), (0FFH) }; Throttle: TYPE = MACHINE DEPENDENT RECORD [ byteMode (0: 0..0): BOOLEAN ← FALSE, interleaveFactor (0: 1..4): [0..15] ← 0, -- n+1 to 1 interleave factor, 0 => 1:1 throttleSetting (0: 5..7): [0..7] ← 5 -- 2**(n+1) DMA cycles per burst, ]; -- 5 => 32 words per burst DriveTypeAndSelect: TYPE = MACHINE DEPENDENT RECORD [ driveType (0: 0..1): DriveType ← cyl823Sect32Head19, reserved (0: 2..5): BOOLEAN ← FALSE, unit (0: 6..7): Unit ← 0 ]; DriveType: TYPE = MACHINE DEPENDENT {cyl823Sect32Head19 (0), cyl823Sect32Head5 (1), cyl842Sect46Head20 (2), cyl2047Sect128Head255 (3)}; SectorCountLow: TYPE = MACHINE DEPENDENT RECORD [ SELECT OVERLAID * FROM writeSectorCountLow => [bits: [0..256)], readDriveStatus => [iOPBByteA: IOPBByteA], ENDCASE]; IOPBByteA: TYPE = MACHINE DEPENDENT RECORD [ notOnCylinder (0: 0..0): BOOLEAN, diskNotReady (0: 1..1): BOOLEAN, diskWriteProtect (0: 2..2): BOOLEAN, dualPortDriveBusy (0: 3..3): BOOLEAN, hardSeekError (0: 4..4): BOOLEAN, diskFault (0: 5..5): BOOLEAN, reserved (0: 6..7): BOOLEAN ← FALSE ]; -- PUBLIC PROCEDURES ActivateIOPB: PROC [hyperOffset: LONG CARDINAL] = BEGIN wordAdd: LONG CARDINAL ← LOOPHOLE[ColorVersatecUtils.hyperStart]; byteAdd: LONG CARDINAL ← (wordAdd + hyperOffset) * 2; WriteByte[Inline.LowByte[Inline.LowHalf[byteAdd]], iOPBAddLow]; Dally[]; WriteByte[Inline.HighByte[Inline.LowHalf[byteAdd]], iOPBAddHigh]; Dally[]; WriteByte[Inline.LowByte[Inline.HighHalf[byteAdd]], iOPBRelocLow]; Dally[]; WriteByte[Inline.HighByte[Inline.HighHalf[byteAdd]], iOPBRelocHigh]; Dally[]; WriteByte[goBusy, controlStatus]; WHILE DiskControllerBusy[] DO ENDLOOP; END; DiskControllerBusy: PROC [] RETURNS [BOOLEAN] = BEGIN bits: CARDINAL ← ReadByte[controlStatus]; RETURN[Inline.BITAND[bits, goBusy] # 0]; END; RawFormat: PUBLIC PROC [unit: Unit ← 0, cmdOffset: CARDINAL] RETURNS [noHardErrors: BOOLEAN] = -- Formats all available sectors (leaves no spares). Only returns FALSE on a double -- error. Should never happen. This code only tries to format 32 sectors per track. -- By changing the drive switches and issuing 'setDriveSize' commands this can be increased to -- 34 sectors per track. CDC9766 compatability is easier for now since it is wired in the -- controller EPROM. BEGIN CylinderFormat: PROC [cylinder: Cylinder] RETURNS [status: StatusByte2] = BEGIN p: LONG POINTER ← @formatPB; formatPB: IOPB ← [ diskCommand: [autoUpdate: TRUE, command: writeFormat], intMode: [autoSeekRetry: TRUE], driveTypeAndSelect: [unit: unit], headAdd: 0, sectorAdd: 0, sectorCountLow: Inline.LowByte[sectorsPerTrack * tracksPerCylinder], sectorCountHigh: Inline.HighByte[sectorsPerTrack * tracksPerCylinder] ]; formatPB.cylAddLow ← Inline.LowByte[cylinder]; formatPB.cylAddHigh ← Inline.HighByte[cylinder]; ColorVersatecUtils.HyperStore[sourcePtr: p, destOffset: cmdOffset, wordCount: SIZE[IOPB]]; ActivateIOPB[hyperOffset: cmdOffset]; ColorVersatecUtils.HyperRead[destPtr: p, sourceOffset: cmdOffset, wordCount: SIZE[IOPB]]; RETURN [formatPB.statusByte2]; END; hardError: BOOLEAN ← FALSE; result: StatusByte2; OthelloDefs.WriteLine["Begin Format"]; FOR currentCyl: CARDINAL IN [0..cylindersPerSpindle) DO ProcessorFace.SetMP[currentCyl]; FOR retry: CARDINAL IN [0..3) DO result ← CylinderFormat [cylinder: currentCyl]; IF DoubleError[] THEN LOOP; IF GoodCompletionCode[result] THEN EXIT; OthelloDefs.WriteString["Retry at Cylinder: "]; OthelloDefs.WriteLongNumber[currentCyl]; OthelloDefs.NewLine[]; REPEAT FINISHED => BEGIN OthelloDefs.WriteLine["ERROR: Hard Error on Format"]; hardError ← TRUE; END; ENDLOOP; ENDLOOP; OthelloDefs.WriteLine["Format Done"]; RETURN [hardError]; END; Read: PUBLIC PROC [head: Head, sector: Sector, cylinder: Cylinder, unit: Unit ← 0, sectorCount: CARDINAL, cmdOffset: CARDINAL, dataOffset: LONG CARDINAL] RETURNS [success: BOOLEAN] = BEGIN RawRead: PROC [] RETURNS [status: StatusByte2] = BEGIN wordAdd: LONG CARDINAL ← LOOPHOLE[ColorVersatecUtils.hyperStart]; byteAdd: LONG CARDINAL ← (wordAdd + dataOffset) * 2; p: LONG POINTER ← @readPB; readPB: IOPB ← [ diskCommand: [autoUpdate: TRUE, relocate: TRUE, command: read], intMode: [autoSeekRetry: TRUE] ]; readPB.driveTypeAndSelect.unit ← unit; readPB.headAdd ← head; readPB.sectorAdd ← sector; readPB.cylAddLow ← Inline.LowByte[cylinder]; readPB.cylAddHigh ← Inline.HighByte[cylinder]; readPB.sectorCountLow ← Inline.LowByte[sectorCount]; readPB.sectorCountHigh ← Inline.HighByte[sectorCount]; readPB.dataAddLow ← Inline.LowByte[Inline.LowHalf[byteAdd]]; readPB.dataAddHigh ← Inline.HighByte[Inline.LowHalf[byteAdd]]; readPB.dataRelocLow ← Inline.LowByte[Inline.HighHalf[byteAdd]]; ColorVersatecUtils.HyperStore[sourcePtr: p, destOffset: cmdOffset, wordCount: SIZE[IOPB]]; ActivateIOPB[hyperOffset: cmdOffset]; ColorVersatecUtils.HyperRead[destPtr: p, sourceOffset: cmdOffset, wordCount: SIZE[IOPB]]; RETURN [readPB.statusByte2]; END; bits: CARDINAL; result: StatusByte2; FOR retry: CARDINAL IN [0..3) DO result ← RawRead[]; IF DoubleError[] THEN LOOP; IF GoodCompletionCode[result] THEN EXIT; OthelloDefs.WriteString["Retry...StatusByte2: "]; OthelloDefs.WriteOctal[bits ← LOOPHOLE[result]]; OthelloDefs.NewLine[]; REPEAT FINISHED => BEGIN OthelloDefs.WriteString["ERROR: Hard Read Error at Cylinder: "]; OthelloDefs.WriteLongNumber[cylinder]; OthelloDefs.WriteString[" Head: "]; OthelloDefs.WriteLongNumber[head]; OthelloDefs.WriteString[" Sector: "]; OthelloDefs.WriteLongNumber[sector]; OthelloDefs.NewLine[]; ERROR; END; ENDLOOP; RETURN[TRUE]; END; Write: PUBLIC PROC [head: Head, sector: Sector, cylinder: Cylinder, unit: Unit ← 0, sectorCount: CARDINAL, cmdOffset: CARDINAL, dataOffset: LONG CARDINAL] RETURNS [success: BOOLEAN] = BEGIN RawWrite: PROC [] RETURNS [status: StatusByte2] = BEGIN wordAdd: LONG CARDINAL ← LOOPHOLE[ColorVersatecUtils.hyperStart]; byteAdd: LONG CARDINAL ← (wordAdd + dataOffset) * 2; p: LONG POINTER ← @writePB; writePB: IOPB ← [ diskCommand: [autoUpdate: TRUE, relocate: TRUE, command: write], intMode: [autoSeekRetry: TRUE] ]; writePB.driveTypeAndSelect.unit ← unit; writePB.headAdd ← head; writePB.sectorAdd ← sector; writePB.cylAddLow ← Inline.LowByte[cylinder]; writePB.cylAddHigh ← Inline.HighByte[cylinder]; writePB.sectorCountLow ← Inline.LowByte[sectorCount]; writePB.sectorCountHigh ← Inline.HighByte[sectorCount]; writePB.dataAddLow ← Inline.LowByte[Inline.LowHalf[byteAdd]]; writePB.dataAddHigh ← Inline.HighByte[Inline.LowHalf[byteAdd]]; writePB.dataRelocLow ← Inline.LowByte[Inline.HighHalf[byteAdd]]; ColorVersatecUtils.HyperStore[sourcePtr: p, destOffset: 0, wordCount: SIZE[IOPB]]; ActivateIOPB[hyperOffset: 0]; ColorVersatecUtils.HyperRead[destPtr: p, sourceOffset: 0, wordCount: SIZE[IOPB]]; RETURN [writePB.statusByte2]; END; bits: CARDINAL; result: StatusByte2; FOR retry: CARDINAL IN [0..3) DO result ← RawWrite []; IF DoubleError[] THEN LOOP; IF GoodCompletionCode[result] THEN EXIT; OthelloDefs.WriteString["Retry...StatusByte2: "]; OthelloDefs.WriteOctal[bits ← LOOPHOLE[result]]; OthelloDefs.NewLine[]; REPEAT FINISHED => BEGIN OthelloDefs.WriteString["ERROR: Hard Write Error at Cylinder "]; OthelloDefs.WriteLongNumber[cylinder]; OthelloDefs.WriteString[" Head: "]; OthelloDefs.WriteLongNumber[head]; OthelloDefs.WriteString[" Sector: "]; OthelloDefs.WriteLongNumber[sector]; OthelloDefs.NewLine[]; ERROR; END; ENDLOOP; RETURN[TRUE]; END; Seek: PUBLIC PROC [head: Head ← 0, cylinder: Cylinder ← 0, unit: Unit ← 0, cmdOffset: CARDINAL] RETURNS [success: BOOLEAN] = BEGIN RawSeek: PROC [] RETURNS [status: StatusByte2] = BEGIN p: LONG POINTER ← @seekPB; seekPB: IOPB ← [ diskCommand: [autoUpdate: TRUE, relocate: TRUE, command: seek], intMode: [autoSeekRetry: TRUE, enableExtendedFunc: TRUE] ]; seekPB.driveTypeAndSelect.unit ← unit; seekPB.headAdd ← head; seekPB.cylAddLow ← Inline.LowByte[cylinder]; seekPB.cylAddHigh ← Inline.HighByte[cylinder]; ColorVersatecUtils.HyperStore[sourcePtr: p, destOffset: 0, wordCount: SIZE[IOPB]]; ActivateIOPB[hyperOffset: 0]; ColorVersatecUtils.HyperRead[destPtr: p, sourceOffset: 0, wordCount: SIZE[IOPB]]; RETURN [seekPB.statusByte2]; END; bits: CARDINAL; result: StatusByte2; -- The controller does automatic retries, but I'll do them as well... FOR retry: CARDINAL IN [0..3) DO result ← RawSeek[]; IF DoubleError[] THEN LOOP; IF GoodCompletionCode[result] THEN EXIT; OthelloDefs.WriteString["Retry...StatusByte2: "]; OthelloDefs.WriteOctal[bits ← LOOPHOLE[result]]; OthelloDefs.NewLine[]; REPEAT FINISHED => BEGIN OthelloDefs.WriteString["ERROR: Hard Seek Error at Cylinder "]; OthelloDefs.WriteLongNumber[cylinder]; OthelloDefs.WriteString[" Head: "]; OthelloDefs.WriteLongNumber[head]; OthelloDefs.NewLine[]; RETURN[FALSE]; END; ENDLOOP; RETURN[TRUE]; END; DoubleError: PROC [] RETURNS [BOOLEAN] = BEGIN bits: CARDINAL; IF Inline.BITAND[bits ← ReadByte[controlStatus], doubleError] # 0 THEN BEGIN ErrorReset[]; DriveReset []; OthelloDefs.WriteString["ERROR: Double Error...CSR: "]; OthelloDefs.WriteOctal[bits]; OthelloDefs.NewLine[]; RETURN[TRUE]; END; RETURN[FALSE]; END; DriveReset: PROC [] = BEGIN p: LONG POINTER ← @resetPB; resetPB: IOPB ← [ diskCommand: [autoUpdate: TRUE, relocate: TRUE, command: driveReset], intMode: [autoSeekRetry: TRUE] ]; ColorVersatecUtils.HyperStore[sourcePtr: p, destOffset: 0, wordCount: SIZE[IOPB]]; ActivateIOPB[hyperOffset: 0]; END; ControllerReset: PROC [] = BEGIN -- reading has the side effect of resetting the controller, selecting drive 0, -- and testing the drive ready status bits: CARDINAL ← ReadByte[controllerResetAndUpdate]; Dally[100]; END; ErrorReset: PROC [] = BEGIN bits: CARDINAL; WriteByte[generalError, controlStatus]; Dally[]; IF Inline.BITAND[bits ← ReadByte[controlStatus], doubleError] # 0 OR Inline.BITAND[bits ← ReadByte[controlStatus], generalError] # 0 THEN OthelloDefs.WriteLine["ERROR ERROR: Could not clear error status"]; END; GoodCompletionCode: PROC [status: StatusByte2] RETURNS [BOOLEAN] = BEGIN bits: CARDINAL; IF Inline.BITAND[bits ← ReadByte[controlStatus], generalError] # 0 THEN BEGIN ErrorReset[]; OthelloDefs.WriteString["ERROR: General Error bit set...StatusByte2: "]; OthelloDefs.WriteOctal[bits ← LOOPHOLE[status]]; OthelloDefs.NewLine[]; END; SELECT status FROM success, seekRetry, softECCErrorRecovered => RETURN[TRUE]; operationTimeOut, headerNotFound, hardECCError => RETURN[FALSE]; cylAndHeadHeaderError, driveFaulted, driveNotReady, seekError => BEGIN ControllerReset[]; DriveReset[]; RETURN[FALSE]; END; intPending, busyConflict, illegalCylAdd, illegalSectorAdd, unimplementedCommand, sectorCountZero, illegalSectorSize, selfTestA, selfTestB, selfTestC, illegalHeadError => BEGIN OthelloDefs.WriteLine["ERROR: Program error or hard failure"]; ERROR; END; sectorSlipError => BEGIN OthelloDefs.WriteLine["ERROR: Sector Slip Error"]; ERROR; END; slaveACKError => BEGIN OthelloDefs.WriteLine["ERROR: XACK timeout"]; ERROR; END; lastSectorTooSmall, illegalSectorSize => BEGIN OthelloDefs.WriteLine["ERROR: Formatting Error, check drive configuration"]; ERROR; END; writeProtectError => BEGIN OthelloDefs.WriteLine["ERROR: Write Protect Error"]; ERROR; END; ENDCASE => BEGIN OthelloDefs.WriteLine["ERROR: Unknown Type"]; ERROR; END; END; ReadWriteTest: PUBLIC PROC [] = BEGIN BufferSetup: PROC [ptr: LONG POINTER TO ARRAY [0..wordsPerSector) OF CARDINAL, startValue: CARDINAL] = BEGIN FOR i: CARDINAL IN [0..LENGTH[buffer]) DO ptr↑[i] ← i + startValue; ENDLOOP; END; BufferClear: PROC [ptr: LONG POINTER TO ARRAY [0..wordsPerSector) OF CARDINAL] = BEGIN FOR i: CARDINAL IN [0..LENGTH[buffer]) DO ptr↑[i] ← 0; ENDLOOP; END; BufferOK: PROC [ptr: LONG POINTER TO ARRAY [0..wordsPerSector) OF CARDINAL, startValue: CARDINAL] RETURNS [BOOLEAN] = BEGIN FOR i: CARDINAL IN [0..LENGTH[buffer]) DO IF ptr↑[i] # (i + startValue) THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; END; PrintBuffer: PROC [ptr: LONG POINTER TO ARRAY [0..wordsPerSector) OF CARDINAL] = BEGIN FOR i: CARDINAL IN [0..LENGTH[buffer]/16) DO FOR j: CARDINAL IN [0..16) DO OthelloDefs.WriteLongNumber[ptr↑[i+j]]; ENDLOOP; OthelloDefs.NewLine[]; ENDLOOP; END; p: POINTER ← @buffer; buffer: ARRAY [0..wordsPerSector) OF CARDINAL; OthelloDefs.WriteLine["ReadWriteTest: Begin Write Phase"]; FOR i: CARDINAL IN [0..50) DO FOR j: CARDINAL IN [0..tracksPerCylinder) DO BufferSetup[ptr: p, startValue: i+j]; ColorVersatecUtils.HyperStore[sourcePtr: p, destOffset: 32, wordCount: LENGTH[buffer]]; [] ← Write[sector: j, head: j, cylinder: i, sectorCount: 1, cmdOffset: 0, dataOffset: 32]; ENDLOOP; ProcessorFace.SetMP[i]; ENDLOOP; OthelloDefs.WriteLine["ReadWriteTest: Begin Read Phase"]; FOR i: CARDINAL IN [0..50) DO FOR j: CARDINAL IN [0..tracksPerCylinder) DO [] ← Read[sector: j, head: j, cylinder: i, sectorCount: 1, cmdOffset: 0, dataOffset: 32]; ColorVersatecUtils.HyperRead[destPtr: p, sourceOffset: 32, wordCount: LENGTH[buffer]]; IF NOT BufferOK[ptr: p, startValue: i+j] THEN BEGIN OthelloDefs.WriteString["ERROR: Data Compare Error at sector: "]; OthelloDefs.WriteLongNumber[j]; OthelloDefs.WriteString[" head: "]; OthelloDefs.WriteLongNumber[j]; OthelloDefs.WriteString[" cylinder: "]; OthelloDefs.WriteLongNumber[i]; OthelloDefs.NewLine[]; PrintBuffer[ptr: p]; END; ENDLOOP; ProcessorFace.SetMP[i]; ENDLOOP; OthelloDefs.WriteLine["End ReadWriteTest"]; END; SeekTest: PUBLIC PROC [] = BEGIN success: BOOLEAN ← TRUE; OthelloDefs.WriteString["Begin Seek Test..."]; FOR i: CARDINAL IN [0..cylindersPerSpindle) DO success ← success AND Seek[cylinder: i, cmdOffset: 0]; success ← success AND Seek[cylinder: cylindersPerSpindle - i - 1, cmdOffset: 0]; ENDLOOP; IF NOT success THEN OthelloDefs.WriteLine["ERROR: Seek Test Failed"] ELSE OthelloDefs.WriteLine["Seek Test Complete"]; END; Dally: PROC [i: CARDINAL ← 10] = BEGIN FOR j: CARDINAL IN [0..i) DO ENDLOOP; END; DiskInitialCheckout: PUBLIC PROC [] = BEGIN bits: CARDINAL ← 0; WHILE TRUE DO ControllerReset[]; bits ← Inline.BITAND[driveReady, ReadByte[controlStatus]]; Dally[]; IF bits # 0 THEN EXIT; OthelloDefs.WriteLine["Drive 0 Offline: Make sure drive is spun up and the A-access light is lit"]; Process.Pause[Process.SecondsToTicks[2]]; ENDLOOP; END; ReadDriveStatus: PROC [unit: Unit ← 0] RETURNS [] = BEGIN bits: CARDINAL; p: LONG POINTER ← @rdsPB; rdsPB: IOPB ← [ diskCommand: [autoUpdate: TRUE, relocate: TRUE, command: readDriveStatus], intMode: [autoSeekRetry: TRUE, enableExtendedFunc: TRUE], driveTypeAndSelect: [unit: unit] ]; ColorVersatecUtils.HyperStore[sourcePtr: p, destOffset: 0, wordCount: SIZE[IOPB]]; ActivateIOPB[hyperOffset: 0]; ColorVersatecUtils.HyperRead[destPtr: p, sourceOffset: 0, wordCount: SIZE[IOPB]]; OthelloDefs.WriteString["Drive Status: "]; OthelloDefs.WriteOctal[bits ← LOOPHOLE[rdsPB.sectorCountLow]]; OthelloDefs.NewLine[]; END; SelfTest: PROC [] = BEGIN bits: CARDINAL; p: LONG POINTER ← @stPB; stPB: IOPB ← [diskCommand: [autoUpdate: TRUE, command: selfTest], intMode: [autoSeekRetry: TRUE, enableExtendedFunc: TRUE] ]; ColorVersatecUtils.HyperStore[sourcePtr: p, destOffset: 0, wordCount: SIZE[IOPB]]; ActivateIOPB[hyperOffset: 0]; ColorVersatecUtils.HyperRead[destPtr: p, sourceOffset: 0, wordCount: SIZE[IOPB]]; OthelloDefs.WriteString["SelfTest: StatusByte1: "]; OthelloDefs.WriteOctal[bits ← LOOPHOLE[stPB.statusByte1]]; OthelloDefs.WriteString[" StatusByte2: "]; OthelloDefs.WriteOctal[bits ← LOOPHOLE[stPB.statusByte2]]; OthelloDefs.NewLine[]; END; NoOp: PROC [] = BEGIN bits: CARDINAL; p: LONG POINTER ← @noPB; noPB: IOPB ← [diskCommand: [autoUpdate: TRUE, command: noOp], intMode: [autoSeekRetry: TRUE, enableExtendedFunc: TRUE] ]; ColorVersatecUtils.HyperStore[sourcePtr: p, destOffset: 0, wordCount: SIZE[IOPB]]; ActivateIOPB[hyperOffset: 0]; ColorVersatecUtils.HyperRead[destPtr: p, sourceOffset: 0, wordCount: SIZE[IOPB]]; OthelloDefs.WriteString["NoOp: StatusByte1: "]; OthelloDefs.WriteOctal[bits ← LOOPHOLE[noPB.statusByte1]]; OthelloDefs.WriteString[" StatusByte2: "]; OthelloDefs.WriteOctal[bits ← LOOPHOLE[noPB.statusByte2]]; OthelloDefs.NewLine[]; END; -- BEWARE HACKED TO BE BACKWARDS baseAddress: DicentraInputOutput.IOAddress = LOOPHOLE[LONG[4000H]]; ReadByte: PROC [y: DiskControllerRegister] RETURNS [CARDINAL] = BEGIN byteOffset: CARDINAL = y.ORD; IF (byteOffset MOD 2) # 0 THEN RETURN[DicentraInputOutput.InputByteEven[baseAddress+byteOffset/2]] ELSE RETURN[DicentraInputOutput.InputByteOdd[baseAddress+byteOffset/2]]; END; WriteByte: PROC [x: CARDINAL, y: DiskControllerRegister] = BEGIN byteOffset: CARDINAL = y.ORD; IF (byteOffset MOD 2) # 0 THEN DicentraInputOutput.OutputByteEven[x, baseAddress+byteOffset/2] ELSE DicentraInputOutput.OutputByteOdd[x, baseAddress+byteOffset/2]; END; DiskInitialCheckout[]; END.