:TITLE[Disk];	*Alto rigid disk controller
		*Last edited: 15 October 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]];

*Next four registers also quadaligned (for reading KBLK)
*dummy name, for quadword KBLK fetch.
RV2[rdcAltoStatus,rdcAltoDA,Add[10,rdcRBase]];
*rdcAltoDA used during setup for transfer, rdcDiskCommand during transfer
RM[rdcDiskCommand,IP[rdcAltoDA]];

RV[rdcDiskStatus,Add[12,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[13,rdcRBase]];

*Don't move these base regs without fixing initialization in Initialize.Mc
RV2[rdc520,rdc520hi,14];
RV2[rdcDCB,rdcDCBhi,Add[16,rdcRBase]];

*The following two regs are for testing and may be removed.
RV[HeaderFlakeCnt,Add[70,rdcRBase]];
RV[ServiceLateCnt,Add[71,rdcRBase]];

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[rdcInitLoc];
	rdc520 ← (rdc520) or (120C);
*	rdcDCBhi ← 0C;			*In Initialize.Mc
*	rdc520hi ← 0C;			*In Initialize.Mc
	Output[rdc520hi,rdcGenReset];	*Reset 
	Output[rdc520hi,rdcDr/HdReg];	*SET rdcDISK=0 HEAD=0
*Initialize put BootType in MNBR--start at partition 1 or 2 based upon bit 8.
	T ← MNBR;
	T ← rdcIntWord ← Zero, Skip[H2Bit8];
	  rdcPartition ← 41400C, Skip;	*Par 1
	rdcPartition ← 141400C;		*Par 2
	rdcPosition ← T, LoadPage[rdcInitPage2];
	rdcPartition ← (rdcPartition) or (54C);
*ON ETHERBOOT, DISK INIT WON'T FINISH FOR A LONG TIME, SO CAN'T OVERWRITE
*THIS WITH FINAL OVERLAY.
OnPage[rdcInitPage2];
	rdcCount ← 34C, Call[rdcSetCmd];	*Head settle delay

*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];
	LU ← (LdF[rdcDiskStatus,12,2]) xor T;	*SeekComplete, DevSelOK
	LU ← LdF[rdcDiskStatus,11,1], Skip[ALU=0]; *field is Trk0
	  IOStrobe, GoTo[rdcIniRet];
	rdcDiskCommand ← 6000C, GoTo[AtTrk0,ALU#0];
*IOStrobe cancels the sector wakeup.
	rdcPosition ← (rdcPosition) + 1, IOStrobe, 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
	rdcDiskCommand ← rdcDiskCommand, Return;

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