-- DicentraDiskDriverImpl.mesa -- Driver for Century Data AMS-315 disk drive and Xylogics SMD controller -- Jim Gasbarro 19-Feb-85 14:04:11 -- -- ************** 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, ProcessorFace USING [SetMP], SpecialRuntime; DicentraDiskDriverImpl: MONITOR IMPORTS OthelloDefs, ColorVersatecUtils, DicentraInputOutput, Inline, Process, ProcessorFace, SpecialRuntime 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 ← TRUE, relocate (0: 1..1): BOOLEAN ← FALSE, chainEnable (0: 2..2): BOOLEAN ← FALSE, intEnable (0: 3..3): BOOLEAN ← TRUE, 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 ← TRUE, intOnError (0: 2..2): BOOLEAN ← FALSE, holdDualPort (0: 3..3): BOOLEAN ← FALSE, autoSeekRetry (0: 4..4): BOOLEAN ← TRUE, 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: ENTRY 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 WAIT cv↑; ENDLOOP; WriteByte[intPending, controlStatus]; WriteByte[0, controlStatus]; 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: [command: writeFormat], 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: [relocate: TRUE, command: read]]; 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: [relocate: TRUE, command: write]]; 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: [relocate: TRUE, command: seek]]; 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: [relocate: TRUE, command: driveReset]]; 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; 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: PUBLIC PROC [unit: Unit ← 0] RETURNS [] = BEGIN bits: CARDINAL; p: LONG POINTER ← @rdsPB; rdsPB: IOPB ← [ diskCommand: [relocate: TRUE, command: readDriveStatus], 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["Status (see pg. 47 of controller manual): "]; OthelloDefs.WriteOctal[bits ← LOOPHOLE[rdsPB.statusByte2]]; OthelloDefs.WriteLine["B"]; OthelloDefs.WriteString["Max Head: "]; OthelloDefs.WriteLongNumber[bits ← LOOPHOLE[rdsPB.headAdd]]; OthelloDefs.WriteLine[""]; OthelloDefs.WriteString["Max Sector: "]; OthelloDefs.WriteLongNumber[bits ← LOOPHOLE[rdsPB.sectorAdd]]; OthelloDefs.WriteLine[""]; OthelloDefs.WriteString["Max Cylinder: "]; bits ← (CARDINAL[rdsPB.cylAddHigh] * 256) + CARDINAL[rdsPB.cylAddLow]; OthelloDefs.WriteLongNumber[bits]; OthelloDefs.WriteLine[""]; OthelloDefs.WriteString["Head Offset: "]; OthelloDefs.WriteLongNumber[bits ← LOOPHOLE[rdsPB.headOffset]]; OthelloDefs.WriteLine[""]; OthelloDefs.WriteString["Firmware Rev: "]; OthelloDefs.WriteOctal[bits ← LOOPHOLE[rdsPB.sectorCountHigh]]; OthelloDefs.WriteLine["B"]; OthelloDefs.WriteString["Sector Size: "]; bits ← (CARDINAL[rdsPB.dataAddHigh] * 256) + CARDINAL[rdsPB.dataAddLow]; OthelloDefs.WriteLongNumber[bits]; OthelloDefs.WriteLine[""]; OthelloDefs.WriteString["Disk Sectors per Track (including runts): "]; OthelloDefs.WriteLongNumber[bits ← LOOPHOLE[rdsPB.dataRelocLow]]; OthelloDefs.WriteLine[""]; END; SelfTest: PUBLIC PROC [] = BEGIN bits: CARDINAL; p: LONG POINTER ← @stPB; stPB: IOPB ← [diskCommand: [command: selfTest]]; 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: [command: noOp]]; 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; cv: LONG POINTER TO CONDITION; mask: WORD; [cv, mask] ← SpecialRuntime.AllocateNakedCondition[]; [] ← DicentraInputOutput.SetWakeupMask[bits: mask, intLevel: disk]; Process.SetTimeout[cv, Process.SecondsToTicks[2]]; DiskInitialCheckout[]; END.