:TITLE[Disk]; *Alto rigid disk controller *Last edited: 27 March 1981 by Ed Fiala Set[rdcDRun0,LShift[rdcTask,4]]; *Not task 3 mod 4 because of rdcDCB *and rdc520 Set[rdcStatus,1]; Set[rdcBufferInput,17]; *Input[x,rdcBufferInput] gets buffer word into x Set[rdcBufferStore,Add[rdcDRun0,17]]; *IOStoren[memBase,rdcBufferStore] stores n words from buffer into mem Set[rdcGenReset,0]; Set[rdcDr/HdReg,1]; Set[rdcErrRst,2]; Set[rdcDevOpReg,3]; Set[rdcBufferOutput,4]; *Output[x,rdcBufferOutput] stores x into buffer Set[rdcMemBufAddr,6]; Set[rdcPrimeIData,7]; Set[rdcBufferFetch,Add[rdcDRun0,4]]; *IOFetchn[memBase,rdcBufferFetch] gets n words from mem into buffer SetTask[rdcTask]; Set[rdcRBase,And[60,rdcDRun0]]; *first of the 16 RM locations used by the disk *Keep following four registers together and quadaligned *DoIntR requires Temp and Temp1 at 0 and 1 RV4[rdcTemp,rdcTemp1,rdcLongPointer,rdcLongPointer1,Add[0,rdcRBase]]; RV[rdcCount,Add[4,rdcRBase]]; *Bit 0 default partition, 10b in 1:4, 626b in 6:16b, and zeroes in 5&17b. RV[rdcPartition,Add[5,rdcRBase]]; RV[rdcAltoCommand,Add[6,rdcRBase]]; RV[rdcIntWord,Add[7,rdcRBase]]; *The following two regs are for testing. They must be removed if the task *following the disk's is used. RV[HeaderFlakeCnt,Add[10,rdcRBase]]; RV[ServiceLateCnt,Add[11,rdcRBase]]; *Next four registers also quadaligned (for reading KBLK) *dummy name, for quadword KBLK fetch. RV2[rdcAltoStatus,rdcAltoDA,Add[12,rdcRBase]]; *rdcAltoDA used during setup for transfer, rdcDiskCommand during transfer RM[rdcDiskCommand,IP[rdcAltoDA]]; RV[rdcDiskStatus,Add[14,rdcRBase]]; *read from controller at start of every sector *Bits 0:4 current sector, head settle flag in bit 5, bits 8:15 track. RV[rdcPosition,Add[15,rdcRBase]]; *Don't move these base regs without fixing initialization in Initialize.Mc RV2[rdcDCB,rdcDCBhi,Add[16,rdcRBase]]; RV2[rdc520,rdc520hi,70]; SetTask[0]; *Common subroutine for Mesa/Alto Partition instruction. *newP in RTemp, result in T. OnPage[XMiscPage]; MXPar: RTemp1 ← IP[rdcPartition]C; T ← (SStkP&NStkP) xor (377C); StkP ← RTemp1, RTemp1 ← T, NoRegILockOK; RTemp ← (RTemp) - 1; *internally, partitions are 0..1, not 1..2. *Return current default without change if newP is 0 LU ← (RTemp) and not (1C), Skip[ALU>=0]; T ← (Rsh[Stack,17]) + 1, Goto[RestSP]; *RestSP in Timer.Mc T ← (Rsh[Stack,17]) + 1, Skip[ALU=0]; T ← 0C, Goto[RestSP]; *Return 0 on illegal newP LU ← RTemp, Skip[R Odd]; Stack ← (Stack) and not (100000C), Goto[RestSP]; *new value = 0 Stack ← (Stack) or (100000C), Goto[RestSP]; *new value = 1 *Throwaway initialization code SetTask[rdcTask]; rdcInit: T ← rdc520 ← 400C, At[DiskInitLoc]; rdc520 ← (rdc520) or (120C); * rdcDCBhi ← 0C; *In Initialize.Mc * rdc520hi ← 0C; *In Initialize.Mc Output[rdc520hi,rdcGenReset]; *Reset rdcPosition ← 0C; rdcCount ← 34C; *head settle delay Output[rdc520hi,rdcDr/HdReg], Call[rdcSetCmd]; *SET rdcDISK=0 HEAD=0 *Look for DevSelOk and SeekComplete. When we have them, look for *Track0. If not, issue an outward step pulse on alternate sectors. *When the disk is at track 0, go to IndexError to acquire index. Input[rdcDiskStatus,rdcStatus]; IOStrobe; *cancel the sector wakeup LU ← (LdF[rdcDiskStatus,12,2]) xor T; *SeekComplete, DevSelOK LU ← LdF[rdcDiskStatus,11,1], Skip[ALU=0]; *field is Trk0 *Finish partition init rdcInitRtx: rdcPartition ← (rdcPartition) or (54C), Goto[rdcIniRet]; rdcDiskCommand ← 6000C, Goto[AtTrk0,ALU#0]; rdcPosition ← (rdcPosition) + 1, Skip[R Odd]; rdcIniRet: T ← 3C, Return; *wrong sector for step pulse Output[rdcDiskCommand,rdcDevOpReg]; *WakeAllow, Step, Out rdcSetCmd: rdcDiskCommand ← 4000C; Output[rdcDiskCommand,rdcDevOpReg]; *WakeAllow, NoStep, Out *Initialize has left BootType in MNBR--start at partition 1 or 2 *based upon bit 8. T ← MNBR; rdcIntWord ← Zero, Skip[H2Bit8]; rdcPartition ← 41400C, Goto[rdcInitRtx]; *Par 1 rdcPartition ← 141400C, Goto[rdcInitRtx]; *Par 2 *recalibrate and synchronize: get to track zero, sector 0 AtTrk0: LoadPage[rdcPage]; rdcPosition ← 2000C, GotoP[IndexError]; *Track0, heads settling %Here are codes, locations, and reasons for all errors posted by this microcode: CODE LOCATION REASON 10 SectorWakeupx+13 No transfer last sector (normal) 203 HaveCommand+13 Seal comparison with 110b failed 3 HaveCommand+15 Command specified sector .gr. 15b 10 Recal+10 No transfer last sector (normal during recal) 100 NoRecalx+3 Seek in progress 100 ArmStillMoving Seek in progress 100 SeekUnnecessary+6 Seek in progress 10 SeekOnlyCmd Seek only command is done -- HeaderFlake+1 RDC raised IOAtten when we expected it to have read the header data into the buffer. Controller retries the command automatically. 200 HeaderCheckFlake+1 RDC raised IOAtten when header was read by a Check command. We should determine whether the disk is at the correct TRACK position, and declare a check error if it is, but instead, we say SeekFailed. 1 LabelIn+2 RDC raised IOAtten at second label wakeup 2 HLReturn+1 Header or Label check failed. 1 FirstSectorError RDC raised IOAtten at start of the 2nd sector. 21 rdcWriteDone+3 RDC raised IOAtten during or after writing data field. 1 ReadDataField+10 RDC raised IOAtten before reading data field. 1 rdcReadDone+3 RDC raised IOAtten after read, but Status.RdErr was off. 5 rdcReadDone+3 RDC raised IOAtten after read, Status.RdErr was on % OnPage[rdcPage]; rdcIOSRet: IOStrobe; *interlock Outputs at NoHeaderFlake, LabelFlake+1, & ReadDataField+4 rdcReturn: rdcAltoCommand ← rdcAltoCommand, Return; UpdateChain: PStore1[rdc520,rdcTemp1,1], Goto[rdcNTTEr]; *Get here on an error: FlushCmd: Input[rdcDiskStatus,rdcStatus]; rdcTemp1 ← 0C, Call[UpdateChain]; *zap the controller rdcAltoStatus ← (rdcAltoStatus) or (7400C); *Status ← Done *store final status into the DCB. UNLIKE ALTO, Sector←0. Then resynchronize PStore1[rdcDCB,rdcAltoStatus,1], Call[rdcNTTEr]; *Fetch error interrupt word; interrupt starts next proper sector. PFetch1[rdcDCB,rdcIntWord,7], Goto[IndexReset]; *Get here when a seek-only (status = 10b), write (status = 0), or read *(status = 100000) is done. Chain to next command. Note: must not task *to emu between DCB chain update and storing status in DCB. Chain: PFetch1[rdcDCB,rdcTemp1,0], Call[UpdateChain]; *Chain to next DCB PFetch1[rdcDCB,rdcIntWord,6]; *IntWord for next sector interrupt. rdcPosition ← (rdcPosition) and not (1000C); *clear 'Ignore Recal' bit *skip if read rdcAltoStatus ← (rdcAltoStatus) and not (100000C), Skip[R<0]; IOStrobe; rdcAltoStatus ← (rdcAltoStatus) or (7400C); *Status ← Done *Store final status into the DCB. UNLIKE ALTO, Sector←0. PStore1[rdcDCB,rdcAltoStatus,1], Call[rdcReturn]; SectorWakeup1: PFetch1[rdc520,rdcDCB,1], Goto[SectorWakeupx]; IndexReset: *Keep looking for index pulse Output[rdcAltoCommand,rdcErrRst]; IndexError: IOStrobe, Call[rdcReturn]; *Here one sector later--acquire index. Input[rdcDiskStatus,rdcStatus]; LU ← (rdcDiskStatus) and (200C); *note: 'Ignore Recal' is cleared. rdcPosition ← (rdcPosition) and not (175000C), Goto[IndexReset,ALU=0]; *Fall through to here when index found *Jump here if there is a command, but the disk is not on sector yet. ContinueCmd: IOStrobe, Call[rdcReturn]; *Start of next sector SectorWakeup: PFetch1[rdc520,rdcDCB,1]; SectorWakeupx: T ← (rdcPosition) and (170000C); *Last sector's number, Alto style. rdcAltoStatus ← (LdF[rdcAltoStatus,4,14]) or T; *Last sector's status Input[rdcDiskStatus,rdcStatus]; T ← rdcIntWord; *Interrupts from last sector rdcIntWord ← 0C; PFetch1[rdc520,rdcTemp,4]; *Fetch sector int mask LU ← (rdcDiskStatus) and (200C); *Check for index mark LU ← rdcDCB, Goto[rdcIndex,ALU#0]; *Clear or inc sector based on index rdcPosition ← (rdcPosition) + (4000C), Goto[HaveCommand,ALU#0]; NoCommand: PStore1[rdc520,rdcAltoStatus,2]; LoadPage[DoIntPage]; rdcTemp ← (rdcTemp) or T, IOStrobe, CallP[DoIntR]; rdcAltoStatus ← 10C, Goto[SectorWakeup]; *no-transfer status rdcIndex: rdcPosition ← (rdcPosition) xor (154000C); *Sector ← 0 (?) LU ← (rdcPosition) and (174000C); LU ← rdcDCB, Goto[IndexError,ALU#0]; Goto[NoCommand,ALU=0]; *Get here if there is a command. HaveCommand: PFetch1[rdcDCB,rdcAltoDA,11]; *fetch disk address of command *Sector interrupt mask in reg 0 (rdcTemp); store last sector's status and *this command's AltoDA in core for the emulator. rdcTemp ← (rdcTemp) or T, LoadPage[DoIntPage]; PStore2[rdc520,rdcAltoStatus,2], CallP[DoIntR]; PFetch1[rdcDCB,rdcAltoCommand,2]; *fetch command *Shugart sector number ← 2*AltoSector + AltoHead T ← (rdcAltoDA) and (170000C); LU ← (rdcAltoDA) and (4C); *Test 'head' bit rdcTemp ← T, Skip[ALU=0]; *rdcTemp holds Shugart sector rdcTemp ← (rdcTemp) + (4000C); *Check command Seal and 'complement disk' bit rdcAltoCommand ← (rdcAltoCommand) xor (44000C), Skip[R Even]; rdcAltoDA ← (rdcAltoDA) xor (2C); *complement the disk bit in AltoDA LU ← (rdcAltoCommand) and (176000C); *check for good seal (bits 6,7 hold partition no.) LU ← (rdcAltoDA) + (20000C), Skip[ALU=0]; *check for legal Alto sector (0..15b) *Bad seal; Status ← seek failed, illegal sector rdcAltoStatus ← 203C, Goto[FlushCmd]; *Shugart Track ← (406c*AldoDisk + AltoTrack)/8 +1 T ← (rdcAltoDA) and (2C), Skip[Carry']; *Test disk bit (T←0 if off) rdcAltoStatus ← 3C, Goto[FlushCmd]; *Status ← illegal sector *Test partition bits in the command LU ← LdF[rdcAltoCommand,6,2], Skip[ALU=0]; T ← LdF[rdcPartition,6,11], DblGoto[DefPar,NDefPar,ALU=0]; *add 406d to address T ← 0C, DblGoto[DefPar,NDefPar,ALU=0]; DefPar: T ← (LdF[rdcPartition,1,4]) + T, DblGoto[ParOne,ParZero,R<0]; *T ← 10, check partition NDefPar: LU ← LdF[rdcAltoCommand,6,1]; T ← (LdF[rdcPartition,1,4]) + T, DblGoto[ParOne,ParZero,ALU#0]; ParOne: T ← (LdF[rdcPartition,6,12]) + T; *add 812d to address ParZero: rdcAltoDA ← (LdF[rdcAltoDA,4,11]) + T, Goto[NoRecal,R Even]; *rdcAltoDA now has (8*Shugart track) + head Recal: LU ← (rdcPosition) and (1000C); *check 'ignore recal' *set up as if ignoring recal T ← LdF[rdcPosition,7,10], Goto[NoRecalx,ALU#0]; LU ← (rdcDiskStatus) and (40C); *Check seek complete LU ← (rdcDiskStatus) and (100C), Skip[ALU#0]; *check TRACK0 rdcIntWord ← 0C, Goto[ArmStillMoving]; rdcPosition ← (rdcPosition) and (177000C), Goto[StepOutx,ALU=0]; *rdcPosition.Track←0 *Get here when recal is complete. Set head settling flag and 'ignore recal' *bits in rdcPosition and settling count in rdcCount. *The next command will incur head settling delay. rdcPosition ← (rdcPosition) or (3000C), Task; rdcCount ← 34C; *Status ← no transfer last sector rdcAltoStatus ← 10C, Goto[ContinueCmd]; *Get here if the command is not a recal, and is OK so far. rdcTemp contains *the desired Shugart sector in bits 0-4, rdcAltoDA holds the Shugart *track,,head number. Check for seek required. NoRecal: T ← LdF[rdcPosition,7,10]; *Current Shugart track NoRecalx: LU ← (LdF[rdcAltoDA,5,10]) - T; FreezeResult, Goto[SeekUnnecessary,ALU=0]; *issue step pulses every other sector rdcPosition ← (rdcPosition) + 1, FreezeResult, Skip[R Odd]; rdcAltoStatus ← 100C, Goto[ContinueCmd]; rdcCount ← 34C, DblGoto[StepOut,StepIn,ALU<0]; *desired-current (negative => OUT) *We incremented rdcPosition by 1 twice at NoRecalx+2, so subtract 4. StepOut: rdcPosition ← (rdcPosition) - (4C); StepOutx: rdcTemp ← 6000C, Skip; StepIn: rdcTemp ← 7000C; *We incremented rdcPosition at NoRecalx+2 Output[rdcTemp,rdcDevOpReg]; *step, wake allow, direction rdcPosition ← (rdcPosition) or (2000C); *set head settling flag rdcTemp ← (rdcTemp) and not (2000C); *clear seek command bit, interlock Output[rdcTemp,rdcDevOpReg]; *no step, wake allow, direction unchanged ArmStillMoving: rdcAltoStatus ← 100C, Goto[ContinueCmd]; *status ← SeekInProgress *Get here if the arm is at the correct position. SeekUnnecessary: T ← LdF[rdcTemp,0,6]; LU ← (LdF[rdcPosition,0,6]) xor T; *Compare sector addresses LU ← (rdcDiskStatus) and (40C), Goto[DoTransfer,ALU=0]; *Test seek complete *Get here if arm is at the right place, but sector comparison failed, either *because angular position wrong or because heads not yet settled after seek. *If SeekComplete, decrement head settle count in rdcCount. If rdcCount *negative, clear head settling flag. rdcIntWord ← 0C, Goto[ArmStillMoving,ALU=0]; *ALU=0 means seek incomplete rdcCount ← (rdcCount) - 1, Skip[R>=0]; *test settling count rdcPosition ← (rdcPosition) and not (2000C); *clear settling flag rdcAltoStatus ← 100C, Goto[ContinueCmd]; *status ← SeekInProgress rdcSetLPhi: T ← rdc520hi; *Set high half of base register rdcLongPointer1 ← T, Return; rdcIncLPNT: UseCTask, Goto[rdcWTx]; DoTransfer: LU ← (rdcAltoCommand) and (2C); *check SeekOnly flag *clear all but head number. rdcAltoDA ← (rdcAltoDA) and (7C), Skip[ALU=0]; rdcAltoStatus ← 10C, Goto[Chain]; *Seek-only cmd--chain *Here when ready to do a transfer. LU ← LdF[rdcAltoCommand,12,1]; Output[rdcAltoDA,rdcDr/HdReg], Skip[ALU#0]; *load head register rdcTemp1 ← 0C, Goto[DoHeader]; *Load Label buffer PFetch1[rdcDCB,rdcLongPointer,4], Call[rdcSetLPhi]; rdcTemp1 ← 4C; T ← LdF[rdcLongPointer,16,2]; rdcTemp1 ← (rdcTemp1) - T; Output[rdcTemp1,rdcMemBufAddr]; ***Why isn't this bypass kludge here?*** rdcTemp1 ← Rsh[rdcTemp1,2], Call[rdcReturn]; *interlock, task rdcTemp1 ← 0C, Skip[R Odd]; *If quadaligned label, do 2 IOFetch4's IOFetch4[rdcLongPointer,rdcBufferFetch,0], Call[rdcIncLPNT]; IOFetch4[rdcLongPointer,rdcBufferFetch,0], Call[rdcIncLPNT]; IOFetch4[rdcLongPointer,rdcBufferFetch,0], Call[rdcReturn]; DoHeader: PFetch1[rdcDCB,rdcLongPointer,3], Call[rdcSetLPhi]; *fetch header pointer into base register LU ← LdF[rdcAltoCommand,10,2]; *test for header read (0) LU ← LdF[rdcAltoCommand,10,1], Goto[HeaderWriteOrCheck,ALU#0]; HdrRead: rdcDiskCommand ← 4400C; *inhibit header abort allowing read to finish LU ← LdF[rdcAltoCommand,12,1]; *set header verify bit, test label operation rdcDiskCommand ← (rdcDiskCommand) or (100C), DblGoto[rdcLabelWrite,rdcLabelRdChk,ALU#0]; HeaderWriteOrCheck: rdcDiskCommand ← 4000C, Skip[ALU#0]; rdcDiskCommand ← (rdcDiskCommand) or (100C), Goto[LoadHeaderBuffer]; *check => verify rdcDiskCommand ← (rdcDiskCommand) or (200C); *Header write *When writing headers, clear the OtherDisk bit in rdcAltoCommand so that the *test in LoadHeaderBuffer will skip (causing the header to be written as *specified in the header record (allows writing DP0 style headers on DP1). rdcAltoCommand ← (rdcAltoCommand) and not (1C); LoadHeaderBuffer: *clear buffer pointer in RDC (rdcTemp1 contains 0) Output[rdcTemp1,rdcMemBufAddr]; PFetch1[rdcLongPointer,rdcTemp,1], Call[rdcNTTE]; *fetch SECOND header word *Here is an error in M31 emulation--we force 1st word of header to be 0. Output[rdcTemp1,rdcBufferOutput]; *send first header word. LU ← LdF[rdcAltoCommand,12,1], Skip[R Even]; *test label write, OtherDisk bit rdcTemp ← (rdcTemp) xor T, FreezeResult; *T = 2 from rdcNTTE *send second header word, test label operation. Output[rdcTemp,rdcBufferOutput], Skip[ALU=0]; rdcLabelWrite: rdcDiskCommand ← (rdcDiskCommand) or (40C), Skip; *Label write rdcLabelRdChk: rdcDiskCommand ← (rdcDiskCommand) or (20C); *label read Output[rdcDiskCommand,rdcDevOpReg]; *send command to controller rdcDiskCommand ← rdcDiskCommand; *interlock ****************************************************************************** * header and/or label have been sent if writes were requested * * wakeups are generated as follows: * * 1 wakeup when header read is completed * * 1 wakup when first six label words are read * * 1 wakup when entire label has been read * * 1 wakup at end of data NOP * ****************************************************************************** rdcWaitD: rdcTemp ← 20C; LU ← (LdF[rdcAltoCommand,10,1]) - 1; *if header write then no wakeup *MemBufAddr ← 20 to avoid ServiceLate ***Possible Gotcha here--Output, x, x, Return*** Output[rdcTemp,rdcMemBufAddr], Goto[FirstLabel,ALU=0]; rdcTemp1 ← 2C, Call[rdcIOSRet]; *Wait for wakeup *Get here when RDC has read the header data from the disk into buffer[2..3]. *Check it (if Alto command was check) or transfer it to memory (if Alto *command was read). HeaderIn: Output[rdcTemp1,rdcMemBufAddr]; *load buffer address LU ← LdF[rdcAltoCommand,10,2], Goto[NoHeaderFlake,IOAtten']; *check for happy RDC *If Alto command was Read, set Inhibit Header Abort, and only get here on *data late. We can therefore retry the command. If the command was check, *we must abandon the command now. HeaderFlake: Input[rdcDiskStatus,rdcStatus], Skip[ALU#0]; *skip if command was check. *retry the command without notifying software. HeaderFlakeCnt ← (HeaderFlakeCnt) + 1, Goto[IndexReset]; HeaderCheckFlake: LU ← (rdcDiskStatus) and (2000C); *check service late *command was check. Declare SeekFailed. rdcAltoStatus ← 200C, Goto[FlushCmd,ALU=0]; *service late--retry command ServiceLateCnt ← (ServiceLateCnt) + 1, Goto[IndexReset]; NoHeaderFlake: Output[rdcAltoCommand,rdcPrimeIData], Call[rdcReturn]; *interlock the PrimeIdata LU ← (LdF[rdcAltoCommand,11,1]) - 1, Call[rdcHLRC]; LU ← (LdF[rdcAltoCommand,11,1]) - 1, Call[rdcHLRC]; FirstLabel: LU ← (LdF[rdcAltoCommand,12,1]) - 1; *Even placement rdcTemp1 ← 6C, Goto[NoLabelWait,ALU=0]; IOStrobe, Call[rdcReturn]; *wait for 1st label wakeup *Fetch Label pointer and wait for 2nd label wakeup PFetch1[rdcDCB,rdcLongPointer,4], Call[rdcIOSRet]; *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: Output[rdcTemp1,rdcMemBufAddr]; *load buffer address rdcCount ← 7C, Skip[IOAtten']; *call rdcHLRC eight times rdcAltoStatus ← 1C, Goto[FlushCmd]; *Whoops; label flake Output[rdcAltoCommand,rdcPrimeIData], Call[rdcReturn]; *Interlock the PrimeIdata; put command in the proper place for the subroutine. rdcAltoCommand ← Lcy[rdcAltoCommand,2], Call[.+1]; rdcCount ← (rdcCount) - 1, Skip[R<0]; LU ← (LdF[rdcAltoCommand,11,1]) - 1, Goto[rdcHLRC]; rdcAltoCommand ← Rcy[rdcAltoCommand,2], Goto[NoLabelWait]; *put command back *SUBROUTINE rdcHLRC gets one word of the header or label, and does a read or *check. On entry, rdcLongPointer contains the memory address, and *rdcCommand[11b] = 1 for check, 0 for read; updates rdcLongPointer. rdcHLRC: Input[rdcTemp1,rdcBufferInput], Skip[ALU=0]; *get word from disk PStore1[rdcLongPointer,rdcTemp1,0], Goto[HLReturn]; *store the data. PFetch1[rdcLongPointer,rdcTemp,0]; *get the data from memory. T ← rdcTemp; *SIGH!, long abort LU ← (rdcTemp1) xor T, Skip[ALU#0]; PStore1[rdcLongPointer,rdcTemp1,0], Goto[HLReturn]; *memory zero - store disk data, no check Skip[ALU#0]; *check for data equal HLReturn: rdcLongPointer ← (rdcLongPointer) + 1, Return; *data were equal, no store, check OK. rdcAltoStatus ← 2C, Goto[FlushCmd]; *Status ← Check error *Get here when the header and label have been processed. IOStrobe has not been issued. *Get the header for the second sector. NoLabelWait: PFetch1[rdcDCB,rdcTemp,3]; *Header pointer; even placement *Fetch data block pointer for 2nd sector. PFetch1[rdcDCB,rdcLongPointer,5]; *Now fetch 2nd header word (the disk address) and turn off 1st sector wakeup. rdcTemp ← (rdcTemp) - (400C); T ← (rdcTemp) - (117C); PFetch1[rdc520,rdcCount], IOStrobe, Call[rdcReturn]; *Second sector wakes up here rdcAltoStatus ← 0C; Output[rdcAltoStatus,rdcMemBufAddr]; *clear buffer pointer in RDC rdcDiskCommand ← 104000C; *CommandSentFlag + Wake Allow Output[rdcDiskCommand,rdcDevOpReg]; *zap the command in case of error LU ← (rdcPosition) + (24000C); *carries if sector =33b *Send first header word (zero) to the buffer, test for sector overflow Output[rdcAltoStatus,rdcBufferOutput], Skip[Carry']; rdcPosition ← (rdcPosition) and not (174000C), Skip; *clear sector rdcPosition ← (rdcPosition) + (4000C); *increment sector *Inc Alto header: if head=0 then [head←1] else [ head←0; sector←sector+1] LU ← (rdcCount) and (4C); rdcCount ← (rdcCount) xor (4C), Goto[rdcSetupDone,ALU=0]; *complement head LU ← (rdcCount) + (30000C); *produces carry if sector now 15b. Skip[Carry]; rdcCount ← (rdcCount) + (10000C), Skip; *inc header sector no. rdcCount ← (rdcCount) and not (170000C); *clear header sector no. rdcSetupDone: LU ← (rdcAltoCommand) and (200C), Skip[IOAtten']; *check for header write FirstSectorError: rdcAltoStatus ← 1C, Goto[FlushCmd]; *Status ← Hardware Error (type unknown) LU ← (rdcAltoCommand) and (10C), DblGoto[WriteHeader,VerifyHeader,ALU#0]; *test WriteData *swallow 1 wakeup after data WriteHeader: rdcDiskCommand ← (rdcDiskCommand) or (204C); *write,nop,write rdcAltoStatus ← (rdcAltoStatus) - (1C), Goto[WDFx]; *swallow 2 wakeups after data VerifyHeader: rdcAltoStatus ← (rdcAltoStatus) - (2C), Goto[ReadDataField,ALU=0]; WriteDataField: rdcDiskCommand ← (rdcDiskCommand) or (104C); *verify,nop,write WDFx: rdcTemp1 ← 4C; T ← LdF[rdcLongPointer,16,2]; rdcTemp1 ← (rdcTemp1) - T, Goto[SendSecondHeader,ALU=0]; Output[rdcTemp1,rdcMemBufAddr], Call[rdcNTTE]; *send buffer pointer IOFetch4[rdcLongPointer,rdcBufferFetch,0]; *one IOF4 to quadalign rdcTemp1 ← (rdcTemp1) + (4C); *update data buffer pointer rdcLongPointer ← (rdcLongPointer) + (4C); SendSecondHeader: rdcTemp ← 1C; Output[rdcTemp,rdcMemBufAddr], Call[rdcNTTE]; *send buffer pointer (1) Output[rdcCount,rdcBufferOutput]; *send second header word Output[rdcTemp1,rdcMemBufAddr]; *reset pointer for data field rdcCount ← T ← 2C; rdcTemp ← 17C, Call[.+1]; *Setup loop *Send 16d blocks of 4 quadwords IOFetch4[rdcLongPointer,rdcBufferFetch,0]; rdcCount ← (rdcCount) - 1, UseCTask, Skip[R<0]; *Non-tasking rdcWTx: rdcLongPointer ← (rdcLongPointer) + (4C), Return; rdcWTy: rdcTemp ← (rdcTemp) - 1, Goto[rdcWriteDone,R<0]; rdcDiskCommand ← (rdcDiskCommand) and not (100000C), Skip[R>=0]; Output[rdcDiskCommand,rdcDevOpReg], Goto[rdcWTy]; *Send the command (once) and interlock rdcCount ← T, IOStrobe, Goto[rdcWTx]; *rdcCount← 2 (do 4 IOStore4's between tasks) rdcWriteDone: Call[.+1]; *1 or 2 more wakeups rdcAltoStatus ← (rdcAltoStatus) + 1, Goto[rdcIOSRet,R<0]; rdcAltoStatus ← 0C, Goto[Chain,IOAtten']; *Status ← good *WriteDataFlake (status ← SectorLate for now) rdcAltoStatus ← 21C, Goto[FlushCmd]; ReadDataField: rdcDiskCommand ← (rdcDiskCommand) or (102C); *verify, nop, read Output[rdcCount,rdcBufferOutput]; *send second header word Output[rdcDiskCommand,rdcDevOpReg]; rdcAltoCommand ← 20C; *want this to be 20 when Header Sync is found to avoid abort; cancel sector wakeup Output[rdcAltoCommand,rdcMemBufAddr], Call[rdcIOSRet]; *data starts here since the label is a nop; cancel the header wakeup. rdcTemp1 ← 5C, Call[rdcIOSRet]; Output[rdcTemp1,rdcMemBufAddr]; *First data wakeup here. 16 words are ready. rdcTemp ← 16C, Skip[IOAtten']; *go through the outer loop below 16d times. *PreReadFlake rdcAltoStatus ← 1C, Goto[FlushCmd]; *use rdcCount so later write will interlock it Output[rdcCount,rdcPrimeIData]; *Dispatch to get starting singles; T ← number of final singles - 1. T ← (Dispatch[rdcLongPointer,16,2]) - 1; rdcIntWord ← T, Disp[.+1]; *Save final singles count - 1. rdcCount ← 2C, Goto[rdcQuadReadLoopStart], DispTable[4,17,0]; rdcCount ← 1C, Call[rdcHLRC]; *3 starting singles rdcCount ← 1C, Call[rdcHLRC]; *2 starting singles rdcCount ← 1C, Call[rdcHLRC]; *1 starting single rdcQuadReadLoopStart: T ← 2C, Call[.+1]; *Loop here 4 times/tasking return (3 times if initial singles) IOStore4[rdcLongPointer,rdcBufferStore,0]; rdcCount ← (rdcCount) - 1, UseCTask, Skip[R<0]; *Non-tasking rdcRTX: rdcLongPointer ← (rdcLongPointer) + (4C), Return; rdcTemp ← (rdcTemp) - 1, Skip[R<0]; rdcCount ← T, IOStrobe, Goto[rdcRTX]; *Tasking *0 to 3 words remain; read final singles as efficiently as possible. *Don't task to emulator ReadFinalSingle: rdcIntWord ← (rdcIntWord) - 1, Goto[rdcReadDone,R<0]; Input[rdcTemp,rdcBufferInput]; rdcLongPointer ← (rdcLongPointer) + (2C); rdcIntWord ← (rdcIntWord) - 1, Skip[R>=0]; *Skip for 2 words PStore1[rdcLongPointer,rdcTemp,2], Goto[rdcReadDone]; Input[rdcTemp1,rdcBufferInput]; Nop; PStore2[rdcLongPointer,rdcTemp,2], Goto[ReadFinalSingle]; *We have read all data. *The syndrome will not be checked until the next sector wakeup, so we must *set up for it. Note that we never actually read the syndrome, since doing *so will cause an H4PE. Instead, we use ReadError to differentiate between *random hardware problems (status = 1) and checksum errors (status = 4). rdcReadDone: IOStrobe, Call[rdcReturn]; Input[rdcAltoStatus,rdcStatus]; *Sector wakeup comes here *Reason for IOAtten: 1 = random hardware problem; 5 = read error *Construct error code from read-error bit in status. LU ← rdcAltoStatus, Skip[IOAtten']; rdcAltoStatus ← (Form4[rdcAltoStatus]) + 1, Goto[FlushCmd]; rdcAltoStatus ← 100000C, Goto[Chain]; *Read was good *SUBROUTINE rdcNTTE does a Return, but not to emulator. Also, T ← 2. rdcNTTE: LU ← APCTask; Skip[ALU#0]; rdcNTTEr: UseCTask; T ← 2C, Return; :END[Disk];