*-----------------------------------------------------------
Title[DiskSubrs.mc...November 23, 1982  10:03 AM...Taft];
*** Pilot-only version ***
*-----------------------------------------------------------

Set[XTask, IP[DSK]];
KnowRBase[DiskRegs];
TopLevel;


*-----------------------------------------------------------
Read1Muff:	* Read one muffler
* Enter: KTemp0 = muffler number
* Exit: KTemp0 = T = muffler value (0 or 1, right-justified); TIOA[DiskMuff]
*-----------------------------------------------------------
Subroutine;

	TIOA[DiskMuff], Global;
	Output← KTemp0;
	Nop;				* 1-cycle wait required by hardware
	KTemp0← T← Input, Return;


*-----------------------------------------------------------
Read20Muffs:	* Read 20 consecutive mufflers
* Enter: T = starting muffler number
* Exit: KTemp0 = T = word full of muffler values, left-to-right
*	TIOA[DiskMuff]
* Clobbers KTemp1
*-----------------------------------------------------------
Subroutine;

	KTemp1← T, TIOA[DiskMuff];
	KTemp0← T← 1C;			* Flag bit shifted out when done
Read20MLoop:
	KTemp1← (KTemp1)+1, Output← KTemp1;
	KTemp0← ((KTemp0) OR T) LSH 1;
	T← Input, Branch[Read20MLoop, ALU>=0]; * Tests unshifted ALU output
	KTemp0← T← (KTemp0) OR T, Return;


*-----------------------------------------------------------
DoMuffOutput:	* Do output to muffler register, for clearing TWs, etc.
* Enter: T = muffler command
* Exit: TIOA[DiskMuff]
*-----------------------------------------------------------
Subroutine;

	TIOA[DiskMuff], Global;
	Output← T, Return;

*-----------------------------------------------------------
SetDriveAndSubSector:	* Select new drive and set subsector counter
* Enter: T = drive number
*	KTemp0 = subsector count -1 (the way the hardware wants it)
* Exit:	KSelect updated to new drive number
* Clobbers T, KTemp0; invalidates Sector
* Clears all TWs and sets BlockTilIndex.
*-----------------------------------------------------------
Subroutine;

	Sector← Link;
TopLevel;
	KSelect← (KSelect) AND (Not[tagDriveNumber!]C); * Mask out old drive number
	KSelect← T← (KSelect) OR T;	* Insert new drive number
	KTemp0← LSH[KTemp0, 6];		* Combine subsector count with drive
	T← (KTemp0) OR T, Call[SendDriveTag]; * Select the drive
	T← T OR (tagLoadSubSector), Call[SendDriveTag]; * Load subsector count

* There is a potential race here.  We must first clear all TWs, then set
* BlockTilIndex, then clear SectorTW which might have come up in the meantime.
* Fortunately, IndexTW does a DC reset of BlockTilIndex if it comes up
* in the meantime.
	T← clearAllTWs, Call[DoMuffOutput];
	T← K400, TIOA[DiskControl], Call[OutputGetsT]; * T← blockTilIndex
	T← clearSectorTW;
	Sector← (Sector)-(Link← Sector)-1, * Sector← -1 to invalidate it
		Branch[DoMuffOutput];	* Clear SectorTW and return


*-----------------------------------------------------------
SendDriveTag:	* Select new drive
* Enter: T = drive number (perhaps including subsector count)
* Exit: T unchanged; TIOA[DiskTag];
* Clobbers KTemp0
*-----------------------------------------------------------
Subroutine;

	KTemp0← Link;
TopLevel;
	TIOA[DiskTag], Call[OutputGetsT]; * Send just drive number
	T← T OR (tagDrive), Call[OutputGetsT]; * Strobe the DriveTag
	T← T AND (tagBus);
	Link← KTemp0, Branch[OutputGetsT]; * Lower DriveTag and return

*-----------------------------------------------------------
SeekAndWaitForReady:	* Issue seek command and wait until it is done
* Enter: T = cylinder number
* Exit: Seek completed
* Clobbers T, KTemp0, KTemp1, KTemp2
* May leave a SectorTW or IndexTW dangling.
*-----------------------------------------------------------
Subroutine;

	KTemp2← Link;
TopLevel;
	T← T OR (tagCylinder), Call[SendTag];
SeekWait:				* Wait for seek to complete
	KTemp0← muffNotReady, Call[Read1Muff];
	KTemp0, Branch[SeekDone, R even]; * Branch if ready
	KStatus← A0, Block, Call[UpdateSector]; * Not ready, await next sector
	Branch[SeekWait];
SeekDone:
	T← A0, Link← KTemp2,		* Restore Link and return
		Branch[OutputGetsT];	* (Output← 0 to DiskMuff does nothing)

*-----------------------------------------------------------
SendTag:	* Send tag command and wait for tag sequence to complete
* Enter: T = tag command
* Exit: TIOA[DiskMuff]
* Clobbers T, KTemp0, KTemp1
* Clears SeekTagTW, and counts sectors while waiting.
*-----------------------------------------------------------
Subroutine;

	KTemp1← Link;
TopLevel;
	KTemp0← T, TIOA[DiskMuff];	* First, clear SeekTagTW
	T← clearSeekTagTW, Call[OutputGetsT];
	T← KTemp0, TIOA[DiskTag], Call[OutputGetsT]; * Issue the tag command

* Wait until a SeekTagTW occurs, and handle sector wakeups while waiting.
SendTagWait:
	Call[UpdateSector];
	KTemp0← muffSeekTagTW, Block, Call[Read1Muff];
	T← clearSeekTagTW, KTemp0, Branch[.+2, R odd];
	Branch[SendTagWait];		* Wakeup wasn't SeekTagTW, wait more

* Got a SeekTagTW.  Clear it and return.  Know TIOA[DiskMuff].
	Link← KTemp1, Branch[OutputGetsT];

*-----------------------------------------------------------
WaitForSector:		* Wait for desired sector to arrive
* Enter: KAddr = desired sector number
* Call:	SCall[WaitForSector];
* Returns +1: failed to reach desired sector within 64 sector times
*	+2: succeeded:
*	Sector = KAddr
*	TIOA[DiskControl]
* Clobbers T, KTemp0, KTemp1, KTemp2
*-----------------------------------------------------------
Subroutine;

	KTemp1← Link;
TopLevel;
	KTemp2← -100C;
SectorWaitLoop:
	T← ClearSeekTagTW, Call[DoMuffOutput]; * Clear spurious SeekTagTWs
	Call[UpdateSector];		* Returns with T = Sector
	PD← (KAddr) XOR T, TIOA[DiskControl];
	PD← A0, Link← KTemp1, Branch[.+2, ALU=0];
Subroutine;
	KTemp2← (KTemp2)+1, Block, DblBranch[SectorWaitLoop, .+2, R<0];
	Return[ALU=0];			* Normal +2 return
	Return;				* Failure return

*-----------------------------------------------------------
UpdateSector:	* Check for sector/index wakeups and update Sector register
* Enter: Sector = current sector number (-1 => invalid)
*	MaxSectors = maximum number of sectors expected for this disk format
*	KSelect[4] = 1 iff sectors do not evenly divide the disk
* Exit:	Sector updated; T = Sector
*	TIOA[DiskMuff]
* Clobbers KTemp0
* Clears any SectorTW or IndexTW that occurs, but leaves other wakeups alone.
* Note that due to a design blunder, IndexTWs do not occur if the current drive
* is deselected.  When we see SectorTW, we compute Sector← (Sector+1) mod MaxSectors.
* Thus once we have seen one IndexTW we will stay synchronized.
* Additionally, due to another design botch, the index pulse does not cause a SectorTW
* if the number of subsectors/sector does not evenly divide the number of
* subsectors/revolution; so when the drive is deselected we miss the index pulse entirely!
* Therefore we must let the successor of MaxSectors-1 be 1 rather than 0; we can
* reset to 0 only when we actually see an index pulse.
* Note on sector numbering:
* For an n-sector format, the sector immediately before Index is numbered n-1
* and the sector immediately after Index is numbered 0, for the purpose
* of issuing commands.  However, since commands are executed in the sector
* after they are issued, the sector before Index is n-2 and the sector after
* Index is n-1, as physically recorded on the disk.
* This means that if the sectors do not evenly divide the disk, the unusable
* short sector immediately before Index is the one numbered n-2, not n-1
* as you might expect.
*-----------------------------------------------------------
Subroutine;

	TIOA[DiskMuff], Global;
	T← muffSectorTW;		* Select SectorTW muffler
	PD← (Sector)+1, Output← T;	* Carry← 1 iff Sector = -1
	T← muffIndexTW;
	KTemp0← Input;			* Bit 15 ← SectorTW muffler
	KTemp0← Output← T,		* Select IndexTW muffler
		Branch[NoSectorTW, R even]; * Branch if SectorTW not present
	Sector← T← (Sector)+1, XorSavedCarry; * Increment sector unless it is -1
	PD← (MaxSectors) XOR T;
	KTemp0← (KTemp0) OR (clearSectorTW),
		Branch[.+2, ALU#0];	* Branch if did not wrap around
	T← LDF[KSelect, 1, 13];		* Reset to 0 or 1, depending on whether or not
					* IndexTW causes a SectorTW
	Output← KTemp0, Branch[.+2];	* Clear SectorTW, leave muffIndexTW selected
NoSectorTW:
	T← Sector;
	KTemp0← Input;			* Bit 15 ← IndexTW muffler
	KTemp0← Or[clearSectorTW!, clearIndexTW!]C,
		Branch[.+2, R even];	* Branch if IndexTW not present
* If we see IndexTW, zero Sector and clear BOTH SectorTW and IndexTW.
	T← A0, Output← KTemp0;
	Sector← T, Return;

*-----------------------------------------------------------
ReadECC:	* Read ECC following a block that was read (not checked).
* A disk block read has: data, 2 garbage words, 2 ECC words.
* Control gets here having read the first garbage word and executed at
* least one additional microinstruction since the last Input.
* Enter: TIOA[DiskData]
* Exit:	T = first ECC word
*	KTemp0 = second ECC word
*	ALU = (KTemp0) OR T
*-----------------------------------------------------------
Subroutine;

* ReadFifoTW drops at T0 of the 4th cycle after Input, so we can Block
* no earlier than the 5th cycle after Input.
	T← A0;				* 2nd cycle
	T← T-1, Branch[., ALU=0];	* 3rd & 4th cycles
	Block;				* 5th cycle

* When we are awakened, we know we can input 3 words.
* Note: cannot issue back-to-back Inputs, because OutReg is not reloaded
* from the Fifo quickly enough.
	PD← Input;			* Garbage word
	Nop;
	T← Input;			* First ECC
	PD← A0, Branch[CheckECCLoop];	* Read second ECC and return


*-----------------------------------------------------------
CheckECC:	* Read ECC following a block that was checked
* Control gets here having read neither of the garbage words and executed at
* least 2 additional instructions since the last Input.
* Enter: TIOA[DiskData]
* Exit:	T = first ECC word
*	KTemp0 = second ECC word
*	ALU = (KTemp0) OR T
* Clobbers KTemp1
*-----------------------------------------------------------
Subroutine;

	KTemp1← 3C, Block;

* ReadFifoTW drops at T0 of the 2nd cycle after Input, so we can Block
* no earlier than the 3rd cycle after Input.
CheckECCLoop:
	KTemp0← Input, Branch[CheckECCEnd, ALU=0];
	T← KTemp0;
	Nop;
	KTemp1← (KTemp1)-1, Block, Branch[CheckECCLoop];

CheckECCEnd:
	PD← (KTemp0) OR T, Return;

*-----------------------------------------------------------
ClearDisk:	* Reset disk controller and reset all TWs.
* Enter: TIOA unknown
* Exit: TIOA[DiskMuff]; KSelect initialized
* Clobbers T, KTemp0, KTemp1
*-----------------------------------------------------------
Subroutine;

	KTemp1← Link;
TopLevel;
	T← DiskControl;			* Need full TIOA for init
	TIOA← T;

* Initialization after power-on is somewhat hairy because IOReset does not
* completely reset the controller; in particular, it does not reset the
* control or tag registers.  It DOES reset the drive select register, so no
* drive is selected and the undefined state of the tag register is harmless.
* However, we must be careful to reset the tag register sufficiently in
* advance of issuing a DriveTag so that there won't be a race between changing
* the Select and Tag lines to the drives.  The drive select register is loaded
* on the 1-to-0 transition of the DriveTag signal.
	T← clearEnableRun;		* Reset controller twice
	Output← T, Call[OutputGetsT];
	TIOA[DiskTag];
	T← tagDrive, Call[OutputGetsT];	* Ensure no 1-to-0 transition while
					* tag bus is changing
	T← A0, Call[OutputGetsT];	* Reset drive select register and clear
					* all tag bus latches
	T← clearAll, Call[DoMuffOutput]; * Clear all TWs

* Initialize KSelect to select drive 0.
	Link← KTemp1;
Subroutine;
	KSelect← tagSelectDrive, Return;