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