:TITLE[Trident];	*Trident disk controller
			*Last edited: 24 November 1980 by Fiala

*Input device definitions
Set[triStatus,1];	*Status register
  MC[SeekInc,100000];		*Seek incomplete
  MC[BitClockFailed,40000];	*(catastrophic)
  MC[SectorFailed,20000];	*Sector or index pulse failed (catastrophic)
  MC[RPE,10000];		*RFIFO parity error
  MC[WPE,4000];			*WFIFO parity error (catastrophic)
  MC[NotReady,2000];
  MC[RTypeErr,1000];		*(catastrophic)
  MC[WDTL,400];			*WFIFO data late (catastrophic)
  MC[RDTL,200];			*RFIFO data late (catastrophic)
  MC[OffLine,100];
  *bits 10:15 are the current sector number
  *Catastrophic means causes emergency wakeup

*Bits in RFIFO status word:
  *MC[SeekInc,100000];
  MC[CylOvf,40000];
  MC[DevChk,20000];	*Head select, cylinder select, or write command and
			*disk not ready; illegal cylinder address; offset
			*active and cylinder select command; read-only and
			*write; certain errors during writing (e.g., more
			*than one head selected, no transitions of encoded
			*data, or heads more than 80 micro-inches off cylinder
			*A Re-zero command clears this error.
  MC[NotSelected,10000];	*Drive is offline or not powered up.
  MC[OffLine,4000];	*The drive is in test mode or the heads are not loaded
  *MC[NotReady,2000];	*A seek is in progress or the heads are not loaded
  MC[SecOvf,1000];	*Write command was active when the next sector pulse
			*occurred.
  *MC[WDTL,400];	*The 256-word WFIFO became empty while a read or write
			*was in progress.
  *MC[RDTL,200];	*The RFIFO became full.
  MC[CompareErr,100];	*The data read during a "Read and compare" operation
			*did not match the data read off the disk.
  MC[ReadOnly,40];	*Drive's "Read-only" switch is on.
  MC[Offset,20];	*Cylinder position is offset (error recovery mode).
*Bottom 4 bits are cleared by microcode before storing in core.
  MC[MpxPresent,10];
  MC[Sequenced,4];
  MC[Offline,2];
  *1 unused
  MC[ECCError,10];	*Microcode uses this when ECC0 or ECC1 non-zero

Set[triDataIn,2]; Set[triDataStore,Add[LShift[triTask,4],2]];
Set[triRStatus,3];

*Output device definitions
Set[triReset,0];	*Disables wakeups, empties FIFOs, clears errors, etc.
			*Value in RM is don't care.
Set[triWakeup,1];
  MC[ClrErr,200];
  MC[RunEn,100];
  MC[SecWake,40];
  MC[WFWake,20];
  MC[R1Wake,10];
  MC[R4Wake,4];
  MC[R16Wake,2];
  MC[EmerWake,1];
  MC[R16orEMer,3]; MC[R4orEmer,5]; MC[R1orEmer];
*Set[triWPtrs,2];	*Loads WFIFO Dolphin pointer from OData[0:7], disk
			*pointer from lsbyte of WFIFO (for checkout)
*Set[triRPtrs,3];	*Like triWPtrs but for RFIFO

*WFIFO is 20d bits wide (16 data, 1 parity, and 3 type). Five types are
*anticipated useful, represented by the five output operations below.
Set[triDataFetch,Add[LShift[triTask,4],10]];
Set[triDataOut,10];	*WFIFO ← vanilla data word
Set[triDataNCFetch,Add[LShift[triTask,4],11]];
Set[triDataNCOut,11];	*WFIFO ← data no compare--vanilla, but if checking
			*is in progress, word will not participate in the
			*check calculation.
Set[triWCount,12];	*Either a gap count, block size, or ECC field size in
			*units of disk words.
Set[triWTag,13];	*WFIFO ← tag command in Alto format; a status word
			*will be placed in RFIFO after execution.
Set[triWTagNS,17];	*WFIFO ← tag command; no status will be placed in
			*RFIFO after execution.


%Commands are initiated in a VM block arranged as follows:
640	pointer to first DCB (0 if none)
641	last drive selected; software stores 100000b+drive to change drive
	select; microcode clears 100000b when new drive is selected.
642	last cylinder selected; microcode writes this word when seek started
	???microcode sets to -1 when new drive is selected???
643	raw hardware status at last sector mark (not updated when
	the microcode is busy processing a disk command block.??).
644	error status written by microcode on last catastrophic error;
	microcode writes either 644 or 643 according to whether or not a
	catastrophic error occurred:
	0:4	unused
	5	Seek incomplete
	6:9	unused
	10	WDTL
	11	Invalid seal
	12:13	unused
	14	Last DCB was aborted
	15	Invalid sector (last DCB was aborted because the hardware
		sector counter never equaled the specified sector number
		within 64 sector times).

DCB's consist of an initial 5-word block:
DCB+0	cylinder address (0:3 zeroes, 4:15 cylinder)
DCB+1	head and sector select
	0	off track
	1	direction of off track
	2:4	unused
	5:7	head select
	8:11	unused
	12:15	sector
DCB+2	drive select (0:11 must be zeroes; 12:15 = drive number)
DCB+3	pointer to next DCB (0 if none); this word is read at the end of
	a disk command and written into 640.
DCB+4	ID (122645b); if this word doesn't contain 122645b, KBLK is zeroed
	and processing is terminated.  This word is overwritten with status
	info during disk processing so that a DCB table cannot be used twice
	by the controller without program intervention.

followed by an indefinite number of repetitions of the following:
DCB+5+6n	command (0 indicates done with all commands at this sector)
		0:3	unused
		4	check data
		5	unused
		6	strobe late (for error recovery)
		7	strobe early (for error recovery)
		8	write
		9	read
		10	unused
		11	head address reset (don't use this bit)
		12	device check reset
		13	head select (activates selected head--always set this
			bit for read or write)
		14	ReZero (causes drive to completely retract its arms
			and reposition them on cylinder 0).
		15	Advance head address (don't use this).
DCB+6+6n	word count (exact count of data words only--does not include
		check sum at end of block)
DCB+7+6n	VM address for data transfer
DCB+8+6n	ECC0 filled in after read (???untouched on write???)
DCB+9+6n	ECC1 filled in after read
DCB+10+6n	0:10 drive RFIFO status; 11 ECC error; 12:15 the constant 1
%

SetTask[triTask];

Set[triRBase,And[60,triDRUN0]]; *first of the 16 RM locations used by the disk

RV4[triTemp,triTemp1,triLongPointer,triLongPointer1,Add[0,triRBase]];
RV2[tri640,tri640hi,Add[?,triRBase]];
RV2[triDCB,triDCBhi,Add[?,triRBase]];
RV4[triCoreDCB,triDrive,triTrack,triHWStatus,Add[?,triRBase]];

RV[triCount,Add[4,triRBase]];
*Bit 0 default partition, 10b in 1:4, 626b in 6:16b, and zeroes in 5&17b.
RV[triPartition,Add[5,triRBase]];
RV[triAltoCommand,Add[6,triRBase]];
RV[triIntWord,Add[7,triRBase]];

*Next two registers are for testing and can be removed if necessary.
RV[triHeaderFlakeCnt,Add[10,triRBase]];
RV[triServiceLateCnt,Add[11,triRBase]];

RV2[triAltoStatus,triAltoDA,Add[12,triRBase]];
*triAltoDA used during setup for transfer, triDiskCommand during transfer
RM[triDiskCommand,IP[triAltoDA]];

RV[triDiskStatus,Add[14,triRBase]];	*read from controller at start of every sector

*Bits 0:4 current sector, head settle flag in bit 5, bits 8:15 track.
RV[triPosition,Add[15,triRBase]];

*Throwaway initialization code

OnPage[DiskInitPage];
triInit:
	triDCBhi ← 0C, At[triInitLoc];
	tri640hi ← 0C;
	tri640 ← 400C;
	tri640 ← (tri640) or (240C);
	Output[triTemp,triReset];	*Reset 
	triPosition ← 0C;
*recalibrate and synchronize: get to track zero, sector 0
	LoadPage[triPage];
	GotoP[?];	*Track0, heads settling

%Here are codes, locations, and reasons for all errors posted by this microcode:

CODE	LOCATION		REASON

10	NoCommand		No transfer last sector (normal)
203	BadSeal			Seal comparison with 110b failed
%

OnPage[triPage];

triIOSRet:
	IOStrobe;
triRet:	triTemp ← triTemp, Return;

triIdle:
	triTemp ← SecWake;	*Wakeup only at sectors
	Output[triTemp,triWakeup], Call[triIOSRet];

*Wakeup here once/sector when there have been no commands.
*Store status register; start command execution (if any) else execute drive
*select (if any).
	PFetch2[tri640,triCoreDCB,0];
	Input[triHWStatus,triStatus];
*Ignore possible drive change if also have new DCB
	T ← triCoreDCB;
	triDCB ← T, Goto[triNewDCB,ALU#0];
	triDrive ← RHMask[triDrive], Skip[R>=0];
	  triTrack ← (Zero) - 1, Goto[triNewDrive];	*Drive select
	PStore1[tri640,triHWStatus,3], Goto[triIOSRet];


*Connect to new drive; then ignore DCB pointer because an operation cannot
*be setup until the next sector anyway.
triNewDrive:
	Nop;
	PStore4[tri640,triCoreDCB,0];
***Issue drive select tag and task
**??maybe should wait for drive select completion before reading status??
**??maybe should do seek if command also setup??

*A new DCB chain is starting
triNewDCB:


*Get here at the end of sector with final status words for the previous
*transfer in RFIFO.
triChain:


triSetLPhi:
	T ← triDCBhi;		*Set high half of base register
	triLongPointer1 ← T, Return;

triIncLPNT:
	UseCTask, Goto[triWTx];

DoTransfer:
	LU ← (triAltoCommand) and (2C);		*check SeekOnly flag
*clear all but head number.
	triAltoDA ← (triAltoDA) and (7C), Skip[ALU=0];
	  triAltoStatus ← 10C, Goto[Chain];	*Seek-only cmd--chain
*Here when ready to do a transfer.
	LU ← LdF[triAltoCommand,12,1];
	Output[triAltoDA,triDr/HdReg], Skip[ALU#0];	*load head register
	  triTemp1 ← 0C, Goto[DoHeader];
*Load Label buffer
	PFetch1[triDCB,triLongPointer,4], Call[triSetLPhi];
	triTemp1 ← 4C;
	T ← LdF[triLongPointer,16,2];
	triTemp1 ← (triTemp1) - T;
	Output[triTemp1,triMemBufAddr];
***Why isn't this bypass kludge here?***
	triTemp1 ← Rsh[triTemp1,2], Call[triReturn]; *interlock, task
	triTemp1 ← 0C, Skip[R Odd];	*If quadaligned label, do 2 IOFetch4's
	  IOFetch4[triLongPointer,triDataFetch,0], Call[triIncLPNT];
	IOFetch4[triLongPointer,triDataFetch,0], Call[triIncLPNT];
	IOFetch4[triLongPointer,triDataFetch,0], Call[triReturn];

DoHeader:
HeaderWriteOrCheck:
LoadHeaderBuffer:
triLabelWrite:
triLabelRdChk:
triWaitD:
HeaderIn:
HeaderFlake: 
HeaderCheckFlake:
NoHeaderFlake:
FirstLabel:
*Get here when the RDC has read the entire label into buffer[6-13d].
*Check it (if Alto command was check) or transfer it to memory (if Alto command was read).
LabelIn:
*SUBROUTINE triHLRC gets one word of the header or label, and does a read or
*check.  On entry, triLongPointer contains the memory address, and
*triCommand[11b] = 1 for check, 0 for read; updates triLongPointer.
triHLRC:
	Input[triTemp1,triBufferInput], Skip[ALU=0];	*get word from disk
	  PStore1[triLongPointer,triTemp1,0], Goto[HLReturn]; *store the data.
	PFetch1[triLongPointer,triTemp,0];	*get the data from memory.
	T ← triTemp;		*SIGH!, long abort
	LU ← (triTemp1) xor T, Skip[ALU#0];
	  PStore1[triLongPointer,triTemp1,0], Goto[HLReturn]; *memory zero - store disk data, no check
	Skip[ALU#0];		*check for data equal
HLReturn:	triLongPointer ← (triLongPointer) + 1, Return;	*data were equal, no store, check OK.
	triAltoStatus ← 2C, Goto[FlushCmd]; *Status ← Check error
NoLabelWait:
VerifyHeader:

WriteDataField:
	triDiskCommand ← WForEmer;
*Wakeup on WFIFO enough space or emergency
	Output[triDiskCommand,triWakeup], Call[triIOSRet];

	triTemp1 ← 4C;
	T ← LdF[triLongPointer,16,2];
	triTemp1 ← (triTemp1) - T, Goto[SendSecondHeader,ALU=0];
	Output[triTemp1,triMemBufAddr], Call[triNTTE];	*send buffer pointer
	IOFetch4[triLongPointer,triDataFetch,0];	*one IOF4 to quadalign
	triTemp1 ← (triTemp1) + (4C), Call[triIncLPNT];	*update data buffer pointer
SendSecondHeader:
	triTemp ← 1C;
	Output[triTemp,triMemBufAddr], Call[triNTTE];	*send buffer pointer (1)
	Output[triCount,triBufferOutput]; *send second header word
	Output[triTemp1,triMemBufAddr]; *reset pointer for data field
	triCount ← T ← 2C;
	triTemp ← 17C, Call[.+1];	*Setup loop
*Send 16d blocks of 4 quadwords
	IOFetch4[triLongPointer,triDataFetch,0];
	triCount ← (triCount) - 1, UseCTask, Skip[R<0];	*Non-tasking
triWTx:	  triLongPointer ← (triLongPointer) + (4C), Return;
triWTy:	triTemp ← (triTemp) - 1, Goto[triWriteDone,R<0];
	triDiskCommand ← (triDiskCommand) and not (100000C), Skip[R>=0];
	  Output[triDiskCommand,triDevOpReg], Goto[triWTy]; *Send the command (once) and interlock
	triCount ← T, IOStrobe, Goto[triWTx]; *triCount← 2 (do 4 IOStore4's between tasks)

triWriteDone:

triReadData:
	triDiskCommand ← R16orEmer;
*Enable wakeup on either 16 or more words in RFIFO or emergency
	Output[triDiskCommand,triWakeup], Call[triIOSRet];

*First data wakeup here.  16 words are ready, but must quadalign the data
*so only 12 + (1 to 4) words will be done the first time.
	triTemp ← 16C;	*Go through outer loop below 20b times
*Dispatch to get starting singles; T ← number of final singles - 1.
	T ← (Dispatch[triLongPointer,16,2]) - 1;
	triIntWord ← T, Disp[.+1];	*Save final singles count - 1.
	triCount ← 2C, Goto[triQuadReadStart], DispTable[4,17,0];
	triCount ← 1C, Call[triReadOne];	*3 starting singles
	triCount ← 1C, Call[triReadOne];	*2 starting singles
	triCount ← 1C, Call[triReadOne];	*1 starting single
triQuadReadStart:
	T ← 2C, Call[.+1];
*Loop here 4 times/tasking return (3 times if initial singles)
	IOStore4[triLongPointer,triDataStore,0];
	triCount ← (triCount) - 1, UseCTask, Skip[R<0];	*Non-tasking
triRTX:	  triLongPointer ← (triLongPointer) + (4C), Return;
	triTemp ← (triTemp) - 1, Skip[R<0];
	  triCount ← T, IOStrobe, Goto[triRTX];	*Tasking
*0 to 3 data words remain and 5 more words will be put into RFIFO after the
*last data word, so we can wakeup on the at-least-four condition and read
*final singles as efficiently as possible.
	triDiskCommand ← R4orEmer;
	Output[triDiskCommand,triWakeup], Call[triIOSret];

triReadFinalSingle:
	triIntWord ← (triIntWord) - 1, Goto[triReadDone,R<0];
	Input[triTemp,triDataIn];
	triLongPointer ← (triLongPointer) + (2C);
	triIntWord ← (triIntWord) - 1, Skip[R>=0];	*skip for 2 words
	  PStore1[triLongPointer,triTemp,2], Goto[triReadDone];
	Input[triTemp1,triDataIn];
	Nop;
	PStore2[triLongPointer,triTemp,2], Goto[triReadFinalSingle];

*All data words have been read; 2 words raw checksum, 2 words computed ECC,
*and an RFIFO status word will appear after the last data word.  The two
*ECC words are both zero if no error has occurred.  Wakeup on the
*at-least-four condition again; then read not only the checksum and ECC words
*but also RFIFO status because the hardware will append the ECC words and
*RFIFO status in consecutive cycles after the last raw checksum word.
triReadDone:
	Call[triIOSret];

*Flush the two raw checksum words; ???are these status words or data words???
	Input[triTemp,triRStatus];
	Input[triTemp1,triRStatus];

*Read and store ECC0/1
*Then deliver the RFIFO status (sans bits 12:15) with 1 ORed in indicating
*completion and 10b ORed in if an ECC error has occurred.
	Input[triTemp,triRStatus];
	Input[triTemp1,triRStatus];
	T ← triTemp;
	LU ← (triTemp1) or T;
	Input[triAltoStatus,triRStatus], Skip[ALU=0];
	  T ← ECCError;
***Should do something with the OnLine error, Sequenced, and maybe
***multiplexor present here
	triAltoStatus ← (triAltoStatus) and not (17C);
	triAltoStatus ← (triAltoStatus) + T + 1;
	PStore1[triDCB,triTemp,10];
	PStore1[triDCB,triTemp1,11];
	PStore1[triDCB,triAltoStatus,12];

***Stuff missing here***
	Goto[triChain];

:END[Disk];