: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];