{File name: LispTridentDLion.mc
Description: Large Disk Controller microcode, Version 1.0
Author: S. Furman, J. Furst
Created: November 24, 1980 11:03 AM

charnley 28-Jan-85 12:54:15 made consistent with Large Virtual Memory/new IOPage placement {%L}
Lichtenberg 20-Jul-84 18:07:16 removed StartAddress for combo assembly

Edited: Amy Fasnacht 11-Oct-83 15:26:53: Change IOPage references for moving of the IOPage
Edited: December 4, 1980 9:35 AM
Edited: J. Furst, December 6, 1980 1:46 PM
Edited: J. Furst, December 16, 1980 6:06 PM
Edited: J. Furst, January 13, 1981 8:28 AM
Edited: J. Furst, March 20, 1981 8:24 AM
Edited: J. Furst, May 19, 1981 9:32 AM
Edited: E. Neely December 8, 1981 7:24 AM - Make inProgress bit last change to IOCB
- Last IOCB bit added instead of Next IOCB Ptr = 0 if end of chain.
- Client Label field update aded after the data field is processed succsessfully (ULblWd5 register holds the value to bo updated.
- U0100 register was eliminated
Last Edited: J. Furst, June 1, 1981 5:34 PM
-The IOCB Operation Block changed to match the Operation portion of the Face.
The following changes were implemented in ucode:
- U-registers reassigned to match the Operation Block
- Initial IOCB displacement for IOCB xfer added (CDisplIOCB=1)
- Displacements within the Operation block changed to match the format
- ECC Syndrom moved to location IOCBPtr+3E (CDisplECC changed to 3E)
- Pointer for Hdr Rd operation changed to IOCBPtr+CDisplDHdr (CDisplDHdr=0A0
The Trident Head is required to copy initially the Client Header into Disk Header locations.
The Increment Data Pointer bit has to be copied into Data Ptr High bit position 7.
Last Edited: J. Furst, June 9, 1981 10:43 AM
- Data xfer routines changed to accomodate 30 sectors.
Last Edited: J. Furst, July 20, 1981 8:16 AM
- Last IOCB bit deleted (the Next IOCB Ptr = 0 indicates the last IOCB in the chain).
Last Edited: J. Furst, August 17, 1981 3:55 PM
- ZeroPgCntTest changed from after memory UpdIOCBSec to before UpdIOCBSec.
- Last word write into memory fixed.
Last Edited: J. Furst, August 19, 1981 9:01 AM
- Clean-up: some unneeded statments in microcode deleted (RAdr ← RAdr +CDisplCCyl) in Hdr routines;
front and tail end of the data transfer routines rearranged.
- Unnneded constants (CDisplCCyl and CECCRst) deleted from *.df.
- TermIn signal to the disk is not required and as the consequence some of the U register values will change:
from U1C00 to U0C00
from U1400 to U0400
from U1C04 to U0C04
from U3C04 to U2C04
from U3C05 to U2C05
This change effects the *.df and the initialization microcode.
Last Edited: J. Furst, August 19, 1981 5:11 PM
- Problem with controller status update fixed.
Last Edited: J. Furst, August 22, 1981 10:38 AM
- Reorientation on index added (if sector address =0 set WU: IndxFound, if sectror address not 0 set WU: SecFnd).
- A new loop specially used for data field transfer is added. This saves two microinstructions.
Last Edited: J. Furst, August 26, 1981 10:10 PM
- Changed location of the I* bit in IOCB.
- Data Ptr Lo and Hi locations swapped in the IOCB.
Last Edited: J. Furst, September 1, 1981 8:15 PM
- Delay added before transferring ECC.
Last Edited: J. Furst, September 6, 1981 10:21 AM
- Corrected marginal head advance timing.
- In FirstIOCBTst routine NZeroBr replaced with XRefBr to save microintructions. As a consequence CTstFirstIOCB is deleted.
Last Edited: J. Furst, September 15, 1981 1:56 PM
- Shutting down the read and write circuitry after sector is done is changed.
Last Edited: J. Furst, December 8, 1981 2:12 PM
- Corrected problem with RCnt rot by 12 in AnySIP +1 instruction.
Edited: E Neely, March 24, 1982 7:53 AM
- Changed comments about value of CRetry and URetry to 90 headers
before header not found.
Edited: E Neely, March 29, 1982 7:33 PM
- Swapped HeadAdv + 3 and +4 to allow more time for KCmd.
- @DskCheck changed GOTO[UpdUCStat] to GOTO[UpdUCStatDC].
- Near UpdUCStat: added UpdUCStatDC: RCnt ←6, GOTO[SetUCStat] .
Edited: Forrest and J. Furst, 27-Apr-82 15:31:52
- Changed CANCELBR[$, 2] to CANCELBR[$, 1] IN XferHdrEr.
- Increase delay between read-reset and write-set for lbl and data fields.
Edited: Amy Fasnacht, August 4, 1982August 4, 1982 1:52 PM 1:52 PM
- Moved all references to IOPage into Dandelion.df.
Edited: Neely, 31-Mar-83 12:21:03
- Added check for notReady after setHead because changing offset
causes notReady.
Edited: Neely, 11-May-83 13:46:57
- SetHead must be done AFTER seek in offset cases; so do it after seek always.


This microcode will not run properly without first beeing initialized. The following U and Output registers have to be initialized by FileServerInit microcode: UStatMask (1E07), U0C00, U4000, UF001, U0400, U0100, U0C04, U2C04, U2C05, KCtl, and KCmd.
Initially, the microcode is in the dormant state. Upon awakening, it constructs the pointer to the CSB array (table) and gets from there the virtual pointer of the first IOCB for Device 0. Validity of the IOCB is checked: if the IOCB pointed at by the designated pointer is a valid one; if not a new CSB address is constructed to poll the following device, etc.

----------------------------------------------------------------------------------------------------------------------------------------------------------------
Initilization
----------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------Start of Code -------------------------------------------------------------------------}

{The microcode starts at location CSBStart. Upon this entry the indexing within the CSB array (in RCnt register) is set for device 0. There are two other entries into this section of code. CSBNextDev: an entry after a Nill IOCB is found in CSB. Upon this entry the pointer in CSB array is moved to next device. The third entry CSBDevN is used after seek or recalibrate operation is issued to the disk. It loads UDskAdr { 4*(disk address)} into RCnt register to index the next device in CSB. All this entries utilize a common section of code starting at locatin CSBStartC3, which sets the CSB real address for device 0 into RHAdr,,RAdr registers (RHAdr, RAdr ← 14001) and then adds to this value the contents of RCnt register.
After the seek operation for a particular disk has been completed (attention from the disk) the physical disk address times 4 is loaded into RCnt register to fetch the IOCB again to start the data transfer operation for that device (SIP bit is equal to 1).}


CSBDevN:
RCnt ← UDskAdr, GOTO[CSBNextDevC2],c1;

CSBNextDev:
RHCnt ← CLastDev,c1;
CSBNextDevC2:
RCnt ← RCnt+CNextDev, GOTO[CSBStartC3],c2;

TridentStart:
RHCnt ← CLastDev, SetTask[4], {***StartAddress[CSBStart],***} c1;
RCnt ← 0,c2, at[0F,10];
{%L}{CSBStartC3:
RAdr ← uIOPage,c3;

{RAdr gets the low 16 bits of the real CSB address, then the value used to index the CSB array is added to it.}

RHAdr ← IOPageHigh,c1;
Noop,c2;
RAdr ← RAdr+RCnt,c3;
}{%L}

CSBStartC3:
RHAdr ← RAdr ← uIOPage,c3;

RAdr ← RAdr and ~0FF,,c1;
RAdr ← RAdr or 1{old},,c2;
RAdr ← RAdr+RCnt,c3;


{Test for last device (device# 4) in CSB array. If not last device go into CSB array to get the IOCB virtual pointer for that device; if last device (it means first seeks to all devices have been issued) go to to test if any seek operations has been completed (AnySIP).}

CSBLastDevTest:
[] ← RCnt xor RHCnt, ZeroBr,c1;
KCmd ← U0C00, BRANCH[CSBNLastDev, CSBLastDev],c2;
CSBNLastDev:
GOTO[GetIOCBPtr] ,c3, at[0,2,CSBLastDev];
CSBLastDev:
RCnt ← 0F, GOTO[AnySIP],c3, at[1,2,CSBNLastDev];



{Load low 16 bits of the IOCB virtual pointer from the CSB array into RAdr register; set upper bits in RHAdr register to zero (IOCBs in first 64k of virtual space).}

GetIOCBPtr:
MAR ← [RHAdr, RAdr + DiskCSBOffsetIOCB],c1;
RHAdr ← 0, CANCELBR[$, 2],c2;
RAdr←MD,c3;



{If NilI IOCB procced to the next device in CSB array (CSBNextDev); upon this entry load into RCnt the value to index the next device. If good IOCB call MapVirt subroutine to get the real IOCB address for further use.}

[] ← RAdr, ZeroBr,c1;
BRANCH[TGoodIOCB, TNilIOCB], pCall4,c2;
TGoodIOCB:
CALL[MapVirt],c3, at[0,10,TNilIOCB];
TNilIOCB:
GOTO[CSBNextDev],c3, at[1,10,TGoodIOCB];



{After return from MapVirt subroutine the real pointer is in RHAdr,,RAdr registers. Store low 16 bits of IOCB address to USvAdrLo register for use in xfer and error processing routines.}

IOCBStart:
USvAdrLo ← RAdr,c3, at[0,10,RetMap];



{Save high 2 bits of the IOCB real address in USvAdrHi register. Reset the memory controller before starting the IOCB fetch operation.}

IOCBStartC1:
RAdr ← RHAdr,c1;
USvAdrHi ← RAdr, pCall4,c2;
RAdr ← USvAdrLo, CALL[IOCBLoad],c3, at[0,10];



{This routine is entered from CSB processing after recalibrate or seek operations to all disks have been issued, and after an operation defined by IOCB has been completed. It tests for SIP for any disk. If there is no SIP it resets FirmwareEn bit and goes to sleep. If there is some seek in progress it prepares for any attention test for those disks which have SIP bits on (after seek or recal operation but not after reset offset has been issued).}

AnySIP:
RAdr ← USIP, NZeroBr,c1, at[0,2,ChainedIOCB];
RCnt ← UF000, BRANCH[NoSIP, SomeSIP],c2;
NoSIP:
KCmd ← U0400, GOTO[TridentStart],c3, at[0,2,SomeSIP];
SomeSIP:
RAdr ← ~KStatus and RAdr,c3, at[1,2,NoSIP];



{Determine which disks with SIP bit on has Attention active. If none set WU: AnnyAtention and after WU go to take a new snapshot of disk status. If there is an Attention then go to refetch the IOCB for that particular disk. For multiple Attentions disk with lower address has higher priority.}

AttenTest:
[] ← RAdr LRot8, XDisp,c1;
RCnt ← CWUAnyAtt or RCnt, DISP4[Attention],c2;
KCtl ← RCnt LRot0, GOTO[AnySIP],c3, at[0,10,Attention];
RCnt ← CDsk3, GOTO[EndAtten],c3, at[1,10,Attention];
RCnt ← CDsk2, GOTO[EndAtten],c3, at[2,10,Attention];
RCnt ← CDsk2, GOTO[EndAtten],c3, at[3,10,Attention];
RCnt ← CDsk1, GOTO[EndAtten],c3, at[4,10,Attention];
RCnt ← CDsk1, GOTO[EndAtten],c3, at[5,10,Attention];
RCnt ← CDsk1, GOTO[EndAtten],c3, at[6,10,Attention];
RCnt ← CDsk1, GOTO[EndAtten],c3, at[7,10,Attention];
RCnt ← 0, GOTO[EndAtten],c3, at[8,10,Attention];
RCnt ← 0, GOTO[EndAtten],c3, at[9,10,Attention];
RCnt ← 0, GOTO[EndAtten],c3, at[0A,10,Attention];
RCnt ← 0, GOTO[EndAtten],c3, at[0B,10,Attention];
RCnt ← 0, GOTO[EndAtten],c3, at[0C,10,Attention];
RCnt ← 0, GOTO[EndAtten],c3, at[0D,10,Attention];
RCnt ← 0, GOTO[EndAtten],c3, at[0E,10,Attention];
RCnt ← 0, GOTO[EndAtten],c3, at[0F,10,Attention];



{Set WU: FirmwareEn then go to CSB array to refetch the IOCB.}

EndAtten:
KCtl ← UF000,c1;
RHCnt ← CLastDev, GOTO[CSBStartC3],c2;



{This is the return point from IOCB fetch in case of double memory error. Set memoryError bit in CStatus word.}

IOCBMemError:
RCnt ← CMemErrorTri, CANCELBR[DtMemErrorC3, 2],c2, at[0,10,RetMemEr];



{Here the opeation starts. First select the disk; inProgress and goodCompletion bits will be set in IOCB in memory by the device Head and then copied into UCStat register. Prepare to test for diskReady (onLine)}

OperStart:
KCtl ← UBitSignAdr,c2, at[0,10,RetNoMemEr];
RCnt ← CNotRdy,c3;



{Test for diskReady. If not set both status words and then flush the operation. If yes proceed with operation.}

TestDskRdy:
[] ← ~KStatus and RCnt, ZeroBr,c1;
BRANCH[DskNotRdy, DskRdy],c2;
DskNotRdy:
Noop, GOTO[UpdUCStat],c3, at[0,2,DskRdy];
DskRdy:
Noop,c3, at[1,2,DskNotRdy];



{Prepare to reset the Attention from the selected disk. Attention is reset by setting the read circuitry: CntlTag and read bit (CRstAtt = read).}

RCnt ← U0C00,c1;
RCnt ← RCnt or CRstAtt, pCall4,c2;
RAdr ← CCtlTag, CALL[TagSetRst],c3, at[0,10];



{Set unconditionally WU: DskRdy in KCtl register. If disk not ready the microcode will get dormant until disk goes ready.}

RAdr ← UBitSignAdr,c1, at[0,10,RetTagSetRst];
RCnt ← RAdr or CWURdy,c2;
KCtl ← RCnt LRot0,c3;



{Load the bit significant disk address into the RAdr register (the ms nibble and the ls bit must be reset) in order to set and test the SIP bit of the selected disk. Switch back to WU: FirmwareEn in c3.}

RCnt ← ~UF001,c1;
RAdr ← RAdr and RCnt,c2;
KCtl ← UBitSignAdr,c3;



{Test for SIP and set SIP bit for the selected disk. In either case prepare to test for need to issue recalibrate.}

SIPTest:
[] ← RAdr and USIP, ZeroBr,c1;
RAdr ← RAdr or USIP, BRANCH[SkInProg, SkNotInProg],c2;
SkInProg:
RCnt ← UBitSignAdr, GOTO[SIPTstRecal],c3, at[0,2,SkNotInProg];
SkNotInProg:
RCnt ← UBitSignAdr,c3, at[1,2,SkInProg];



{If SIP bit is 0 and the Recal bit is 1 issue recalibrate; if Recal bit is 0 go to issue a Seek, set SIP in c2.}

NotSIPTstRecal:
[] ← RCnt and CTstRecal, ZeroBr,c1;
USIP ← RAdr, BRANCH[NotSIPRecal, NotSIPNoRecal],,c2;
NotSIPRecal:
Noop, GOTO[RecalStart],c3, at[0,2,NotSIPNoRecal];
NotSIPNoRecal:
RCnt ← CTstRecalEr, GOTO[SeekStart],c3, at[1,2,NotSIPRecal];



{If SIP bit is a 1 and Recal bit is also 1 prepare for to test for Recal error and then go to issue Seek; if Recal bit is 0 (it gets reset in Seek routine) prepare to test for Seek error and then go to data xfer.}

SIPTstRecal:
[] ← RCnt and CTstRecal, ZeroBr,c1;
USIP ← RAdr, BRANCH[SIPRecal, SIPNoRecal] ,c2;
SIPRecal:
RCnt ← CTstRecalEr, GOTO[SeekStart],c3, at[0,2,SIPNoRecal];
SIPNoRecal:
RCnt ← CTstSkEr, GOTO[SetHead],c3, at[1,2,SIPRecal];



{The recalibrate operation starts here. It utilizes the TagSetRst subroutine with CtlTag and recal bit active. It returns to FirstIOCBTest.}

RecalStart:
RCnt ← U0C00,c1;
RCnt ← RCnt or CRecal, pCall4,c2;
RAdr ← CCtlTag, CALL[TagSetRst],c3, at[1,10];



{The seek operation starts here. First test for recalibrate error.}

SeekStart:
[] ← ~KStatus and RCnt, ZeroBr,c1;
BRANCH[RecalError, RecalOK], pCall4,c2;
RecalError:
RCnt ← CRecalError, GOTO[UpdUCStatN],c3;
RecalOK:
Noop,c3;


{Prepare CylAddr in RCnt and CylTag constant in RAdr for TagSetRst subroutine use and then call the TagSetRst subroutine to transfer this value to the disk. Also switch KCtl.WU back to FirmwareEn.}

SeekIssue:
RCnt ← UCylAdr,c1;
RAdr ← CCylTag, pCall4,c2;
KCtl ← UBitSignAdr, CALL[TagSetRst],c3, at[4,10];



{After SetCylTag the microcode returns here. Prepare to reset the Recalibrate bit in IOCB (memory only) and then go to BSAdrUpdate subroutine to do the actual writting.}

RAdr ← UBitSignAdr,c1, at[4,10,RetTagSetRst];
RCnt ← ~CTstRecal, pCall4,c2;
RCnt ← RCnt and RAdr, CALL[BSAdrUpdate],c3, at[0,10];



{Test if recalibrate had been issued before this seek operation. If yes go directly to test for Any Attention. If no prepare to test for first IOCB in chain.}

[] ← UBitSignAdr and RCnt, ZeroBr,c1, at[0,10,RetStatUpd];
KCtl ← UF001, BRANCH[RclBefSk, NoRclBefSk] ,c2;
RclBefSk:
RCnt ←0F, GOTO[AnySIP],c3, at[0,2,NoRclBefSk];
NoRclBefSk:
Noop,c3, at[1,2,RclBefSk];



{If seek was not preceeded with a recalibrate then if this is the first IOCB in chain go to CSB processing [CSBDevN] to issue seeks for for all remaining disks in that particular queue; go to any Attention test otherwise.}

FirstIOCBTst:
Xbus ← UHdrOp, XRefBr,c1, at[1,10,RetTagSetRst];
Noop, BRANCH[NotFirstIOCB, FirstIOCB] ,c2;
NotFirstIOCB:
RCnt ←0F, GOTO[AnySIP],c3, at[0,2,FirstIOCB];
FirstIOCB:
RHCnt ←CLastDev, GOTO[CSBDevN],c3, at[1,2,NotFirstIOCB];



{Here because SIP bit = 1 and Recal bit = 0. First test for error after seek operation. If error go to update the status words and flush the operation for this disk. If OK , then call TagSetRst subroutine (RCnt ← HeadAddr+Offset, RAdr ← CHdTag constant for use in subroutine) to transfer head address and offset value (if any) to the disk.}

SetHead:
[] ← ~KStatus and RCnt, ZeroBr,c1;
RCnt ← UHdOffset, BRANCH[SeekError, SeekOK], pCall4,c2;
SeekError:
RCnt ← CTstSkEr, GOTO[UpdUCStat],c3, at[2,10];
SeekOK:
RAdr ← CHdTag, CALL[TagSetRst],c3, at[3,10];


{After setHead the microcode returns here. If setHead caused a change in the offset status, the disk goes not ready within approx 100ns for approx 2.5ms; otherwise it stays ready. Set KCtl.WU to DskRdy so that after this click the microcode will remain dormant as long as the disk is notReady. RHCnt gets the sector wake-up constant needed by xfer routines.}

{Note: TagSetRst returns with RAdr = UBitSignAdr.}
RCnt ← RAdr or CWURdy,c1, at[3,10,RetTagSetRst];
KCtl ← RCnt LRot0, pCall4,c2;
RHCnt ← CWUSecFnd, CALL[XferSecStart],c3, at[1,10];


{Set the HdSel line for the disk in c1. Transfer sector starts here with setting the disk Bus with head select constant. RHAdr gets high 16 bits of IOCB real pointer in c2, and in c3 the CntlTag is set.}

XferSecStart:
KCmd ← U0C04,c1;
RHAdr ← USvAdrHi,c2;
XferSecStartC3:
KCmd ← U2C04,c3;



{After finishing a sector the microcode returns to this point. Prepare to set WU: SectorFound/IndxFnd into KCtl (RHCnt is loaded with proper constant upon entry or in LastSecTest routine).}

XferSecLp:
RAdr ← USvAdrLo,c1;
RCnt ← RHCnt,c2;
RCnt ← UBitSignAdr or RCnt, pRet4,c3;



{Set WU: SectorFound into KCtl and then return from the XferSecStart subroutine. First time in the microcode returns to XferHdrPass1 to perform a 16-way dispatch on Hdr operation. Reset SectorFnd and IdxFnd latches before exiting this click.}

KCtl ← RCnt LRot0, RET[RetSecStart],c1;

XferHdrPass1:
Xbus ← UHdrOp, XDisp,c2, at[1,10,RetSecStart];
ClrKFlags, DISP4[Operation], pCall4,c3;



{First time in all unoriented operations (Hdr Noop, Wr, and Rd) call the FindHdr subroutine to get oriented on client address. For verify operation call SecZrTest subroutine to set the proper wake-up. Readjust the address for each operation, and after return from FindHdr set the sequencer into proper routine. The HdrVrf operation in a case of an error will be retried 90 times before setting HdrNotFnd (URetry contains 90 at this point). For Noop, Rd, and Vrf operation set a delay constant to raise the read gate in the drive. For Wr operation write gate gets set immediatelly.}

HdrNoopPass1:
Noop, CALL[FindHdr],c1, at[4,10,Operation];
HdrWrPass1:
Noop, CALL[FindHdr],c1, at[5,10,Operation];
HdrRdPass1:
RAdr ← RAdr+CDisplDHdr, CALL[FindHdr],c1, at[6,10,Operation];
HdrVrfPass1:
RHCnt ← UCurHdSec, CALL[SecZrTest],c1, at[7,10,Operation];

HdrNoopRet:
KCtl ← UHCntl, pCall4, GOTO[HdrNoopRdVrf],c2, at[4,10,RetFindHdr];
HdrWrRet:
KCtl ← UHCntl, pCall4, GOTO[HdrWrCont],c2, at[5,10,RetFindHdr];
HdrRdRet:
KCtl ← UHCntl, pCall4, GOTO[HdrNoopRdVrf],c2, at[6,10,RetFindHdr];
HdrVrfRet:
KCtl ← UHCntl, pCall4,c2, at[7,10,RetSecZrTest];

HdrNoopRdVrf:
RCnt ← CHDelRdSet, CALL[DelSetRd],c3, at[1,10];
HdrWrCont:
RCnt ← CHDelWrSet, CALL[DelSetRd],c3, at[1,10];



{NextHdrOp and NextHdrOpC3 are return points from XferSecStart subroutine after Hdr retry and after the data field has been processed (if PgCnt not 0). The microcode performes a 4-way dispatch on HdrOp. The only diferrence between this section of the code, and XferHdrPass1 is that at this time the operation is already oriented and the FindHdr subroutine is bypassed.}

NextHdrOp:
Xbus ← UHdrOp, XDisp,c2, at[2,10,RetSecStart];
ClrKFlags, DISP4[HdrOper, 0C],c3;



{The 4-way dispatch is performed here. The microcode then merges with the "IOCB first pass" microroutines.}

HdrNoop:
Noop, GOTO[HdrNoopRet],c1, at[0C,10,HdrOper];
HdrWr:
Noop, GOTO[HdrWrRet],c1, at[0D,10,HdrOper];
HdrRd:
RAdr ← RAdr+CDisplDHdr, GOTO[HdrRdRet],c1, at[0E,10,HdrOper];
HdrVrf:
Noop, GOTO[HdrRdRet],c1, at[0F,10,HdrOper];



{Set the CtlTag and bus for all Hdr operations here. Prepare to set the DataReq WU in next click (RCnt was set with proper constant in the subroutine.}

HdrSetRdRet:
KCmd ← UHCmd,c2, at[1,10,RetDelSetRd];
RCnt ← UHCntl or RCnt, GOTO[HdrLoopDisp],c3;



{Dispatch again on Hdr operation to determine which of the data Xfer loops to call. Set the word count into RCnt register (2 for Hdr op) and the call the loop. The sequencer generates always WordCount+1 DataReqs in order to exit the loop.}

HdrLoopDisp:
Xbus ← UHdrOp, XDisp, GOTO[LoopDisp],c1;
LblLoopDisp:
Xbus ← ULblOp, XDisp, GOTO[LoopDisp],c1;
DtLoopDisp:
Xbus ← UDtOp, XDisp, GOTO[LoopDisp],c1;

LoopDisp:
KCtl ← RCnt LRot0, DISP4[CallLoop], pCall4,c2;

CallHdrNoop:
RCnt ← 2, CALL[NoopLp], ZeroBr,c3, at[4,10,CallLoop];
CallHdrWrt:
RCnt ← 2, CALL[WrVrfLp],c3, at[5,10,CallLoop];
CallHdrRd:
RCnt ← 2, CALL[RdLp],c3, at[6,10,CallLoop];
CallHdrVrf:
RCnt ← 2, CALL[WrVrfLp],c3, at[7,10,CallLoop];



{This are the return points from loops. The ClientLabel is starts at IOCBPtr+20, the DiskLabel starts at location IOCBPtr+30. RCnt is loaded with a delay constant for use in DelRdRst subroutine, which control the read circuitry reset. In c2 and c3 the IOCB pointer is set into RHAdr,, RAdr register pair to address one of the label areas in the IOCB.}

HdrNLpRet:
RCnt ← CHNRDelRdRst, GOTO[GetLblAdr],c1, at[4,10,RetXfrLp];
HdrWLpRet:
RCnt ← CHWVDelRdRst, GOTO[GetLblAdr],c1, at[5,10,RetXfrLp];
HdrRLpRet:
RCnt ← CHNRDelRdRst, GOTO[GetLblAdr],c1, at[6,10,RetXfrLp];
HdrVLpRet:
RCnt ← CHWVDelRdRst, GOTO[GetLblAdr],c1, at[7,10,RetXfrLp];

GetLblAdr:
RAdr ← USvAdrLo, pCall4,c2;
RHAdr ← USvAdrHi, CALL[DelRdRst],c3, at[0,10];



{Set the Hdr mask into RCnt for later use. Perform a dispatch on label operation. RAdr gets the client label pointer.}

LblOpDISP:
RCnt ← UHMask,c1, at[0,10,RetDelRdRst];
Xbus ← ULblOp, XDisp,c2;
RAdr ← RAdr + CDisplClntLbl, DISP4[Operation],c3;



{These are the dispatch points after the DISP on Lbl opeartion. The sequencer had been set with proper Function code and in the case of disk read type of operations (OL code in IOCB = LblNoop, LblRd, LblVrf) RCnt gets the constant for delay set read. The write is set immediatelly. In case of LblRd operation the relative displacement (from client label) is added to RAdr to point to DskLbl area of the IOCB. Test for header operation OK for label read operation if OK call DelSetRd subroutine in next click.}

LblNoop:
Noop, GOTO[LblNRVCont],c1, at[08,10,Operation];
LblWr:
RCnt ← ~KTest and RCnt, NZeroBr,
GOTO[LblWrCont],c1, at[09,10,Operation];
LblRd:
RAdr ← RAdr + CRelDisplDskLbl,
GOTO[LblNRVCont],c1, at[0A,10,Operation];
LblVrf:
Noop, GOTO[LblNRVCont],c1, at[0B,10,Operation];

LblNRVCont:
RCnt ← ~KTest and RCnt, NZeroBr,c2;
KCtl ← ULCntl,
BRANCH[HdrOpOKRd, HdrOpErrRd], pCall4,c3;



{Call DelSetRd subroutine to raise the disk read circuitry.}

HdrOpOKRd:
RCnt ← CLDelRdSet, ZeroBr,
CALL[DelSetRdC2],c1, at[0,10,HdrOpErrRd];



{Test for header operation OK for label write operation. If OK go to raise disk write circuitry without a delay.}

LblWrCont:
KCtl ← ULCntl,
BRANCH[HdrOpOKWr, HdrOpErrWr], L4 ← 0,c2;
HdrOpOKWr:
KCmd ← ULCmd, L4Disp, CALL[EndDelSetRd],c3, at[0,2,HdrOpErrWr];
HdrOpErrWr:
Noop, GOTO [HdrOpErrWrCont],c3, at[1,2,HdrOpOKWr];


{In case of any header operation error store the controller status in UCStat register (this is needed because the laches are reset by LdSequencer signal).}

HdrOpErrRd:
RCnt ← RCnt or 2, GOTO[HdrErrC2],c1, at[1,10,HdrOpOKRd];
HdrOpErrWrCont:
RCnt ← RCnt or 2,c1;

HdrErrC2:
UCStat ← RCnt,c2;
Noop,c3;



{RCnt register gets ’F’ to strip the FirstIOCB bit in UHdrOp register and go to test for verify.}

RCnt ← 0F,c1;
RCnt ← UHdrOp and RCnt, GOTO[TestForVrf],c2;



{This is the end of delay. Set the CtlTag and Bus for Lbl operation. Prepare to set DataReq WU for Lbl loops and then go to do the DISP for Noop, Rd, WrVrf loops.}

LblSetRd:
KCmd ← ULCmd,c2, at[0,10,RetDelSetRd];
RCnt ← ULCntl or RCnt, GOTO[LblLoopDisp],c3;



{This are the dispatch points to call the loops for Lbl operation. Set hte Lbl Word Count into RCnt register.}

CallLblNoop:
RCnt ← 0A, CALL[NoopLp], ZeroBr,c3, at[8,10,CallLoop];
CallLblWrt:
RCnt ← 0A, CALL[WrVrfLp],c3, at[9,10,CallLoop];
CallLblRd:
RCnt ← 0A, CALL[RdLp], ZeroBr,c3, at[0A,10,CallLoop];
CallLblVrf:
RCnt ← 0A, CALL[WrVrfLp],c3, at[0B,10,CallLoop];



{This are the return points from loops. RHCnt reegister is loaded with constant to delay the disk read circuitry after label operations. In c2 and c3 load the RHAdr,, RAdr register pair with virtual data pointer for use in MapVirt subroutine.}

LblNLpRet:
Noop, GOTO[GetDtAdrNR],c1, at[8,10,RetXfrLp];
LblWLpRet:
RHCnt ← CLWVDelRdRst,
GOTO[GetDtAdrWrV],c1, at[9,10,RetXfrLp];
LblRLpRet:
Noop, GOTO[GetDtAdrNR],c1, at[0A,10,RetXfrLp];
LblVLpRet:
RHCnt ← CLWVDelRdRst,
GOTO[GetDtAdrWrV],c1, at[0B,10,RetXfrLp];

GetDtAdrNR:
RAdr ← UDtPtrLo, pCall4,c2;
RHAdr ← UDtPtrHi, CALL[MapVirt],c3, at[1,10];


GetDtAdrWrV:
RAdr ← UDtPtrLo, pCall4,c2;
RHAdr ← UDtPtrHi, CALL[MapVirt],c3, at[2,10];



{Call DelRdRst subroutine to reset the read gate if any.}

DtMapRetNR:
RCnt ← RHCnt, CALL[RdGateRst],c3, at[1,10,RetMap];

DtMapRetWrV:
RCnt ← RHCnt, CALL[DelRdRst],c3, at[2,10,RetMap];



{Prepare label mask into RCnt register to test the label operation. Dispatch on data opcode.}

DtOpDISPNR:
RCnt ← ULMask, GOTO[DtOpDISP],c1, at[1,10,RetDelRdRst];
DtOpDISPWrV:
RCnt ← ULMask,c1, at[2,10,RetDelRdRst];
DtOpDISP:
Xbus ← UDtOp, XDisp,c2;
DISP4[Operation],c3;



{These are the dispatch points after the DISP on data operation. The sequencer is loaded with proper Function code and in case of disk read type of operations (OD code in IOCB = Noop, Read, Verify) RCnt will get the constant for delayed set of read circuitry. The write will be set immediatelly.}

DtNoop:
Noop, GOTO[DtNRVCont],c1, at[0C,10,Operation];
DtWr:
RCnt ← ~KTest and RCnt, NZeroBr,
GOTO[DtWrCont],c1, at[0D,10,Operation];
DtRd:
Noop, GOTO[DtNRVCont],c1, at[0E,10,Operation];
DtVrf:
Noop, GOTO[DtNRVCont],c1, at[0F,10,Operation];

DtNRVCont:
RCnt ← ~KTest and RCnt, NZeroBr,c2;
KCtl ← UDCntl,
BRANCH[LblOpOKRd, LblOpErrRd], pCall4,c3;



{Call DelSetRd subroutine to raise the disk read circuitry if read type of label operation was OK.}

LblOpOKRd:
RCnt ← CDDelRdSet, ZeroBr,
CALL[DelSetRdC2],c1, at[2,10,LblOpErrRd];

DtWrCont:
KCtl ← UDCntl,
BRANCH[LblOpOKWr, LblOpErrWr], L4 ← 2,c2;
LblOpOKWr:
KCmd ← UDCmd, L4Disp, CALL[EndDelSetRd],c3, at[0,2,LblOpErrWr];
LblOpErrWr:
Noop, GOTO[LblOpErrCont],c3, at[1,2,LblOpOKWr];


{n case of any label operation error store the controller status in UCStat register (this is needed because the laches are reset by LdSequencer signal).}

LblOpErrRd:
RCnt ← RCnt or 4, GOTO[LblErrC2],c1 ,at[3,10,LblOpOKRd];
LblOpErrCont:
RCnt ← RCnt or 4,c1;

LblErrC2:
UCStat ← RCnt,c2;
Noop, GOTO[UpdUCStat],c3;



{This is the end of delay. Set the CtlTag and Bus for Dt operation. Prepare to set WU DataReq for data xfer loops and then go to do the DISP for Noop, Rd, WrVrf loops.}

DtSetRd:
KCmd ← UDCmd,c2, at[2,10,RetDelSetRd];
RCnt ← UDCntl or RCnt, GOTO[DtLoopDisp],c3;



{This are the dispatch points to call the loops for Dt operation. Set proper word counts into RCnt register for loops (RHCnt got FE in DelSetRd subroutine).}

CallDtNoop:
RCnt ← U0100, ZeroBr, CALL[NoopLp],c3, at[0C,10,CallLoop];
CallDtWrt:
RCnt ← 0FF + 1, CALL[WrVrfLp],c3, at[0D,10,CallLoop];
CallDtRd:
RCnt ← 0 + 0, PgCarryBr, CALL[DtRdLp],c3, at[0E,10,CallLoop];
CallDtVrf:
RCnt ← 0FF + 1, CALL[WrVrfLp],c3, at[0F,10,CallLoop];



{This are the return points from loops after Dt operations. The sector data xfer is completed here. Start housekeeping after the data field. First decrement the PgCnt in UPgCnt register.}

DtNLpRet:
RAdr ← UPgCnt, GOTO[DtXferFin],c1, at[0C,10,RetXfrLp];
DtWLpRet:
RAdr ← UPgCnt, GOTO[DtXferFin],c1, at[0D,10,RetXfrLp];
DtRLpRet:
RAdr ← UPgCnt, GOTO[DtXferFin],c1, at[0E,10,RetXfrLp];
DtVLpRet:
RAdr ← UPgCnt, GOTO[DtXferFin],c1, at[0F,10,RetXfrLp];

DtXferFin:
RAdr ← RAdr - 1,c2;
UPgCnt ← RAdr,c3;



{Increment client label word 5 in ULblWd5 register for the next sector operation.}

RCnt ← ULblWd5,c1;
RCnt ← RCnt + 1,c2;
ULblWd5 ← RCnt,c3;



{Conditionally increment DtPtrLo in UDtPtrLo if I bit in IOCB is =1 (bit7 in UHdrOp register). First load the I bit (value = 256) into RCnt register to extract the I*, and then load RAdr with UDtPtrLo.}

RCnt ← 0FF + 1,c1;
RCnt ← UHdrOp and RCnt,c2;
RAdr ← UDtPtrLo,c3;



{Now add the value of I* (I* is 0 or 256 - set by device Head) to UDtPtrLo, which is already in RAdr and then load this value into UDtPtrLo, thus UDtPtrLo is incremented conditionally by 256 as a function of I*. Prepare to test for disk check.}

RCnt ← RAdr+RCnt ,c1;
UDtPtrLo ← RCnt,c2;
RAdr ← ~KStatus xor 0,c3;



{Test for disk check. If DiskCheck save controller status in UCStat register and on flush the opration. If OK call the memory error test subroutine. Set RCnt to 0 for subroutine use. This will ensure that URetry will have 0 contents after exit from subroutine.}

DskCheckTest:
[] ← RAdr and CDskCheck, ZeroBr,c1;
RAdr ← ~KTest xor 0, BRANCH[DskCheck, DskOK], pCall4,c2;
DskCheck:
UCStat ← RAdr, GOTO[UpdUCStatDC],c3, at[0,10,DskOK];
DskOK:
RCnt ← 0, CALL[MemErTest],c3, at[1,10,DskCheck];



{This is the return point from memory error test subroutine in case there is no double bit errror. Prepare to test for data check. This test is done late on purpose because of delay in ECCError detection.}

MemTestOK:
RCnt ← UDMask,c2, at[1,10,RetNoMemEr];
RCnt ← ~KTest and RCnt, GOTO[DtCheckTest], ZeroBr,c3;



{If double bit memory error this is the return point from test memory error subroutine. Save controller status in UCStat register.}

DtMemError:
RAdr ← ~KTest xor 0, CANCELBR[$, 2],c2, at[1,10,RetMemEr];
DtMemErrorC3:
UCStat ← RAdr,c3;



{Set MemoryError bit in RCnt to terminate the operation.}

RCnt ← CMemErrorTri, GOTO[SetUCStat],c1;



{This is the test for data check. The proper mask from the IOCB is applied in this test. If data check prepare to save controller status in UCStat register and then go to transfer the ECC syndrom. If OK reset the read and write bits and the CNTLTAG for potential HDADV in c1 and in c3. In c2 load the CMaxSec into RCnt to test for last sector (sector address = 28) on the track.}

DtCheckTest:
KCmd ← U2C04, BRANCH[DtCheck, DtOpOK],c1;
DtCheck:
RCnt ← RCnt or 6, GOTO[ECCXfer],c2, at[0,2,DtOpOK];
DtOpOK:
RCnt ← CMaxSec,c2, at[1,2,DtCheck];
KCmd ← RAdr ← U0C04,c3;



{Test for PgCnt = 0. If yes go to EndProc; if no test for the last sector (sector address = 28). RHAdr gets the high 2 bits of the IOCB pointer for future IOCB update.}

ZrPgCntTest:
Ybus ← UPgCnt, NZeroBr,c1;
ZrPgCntTestC2:
RHCnt ← UCurHdSec, BRANCH[ZrPgCnt, NotZrPgCnt],c2;
ZrPgCnt:
RHAdr ← USvAdrHi, GOTO[EndProc],c3, at[0,2,NotZrPgCnt];
NotZrPgCnt:
Ybus ← RCnt xor RHCnt, ZeroBr,c3, at[1,2,ZrPgCnt];



{This is the test for last sector (sector address = 28). If not last sector increment the sector number in RCnt register. RHCnt gets the sector wake-up constant for use in transfer routines.}

RCnt ← UCurHdSec, BRANCH[NotLastSec, HeadAdv],c1;
NotLastSec:
RCnt ← RCnt + 1,c2, at[0,2,HeadAdv];
RHCnt ← CWUSecFnd,c3;


{Go to update the IOCB after this sector is processed. Load RAdr with U0C04 for use in UpdIOCBSec routine.}

RAdr ← U0C04, GOTO[UpdIOCBSec],c1;



{If last sector advance the head (by issueing head advance), first reset sector address and then increment head address. Or the HdAdv constant into RAdr register to set the disk bus in c3.}

HeadAdv:
RAdr ← RAdr or CHdAdv,c2, at[1,2,NotLastSec];
KCmd ← RAdr LRot0,c3;


{Reset the sector number bits in RCnt register in c1. In c2 increment hte head address. The CNLTTAG and BUS gets set in c3 (with HdSel and HdAdv bits on the bus).}

RCnt ← RHCnt xor RCnt,c1;
KCmd ← U2C05,c2;
RCnt ← RCnt + 0FF+1,c3;



{RHCnt gets the index wake-up logic for next sector operation in c1. Reset the CNTLTAG in c2. Load RAdr registers with IOCB physical address to update the IOCB for next sector transfer (RHAdr is loaded @ EndLoops) and reset the ECC logic for next sector operation (set sequencer to Idle) in c3.}

RHCnt ← CWUIndxFnd,c1;
UpdIOCBSec:
KCmd ← RAdr LRot0,c2;
RAdr ← USvAdrLo, ClrKFlags,c3;



{Update the current Head/Sector address in IOCB and also load UCurHdSec register with this updated value. Reset CNTLTAG for next sector operation.}

MAR ← [RHAdr, RAdr+CDisplCHdSec],c1;
MDR ← UCurHdSec ← RCnt, CANCELBR[$, 2], LOOPHOLE[wok],c2;
KCmd ← U0C04,c3;



{Write the updated client label word 5 into the IOCB location (IOCBPtr + 25). Prepare to set the ECC logic into Reset sequence. Set CNTLTAG and HdSel bits in KCmd for next sector operation and then call XferSecLp for next Hdr operation.}

MAR ← [RHAdr, RAdr + CDisplLblWd5], pCall4,c1;
MDR ← ULblWd5, LOOPHOLE[wok],
CANCELBR[XferSecStartC3, 2], CALL[XferSecStartC3],c2, at[2,10];



{The ECC transfer is entered only in case the there is a data check, even the reason might be something else then ECC error. First restore the current IOCB physical Address in RHAdr,,RAdr register pair....}

ECCXfer:
RAdr ← USvAdrLo,c3;



{... load RCnt with delay constant and call DelRdRst for delay before loadinfg the sequencer into ECCXfer routine....}

UCStat ← RCnt,c1, at[3,10];
RCnt ← 48, pCall4,c2;
CALL[DelRdRst],c3, at[3,10];



{... and then add the proper dislacement to this address for syndrom transfer and prepare to set the sequencer into ECCXfer routine.}

RAdr ← RAdr +CDisplECC,c1, at[3,10,RetDelRdRst];
RHAdr ← USvAdrHi,c2;
RCnt ← UBitSignAdr,c3;




{Set the word sequencer into ECC Xfer routine and set the number of words being transfered in RdLp to 2. The sequencer will generate 3 DataReqs the third one to exit from the loop.}

RCnt ← RCnt or CECCXfer,c1;
KCtl ← RCnt LRot0, pCall4,c2;
RCnt ← 2, CALL[RdLp],c3, at[3,10];



{In case of HdrErr test for Vrf operation. RCnt has UHdrOp (with the FirstIOCB bit masked off) in it.}

TestForVrf:
[] ← RCnt xor CTestForVrf, ZeroBr,c3;



{If it is not a Vrf operation then we have a HdrError condition. Then go to eror processing.}

RCnt ← URetry, BRANCH[XferHdrEr, VrfOper], ZeroBr,c1;
XferHdrEr:
KCmd ← U2C04, CANCELBR[$, 1],c2, at[0,2,VrfOper];
XferHdrErC3:
Noop, GOTO[UpdUCStat],c3;



{If it is a Vrf operation test number of remaining retries. if 0 then it means one of two things; number of retries was exasted (for unoriented operation) or this is not the first pass (oriented opearation). For either case it is a Hdr error and HdrNotFound status is posted into controller status word and then go to error processing. if not 0 then call XferSecStart subroutine to retry the header compare.}

VrfOper:
RCnt ← RCnt-1, BRANCH[HdrRetry, HdrError],c2, at[1,2,XferHdrEr];
HdrRetry:
URetry ← RCnt, GOTO[RestoreUCStat],c3, at[0,2,HdrError];
HdrError:
RCnt ← CHdrNotFnd, GOTO[UpdUCStatN],c3, at[1,2,HdrRetry];



{Restore UCStat with GoodCompl status.}

RestoreUCStat:
RCnt ← 0C0,c1;
RCnt ← RCnt LRot8, pCall4,c2;
UCStat ← RCnt, CALL[XferSecStart],c3, at[2,10];



{This is the beginning of error processing routine. It updates both controller and disk status words. UpdUCStatN is used by those error exits they set some of the controller status bits (RecalError, MemError, HdrNotFound). UpdUCStat is also the exit from ECCXfer routine. Then the controller status mask is applied to reset unused bits. It also will reset InProgress and GoodCompletion bit in UCStat register....}

UpdUCStatDC: RCnt ←6, GOTO[SetUCStat]
,c1;
UpdUCStat:
RCnt ←0, GOTO[SetUCStat],c1, at[3,10,RetXfrLp];
UpdUCStatN:
Noop,c1;

SetUCStat:
RAdr ← UCStat,c2;
RAdr ← RAdr or RCnt,c3;



{... and actually reset goodCompletion bit in UCStat register and then call subroutine to write UCStat into IOCB.}

RCnt ← ~U4000 xor 0,c1;
UCStat ← RAdr and RCnt,c2;
RHAdr ← USvAdrHi,c3;


{Update UDStat, then reset read and write lines and call subroutine to write it into the proper IOCB location}

EndProc:
RCnt ← ~KStatus xor 0,c1;
UDStat ← RCnt, pCall4,c2;
KCmd ← U2C04, CALL[DStatUpdate],c3, at[1,10];



{Reset CtlTag, reset memory controller for task 4, and call TagSetRst subroutine to reset track offset if any.}

KCmd ← RCnt ← U0C00,c1, at[1,10,RetStatUpd];
RAdr ← CHdTag, pCall4,c2;
MCtl ← U8C02, CALL[TagSetRst],c3, at[5,10];



{Reset the SIP bit for the current disk in USIP register.}

RAdr ← ~UBitSignAdr xor 0,c1, at[5,10,RetTagSetRst];
RAdr ← USIP and RAdr, pCall4,c2;
USIP ← RAdr, CALL[PgCntUpdate],c3, at[3,10];



{Issue Naked Notify and request Mesa Interrupt.}

RCnt ← uWP,c1, at[3,10,RetStatUpd];
RCnt ← UIntMask or RCnt, pCall4,c2;
uWP ← RCnt, MesaIntRq, CALL[DtPtrUpdate],c3, at[4,10];



{Reset inProgress bit in UCStat register and call subroutine to update that IOCB location in memory.}

RAdr ← UCStatMask,c1, at[4,10,RetStatUpd];
RAdr ← UCStat and RAdr, pCall4,c2;
UCStat ← RCnt ← RAdr, CALL[CStatUpdate],c3, at[2,10];



{Test for IOCB good completion}

RAdr ← CGoodCompl,c1, at[2,10,RetStatUpd];
RAdr ← RAdr LRot8,c2;
Ybus ← UCStat and RAdr, NZeroBr,c3;



{Deselect the disk. In case of good completion test for chaining, if not chained (the next IOCBLink = 0) or if bad completion go to AnySIP to test if there is naything else to do for the current IOCB queue.}

KCtl ← UF001, BRANCH[BadCompl, GoodCompl],c1;
BadCompl:
Ybus ← 0, NZeroBr, GOTO[GoodComplC3],c2, at[0,2,GoodCompl];

GoodCompl:
Ybus ← UIOCBLink, NZeroBr,c2, at[1,2,BadCompl];
GoodComplC3:
RCnt ← 0F, BRANCH[AnySIP, ChainedIOCB],c3;



{Here the chained IOCB processing starts. If good completion and chained (not the last IOCB) IOCB get the IOCB real pointer. Set-up the pointer to CSB’s IOCB pointer in RHAdr,,RAdr register pair in this and c1 of the next click.}

{%L}{
ChainedIOCB:
RAdr ← uIOPage,c1, at[1,2,AnySIP];
RHAdr ← IOPageHigh,c2;
RAdr ← RAdr or UDskAdr,c3;
}{%L}
ChainedIOCB:
RHAdr ← RAdr ← uIOPage,c1, at[1,2,AnySIP];
RAdr ← RAdr and ~0FF,,c2;
RAdr ← RAdr or UDskAdr,c3;{1{old} will occur with DiskCSBOffsetIOCB}


{Update the CSB’s IOCB pointer to point to the next IOCB (first in RCnt register and then in memory.}

RAdr ← RAdr or DiskCSBOffsetIOCB,c1;
RCnt ← UIOCBLink, pCall4,c2;
CALL[StatWrite],c3, at[5,10];



{Get the real IOCB pointer and start the next IOCB (return to IOCBStart).}

RHAdr ← 0,c1, at[5,10,RetStatUpd];
RAdr ← UIOCBLink, pCall4,c2;
CALL[MapVirt],c3, at[0,10];



{---------------------------------------------------------------------------------------------------------------------------------------------------------------
SUBROUTINES
---------------------------------------------------------------------------------------------------------------------------------------------------------------}


{This is a subroutine which maps the virtual address to physical address. It is used to map the IOCB address, the Client Label address, and the Data address. The map look-up table is in 10000 - 13FFF of physical memory. Upon entry the virtual address is in RHAdr,,RAdr register pair; upon exit the real address is in the same register pair. Then low 16 address bits are saved in RCnt for re-assembly in next click.}

MapVirt:
Map ← [RHAdr, RAdr+0],c1;
RCnt← RAdr,c2;
RHAdr ← MD, RAdr ← MD,c3;



{Here the 8 lsbs of the real address are re-assembled and stored in RAdr register. The sbroutine then returns returns to the caller. In c2 a pCall4 is executed to call (after return) the DelRdRst subroutine before the date field operation.}

RAdr ← MAR ← [RHAdr, RCnt+0], pRet4,c1;
RET[RetMap], pCall4,c2;



{This is a subroutine to load an IOCB into the U registers. At this point RHAdr,,RAdr registers contain the proper real address of the block to be xfered from memory. This loop xfers a block of 12 words from the IOCB to 75 -7F U registers and then to 70 U-reg.}

IOCBLoad:
RAdr ← MAR ← [RHAdr, RAdr+CDisplIOCB],c1;
MCtl ← U8C02, CANCELBR[IOCBLpC3, 2],c2;

IOCBLp:
RAdr ← MAR ← [RHAdr, RAdr+1], AltUaddr, NibCarryBr,c1;
IOCBLpC2:
URAdrBlk ← RCnt, BRANCH[IOCBLpC3, IOCBLpFin],
CANCELBR[$, 2], LOOPHOLE[wok],c2;
IOCBLpC3:
RCnt ← MD, GOTO[IOCBLp],c3, at[0,2,IOCBLpFin];
IOCBLpFin:
RCnt ← MD, GOTO[IOCBParLp],c3, at[1,2,IOCBLpC3];



{Upon exit from the previous loop the 4 LSBs of RAdr are = 0. This loop xfers the next 16 word block from the IOCB in memory to 8X U registes. First registers 81 - 8F and then register 80 is loaded. It also prepares to load the URetry register with proper constant=90. This value will then be saved in URetry register in memory error test subroutine (which follows IOCB Load) for use in Hdr verification routine.}

IOCBParLp:
MAR ← [RHAdr, RAdr+1], RAdr ← RAdr+1, AltUaddr, NibCarryBr,c1;
URCntBlk ← RCnt, BRANCH[IOCBParLpC3, ParLpFin],
CANCELBR[$, 2], LOOPHOLE[wok],c2;
IOCBParLpC3:
RCnt ← MD, GOTO[IOCBParLp],c3, at[0,2,ParLpFin];
ParLpFin:
RCnt ← CRetry, GOTO[MemErTest],c3, at[1,2,IOCBParLpC3];



{This subroutine checks for double memory error. It is entered from IOCB Load subroutine and after data transfer for any sector is completed.}

MemErTest:
RAdr ← 0FF + 1,c1;
[] ←MStatus and RAdr, NZeroBr,c2;
BRANCH[NoMemEr, MemEr], pRet4,c3;



{This are the exits from MemErTest subroutine. If Ok it stores RCnt into URetry register. Upon entry from IOCB Load subroutine RCnt contains the retry constant; upon enty from DtXferFin it contains 0. If error RAdr gets UCStat register for use in IOCB error processing routine.}

NoMemEr:
URetry ← RCnt, RET[RetNoMemEr],c1, at[0,2,MemEr];
MemEr:
RAdr ← UCStat, RET[RetMemEr],c1, at[1,2,NoMemEr];



{This subroutine is used to transfer commands to the selected disk. Upon entry RCnt has the Bus value and RAdr the Tag constant shifted right by 8. Subroutine sets the Bus in c1 and the Tag in c3 of click1.}

TagSetRst:
KCmd ← RCnt LRot0,c1;
RAdr ← RAdr LRot8,c2;
KCmd ← (RCnt or RAdr) LRot0,c3;



{In c3 of click2 the tag is reset. Before return RCnt is loaded with CTstFirstIOCB constant for use in seek routine if entered from Recal routine.}

Noop,c1;
RAdr ← UBitSignAdr{used some ret points}, pRet4,c2;
KCmd ← RCnt LRot0, RET[RetTagSetRst],c3;



{This subroutine is used to find the header (first time in) for all non-oriented header operations. The subroutine starts with loading the sector number into RHCnt and the CWUSecFnd into RCnt register.}

FindHdr:
RHCnt← UCurHdSec ,c2;
RCnt ← CWUIndxFnd,c3;



{The subroutine then test for client sector = 0 and resets SecFound and IdxFound latches. If client 0 the subroutine sets WU: IdxFound and then returns. If not 0 it orients on index by setting WU: IdxFound and and then counts SecFnd wake-ups.}

Ybus ← RHCnt, NZeroBr,c1;
RCnt ← RCnt or UBitSignAdr, BRANCH[IndxOrient, SecOrient],
ClrKFlags,c2;
IndxOrient:
KCtl ← RCnt LRot0, pRet4, GOTO[FindHdrEnd],c3, at[0,2,SecOrient];
SecOrient:
KCtl ← RCnt LRot0,c3, at[1,2,IndxOrient];



{UScratch gets the sector WU constant and RCnt gets client sector address for use in next click.}

RCnt ← RCnt or CWUSecFnd,c1;
UScratch ← RCnt,c2;
RCnt ← RHCnt,c3;



{Decrement RCnt, clear SecFound latch and count number of sector wake-ups equal to client sector address. If sector is found exit, if not go to the FindHdrLp and try again. In either case set SecFnd wake-up...}

FindHdrLp:
RCnt ← RCnt-1, ZeroBr,c1;
ClrKFlags, BRANCH[SecNotFnd, SecFnd],c2;
SecNotFnd:
KCtl ← UScratch, GOTO[FindHdrLp],c3, at[0,2,SecFnd];
SecFnd:
KCtl ← UScratch, pRet4,c3, at[1,2,SecNotFnd];



{... and return to Hdr routines.}

FindHdrEnd:
RET[RetFindHdr],c1;



{Test for sector address = 0. If 0 set WU IndxFnd into RHCnt; if not 0 set Set CWUSecFnd into RHCnt register and then return.}

SecZrTest:
Ybus ← RHCnt, ZeroBr,c2;
BRANCH[SecNotZero, SecZero], pRet4,c3;
SecNotZero:
RHCnt ← CWUSecFnd, RET[RetSecZrTest],c1, at[0,2,SecZero];
SecZero:
RHCnt ← CWUIndxFnd, RET[RetSecZrTest],c1, at[1,2,SecNotZero];



{This is the entry point of the write/verify main loop. RAdr contains the physical memory address; RCnt the word count for that particular field. The loop transfers one word/click from memory to either KODataA or KODataB register respectively. KStrobe serves as na early indicator that either of the KOData registers willbe serviced in click3.}

WrVrfLp:
MAR ← [RHAdr, RAdr], RAdr ← RAdr+1, KStrobe,c1, at[0,2,EndWrVrf];
RCnt ← RCnt-1, ZeroBr, CANCELBR[$, 2],c2;
KOData ← MD, BRANCH[WrVrfLp, EndWrVrf],c3;



{This is the entry point of the read loop for Hdr, Lbl and ECC syndrom. It transfers one word/click from KIData registers to the main memory. KStrobe is again used as an early service indicator. Upon entering this routine the word count is equal to number of words transferred.}

RdLp:
MAR ← [RHAdr, RAdr], RAdr ← RAdr+1, KStrobe,
BRANCH[RdLpC2, EndRdC2],c1;
RdLpC2:
MDR ← KIData, CANCELBR[$, 2], LOOPHOLE[wok],c2, at[0,2,EndRdC2];
RCnt ← RCnt - 1, ZeroBr, GOTO[RdLp],c3;



{This is the special loop, which transferes the datafield from the controller to memory. The buffer has to start on the word boundary for this loop to work properely.}

DtRdLp:
MAR ← [RHAdr, RAdr + 0], BRANCH[DtRdLpC2, DtRdEndC2],
KStrobe,c1;
DtRdLpC2:
MDR ← KIData, CANCELBR[$, 2], LOOPHOLE[wok],c2, at[0,2,DtRdEndC2];
RAdr ← RAdr + 1, PgCarryBr, GOTO[DtRdLp],c3;



{This is the entry point of the Noop loop. It is used to clock over a particular field in order to sustian orientation. KOData registers are loaded with 0 for use in Hdr Wr only operation. KStrobe is used to reset Data Requests.}

NoopLp:
KStrobe, BRANCH[NoopLpC2, EndNoopC2],c1;
NoopLpC2:
KOData ← Xbus ← 0,c2, at[0,2,EndNoopC2];
RCnt ← RCnt-1, ZeroBr, GOTO[NoopLp],c3;



{This are the exits and return points of write/verify, read, and noop loops. RHCnt will save a constant to delay the CRC test after Hdr and Lbl fields. These constants have differrent value for Rd(or Noop) and Vrf operation. There are n+1 WUs generated by hardware for each field in order to exit the loop into this section of code. Then WU condition is set to FirmwareEn in order to do the housekeeping and the last KStrobe is issued. Upon exit RHAdr is loaded with IOCB PtrHi for use in Lbl and after Dt operations (in Dt operations RHAdr is reloaded in GetDtAdr routines).}

EndRdC2:
KCtl ← UBitSignAdr, CANCELBR[EndLoops, 2],
pRet4, LOOPHOLE[wok],c2, at[1,2,RdLpC2];
DtRdEndC2:
KCtl ← UBitSignAdr, CANCELBR[EndLoops, 2],
pRet4, LOOPHOLE[wok],c2, at[1,2,DtRdLpC2];
EndNoopC2:
KCtl ← UBitSignAdr, CANCELBR[EndLoops, 2], pRet4,,c2, at[1,2,NoopLpC2];


EndWrVrf:
KStrobe,c1, at[1,2,WrVrfLp];
KCtl ← UBitSignAdr, pRet4,c2;
EndLoops:
RHAdr ← USvAdrHi, RET[RetXfrLp],c3;



{This loop delays the CRC testing after Hdr and Lbl fields have been transfered. It also prepares to reset the read gate in next click. The constant is designed to reset the read gate and also strobe early/late if any.}

DelRdRst:
Noop,c1, at[0,2,RdGateRst];
DelRdRstC2:
RCnt ← RCnt - 1, ZeroBr,c2;
BRANCH[DelRdRst, RdGateRst],c3;



{This is the end of delay. This section of the code resets unconditionally read gate only (if any) and then returns to data xfer routines. (It does not reset strobe early/late bits.) }

RdGateRst:
RCnt ← ~CRead,c1, at[1,2,DelRdRst];
RCnt ← UHCmd and RCnt, pRet4,c2;
KCmd ← RCnt LRot0, RET[RetDelRdRst],c3;



{This is the delay loop to set the disk read circuitry before label and data Noop, Rd, and Vrf operations.}

DelSetRd:
RCnt ← RCnt - 1, ZeroBr,c1;
DelSetRdC2:
BRANCH[DelSetRdLp, DelSetRdEnd],c2;
DelSetRdLp:
Noop, GOTO[DelSetRd],c3, at[0,2,DelSetRdEnd];
DelSetRdEnd:
pRet4,c3, at[1,2,DelSetRdLp];



{Set constant into RCnt to set WU: DataRq after exit, before starting data xfer.}

EndDelSetRd:
RCnt ← CWUDtReq, RET[RetDelSetRd],c1;



{Update BSAdr in the memory location IOCBPtr+CDisplBSAdr.}

BSAdrUpdate:
RAdr ← USvAdrLo,c1;
RAdr ← RAdr + CDisplBSAdr,c2;
Noop, GOTO[StatWrite],c3;



{Update disk status in the memory location IOCBPtr+CDislDStat.}

DStatUpdate:
RAdr ← USvAdrLo,c1;
RAdr ← RAdr + CDisplDStat,c2;
RCnt ← UDStat, GOTO[StatWrite],c3;



{Update UCStat register in the memory location IOCBPtr+CDislCStat.}

CStatUpdate:
RAdr ← USvAdrLo,c1;
RAdr ← RAdr + CDisplCStat,c2;
RCnt ← UCStat, GOTO[StatWrite],c3;


{Write the updated PgCnt into proper IOCB location after the operation is terminated.}

PgCntUpdate:
RAdr ← USvAdrLo,c1;
RAdr ← RAdr + CDisplPgCnt,c2;
RCnt ← UPgCnt, GOTO[StatWrite],c3;



{Write the updated DtPtrLo into proper IOCB location (DtPtrHi is never updated because the virtual data pointer must not cross 64k virtual boundary) after the operation is terminated.}

DtPtrUpdate:
RAdr ← USvAdrLo,c1;
RAdr ← RAdr + CDisplDtPtr,c2;
RCnt ← UDtPtrLo, GOTO[StatWrite],c3;



{... and do the actual writting.}

StatWrite:
MAR ← [RHAdr, RAdr+0],c1;
MDR ← RCnt, CANCELBR[$, 2], LOOPHOLE[wok], pRet4,c2;
RCnt ← CTstRecal, RET[RetStatUpd],c3;