-- 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.