:Title[Disk]; *Alto rigid disk controller *DEFS FOR RDC Set[rdcDRun0, LSHIFT[KTask, 4]]; Set[RdcBase, LSHIFT[KPage, 10]]; Set[KPage2Base, LSHIFT[KPage2, 10]]; Set[rdcStatus, 1]; 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[rdcBufferInput, 17]; *INPUT[x,rdcBufferInput] gets buffer word into x Set[rdcBufferFetch, ADD[rdcDRUN0, 4]]; *IOFETCHn[memBase,rdcBufferFetch] gets n words from mem into buffer Set[rdcBufferStore, ADD[rdcDRUN0, 17]]; *IOSTOREn[memBase,rdcBufferStore] stores n words from buffer into mem Set TASK[KTask]; set[RdcRBase, AND[60, rdcDRUN0]]; *first of the 16 RM locations used by the disk RV[rdcTemp, Add[0, RdcRBase]]; * keep following four registers RV[rdcTemp1, Add[1, RdcRBase]]; * . . . together and quad aligned RV[rdcLongPointer, Add[2, RdcRBase]]; *DoInt fixes Temp and Temp1 at 0 and 1 RV[rdcLongPointer1, Add[3, RdcRBase]]; RV[rdcCount, Add[4, RdcRBase]]; RV[rdcLastAltoStatus, Add[5, RdcRBase]]; *used with rdcGetAltoStatus RV[rdcAltoCommand, Add[6, RdcRBase]]; RV[rdcIntWord, Add[7, RdcRBase]]; *next four registers also quad aligned (for reading KBLK) RV[rdcKBLKQuad, Add[10, RdcRBase]];*dummy name, for quad word KBLK fetch RV[rdcDCB, Add[11, RdcRBase]]; *NOTE: all memory references to the DCB are offset by -400b to *compensate for the bias in KMDS. RV[rdcAltoStatus, Add[12, RdcRBase]]; RV[rdcAltoDA, Add[13, RdcRBase]]; *used during setup for transfer RV[rdcDiskCommand, Add[13, RdcRBase]]; *used during transfer RV[rdcDiskStatus, Add[14, RdcRBase]]; *read from controller at start of every sector *Contains current sector in bits 0-4, head settle flag in bit 5, and track in 8-15. RV[rdcPosition, Add[15, RdcRBase]]; *Holds default partition in bit 0, 10b in 1:4, 626b in 6:16b, and zeroes in 5&17b. RV[rdcPartition, Add[16, RdcRBase]]; *NOTE: use of this register precludes operation of the RDC *at tasks numbered 3 mod 4. *KMDS is a base register used to point to location 400b of the current MDS. RV[KMDS, 76]; *register 76 of the 100b block used by the disk RV[KMDShi, 77]; *register 77 of the 100b block used by the disk *Throwaway initialization code ON PAGE[DiskInitPage]; rdcInit: KMDS_ 400C, AT[DiskInitLoc]; KMDShi _ 0c; rdcCount _ 34c; *head settle delay rdcPosition _ 0c; OUTPUT[KMDShi,rdcGenReset]; *RESET rdcCount _ 34c; *head settle delay OUTPUT[KMDShi, 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]; T _ 3c; IOStrobe; *cancel the sector wakeup lu _ (ldf[rdcDiskStatus,12,2]) xor (T); *field is SeekComplete, DevSelOK lu _ ldf[rdcDiskStatus,11,1], skip[alu=0]; *field is Trk0 rdcInitRtx: rdcPartition _ (rdcPartition) or (41400c), return; *finish Partition init rdcDiskCommand _ 6000c, goto[AtTrk0,ALU#0]; rdcPosition _ (rdcPosition) + 1, skip[ROdd]; return; *wrong sector to issue step pulse Output[rdcDiskCommand,rdcDevOpReg]; *WakeAllow,Step,Out rdcSetCmd: rdcDiskCommand _ 4000c; Output[rdcDiskCommand,rdcDevOpReg]; *WakeAllow,NoStep,Out nop; rdcPartition _ 54c, goto[rdcInitRtx]; *initialize Partition register *recalibrate and synchronize: get to track zero, sector 0 AtTrk0: LOADPAGE[KPage]; rdcPosition _ 2000c, gotop[IndexError]; *Track0, heads settling % Task code: Here are the codes, locations, and reasons for all the errors posted by this microcode: CODE LOCATION REASON 10 NoCommand No transfer last sector (normal) 203 BadSeal Seal comparison with 110b failed 3 IllegalSector Command specified sector >13b 10 Recal+10b No transfer last sector (normal during recal) 100 SeekUnnecessary+6 Seek in progress 100 ArmStillMoving Seek in progress 10 SeekOnlyCmd Seek only command is done -- HeaderFlake RDC raised IOAtten when we expected it to have read the header data into the buffer. Controller retries the command automatically. 200 HeaderCheckFlake 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 LabelFlake RDC raised IOAtten at second label wakeup 2 HLReturn+1 Header or Label check failed. 1 FirstSectorError RDC raised IOAtten at the start of the second sector. 21 WriteDataFlake RDC raised IOAtten during or after writing the data field. 1 PreReadFlake RDC raised IOAtten before the read of the data field started. 1 PostReadError+1 RDC raised IOAtten after read, but Status.RdErr was off 5 PostReadError+2 RDC raised IOAtten after read, Status.RdErr was on % ON PAGE[KPage]; rdcReturn: rdcAltoCommand _ rdcAltoCommand, RETURN; *interlock Outputs at NoHeaderFlake & LabelFlake+1 UpdateChain: T _ 121c; PStore1[KMDS,rdcKBLKQuad], goto[rdcNTTEr]; *store into 521 *Get here on an error: FlushCmd: Input[rdcDiskStatus,rdcStatus]; *For debugging (breakpoint FlushCmd+1) rdcKBLKQuad _ 0c; Output[rdcKBLKQuad,rdcErrRst], call[UpdateChain]; *zap the controller T _ (rdcDCB) - (371c); *fetch error interrupt word. PFetch1[KMDS,rdcIntWord]; *get IntWord. Interrupt starts next proper sector. rdcLastAltoStatus _ (rdcLastAltoStatus) or (7400c); *Status _ Done T _ (rdcDCB) - (377c); *store final status into the DCB. UNLIKE ALTO, Sector_0. PStore1[KMDS,rdcLastAltoStatus], goto[IndexError]; *resynchronize *Get here if chaining *Note that we must not task to the emulator between the update *of the DCB chain and the storing of the status in the DCB. Chain: T _ (rdcDCB) - (400c); PFetch1[KMDS,rdcKBLKQuad], call[UpdateChain]; *fetch next DCB pointer T _ (rdcDCB) - (372c); PFetch1[KMDS,rdcIntWord]; *get IntWord. Interrupt starts next sector. rdcPosition _ (rdcPosition) and not (1000c); *clear 'Ignore Recal' bit rdcLastAltoStatus _ (rdcLastAltoStatus) and not (100000c), skip[R<0]; *skip if read IOStrobe; rdcLastAltoStatus _ (rdcLastAltoStatus) or (7400c); *Status _ Done T _ (rdcDCB) - (377c), TASK; *store final status into the DCB. UNLIKE ALTO, Sector_0. PStore1[KMDS,rdcLastAltoStatus]; SectorWakeup1: Input[rdcDiskStatus,rdcStatus], goto[SectorWakeupx]; *Get here if there is a command, but the disk is not on sector yet. ContinueCmd: IOSTROBE, call[rdcReturn]; *Start of next sector SectorWakeup: Input[rdcDiskStatus,rdcStatus]; SectorWakeupx: T _ (rdcPosition) and (170000c); *Last sector's number, Alto style. rdcLastAltoStatus _ (rdcLastAltoStatus) or (T); *status of last sector. T _ (120c); PFetch4[KMDS, rdcKBLKQuad]; T _ (124c); *set to fetch sector interrupt mask lu _ (rdcDiskStatus) and (200c); *Check for index mark PFetch1[KMDS, rdcTemp], FreezeResult; *fetch sector interrrupt mask rdcPosition _ (rdcPosition) + (4000c), skip[ALU#0]; T _ rdcLastAltoStatus, goto[NoIndexSeen]; IndexSeen: rdcPosition _ (rdcPosition) xor (160000c); *sector _ 0 (we hope) lu _ (rdcPosition) and (174000c); T _ rdcLastAltoStatus, goto[NoIndexSeen, ALU=0]; IndexError: IOStrobe, call[rdcReturn]; Input[rdcDiskStatus, rdcStatus]; *get here one sector later - acquire index. lu _ (rdcDiskStatus) and (200c); rdcPosition _ (rdcPosition) and not (175000c), skip[ALU=0]; *note: 'Ignore Recal' is cleared. rdcLastAltoStatus _ (rdcLastAltoStatus) and not (170000c), goto[ContinueCmd]; *index found Output[rdcTemp,rdcErrRst], goto[IndexError]; *keep looking for index NoIndexSeen: lu _ rdcDCB; rdcAltoStatus _ T, goto[HaveCommand, ALU#0]; *last sector's status and this sector's position *are stored in KBLK NoCommand: rdcLastAltoStatus _ 10c; *no-transfer status T _ 120c; PStore4[KMDS, rdcKBLKQuad], TASK; T _ (rdcIntWord); *interrupts from last sector rdcIntWord _ 0c; rdcTemp _ (rdcTemp) or (T), loadpage[0]; *sector interrupt mask IOSTROBE, callp[DoRIntT]; *bits are in register 0 (rdcTemp), tasking return SectorWakeup2: Input[rdcDiskStatus,rdcStatus], goto[SectorWakeupx]; *Get here if there is a command. HaveCommand: T _ (rdcDCB) - (367c), Task; PFetch1[KMDS, rdcAltoDA]; *fetch disk address of command T _ rdcIntword; rdcTemp _ (rdcTemp) or (T); T _ 120c; loadpage[0]; PStore4[KMDS, rdcKBLKQuad], callp[DoRIntT]; *bits are in register 0 (rdcTemp), tasking return T _ (rdcDCB) - (376c); PFetch1[KMDS,rdcAltoCommand]; *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[Reven]; 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) + (40000c), goto[GoodSeal, ALU=0]; *check for legal Alto sector (0..13b) BadSeal: rdcLastAltoStatus _ 203c, goto[FlushCmd]; *Status _ seek failed, illegal sector *Shugart Track _ (406c*AldoDisk + AltoTrack)/8 +1 GoodSeal: T _ (rdcAltoDA) and (2c), goto[LegalSector,NoCarry]; *Test disk bit (T_0 if off) IllegalSector: rdcLastAltoStatus _ 3c, goto[FlushCmd]; *Status _ illegal sector LegalSector: lu _ ldf[rdcAltoCommand,6,2], goto[DiskZero,ALU=0];*Test partition bits in the command DiskOne: T _ ldf[rdcPartition,6,11], dblgoto[DefPar,NDefPar, ALU=0]; *add 406d to address DiskZero: 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); 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), dblgoto[Recal,NoRecal,ROdd]; *rdcAltoDA now has (8*Shugart track) + head Recal: lu _ (rdcPosition) and (1000c); *check 'ignore recal' T _ ldf[rdcPosition,7,10], skip[alu=0]; *set up as if ignoring recal lu _ (ldf[rdcAltoDA,5,10]) - (T), goto[NoRecalx]; lu _ (rdcDiskStatus) and (40c); *Check seek complete lu _ (rdcDiskStatus) and (100c), goto[RecalInProgress, ALU=0]; *check TRACK0 rdcPosition _ (rdcPosition) and (177000c), goto[StepOutx, ALU=0]; *rdcPosition.Track_0 *Get here when recal is complete. *Set the head settling flag and the settling count. *The next command will incur the head settling delay. *Set the 'Ignore Recal' bit in rdcPosition rdcPosition _ (rdcPosition) or (3000c), TASK; *set head settling flag and 'Ignore Recal' rdcCount _ 34c; *set settling count rdcLastAltoStatus _ 10c, goto[ContinueCmd]; *Status _ NoTransferLastSector RecalInProgress: rdcIntWord _ 0c, goto[ArmStillMoving]; *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 lu _ (ldf[rdcAltoDA,5,10]) - (T); NoRecalx: FREEZERESULT, goto[SeekUnnecessary, ALU=0]; SeekRequired: rdcPosition _ (rdcPosition) + 1,FREEZERESULT,goto[RealSeek,ROdd]; rdcLastAltoStatus _ 100c,goto[ContinueCmd]; *issue step pulses every other sector RealSeek: rdcCount _ 34c, dblgoto[StepOut, StepIn, ALU<0]; *desired-current (negative => OUT) *We incremented rdcPosition by 2 at SeekRequired, so subtract 4. StepOut: rdcPosition _ (rdcPosition)-(4c); StepOutx: rdcTemp _ 6000c, goto[SeekStep]; StepIn: rdcTemp _ 7000c; *We incremented rdcPosition at RealSeek SeekStep: Output[rdcTemp, rdcDevOpReg]; *step, wake allow, direction rdcPosition _ (rdcPosition) or (2000c); *set head settling flag rdcTemp _ (rdcTemp) and not (2000c); *clear seek command bit Output[rdcTemp, rdcDevOpReg], goto[ArmStillMoving]; *no step, wake allow, direction unchanged *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 the arm is at the right place, but the *sector comparison failed, either because the disk is at the wrong angular *position, or because the heads have not yet settled after a seek. If SeekComplete, decrement *the head settle count in rdcCount. If it is negative, clear the 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 rdcLastAltoStatus _ 100c, goto[ContinueCmd]; *status _ SeekInProgress ArmStillMoving: rdcLastAltoStatus _ 100c, goto[ContinueCmd]; *status _ SeekInProgress DoTransfer: lu _ (rdcAltoCommand) and (2c); *check SeekOnly flag rdcAltoDA _ (rdcAltoDA) and (7c), goto[TransferStart, ALU=0]; *clear all but head number. SeekOnlyCmd: rdcLastAltoStatus _ 10c, goto[Chain]; *chain *Get here when we are ready to do a transfer. TransferStart: lu _ LDF[rdcAltoCommand,12,1]; Output[rdcAltoDA, rdcDr/HdReg], goto[LoadLabelBuffer,ALU#0]; *load the head register rdcTemp1 _ 0c, goto[DoHeader]; rdcSetLPhi: T _ KMDShi; *Set high half of base register rdcLongPointer1 _ T, return; rdcIncLPNT: usectask; rdcIncLP: rdcLongPointer _ (rdcLongPointer) + (4c), return; LoadLabelBuffer: T _ (rdcDCB) - (374c); PFetch1[KMDS, rdcLongPointer], call[rdcSetLPhi]; rdcTemp1 _ 4c; T _ ldf[rdcLongPointer,16,2]; rdcTemp1 _ (rdcTemp1) - (T); Output[rdcTemp1, rdcMemBufAddr], call[rdcNTTE]; lu _ ldf[rdcTemp1,15,1]; rdcTemp1 _ 0c, skip[ALU#0]; IOFetch4[rdcLongPointer, rdcBufferFetch,0], call[rdcIncLPNT]; IOFetch4[rdcLongPointer, rdcBufferFetch,0], call[rdcIncLPNT]; IOFetch4[rdcLongPointer, rdcBufferFetch,0], call[rdcReturn]; DoHeader: T _ (rdcDCB) - (375c); PFetch1[KMDS,rdcLongPointer], 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. Allows 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]; HeaderCheck: rdcDiskCommand _ (rdcDiskCommand) or (100c), goto[LoadHeaderBuffer]; *check => verify HeaderWrite: rdcDiskCommand _ (rdcDiskCommand) or (200C); *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: Output[KMDS,rdcMemBufAddr]; *clear buffer pointer in RDC (KMDS contains 400b) PFetch1[rdcLongPointer,rdcTemp,1], call[rdcNTTE]; *fetch SECOND header word *Here is an error in the emulation of the M31. We force the first word of the header to be 0. Output[rdcTemp1,rdcBufferOutput]; *send first header word. LU _ LDF[rdcAltoCommand,12,1], skip[REven]; *test label write, OtherDisk bit rdcTemp _ (rdcTemp) xor (T), FreezeResult; *T = 2 from rdcNTTE *send second header word, test label operation. Output[rdcTemp,rdcBufferOutput], dblgoto[rdcLabelWrite,rdcLabelRdChk,ALU#0]; rdcLabelWrite: rdcDiskCommand _ (rdcDiskCommand) or (40C), goto[rdcSendCmd]; rdcLabelRdChk: rdcDiskCommand _ (rdcDiskCommand) or (20c); *label read command rdcSendCmd: 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]; *check header write (ifso, then no wakeup) OUTPUT[rdcTemp,rdcMemBufAddr],goto[FirstLabel,ALU#0];*MemBufAddr _ 20 to avoid ServiceLate rdcTemp1 _ 2c; IOSTROBE,call[rdcReturn]; *Wait for wakeup *Get here when the 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, NoAtten]; *check for happy RDC *If the Alto command was Read, we set Inhibit Header Abort, and will only get here due to a *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. SpareFunction, goto[IndexError]; *retry the command without notifying software. HeaderCheckFlake: lu _ (rdcDiskStatus) and (2000c); *check service late skip[ALU=0]; SpareFunction, goto[IndexError]; *service late - retry command rdcLastAltoStatus _ 200c, goto[FlushCmd]; *command was check. Declare SeekFailed. NoHeaderFlake: Output[rdcAltoCommand,rdcPrimeIData], call[rdcReturn]; rdcAltoCommand _ rdcAltoCommand; *interlock the PrimeIdata lu _ (rdcAltoCommand) and (100c), call[rdcHLRC]; lu _ (rdcAltoCommand) and (100c), call[rdcHLRC]; LU _ LDF[rdcAltoCommand,12,1], goto[rdcFirstLabelx]; FirstLabel: LU _ LDF[rdcAltoCommand,12,1]; rdcFirstLabelx: goto[NoLabelWait,ALU#0]; IOSTROBE; T _ (rdcDCB) - (374c), call[rdcReturn]; *wait for first label wakeup PFetch1[KMDS,rdcLongPointer]; *Fetch Label pointer IOSTROBE,Task; *IOSTROBE can't be immediately after a return- wrong controller gets it. rdcTemp1 _ 6c; *wait for second label wakeup *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[NOATTEN]; *call rdcHLRC eight times LabelFlake: rdcLastAltoStatus _ 1c, goto[FlushCmd]; *Whoops Output[rdcAltoCommand,rdcPrimeIData], call[rdcReturn]; rdcAltoCommand _ lcy[rdcAltoCommand,2], call[LabelInLoop]; *interlock the PrimeIdata, put *the command in the proper place for the subroutine. LabelInLoop: rdcCount _ (rdcCount)-1, skip[R<0]; lu _ (rdcAltoCommand) and (100c), 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. *The subroutine updates rdcLongPointer. rdcHLRC: Input[rdcTemp,rdcBufferInput], dblgoto[HLRead,HLCheck,ALU=0]; *get a word from the disk HLRead: PStore1[rdcLongPointer,rdcTemp,0],goto[HLReturn]; *store the data. HLCheck: PFetch1[rdcLongPointer,rdcTemp1,0]; *get the data from memory. T _ rdcTemp1; *SIGH!, long abort lu _ (rdcTemp) xor (T), skip[ALU#0]; PStore1[rdcLongPointer,rdcTemp,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. rdcLastAltoStatus _ 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: T _ (rdcDCB) - (375c), Task; PFetch1[KMDS,rdcTemp]; *Header pointer *Fetch the data block pointer for the second sector. T _ (rdcDCB) - (373c); PFetch1[KMDS,rdcLongPointer], IOSTROBE; *turn off first sector wakeup *Now fetch the second word of the header (the disk address). T _ (rdcTemp) - (377c),Task; PFetch1[KMDS,rdcCount]; *Second sector wakes up here Output[KMDS,rdcMemBufAddr]; *clear buffer pointer in RDC rdcLastAltoStatus _ 0c; rdcDiskCommand _ 104000c; *CommandSentFlag + Wake Allow *Increment rdcPosition lu _ (rdcPosition) + (24000c); *carries if sector =33b *Send first header word (zero) to the buffer, test for sector overflow Output[rdcLastAltoStatus,rdcBufferOutput], skip[NoCarry]; rdcPosition _ (rdcPosition) and not (174000c), SKIP; *clear sector rdcPosition _ (rdcPosition) + (4000c); *increment sector *Increment the 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), goto[rdcSetupDone]; *increment header sector number rdcCount _ (rdcCount) and not (170000c); *clear header sector number rdcSetupDone: lu _ (rdcAltoCommand) and (200c), skip[NoAtten]; *check for header write FirstSectorError: rdcLastAltoStatus _ 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 rdcLastAltoStatus _ (rdcLastAltoStatus) - (1c), goto[WDFx]; *swallow 2 wakeups after data VerifyHeader: rdcLastAltoStatus _ (rdcLastAltoStatus) - (2c), dblgoto[WriteDataField,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]; *do one IOF4 to get quad-aligned 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[rdcQuadWriteLoop]; *Send 16d blocks of 4 quadwords rdcQuadWriteLoop: IOFetch4[rdcLongPointer,rdcBufferFetch,0]; rdcCount _ (rdcCount)-1, usectask, skip[R<0]; rdcWTx: rdcLongPointer _ (rdcLongPointer)+(4c), return; *returns to rdcQuadWriteLoop 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: rdcLastAltoStatus _ (rdcLastAltoStatus)+1, goto[rdcWSDone,R>=0]; *1 or 2 more wakeups IOStrobe, call[rdcReturn]; goto[rdcWriteDone]; rdcWSDone: goto[WriteDataFlake,IOAtten]; rdcLastAltoStatus _ 0c, goto[Chain]; *Status _ good. WriteDataFlake: rdcLastAltoStatus _ 21c, gotop[FlushCmd]; *Status _ SectorLate (for now) ReadDataField: loadpage[KPage2]; rdcDiskCommand _ (rdcDiskCommand) or (102c), gotop[.+1]; *verify ,nop,read OnPage[KPage2]; Output[rdcCount,rdcBufferOutput]; *send second header word Output[rdcDiskCommand,rdcDevOpReg]; rdcTemp1 _ 20c; Output[rdcTemp1,rdcMemBufAddr]; *want this to be 20 when Header Sync is found to avoid abort IOStrobe,Task; *cancel the sector wakeup rdcTemp1 _ 5c; *data starts here since the label is a nop. nop; *header wakeup here - cancel it IOStrobe,Task; *cancel the header wakeup nop; Output[rdcTemp1,rdcMemBufAddr]; *First data wakeup here. 16 words are ready. rdcTemp _ 16c,goto[NoPreReadFlake,NOATTEN]; *go through the outer loop below 16d times. PreReadFlake: Input[rdcDiskStatus,rdcStatus]; loadpage[KPage]; rdcLastAltoStatus _ 1c, gotop[FlushCmd]; NoPreReadFlake: Output[rdcCount,rdcPrimeIData]; *use rdcCount so later write will interlock it dispatch[rdcLongPointer,16,2]; *dispatch to get starting singles. T _ (ldf[rdcLongPointer,16,2])-1, disp[rdcFirstSingles]; *T _ number of final singles-1. SET[kDisp,ADD[LSHIFT[KPage2,10],60]]; rdcFirstSingles: rdcCount _ 2c, goto[rdcQuadReadLoopStart], AT[kDisp,0]; rdcCount _ 1c, call[rdcReadInitialSingle], AT[kDisp,1]; *3 starting singles rdcCount _ 1c, call[rdcReadInitialSingle], AT[kDisp,2]; *2 starting singles rdcCount _ 1c, call[rdcReadInitialSingle], AT[kDisp,3]; *1 starting single rdcQuadReadLoopStart: rdcIntWord _ T; *save final singles count T _ 2c, call[rdcQuadReadLoop]; rdcQuadReadLoop: IOStore4[rdcLongPointer,rdcBufferStore,0]; rdcCount _ (rdcCount)-1, usectask, skip[R<0]; rdcRTX: rdcLongPointer _ (rdcLongPointer)+(4c), return; *returns to rdcQuadReadLoop (no tasking) rdcTemp _ (rdcTemp)-1, skip[R<0]; rdcCount _ T, IOStrobe, goto[rdcRTX]; rdcReadPreDone: rdcLongPointer _ (rdcLongPointer)+(4c), call[.+1]; *0 to 3 words remain rdcIntWord _ (rdcIntWord) - 1, goto[rdcReadDone, R<0]; ReadFinalSingles: *as efficiently as possible Input[rdcTemp,rdcBufferInput], skip[ALU>=0]; *skip if we can do 2 words PStore1[rdcLongPointer,rdcTemp,0], goto[rdcReadDone]; Input[rdcTemp1, rdcBufferInput]; lu _ APCTask; *don't want to task to the emulator rdcIntWord _ (rdcIntWord) - 1, FreezeResult; PStore2[rdcLongPointer, rdcTemp, 0], skip[ALU#0]; usectask; rdcLongPointer _ (rdcLongPointer) + (2c), return; *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, TASK; nop; Input[rdcDiskStatus,rdcStatus]; *Sector wakeup comes here loadpage[KPage], skip[NoAtten]; lu _ (rdcDiskStatus) and (4c), gotop[PostReadError]; *ReadError bit rdcLastAltoStatus _ 100000c, gotop[Chain]; *NoAtten means read was good rdcReadInitialSingle: Input[rdcTemp1,rdcBufferInput]; PStore1[rdcLongPointer,rdcTemp1,0]; rdcLongPointer _ (rdcLongPointer)+1, return; onpage[KPage]; PostReadError: skip[ALU#0]; rdcLastAltoStatus _ 1c, goto[FlushCmd]; *Atten was caused by random hardware problem rdcLastAltoStatus _ 5c, goto[FlushCmd]; *Atten was caused by read error *SUBROUTINE rdcNTTE does a return, but doesn't task if the emulator is waiting. Also, T _ 2. rdcNTTE: lu _ apctask; skip[ALU#0]; rdcNTTEr: usectask; T _ 2c, return; :END[Disk];