Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_SRC_3_19910112 - mit/monitor/phyp2.mac
There are 25 other files named phyp2.mac in the archive. Click here to see a list.
; UPD ID= 2196, SNARK:<5.MONITOR>PHYP2.MAC.22,  12-Jun-81 15:38:34 by LYONS
; UPD ID= 1824, SNARK:<5.MONITOR>PHYP2.MAC.21,  17-Apr-81 09:55:22 by WACHS
; UPD ID= 1335, SNARK:<5.MONITOR>PHYP2.MAC.20,   1-Dec-80 17:40:55 by LYONS
; UPD ID= 1211, SNARK:<5.MONITOR>PHYP2.MAC.19,  31-Oct-80 07:07:29 by UHLER
;TCO 5.1184 - DON'T READ REGISTER 30 UNLESS THE DX20 IS ALREADY HALTED
; UPD ID= 978, SNARK:<5.MONITOR>PHYP2.MAC.18,   2-Sep-80 07:48:47 by UHLER
;MAKE ECC CORRECTION WORK
; UPD ID= 736, SNARK:<5.MONITOR>PHYP2.MAC.17,   7-Jul-80 00:47:21 by DBELL
;TCO 5.1096 - INSERT ROUTINE TO CHECK FOR HALTED MICROCODE
; UPD ID= 731, SNARK:<5.MONITOR>PHYP2.MAC.16,   3-Jul-80 16:06:46 by DBELL
;DISABLE ECC RECOVERY UNTIL IT IS PROVEN TO WORK
; UPD ID= 677, SNARK:<5.MONITOR>PHYP2.MAC.15,  19-Jun-80 12:33:56 by DBELL
;MORE RECALIBRATION WORK
;READ THE CORRECT NUMBER OF SENSE BYTES
;GET PCTR AND PBAR VALUES FOR SYSERR PROPERLY
;MAKE RECALIBRATES WORK PROPERLY
;HANDLE WRITE-LOCKED DISKS BY MAKING THEM SAY OPR ACTION REQUESTED
; UPD ID= 640, SNARK:<5.MONITOR>PHYP2.MAC.11,  15-Jun-80 00:23:08 by DBELL
;FIX TYPO IN ASYNDO ROUTINE
; UPD ID= 636, SNARK:<5.MONITOR>PHYP2.MAC.10,  13-Jun-80 13:17:43 by DBELL
;TCO 5.1065 - CLEAR WRITE-LOCK BIT ON UNEXPECTED ASYCHRONOUS INTERRUPTS
;FIX SOME PROBLEMS IN ERROR RECOVERY
; UPD ID= 609, SNARK:<5.MONITOR>PHYP2.MAC.9,   5-Jun-80 16:25:01 by DBELL
;STORE FINAL CHANNEL STATUS WORDS WHEN ENDING SYSERR BLOCK
;READ ASYCHRONOUS STATUS REGISTER AGAIN IF NO STATUS FLAGS FIRST TIME
;ADD ADDITIONAL DATA FOR BUGCHKS AND BUGINFS
;<DBELL.M4>PHYP2.MAC.19,  6-May-80 23:37:56, EDIT BY DBELL
;<4.1.MONITOR>PHYP2.MAC.2, 30-Apr-80 21:48:04, EDIT BY DBELL
;TCO 4.1.1119 - WRITE RP20 DRIVER




;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1979,1980 BY DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.
	SEARCH PROLOG,PHYPAR,SERCOD ;SYSTEM PARAMETERS
	TTITLE (PHYP2,,< - DEVICE DEPENDENT CODE FOR DX20B/RP20 DISKS>)
	SUBTTL	DAVID I. BELL/GMU  11-AUG-80



	ENTRY DXBDSP		;FOR LIBRARY SEARCH




;PARAMETERS:



	MAXDRV==^D256		;MAXIMUM NUMBER OF DRIVES POSSIBLE
	NUMDRV==^D16		;MOST NUMBER OF DRIVES WE CAN USE
	DUADRV==20		;FIRST DRIVE NUMBER WHICH CAN BE DUAL PORTED
	STADDR==5		;NORMAL START ADDRESS OF DX20 MICROCODE
	STMAX==^D10		;MAXIMUM TIMES TO RESTART MICROCODE
	BYTSEC==^D2304		;NUMBER OF 8-BIT BYTES PER SECTOR
	SNSNUM==^D24		;NUMBER OF SENSE BYTES
	RTYCNT==^D10		;NUMBER OF RETRIES BEFORE RECALIBRATION
	RCLCNT==^D2		;NUMBER OF RECALIBRATES BEFORE GIVING UP





;DEFINITIONS FOR THE RP2FNC TABLE TO TRANSFER PHYSIO FUNCTIONS TO
;THE CORRESPONDING HARDWARE FUNCTIONS:



	DEFSTR (TBDTF,RP2FNC,0,1)	;FUNCTION TRANSFERS DATA (SIGN BIT)
	DEFSTR (TBFNC,RP2FNC,^D17,^D8)	;RP20 FUNCTION CODE
	DEFSTR (TBDSP,RP2FNC,^D35,^D18)	;DISPATCH ADDRESS
;USEFUL MACROS:




	DEFINE RDRG(REGN),<	;;MACRO TO READ REGISTER OF UNIT IN Q2
	MOVX T2,<REGN>		;;SET UP REGISTER NUMBER
	CALL RDREG3		;;READ IT INTO T1
>



	DEFINE REG(NAME,LENGTH<1>),<	;;MACRO TO ALLOCATE REGISTER OFFSETS
	NAME==.REG.		;;DEFINE THIS OFFSET
	.REG.==.REG.+LENGTH	;;THEN ADVANCE OFFSET BY GIVEN AMOUNT
>




	DEFINE SAVREG(REGS),<	;;MACRO TO SPECIFY REGISTERS FOR SYSERR
	REGNUM==<SAVBIT==0>	;;INITIALIZE BIT MASK AND COUNTER
	IRP REGS,<SAVBIT==SAVBIT!<1B<^O<REGS>>>	;;MARK BIT FOR REGISTER
		  REG.'REGS==REGNUM	;;DEFINE OFFSET FOR USE LATER
		  REGNUM==REGNUM+1	;;INCREMENT COUNTER
		  >		;;LOOP OVER ALL REGISTERS
>				;;RETURN BITS AND COUNT


	SAVREG <0,1,2,3,4,5,6,20,21,22,23,24,26,27,30,31,32,33,34,35,36,37>
	SUBTTL	UDB AND KDB EXTENSIONS




;UDB DEVICE DEPENDENT PORTION:


	.REG.==UDBDDD		;START OFFSETS AT DEVICE DEPENDENT PART

;	REG U.XXXX		;ANY UDB LOCATIONS GET DEFINED HERE

	LU.DX2==.REG.		;LENGTH OF RP20 UDB




;STATUS BITS IN LEFT HALF OF UDBERR:


	UE.HRD==1B0		;HARD ERROR
	UE.ECC==1B1		;ECC-CORRECTABLE ERROR
	UE.DAT==1B2		;DATA ERROR
	UE.HDR==1B3		;HEADER ERROR
	UE.DEV==1B4		;DEVICE ERROR (NEITHER DATA NOR HEADER)
	UE.POS==1B5		;POSITIONING ERROR
;KDB DEVICE DEPENDENT PORTION:


	.REG.==KDBDDP		;START OFFSETS AT DEVICE DEPENDENT PART

	REG K.DUDB,NUMDRV	;UDB TABLE (NUMDRV WORDS LONG)
	REG K.DXAD		;MASSBUSS ADDRESS OF THE DX20
	REG K.STCT		;NUMBER OF TIMES MICROCODE WAS RESTARTED
	REG K.DNUM		;NUMBER OF DRIVES IN EXISTENCE
	REG K.SAVQ		;STORAGE FOR Q1 ON ATTENTION INTERRUPTS
	REG K.DVER		;DX20 MICROCODE VERSION NUMBER
	REG K.DREG,REGNUM	;DRIVE REGISTERS READ ON ERROR
	REG K.DEXS,<SNSNUM+3>/4	;EXTENDED STATUS BYTES (MUST FOLLOW K.DREG)
	REG K.DCNI		;CONI OF RH READ ON AN ERROR
	REG K.DCS1		;DATAI OF RH CONTROL REGISTER
	REG K.DDBF		;DATAI OF RH DATA REGISTER

	LK.DX2==.REG.		;LENGTH OF DX20 KDB
	SUBTTL	MASSBUS-DX20 REGISTER DEFINITIONS




;MASSBUS DEVICE REGISTER ASSIGNMENTS.  MISSING REGISTER DEFINITIONS
;ARE EITHER UNUSED OR ARE FOR DIAGNOSTICS



	.DXCTL==0B5		;CONTROL REGISTER
	.DXSTS==1B5		;STATUS REGISTER
	.DXERR==2B5		;ERROR REGISTER
	.DXMAI==3B5		;MAINTENANCE REGISTER
	.DXATN==4B5		;ATTENTION SUMMARY REGISTER
	.DXREC==5B5		;HEAD/RECORD REGISTER (SAME AS REG 25)
	.DXTYP==6B5		;DRIVE TYPE REGISTER
				;REGISTERS 7-17 ARE UNUSED
	.DXEND==20B5		;ENDING STATUS REGISTER
	.DXASY==21B5		;ASYNCHRONOUS STATUS REGISTER
	.DXFLG==22B5		;FLAGS AND ARGUMENT REGISTER
	.DXDRV==23B5		;DRIVE NUMBER REGISTER
	.DXCYL==24B5		;CYLINDER REGISTER
				;REGISTER 25 IS SAME AS REGISTER 5
	.DXES0==26B5		;EXTENDED STATUS REGISTER 0
	.DXES1==27B5		;EXTENDED STATUS REGISTER 1
	.DXDG0==30B5		;DIAGNOSTIC REGISTER 0 (CONTENTS OF MICROSTORE)
	.DXDG1==31B5		;DIAGNOSTIC REGISTER 1 (PC AND FLAGS)
				;REGISTERS 32-36 ARE FOR DIAGNOSTIC USE
	.DXDG7==37B5		;DIAGNOSTIC REGISTER 7 (PARITY ERROR FLAG)
;REGISTER 0 (.DXCTL) BIT DEFINITIONS:


	DX.GO==1B35		;GO BIT
	DEFSTR (DXFNC,,^D35,6)	;FUNCTION CODE (INCLUDING GO BIT)



;FUNCTION CODES (INCLUDING THE GO BIT).  SOME OF THE FUNCTIONS HAVE
;SUB-CODES WHICH MUST BE LOADED INTO THE FLAG REGISTER (.DXFLG) PRIOR
;TO LOADING THE FUNCTION CODE ITSELF.  POSSIBLE SUB-CODES ARE INDENTED
;FOLLOWING THEIR MAJOR CODE.


	XF.NOP==1		;FUNCTION FOR NO OPERATION SUB-CODES
	  XS.TIO==0		;TEST I/O
	  XS.NOP==3		;NO OPERATION
	XF.SEK==5		;SEEK
	XF.REC==7		;RECALIBRATE
	XF.CLR==11		;DRIVE CLEAR
	XF.SRC==31		;SEARCH
	XF.SEN==47		;FUNCTION FOR SENSE SUB-CODES
	  XS.SEN==4		;SENSE
	  XS.REL==224		;DRIVE RELEASE
	  XS.BUF==244		;READ AND RESET BUFFER LOG
	  XS.RSV==264		;DRIVE RESERVE
	XF.WRT==61		;WRITE DATA
	XF.WRF==63		;WRITE FORMAT
	XF.WRS==65		;WRITE SPECIAL
	XF.RED==71		;READ DATA
	XF.RDF==73		;READ FORMAT
	XF.RDS==75		;READ SPECIAL




;REGISTER 1 (.DXSTS) BIT DEFINITIONS:


	ST.ATN==1B20		;ATTENTION (.DXATN IS NONZERO)
	ST.ERR==1B21		;COMPOSITE ERROR (.DXERR IS NONZERO)
	ST.LNK==1B22		;LINK PRESENT (ALWAYS ON)
	ST.RUN==1B23		;MICROPROCESSOR IS RUNNING
;REGISTER 2 (.DXERR) BIT DEFINITIONS:


	DEFSTR (ERCOD,,^D27,4)	;ERROR CLASS CODE FIELD
	DEFSTR (ERSUB,,^D23,4)	;ERROR SUB-CLASS FIELD

	ER.NEW==1B28		;NEW ERROR DETECTED BY MICROPROCESSOR
	ER.STP==1B29		;MICROPROCESSOR STOPPED
	ER.MPE==1B30		;MICROBUS PARITY ERROR
	ER.DPE==1B31		;DATABUS PARITY ERROR
	ER.CPE==1B32		;CONTROL BUS PARITY ERROR
	ER.RMR==1B33		;REGISTER MODIFICATION REFUSED
	ER.ILR==1B34		;REFERENCE TO ILLEGAL REGISTER
	ER.ILF==1B35		;ILLEGAL FUNCTION ATTEMPTED

	ER.ALL==ER.NEW!ER.STP!ER.MPE!ER.DPE!ER.CPE!ER.RMR!ER.ILR!ER.ILF	;ALL


;ERROR CLASS AND SUB-CLASS CODES (FROM ERCOD AND ERSUB).  VALUES
;IN THE SUB-CLASS ARE INDENTED.

	ER.UDS==1		;UNUSUAL DEVICE STATUS
	  ES.FSS==0		;STATUS IS FROM FINAL SEQUENCE
	  ES.ISS==1		;STATUS IS FROM INITIAL SEQUENCE
	ER.RLE==2		;RECORD LENGTH ERROR
	ER.DSE==3		;DRIVE SELECTION ERROR (NONEXISTENT DRIVE)
	ER.RER==4		;RECOVERABLE ERROR
	  ES.DPE==0		;DATA PATH PARITY ERROR
	  ES.EPE==1		;ENDING STATUS BYTE PARITY ERROR
	  ES.AMM==2		;ADDRESS MISMATCH
	  ES.APE==3		;ADDRESS PARITY ERROR
	  ES.IPE==4		;INITIAL STATUS BYTE PARITY ERROR
	  ES.DMP==5		;DATA PATH DETECTED MICRO BUS PARITY ERROR
	  ES.IMP==6		;DEV BUS INTERFACE FOUND MICRO BUS PARITY ERROR
	  ES.HTO==7		;DEVICE BUS HANDSHAKE TIME-OUT
	  ES.ADI==10		;DEVICE ASSERTED DISCONNECT IN
	  ES.RWR==11		;RUN WASN'T RECEIVED FROM RH20
	ER.CRR==5		;COMMAND RETRY REQUEST
	ER.NER==6		;NON-RECOVERABLE ERROR
	  ES.PE1==0		;PARITY ERROR RECEIVING DRIVE ADDRESS
	  ES.PE2==1		;SAME THING
	ER.RIA==7		;RETRY LOG INFORMATION AVAILABLE
	ER.FTL==10		;FATAL ERRORS (BAD MICROCODE OR BROKEN HARDWARE)
	  ES.WDT==0		;HARDWARE DRIVE TYPE DOESN'T MATCH EXPECTED TYPE
	  ES.FCU==1		;FOREIGN CONTROL UNIT
	  ES.MPF==2		;MASSBUS CONTROLLER POWER FAILED
	  ES.II0==3		;MICROPROCESSOR INTERRUPTED FROM INTERFACE 0
;REGISTER 3 (.DXMAI) BIT DEFINITIONS:


	MR.SS==1B31		;SINGLE STEP MICRO PROCESSOR
	MR.WEP==1B32		;WRITE EVEN PARITY ON ALU OPERATION
	MR.ST==1B33		;START/STOP MICRO PROCESSOR
	MR.RES==1B34		;HALT MICRO PROCESSOR AND CLEAR REGISTERS





;REGISTER 4 (.DXATN) BIT DEFINITIONS:


	AT.0==1B35		;DRIVE 0 NEEDS ATTENTION.  FOR OTHER DRIVES,
				;SHIFT LEFT BY DRIVE NUMBER.




;REGISTER 5 (.DXREC) BIT DEFINITIONS:


	DEFSTR (RCHED,,^D27,^D8)	;HEAD NUMBER
	DEFSTR (RCREC,,^D35,^D8)	;RECORD NUMBER




;REGISTER 20 (.DXEND) BIT DEFINITIONS (THESE STATUS FLAGS ARE ALSO
;RETURNED IN REGISTER .DXASY ON ANCHRONOUS STATUS):


	DEFSTR (ESIDX,,^D35,7)	;EXTENDED STATUS TABLE INDEX

	ES.STM==1B21		;STATUS MODIFIER FLAG
	ES.CUE==1B22		;CONTROL UNIT END
	ES.BSY==1B23		;CONTROL UNIT OR DEVICE IS BUSY
	ES.CHE==1B24		;CHANNEL END
	ES.DVE==1B25		;DEVICE END
	ES.UNC==1B26		;UNIT CHECK
	ES.UNE==1B27		;UNIT EXCEPTION
	ES.SUI==1B28		;STATUS UPDATE INTERLOCK
;REGISTER 21 (.DXASY) BIT DEFINITIONS (BITS 20-28 ARE STATUS BITS
;DEFINED THE SAME AS IN REGISTER .DXEND):


	DEFSTR (ASDRV,,^D35,^D8)	;DRIVE NUMBER PRESENTING STATUS





;REGISTER 22 (.DXFLG) BIT DEFINITIONS:


	DEFSTR (DFCOD,,^D35,^D8)	;SUB-FUNCTION TO PERFORM

	DF.IGL==1B21		;IGNORE LENGTH ERRORS
	DF.DIA==1B22		;DIAGNOSTIC MODE FOR READ AND WRITE SPECIAL
	DF.RTY==1B23		;COMMAND RETRY REQUESTED
	DF.DSN==1B25		;DISABLE SENSE ON UNIT CHECKS
	DF.FSN==1B26		;FORCE SENSE OPERATION ON COMMAND COMPLETION
	DF.WHT==1B27		;READ OR WRITE THE WHOLE TRACK





;REGISTER 23 (.DXDRV) BIT DEFINITIONS:


	DEFSTR (DRVNM,,^D35,^D8)	;DRIVE NUMBER





;REGISTER 31 (.DXDG0) BIT DEFINITIONS:


	DG.UIR==1B20		;READ OR WRITE INSTRUCTION REGISTER
	DG.MSE==1B21		;ENABLE MICROSTORE FOR WRITING
	DG.PCS==1B22		;SET DX20 PC
	DG.PCI==1B23		;INCREMENT PC AFTER USING INSTRUCTION REGISTER
	DG.PC==7777B35		;PC VALUE





;REGISTER 37 (.DXDG7) BIT DEFINITIONS:


	DG.IRP==1B22		;IR PARITY ERROR OCCURED
;DEFINITIONS FOR SENSE BYTES 0-3.  WHEN READ INTO AN AC, BITS 2-9
;CONTAIN BYTE 0, BITS 10-17 CONTAIN BYTE 1, BITS 20-27 CONTAIN
;BYTE 2, AND BITS 28-35 CONTAIN BYTE 3.



	S0.REJ==1B2		;COMMAND REJECT
	S0.IVR==1B3		;INTERVENTION REQUIRED
	S0.BPE==1B4		;BUS OUT PARITY ERROR
	S0.ECK==1B5		;EQUIPMENT CHECK
	S0.DCK==1B6		;DATA CHECK OCCURED
	S0.OVR==1B7		;OVERRUN OCCURED

	S1.PRM==1B10		;PERMANENT ERROR (DCU RETRY COUNT EXHAUSTED)
	S1.ITF==1B11		;INVALID TRACK FORMAT
	S1.EOC==1B12		;END OF CYLINDER
	S1.NRF==1B14		;NO RECORD FOUND
	S1.FPE==1B15		;FILE PROTECTED ERROR
	S1.WLK==1B16		;DRIVE WAS WRITE LOCKED
	S1.OPI==1B17		;OPERATION INCOMPLETE

	S2.COR==1B21		;ERROR IS CORRECTABLE
	S2.EDP==1B23		;ENVIRONMENTAL DATA PRESENT (BYTES 8 - 23)
	S2.CMP==1B24		;DRIVE IS IN 3330 COMPATIBLE MODE

	DEFSTR (S3RSC,,^D35,^D8)	;RESTART COMMAND




;OTHER SENSE BYTE INFORMATION OF INTEREST.  ALL OF THESE DEFINITIONS
;ASSUME THAT ALL OF THE SENSE BYTES WERE READ INTO THE K.DEXS TABLE
;IN THE KDB.  THIS TABLE CONTAINS FOUR SENSE BYTES PER WORD.


	DEFSTR (SBECOD,K.DEXS+1(P2),^D35,^D8)	;SENSE BYTE 7:
						;ERROR TYPE FIELD
	DEFSTR (SBEPOS,K.DEXS+4(P2),^D35,^D16)	;SENSE BYTES 18 AND 19:
						;BYTE POSITION OF ECC ERROR
	DEFSTR (SBEMSK,K.DEXS+5(P2),^D17,^D16)	;SENSE BYTES 20 AND 21:
						;MASK TO FIX ECC ERROR WITH

	FC.ECC==123		;VALUE IN SBECOD FOR ECC ERROR
	SUBTTL	DISPATCH FOR DX20B




;THE FOLLOWING TABLE IS THE DISPATCH TABLE FOR THIS DRIVER.  THE OFFSETS
;INTO THE TABLE ARE DEFINED IN PHYPAR.  THE CALLING SEQUENCE TO USE THIS
;TABLE IS:
;
;	HRRZ AC,UDBDSP(P3)	;GET DISPATCH ADDRESS
;	CALL UDSXXX(AC)		;CALL ROUTINE
;	 (NON-SKIP RETURN)	;ROUTINE DIDN'T SKIP
;	(SKIP RETURN)		;ROUTINE SKIPPED
;
;YOU CAN REPLACE THE "HRRZ AC,UDBDSP(P3)" INSTRUCTION WITH "HRRZ AC,KDBDSP(P2)"
;WHEN NECESSARY, SINCE THE KDB AND THE UDB USE THE SAME DISPATCH TABLE.




DXBDSP::JRST DX2INI		;0 - INITIALIZATION
	JRST DX2SIO		;1 - START DATA TRANSFER OPERATION
	JRST DX2INT		;2 - HANDLE INTERRUPT
	JRST DX2ERR		;3 - ERROR RECOVERY
	JRST DX2HNG		;4 - HUNG DEVICE
	JRST DX2CNV		;5 - CONVERT SECTOR NUMBER TO BLOCK/SEC-SURFACE
	JRST DX2LAT		;6 - LATENCY COMPUTATION
	JRST DX2POS		;7 - START POSITION OPERATION (SEARCH)
	JRST DX2ATN		;10 - ATTENTION INTERRUPT
	JRST DX2PCK		;11 - SKIP IF POSITIONING NEEDED
	JRST DX2STK		;12 - STACK SECOND COMMAND
	JRST DX2EXT		;13 - CHECK LEGALITY AND EXISTENCE OF A UNIT
	JRST DX2CCK		;14 - SEE IF DX20 IS HALTED
	SUBTTL	DX20 INITIALIZATION



;DX2INI - INITIALIZATION ROUTINE.  THIS ROUTINE IS CALLED ONCE PER
;DX20 DRIVE ON THE SYSTEM AND BUILDS A KDB FOR THE DX20 AND A UDB
;FOR EACH RP20 DRIVE.  CALL:
;  P1/	CDB
;  Q2/	DX20 NUMBER
;  P5/	UNIT NUMBER TO INITIALIZE, OR -1 FOR ALL UNITS
;RETURNS:
;  +1:	ALWAYS
;	  T1/	KDB



DX2INI:	SAVEQ			;SAVE REGISTERS
	JUMPL P5,INILB		;SKIP ON IF INITIALIZING ALL UNITS
	MOVE P2,CDBIUN(P1)	;GET KDB ADDRESS IF IT EXISTS
	ADD P2,Q2		;POINT TO CORRECT UNIT
	MOVE P2,0(P2)		;GET ENTRY
	SKIPE P3,P2		;FOUND ONE?
	JRST INILA		;ALREADY GOT ONE

INILB:	CALL CHKMIC		;CHECK THE DX20 MICROCODE (STOPS IT TOO)
	 JRST [	HRRZ T1,CDBADR(P1)	;BAD MICROCODE, GET CHANNEL READY
		BUG(DXBDMI,<<T1,CHAN>,<Q2,DX20>>)	;COMPLAIN
		RET]		;AND IGNORE THIS DX20
	MOVEI T1,LK.DX2		;LENGTH OF KDB
	CALL PHYALC		;ALLOCATE SPACE
	 RET			;FAILED
	MOVE P2,T1		;SAVE ADDRESS OF KDB IN PROPER AC
	MOVE P3,T1		;NEED POINTER OF KDB ALSO IN P3
	MOVEI T1,.BTKDB		;MARK AS A KDB
	DPB T1,USYBKT		;SET .BTKDB IN KDB
	MOVEI T1,.UTDXB		;GET CODE FOR DX20B CONTROLLER
	STOR T1,USTYP,(P2)	;AND STORE IT INTO THE KDB
	MOVX T1,KS.DSK		;GET FLAG READY
	IORM T1,KDBSTS(P2)	;REMEMBER THAT THIS CONTROLLER DRIVES DISKS
	MOVEI T1,DXBDSP		;INSERT DISPATCH VECTOR
	MOVEM T1,KDBDSP(P2)	; ...
	HRRZM Q2,K.DXAD(P2)	;SAVE MASSBUSS ADDRESS OF THE KDB
	CALL GETVER		;READ THE MICROCODE VERSION IN THE DX20
	MOVEM T1,K.DVER(P2)	;REMEMBER IT IN THE KDB FOR SYSERR STUFF
	MOVEI T1,STADDR		;GET STARTING ADDRESS OF DX20
	CALL DXSTRT		;START THE DX20 MICROCODE
	MOVSI T1,1		;** TEMPORARY
	SOJG T1,.		;** GIVE DX20 TIME TO START UP HAPPILY
	MOVSI T1,-NUMDRV	;SET UP AOBJN INDEX
	HRRI T1,K.DUDB(P2)	;MAKE RH POINT TO UDB ENTRIES IN KDB
	MOVEM T1,KDBIUN(P2)	;INITIAL POINTER
	MOVEM T1,KDBCUN(P2)	;CURRENT POINTER
	SETZM K.DNUM(P2)	;CLEAR NUMBER OF UDB'S IN EXISTENCE

INILA:	SKIPL Q1,P5		;GET POSSIBLE SPECIFIC UNIT TO INITIALIZE
	TLOA Q1,-1		;WAS ONE, DO ONLY 1 TIME
	MOVSI Q1,-MAXDRV	;WANTS ALL DRIVES, SET UP AOBJN INDEX
	MOVEI Q3,4		;GET MAXIMUM DRIVES TO COMPLAIN ABOUT
				;FALL INTO LOOP
;LOOP TO EXAMINE ALL UNITS:


INILP:	SETZ P5,		;NO UDBSTS BITS YET
	MOVX T2,.DXDRV		;GET DRIVE NUMBER REGISTER READY
	STOR Q1,DRVNM,T2	;INSERT DRIVE NUMBER
	CALL WTREG3		;SET IT
	MOVX T2,.DXFLG+<FLD(XS.NOP,DFCOD)>	;GET SUB-FUNCTION NEEDED
	CALL WTREG3		;PLACE INTO ARGUMENT REGISTER
	MOVX T2,.DXCTL+<FLD(XF.NOP,DXFNC)>	;GET FUNCTION FOR NOP
	CALL WTREG3		;TELL DX20 TO DO A NOP ON THE DRIVE
	CALL ATTNWT		;WAIT FOR COMPLETION
	 JRST SIKDRV		;FAILED, COMPLAIN
	RDRG .DXERR		;READ ERROR REGISTER NOW
	TXNE T1,ER.ALL-ER.NEW	;ANY FATAL ERRORS?
	JRST SIKDRV		;YES, GO COMPLAIN
	TXNN T1,ER.NEW		;ANY NEW ERROR STATUS?
	JRST ISDRV		;NO, THEN DRIVE EXISTS AND IS OK
	LOAD T2,ERCOD,T1	;EXTRACT ERROR CODE
	CAIN T2,ER.DSE		;NONEXISTENT DRIVE?
	JRST NXTDRV		;YEP, JUST IGNORE IT
	CAIE T2,ER.UDS		;UNUSUAL DEVICE STATUS?
	JRST SIKDRV		;NO, SOMETHING ELSE, GO COMPLAIN
	RDRG .DXEND		;GET ENDING STATUS
	TXNN T1,ES.UNC		;WAS THE ERROR UNIT CHECK?
	JRST NXTDRV		;NOPE, SO NO SUCH UNIT, EXAMINE NEXT ONE
	CALL RDSNS		;YES, READ IN SENSE BYTES
	 JRST NXTDRV		;FAILED, SKIP THIS DRIVE
	TXNE T1,S0.OVR!S0.DCK!S0.ECK!S0.BPE!S0.REJ!S1.OPI!S1.FPE!S1.NRF!S1.EOC!S1.ITF!S1.PRM			;ANY AWFUL STATUS?
	JRST SIKDRV		;YES, CALL IT SICK
	MOVX P5,US.OFS		;DRIVE IS OFF-LINE, GET STATUS TO SET

;HERE FOR EACH EXISTENT DRIVE.  DECIDE IF THIS DRIVE IS DUAL PORTED, AND
;IF SO EITHER IGNORE THIS ACCESS PATH TO IT, OR ELSE MOVE THE UDB FROM
;THE ALTERNATE ACCESS PATH TO THIS ONE (WHICH WILL IMPROVE PERFORMANCE).
;CALLED WITH INITIAL UDBSTS FLAGS IN P5.


ISDRV:	HRRZ T1,Q1		;GET DRIVE NUMBER
	CALL CKDUAL		;SEE IF THE DRIVE ALREADY EXISTS ON ANOTHER PORT
	 JRST NEWDRV		;NO, BUILD A UDB THEN
	MOVE P3,T1		;REMEMBER UDB (HOWEVER, P1 AND P2 DON'T MATCH!)
	TLC Q1,-1		;GET READY FOR TEST
	TLCE Q1,-1		;DOING ONLY ONE UNIT (OR LAST UNIT OF SERIES)?
	TRNE Q1,1		;OR IS THE DRIVE NUMBER ODD?
	JRST NXTDRV		;YES, LET OTHER CHANNEL KEEP OWNERSHIP OF DRIVE
	MOVE T1,K.DNUM(P2)	;GET NUMBER OF DRIVES ON THIS CONTROLLER
	CAIL T1,NUMDRV		;ANYMORE ROOM IN UDB TABLE?
	JRST NXTDRV		;NO, LEAVE DRIVE ON ORIGINAL CHANNEL
	MOVE T1,UDBSTS(P3)	;PARANOIA CHECK - IS THE UNIT NOW ACTIVE?
	TXNE T1,US.ACT!US.POS!US.MAI	;WELL?
	JRST NXTDRV		;YES, DON'T SWITCH THE UDB!!
	HRRZ T1,UDBKDB(P3)	;GET OLD KDB ADDRESS FROM UDB WE ARE MOVING
	MOVE T2,KDBIUN(T1)	;SETUP TO SEARCH ITS UDB TABLE
	CAME P3,(T2)		;FIND OLD KDB'S POINTER TO UDB?
	AOBJN T2,.-1		;NO, KEEP LOOKING
	JUMPGE T2,NXTDRV	;IF NOT FOUND (??) LEAVE UDB ALONE
	SOS T3,K.DNUM(T1)	;DECREMENT COUNTER OF UNITS IN OTHER KDB
	ADDI T3,K.DUDB(T1)	;COMPUTE ADDRESS OF LAST UDB POINTER USED
	MOVE T4,(T3)		;GRAB THE POINTER
	MOVEM T4,(T2)		;OVERWRITE STOLEN UDB'S POINTER WITH IT
	SETZM (T3)		;AND CLEAR LAST ENTRY IN UDB TABLE
	HRRZM P1,UDBCDB(P3)	;SETUP NEW CHANNEL IN STOLEN UDB
	HRRZM Q2,UDBADR(P3)	;AND NEW CONTROLLER NUMBER
	MOVEM Q2,UDBAKA(P3)	;SET UP THIS WORD ALSO
	HRLM Q2,UDBAKA(P3)	;WITH COPIES OF THESE NUMBERS
	JRST DONDRV		;JOIN OTHER CODE TO FINISH UP
;HERE TO BUILD THE UDB FOR A DRIVE:


NEWDRV:	MOVE T3,K.DNUM(P2)	;GET NUMBER OF DRIVES FOUND SO FAR
	CAIL T3,NUMDRV		;ROOM IN K.DUDB TABLE?
	JRST [	BUG(DXBTTS)	;NO, COMPLAIN
		MOVE T1,P2	;TELL RETURN TO PUT KDB INTO CDB
		RET]		;GIVE UP NOW
	MOVE T3,[DXBDSP,,LU.DX2] ;SET UP ADDRESS,,LENGTH
	CALL PHYUDB		;AND ASK FOR UDB ALLOCATION
	 RET			;RETURN IF NO SPACE FOUND
	MOVX T1,.UTP20		;RP20 DISK IS ONLY POSSIBLE DEVICE
	STOR T1,USTYP,(P3)	;PUT UNIT TYPE IN UDB
	MOVSI T2,-NDSKUT	;GET READY FOR SEARCH
	CAME T1,DSKUTP(T2)	;LOOK FOR DISK TYPE IN TABLE
	AOBJN T2,.-1		;KEEP SEARCHING
	SKIPL T2		;FIND IT?
	BUG(DXBTNF)		;NO, COMPLAIN
	MOVE T2,DSKSIZ(T2)	;GET POINTER TO PHYSICAL PARAMETER TABLE
	MOVEM T2,UDBSIZ(P3)	;SAVE IN UDB
	TXO P5,US.DSK!US.PRQ	;SET UNIT IS A DISK AND REQUIRES POSITIONING
	IORM P5,UDBSTS(P3)	;REMEMBER FLAGS
DONDRV:	AOS T1,K.DNUM(P2)	;ADVANCE TO NEXT FREE UDB ENTRY
	ADDI T1,K.DUDB-1(P2)	;POINT TO THE NEW TABLE ENTRY
	HRRZM P3,(T1)		;SAVE LINK
	HRRZM P2,UDBKDB(P3)	;SAVE BKWDS LINK
	HRRZM Q1,UDBSLV(P3)	;REMEMBER SLAVE ADDRESS
	JRST NXTDRV		;GO EXAMINE NEXT DRIVE
;HERE TO COMPLAIN IF NECESSARY ABOUT A SICK DRIVE, CLEAR ANY POSSIBLE
;ERROR STATUS, AND LOOP FOR THE NEXT UNIT.



SIKDRV:	SOJL Q3,NXTDRV		;ONLY COMPLAIN ABOUT FIRST FEW DRIVES
	HRRZ T1,CDBADR(P1)	;SET UP CHANNEL
	HRRZ T2,Q1		;AND UNIT NUMBER
	BUG(DXBEUI,<<T1,CHAN>,<Q2,DX20>,<T2,UNIT>>)	;COMPLAIN

NXTDRV:	MOVX T2,.DXERR		;WANT TO CLEAR THE ERROR REGISTER
	CALL WTREG3		;DO IT
	AOBJN Q1,INILP		;TRY NEXT SLAVE
	MOVE T1,P2		;TELL RETURN TO PUT KDB INTO CDB
	RET
	SUBTTL	ROUTINE TO START I/O



;CALLED TO START A DATA TRANSFER FOR A DRIVE.  CALL:
;  P1/	CDB
;  P2/	KDB
;  P3/	CDB
;  P4/	IORB
;RETURNS:
;  +1:	FAILED, IORB MARKED AS FAILED
;  +2:	I/O SUCCESSFULLY STARTED




DX2SIO:	SAVEQ			;SAVE SOME REGISTERS
	MOVE Q2,K.DXAD(P2)	;SET UP DX20 UNIT NUMBER
	HRRZ T1,UDBERR(P3)	;GET FUNCTION IF ERROR RECOVERY
	JUMPN T1,DS2SI1		;GO IF SO
	LDB T1,IRYFCN		;NORMAL, GET FUNCTION
	CAIGE T1,MNRP2V
	CAIG T1,MXEXTF		;MAKE SURE IT IS WITHIN RANGE
	SKIPN RP2FNC(T1)	;AND LEGAL
	BUG(DXBFEX)		;NO, DIE
DS2SI1:	LOAD Q1,TBFNC,(T1)	;TRANSLATE TO HARDWARE FUNCTION
	CALL DX2CON		;CONNECT TO THE DRIVE
	 RET			;FAILED
				;RETURNS: T2/ CYLINDER, T3/ SECTOR WITHIN IT
	CALL GTHWSC		;COMPUTE .DXREC VALUE (HEAD, RECORD NUMBERS)
	MOVE T4,T1		;SAVE FOR AWHILE
	TXO T2,.DXCYL		;ADD REGISTER TO CYLINDER NUMBER
	CALL WTREG		;SET CYLINDER INTO DX20
	MOVE T2,T4		;GET BACK .DXREC DATA
	HRRZM P3,KDBACT(P2)	;REMEMBER WHICH UDB WE ARE DOING I/O ON
	JRST CHSTRT		;GO START CHANNEL AND SKIP IF SUCCESSFUL
	SUBTTL	ROUTINE TO STACK SECOND TRANSFER COMMAND




;CALLED WHEN PRIMARY COMMAND IS BUSY, TO SET UP A SECOND TRANSFER
;COMMAND.  SAME CALL AS DX2SIO.




DX2STK:	SAVEQ			;SAVE ACS
	MOVE Q2,K.DXAD(P2)	;SET UP DX20 UNIT NUMBER
	LDB T1,IRYFCN		;GET FUNCTION TO PERFORM
	CAIGE T1,MNRP2V		;VARIFY?
	CAIG T1,MXEXTF		;VERIFY THAT IS IS NOT TOO LARGE
	SKIPN RP2FNC(T1)	;AND THAT IS IS VALID
	BUG(DXBIF2)		;IT'S BAD
	LOAD Q1,TBFNC,(T1)	;TRANSLATE TO HARDWARE FUNCTION
	CALL PHYBLK		;OBTAIN UNIT RELATIVE ADDRESS IN T2
	MOVE T3,UDBSIZ(P3)	;GET POINTER TO DISK SIZE DATA
	IDIV T2,SECCYL(T3)	;DIVIDE BY SECTORS PER CYLINDER
	MOVEM T3,UDBPS2(P3)	;UPDATE SECTOR
	CALL GTHWSC		;COMPUTE VALUE FOR .DXREC REGISTER
	MOVE T2,T1		;PUT INTO PROPER AC
	JRST CHSTRT		;GO STACK THE COMMAND
	SUBTTL	ROUTINE TO START A POSITION OPERATION




;SEEKS ARE POSSIBLE FOR THE RP20, BUT ARE NOT USED BECAUSE ONCE THE
;DISK IS ON CYLINDER, THERE IS NO WAY TO TELL WHEN THE DESIRED SECTOR
;IS NEAR THE HEADS (IE, LATENCY OPTIMIZATION IS NOT POSSIBLE AS IT WAS
;FOR RPO6 AND SIMILAR DISKS).  THEREFORE TO PROGRAM THE RP20 DISKS
;EFFICIENTLY, THE SEARCH FUNCTION IS USED INSTEAD.  THIS FUNCTION WILL
;DO A SEEK IF NECESSARY, AND THEN WILL INTERRUPT THE CPU WHEN THE
;DESIRED SECTOR IS "NEAR" THE HEADS.  THEN A TRANSFER OPERATION CAN
;BE INITIATED QUICKLY, AND VERY LITTLE ROTATIONAL TIME WILL BE LOST.
;CALL TO START THE SEARCH:
;  P1/	CDB
;  P2/	KDB
;  P3/	UDB
;  P4/	IORB
;RETURNS:
;  +1:	FAILED
;  +2:	SEARCH STARTED SUCCESSFULLY





DX2POS:	SAVEQ			;SAVE ACS
	MOVE Q2,K.DXAD(P2)	;SET UP DX20 UNIT NUMBER
	MOVEI Q1,XF.SRC		;GET SEARCH FUNCTION
	CALL DX2CON		;CONNECT TO DRIVE AND RETURN CYLINDER IN T2
	 RET			;CAN'T DO IT
DOPOS:	CALL GTHWSC		;COMPUTE .DXREC VALUE (HEAD AND RECORD NUMBERS)
	MOVE T4,T1		;SAVE FOR AWHILE
	TXO T2,.DXCYL		;INSERT REGISTER NUMBER
	CALL WTREG		;SET CYLINDER INTO DX20
	MOVE T2,T4		;GET BACK .DXREC DATA
	TXO T2,.DXCYL		;INSERT REGISTER NUMBER
	HRRZM P3,KDBACT(P2)	;REMEMBER UDB WE HAVE TO WAIT FOR
	JRST CHSTRT		;START CHANNEL AND SKIP IF SUCCESSFUL
	SUBTTL	SUBROUTINE TO CONNECT TO A DRIVE



;DX2CON - LOAD DRIVE NUMBER INTO DX20, COMPUTES CYLINDER AND SECTOR.
;CALL:
;  P1/	CDB
;  P2/  KDB
;  P3/  UDB
;  P4/  IORB
;  Q1/  FUNCTION TABLE ENTRY
;  Q2/	DX20 UNIT NUMBER
;RETURNS:
;  +1:	FAILED TO CONNECT
;  +2:  DONE,	T1/  UNIT ADDRESS
;		T2/  CYLINDER
;		T3/  SECTOR WITHIN CYLINDER



DX2CON:	CALL HLTCHK		;MAKE SURE MICROCODE IS STILL HAPPY
	 JRST BADSI1		;IT ISN'T, GO GIVE AN ERROR
	RDRG .DXERR		;READ ERROR REGISTER
	TXNN T1,ER.ALL		;ANY ERRORS?
	JRST CONNOE		;NO, PROCEED NORMALLY
	MOVE T4,T1		;COPY ERROR
	RDRG .DXEND		;GET ENDING STATUS ALSO
	HRL T4,T1		;PUT WITH OTHER DATA
	CALL FNDCKU		;COLLECT NUMBERS
	BUG(DXBEWC,<<T1,CHAN>,<T2,CTRL>,<T3,UNIT>,<T4,20AND2>>)	;COMPLAIN
	MOVX T2,.DXERR		;CLEAR THE ERROR REGISTER
	CALL WTREG		;SO NO CONFUSION RESULTS
				;AND PROCEED
CONNOE:	MOVE T1,UDBSTS(P3)	;GET CURRENT UNIT STATUS
	CAIE Q1,XF.WRT		;STANDARD WRITE FUNCTION?
	CAIN Q1,XF.WRF		;OR FORMATTING WRITE?
	TXNN T1,US.WLK		;YES, SEE IF THE DISK IS WRITE-LOCKED
	TXNE T1,US.OFS		;AND ALWAYS SEE IF IT IS OFF-LINE
	RET			;IF SO, OPERATOR INTERVENTION IS NEEDED
	MOVX T2,.DXDRV		;WANT TO WRITE THE DRIVE REGISTER
	MOVE T1,UDBSLV(P3)	;GET SLAVE UNIT NUMBER
	STOR T1,DRVNM,T2	;STORE WITH REGISTER NUMBER
	CALL WTREG		;TELL DX20 WHICH DRIVE TO TALK TO
	CALL PHYBLK		;RETURN PHYSICAL DISK ADDRESS TO USE IN T2
	MOVE T1,T2		;REMEMBER IT IN T1
	MOVE T4,UDBSIZ(P3)	;GET POINTER TO DISK SIZE DATA
	IDIV T2,SECCYL(T4)	;DIVIDE BY SECTORS PER CYLINDER
	MOVEM T2,UDBPS1(P3)	;SAVE CYLINDER
	MOVEM T3,UDBPS2(P3)	;AND SECTOR WITHIN CYLINDER
	RETSKP			;SUCCESS RETURN



;CANNOT START I/O.  MARK THE IORB AS FAILED AND GIVE AN ERROR RETURN.


BADSI1:	MOVX T1,<IS.ERR!IS.DVE!IS.NRT>	;GET FATAL DEVICE ERROR FLAGS
	IORM T1,IRBSTS(P4)	;MARK THEM IN THE IORB
	RET			;AND DO ERROR RETURN
	SUBTTL	ROUTINE TO COMPUTE LATENCY OPTIMIZATION




;ROUTINE CALLED FROM PHYSIO TO EXAMINE ALL IORBS AND PICK THE ONE
;WITH THE SMALLEST LATENCY GREATER THAN A SPECIFIED MINIMUM.  CALL:
;  P1/	CDB
;  P2/	KDB
;  P3/	UDB
;  T1/	MINIMUM LATENCY IN MICROSECONDS, OR ZERO TO PERFORM THE COMMAND
;	STACKING COMPUTATION
;RETURNS:
;  +1:	NO REQUESTS ARE AVAILABLE
;  +2:	T1/  LATENCY OF CLOSEST REQUEST GREATER THAN SPECIFIED MINIMUM,
;	     WITH ZERO RETURNED IF THIS IORB IS THE PREFERRED ONE
;	T2/  PREDECESSOR OF IORB CORRESPONDING TO TIME IN T1
;	T3/  IORB CORRESPONDING TO TIME IN T1
;
;NOTICE:  IT IS IMPOSSIBLE TO ASK THE RP20 WHICH SECTOR IS NEAR THE
;HEADS, SO THAT LATENCY OPTIMIZATION CANNOT WORK AS WELL AS FOR OTHER
;TYPES OF DISK.  INSTEAD, IORBS ARE SCANNED LOOKING FOR A REQUEST NEAR
;THE LAST KNOWN POSITION OF THE DISK.  THE UNIT WHICH LAST DID I/O IS
;GIVEN PRIORITY, SINCE ITS POSITION HAS MORE ACCURACY.  FINALLY, HIGHEST
;PRIORITY IS GIVEN TO THE IORB WHICH JUST COMPLETED A SEARCH OPERATION.
;ACS ARE USED AS FOLLOWS:
;
;  T3/	BEST SECTOR DISTANCE SO FAR,,SECTOR ON SURFACE TO AIM FOR
;  T4/	MINIMUM LATENCY FLAG,,DSKSIZ POINTER
;  P4/	PREDECESSOR TO CURRENT IORB,,CURRENT IORB TO EXAMINE
;  P5/	PREDECESSOR TO BEST IORB SO FAR,,BEST IORB SO FAR FOUND



DX2LAT:	MOVE T4,UDBSIZ(P3)	;GET POINTER TO DISK SIZE TABLE
	HRL T4,T1		;SAVE LATENCY AS A FLAG (ZERO MEANS DO STACKING)
	MOVEI P4,UDBTWQ(P3)	;GET POINTER TO FIRST IORB
	TLNN T4,-1		;COMMAND STACKING COMPUTATION?
	HRRZ P4,IRBLNK(P4)	;YES, SKIP FIRST IORB SINCE IT'S DOING I/O
	SETZ P5,		;INITIALIZE BEST IORB
	MOVE T1,UDBPS2(P3)	;GET CURRENT SECTOR WITHIN THE CYLINDER
	ADD T1,SECPAG(T4)	;COMPUTE SECTOR OF NEXT PAGE
	TLNN T4,-1		;STACKING COMPUTATION?
	JRST [	CAML T1,SECCYL(T4)	;YES, NEXT SECTOR ON SAME CYLINDER?
		RET			;NO, CAN'T STACK THEN
		JRST LATBEG]	;YES, PROCEED
	ADD T1,SECPAG(T4)	;NO, SKIP TO NEXT SECTOR
LATBEG:	IDIV T1,SECSRF(T4)	;GET SECTOR WITHIN SURFACE TO AIM FOR IN T2
	HRROI T3,(T2)		;INITIALIZE BEST DISTANCE AND DESIRED SECTOR
				;THEN FALL INTO LOOP
;NOW LOOP OVER ALL IORBS LOOKING FOR THE BEST ONE.


LATLOP:	HRL P4,IRBLNK(P4)	;FOLLOW LINK TO NEXT IORB
	TLNN P4,-1		;ANOTHER IORB TO EXAMINE?
	JRST LATFIN		;NO, GO RETURN RESULT
	MOVS P4,P4		;YES, SET UP PREDECESSOR AND CURRENT IORB
	TLNN T4,-1		;COMMAND STACKING COMPUTATION?
	JRST NOPREF		;YES, DON'T CHECK PREFERRED IORB
	HRRZ T2,CDBIRB(T1)	;GET PREFERRED IORB IF ANY
	CAIE T2,(P4)		;IS THIS IT?
	JRST NOPREF		;NO, PROCEED NORMALLY
	MOVEI T1,0		;FLAG PREFERRED IORB WITH ZERO LATENCY
	HLRZ T2,P4		;GET PREVIOUS IORB
	HRRZ T3,P4		;AND THE PREFERRED IORB
	RETSKP			;RETURN IT

NOPREF:	CALL PHYBLK		;COMPUTE UNIT RELATIVE ADDRESS FOR THIS IORB
	MOVE T1,T2		;COPY IT
	IDIV T1,SECCYL(T4)	;COMPUTE SECTOR WITHIN CYLINDER
	MOVE T1,T2		;COPY RESULT AGAIN
	IDIV T1,SECSRF(T4)	;COMPUTE SECTOR WITHIN SURFACE
	CAIGE T2,(T3)		;SECTOR NUMBER BELOW OUR GOAL?
	ADD T2,SECSRF(T4)	;YES, ADD ONE REVOLUTION'S WORTH OF SECTORS
	SUBI T2,(T3)		;SUBTRACT TO GET SECTORS BETWEEN LOCATIONS
	HLRZ T1,T3		;GET BACK OLD BEST DISTANCE
	CAML T2,T1		;THIS IORB CLOSER THAN PREVIOUS ONES?
	JRST LATLOP		;NOPE, LOOK AT NEXT IORB THEN
	MOVE P5,P4		;IT'S BETTER, REMEMBER IORB AND PREDECESSOR
	HRL T3,T2		;REMEMBER NEW BEST DISTANCE ALSO
	JUMPN T2,LATLOP		;LOOK FOR BETTER ONE UNLESS FOUND A BEST CASE

LATFIN:	JUMPE P5,[BUG(DXBLTF)	;IF FOUND NO IORB, COMPLAIN
		  RET]		;AND ERROR RETURN
	HLRZ T1,T3		;GET BEST DISTANCE
	IMULI T1,^D2700		;CONVERT SECTORS INTO MICROSECONDS OF LATENCY
	HRRZ T2,CDBLUN(P1)	;GET UNIT WHICH LAST DID I/O ON THE CHANNEL
	CAIN T2,(P3)		;WAS IT THIS UNIT?
	AOSA T1			;YES, ADD ONE TO INSURE NONZERO RESULT
	ADDI T1,2		;NO, ADD EXTRA ONE TO PENALIZE UNIT
	HLRZ T2,P5		;GET PREDECESSOR OF BEST IORB
	HRRZ T3,P5		;AND THE BEST IORB ITSELF
	RETSKP			;SUCCESSFUL RETURN
	SUBTTL	ROUTINE TO HANDLE NORMAL INTERRUPTS





;ROUTINE CALLED TO PROCESS A DONE INTERRUPT.  ONLY THE COMPLETION OF A
;DATA OPERATION COMES HERE.
;CALL:
;  P1/	CDB
;  P2/	KDB
;RETURNS:
;  +1:	IORB HAD ERRORS
;	  P3/	UDB
;  +2:	I/O WAS SUCCESSFUL
;	  P3/	UDB




DX2INT:	PUSH P,Q1		;SAVE REGISTERS
	PUSH P,Q2		;THAT WE WILL TRASH (NOTE AOS -2(P) BELOW)
	MOVE Q2,K.DXAD(P2)	;SET UP DX20 UNIT NUMBER
	CALL DOINT		;HANDLE THE INTERRUPT
	 SKIPA			;NON-SKIP
	AOS -2(P)		;SKIP RETURN, PASS IT ON
	RDRG .DXCTL		;READ CONTROL REGISTER
	TXNN T1,DX.GO		;DX20 FREE?
	CALL ASYNST		;YES, CHECK ON ASYCHRONOUS STATUS TOO
	MOVX T1,CS.AC2		;GET READY FOR CHECK
	TDNN T1,CDBSTS(P1)	;STILL DOING I/O BECAUSE OF STACKED COMMAND?
	SETZM KDBACT(P2)	;NO, CLEAR ACTIVE UDB
	POP P,Q2		;RESTORE ACS
	POP P,Q1		;THAT WE SAVED
	RET			;DONE
;HERE TO HANDLE THE DONE INTERRUPT.  DX20 UNIT NUMBER IS IN Q2.


DOINT:	SKIPN P3,KDBACT(P2)	;GET THE UDB I/O WAS BEING DONE FOR
	JRST [	HRRZ T1,CDBADR(P1)	;NONE, GET CHANNEL READY
		BUG(DXBNUD,<<T1,CHAN>,<Q2,DX20>>)	;COMPLAIN
		RET]		;AND GIVE ERROR
	MOVE T1,UDBSTS(P3)	;GET UDB STATUS FLAGS
	TXNE T1,US.ACT		;MAKE SURE UDB WAS ACTIVE
	TXNE T1,US.POS		;AND THAT IT WAS NOT POSITIONING
	JRST [	CALL FNDCKU	;NOPE, COLLECT CKU NUMBERS
		BUG(DXBUA1,<<T1,CHAN>,<T2,CTRL>,<T3,UNIT>>)	;COMPLAIN
		RET]		;AND ERROR RETURN
	CALL SETIRB		;SET UP THE CURRENT IORB
	HRRZ T1,UDBERR(P3)	;GET FUNCTION IF DOING ERROR RECOVERY
	SKIPN T1		;DOING IT?
	LDB T1,IRYFCN		;NO, GET FUNCTION FROM IORB
	CAIG T1,MXFUNC		;A LEGAL FUNCTION?
	JRST DOINT2		;YES
	CAIGE T1,MNRP2V		;MAKE SURE IT IS LEGAL
	JRST INTBDF		;NOPE
DOINT2:	CALL HARCHK		;CHECK FOR ANY ERRORS
	 RET			;HAD SOME, ERROR RETURN
	HRRZ T1,UDBERR(P3)	;GET BACK FUNCTION
	SKIPN T1		;ERROR RECOVERY?
	LDB T1,IRYFCN		;NO, NORMAL FUNCTION FROM IORB THEN
	SKIPGE T1,RP2FNC(T1)	;GET DISPATCH ADDRESS
	TRNN T1,-1		;ALSO MAKE SURE HAVE AN ADDRESS
INTBDF:	BUG (DXBILF)		;NO, LOSE
	CALL (T1)		;CALL PROPER ROUTINE
	 RET			;FAILED, ERROR RETURN
	RETSKP			;SUCCESSFUL RETURN
;HERE ON THE VARIOUS CASES OF INTERRUPTS:



RP2WRT:	MOVX T1,US.WLK		;GET WRITE LOCKED FLAG
	ANDCAM T1,UDBSTS(P3)	;CLEAR SINCE WRITE HAS COMPLETED
RP2RED:	RDRG .DXSTS		;GET STATUS REGISTER
	TXNN T1,ST.ERR		;ANY DRIVE ERRORS?
	CALL CKERR		;OR ANY CHANNEL ERRORS?
	 JRST RP2DER		;YES, GO HANDLE THEM
	CALL PHYCNT		;COMPUTE LENGTH OF TRANSFER
	ADDI T1,PGSIZ-1		;ROUND UP PARTIAL SECTORS
	LSH T1,-PGSFT		;CONVERT TO SECTORS (PAGES) TRANSFERED
	LDB T2,IRYFCN		;GET FUNCTION
	CAIE T2,IRFRVC
	CAIN T2,IRFRED		;READING?
	JRST [	ADDM T1,UDBRED(P3)	;YES, INCREMENT SECTORS READ
		AOS UDBRCT(P3)	;YES, COUNT READS DONE ALSO
		RETSKP]
	ADDM T1,UDBWRT(P3)	;YES, INCREMENT SECTORS WRITTEN
	AOS UDBWCT(P3)		;YES, COUNT WRITES DONE
	RETSKP			;SUCCESSFUL



;HERE IF HAD SOME KIND OF ERROR:


RP2DER:	CALL REDALL		;READ ALL REGISTERS AND SENSE BYTES
	MOVX T1,IS.ERR		;GET ERROR FLAG
	IORM T1,IRBSTS(P4)	;FLAG THIS IORB AS FAILED
	RET			;FAIL RETURN
	SUBTTL	ROUTINE TO HANDLE ATTENTION INTERRUPTS




;ROUTINE CALLED ON AN ATTENTION INTERRUPT.  SUCH INTERRUPTS OCCUR FOR
;THE FOLLOWING REASONS:
;  1.	THE CURRENT DATA OPERATION TERMINATED WITH ERRORS.  WE WILL FLAG
;	THE FACT THAT AN ERROR OCCURED, AND WILL DISMISS THE INTERRUPT.
;	A NORMAL INTERRUPT WILL IMMEDIATELY OCCUR, AND WE WILL BE RECALLED
;	AT THE DX2INT ROUTINE, WHICH WILL NOTICE AND HANDLE THE ERROR.
;  2.	THE DRIVE THAT A SEEK/SEARCH WAS OPERATION WAS GIVEN TO HAS
;	DISCONNECTED ITSELF FROM THE CONTROLLER, SO THAT THE CONTROLLER
;	IS NOW FREE TO ACCEPT MORE COMMANDS.  IT IS POSSIBLE THAT THE
;	SEEK/SEARCH HAS COMPLETED AT THIS TIME ALSO (BUT IS UNLIKELY).
;  3.	ASYCHRONOUS STATUS FOR A DRIVE HAS APPEARED.
;
;CALL:
;  P1/	CDB
;  P2/	KDB
;RETURNS:
;  +1:	ALWAYS
;	P3/  0
;	P4/  0  (THIS INDICATES NO IORB WAS IN ERROR)
;	Q1/  LEFT HALF SET TO -1 IF SCHEDULING DESIRED BECAUSE OF A
;	     COMPLETED SEEK OPERATION




DX2ATN:	MOVEM Q1,K.SAVQ(P2)	;SAVE Q1 IN AN ACCESSIBLE LOCATION
	PUSH P,Q2		;SAVE ONE ON THE STACK TOO
	MOVE Q2,K.DXAD(P2)	;SET UP DX20 UNIT NUMBER
	MOVX T2,AT.0		;GET ATTENTION BIT READY
	LSH T2,(Q2)		;POSITION IT
	TXO T2,.DXATN		;INSERT REGISTER NUMBER
	CALL WTREG3		;CLEAR THE ATTENTION
	RDRG .DXCTL		;READ CONTROL REGISTER
	TXNE T1,DX.GO		;CONTROLLER STILL BUSY?
	JRST ATNXIT		;YES, IGNORE THIS INTERRUPT
	CALL DOATTN		;CHECK ON NORMAL ATTENTIONS
	CALL ASYNST		;THEN CHECK ON ASYCHRONOUS STATUS
ATNXIT:	POP P,Q2		;RESTORE ONE AC
	MOVE Q1,K.SAVQ(P2)	;RESTORE Q1 WHICH MAYBE HAD LH SET TO -1
	SETZB P3,P4		;NO IORB ERRORS TO RETURN
	RET			;DONE
;ROUTINE TO HANDLE NORMAL (NON ASYCHRONOUS) ATTENTION INTERRUPTS.
;DX20 UNIT NUMBER IS IN Q2.



DOATTN:	SKIPN P3,KDBACT(P2)	;GET ACTIVE UDB IF ANY
	RET			;NONE
	MOVX T1,US.ACT		;VERIFY UDB IS REALLY ACTIVE
	TDNN T1,UDBSTS(P3)	;WELL?
	JRST [	CALL FNDCKU	;NO, COLLECT CKU NUMBERS
		BUG(DXBUNA,<<T1,CHAN>,<T2,CTRL>,<T3,UNIT>>)	;COMPLAIN
		SETZB P3,KDBACT(P2)	;CLEAR ACTIVE UDB
		RET]		;AND RETURN
	RDRG .DXEND		;READ ENDING STATUS
	TXNN T1,ES.CHE!ES.DVE!ES.UNC!ES.UNE	;DX20 HAVE STATUS FOR US?
	RET			;NO, DON'T EXAMINE DRIVE YET
	MOVE Q3,T1		;SAVE STATUS
	SKIPE T1,UDBERR(P3)	;DOING ERROR RECOVERY?
	JRST ATNERC		;YES, NEED TO USE DISPATCH ADDRESS
	CALL SETIRB		;GET THE CURRENT IORB
	MOVX T2,US.POS		;GET READY FOR CHECK
	TDNE T2,UDBSTS(P3)	;WAS THIS A POSITION OPERATION?
	JRST ATNPOS		;YES, GO HANDLE IT
	RET			;WAS FAILED DATA OPERATION, IGNORE ATTENTION
				;SINCE DX2INT CODE WILL NOTICE ERROR
;HERE ON AN ATTENTION INTERRUPT DURING ERROR RECOVERY:


ATNERC:	SKIPL T1,RP2FNC(T1)	;GET DISPATCH ADDRESS READY
	TRNN T1,-1		;DATA OPERATION OR NO DISPATCH ADDRESS?
	RET			;YES, IGNORE INTERRUPT
	CALL SETIRB		;GET THE CURRENT IORB
	JRST (T1)		;THEN GO TO ROUTINE



;HERE ON AN ATTENTION CAUSED BY A POSITION DONE INTERRUPT:


ATNPOS:	MOVX T1,KS.ACT		;GET ACTIVE FLAG FOR THE CONTROLLER
	ANDCAM T1,KDBSTS(P2)	;CONTROLLER NO LONGER BUSY
	SETZM KDBACT(P2)	;NO MORE ACTIVE UDB EITHER
	TXNE Q3,ES.DVE!ES.UNC!ES.UNE	;DEVICE END UP OR ERRORS?
	JRST ATNSRC		;YES, SEARCH IS FINISHED
	HRROS K.SAVQ(P2)	;SEARCH STILL GOING, REMEMBER TO SCHEDULE
	RET			;DONE



;HERE WHEN A POSITION REQUEST IS COMPLETED (SEARCH FOUND THE SPECIFIED
;SECTOR AND WE HAVE TO START I/O QUICKLY).


ATNSRC:	TXNE Q3,ES.UNC!ES.UNE	;ANY UNIT ERROR?
	JRST SRCBAD		;YES, GO GIVE SEARCH ERROR
	CALL SRCDON		;NOPE, MARK SEARCH AS DONE
	RET			;DONE
	SUBTTL	ROUTINE TO HANDLE ASYCHRONOUS STATUS





;THIS ROUTINE MUST BE CALLED AT THE END OF EVERY INTERRUPT (IF THE DX20
;IS NOT BUSY) TO LOOK FOR ASYCHRONOUS STATUS.  THE DX20 GIVES US SUCH
;STATUS FOR A DRIVE WHICH COMES ONLINE OR HAS COMPLETED A SEARCH/SEEK.
;ASYCHRONOUS STATUS IS PRESENTED BY THE DX20 ONLY IF THE ATTENTION BIT
;AND THE ASYCHRONOUS REGISTER ARE BOTH CLEAR.  THUS AFTER READING THE
;STATUS, WE HAVE TO ZERO THESE TO ALLOW MORE STATUS TO APPEAR.  CALL:
;  P1/	CDB
;  P2/	KDB
;RETURNS:
;  +1:	ALWAYS



ASYNST:	MOVE Q2,K.DXAD(P2)	;SET UP DX20 UNIT NUMBER
	RDRG .DXASY		;READ ASYNCHRONOUS STATUS IF ANY
	JUMPE T1,R		;NOTHING TO DO IF NO STATUS
	MOVX T2,.DXASY		;SET UP IN CASE NEED TO READ IT AGAIN
	TXNN T1,ES.CUE!ES.BSY!ES.CHE!ES.DVE!ES.UNC!ES.UNE	;STATUS YET?
	CALL RDREG3		;NO, READ AGAIN SINCE HE WASN'T YET DONE
	PUSH P,Q3		;SAVE AN AC
	PUSH P,P3		;AND ANOTHER ONE
	PUSH P,P4		;AND ANOTHER ONE
	MOVE Q3,T1		;MOVE STATUS TO SAFE PLACE
	MOVX T2,AT.0		;GET ATTENTION BIT
	LSH T2,(Q2)		;POSITION FOR THIS DX20
	TXO T2,.DXATN		;INSERT REGISTER NUMBER
	CALL WTREG3		;CLEAR ANY ATTENTION CAUSE BY THE STATUS
	MOVX T2,.DXASY		;WANT TO CLEAR ASYCHRONOUS STATUS REGISTER
	CALL WTREG3		;CLEAR IT TO ALLOW NEW STATUS
	LOAD T4,ASDRV,Q3	;GET DRIVE PRESENTING CURRENT STATUS
	CALL DRVSRC		;FIND THE UDB
	 JRST [	CALL NEWONL	;NOT FOUND, CREATE UDB IF POSSIBLE
		JRST ASYNS2]	;AND FINISH UP
	CALL ASYNDO		;FOUND IT, HANDLE STATUS FOR THE DRIVE
ASYNS2:	POP P,P4		;RESTORE AC
	POP P,P3		;RESTORE AC
	POP P,Q3		;AND ANOTHER
	JRST ASYNST		;CHECK FOR MORE STATUS
;ROUTINE TO HANDLE ASYCHRONOUS STATUS FOR A PARTICULAR UNIT.  CALL:
;  P1/	CDB
;  P2/	KDB
;  P3/	UDB
;  Q2/	DX20 NUMBER
;  Q3/	ASYCHRONOUS STATUS FOR THIS DRIVE (FROM .DXASY REGISTER)
;RETURNS:
;  +1:	ALWAYS




ASYNDO:	MOVE T2,UDBSTS(P3)	;GET STATUS
	TXNN T2,US.ACT		;WERE WE EXPECTING AN INTERRUPT?
	JRST ONLINE		;NO, GO SEE IF DRIVE IS USABLE AGAIN
	SKIPE T1,UDBERR(P3)	;IN ERROR RECOVERY?
	JRST ATNERC		;YES, GO TO SPECIAL CODE
	CAMN P3,KDBACT(P2)	;GETTING STATUS FROM CURRENTLY ACTIVE UDB?
	RET			;YES, IGNORE SINCE IT IS EXTRANEOUS
	TXNN T2,US.POS		;DOING A SEEK FOR THIS DRIVE?
	JRST [	CALL FNDCKU	;NO, COLLECT CKU NUMBERS
		BUG(DXBASD,<<T1,CHAN>,<T2,CTRL>,<T3,UNIT>>)	;COMPLAIN
		RET]		;AND RETURN
	CALL SETIRB		;YES, GET THE ACTIVE IORB
	TXNE Q3,ES.DVE		;DEVICE END NOT SET?
	TXNE Q3,ES.UNC!ES.UNE	;OR ANY UNIT ERRORS?
	JRST SRCBAD		;YES, THEN HAVE A FAILED SEEK
				;NOPE, THEN IS A FINISHED SEEK


;HERE WHEN A SEARCH HAS FINISHED SUCCESSFULLY:


SRCDON:	HRRZM P4,CDBIRB(P1)	;WE WANT TO START TRANSFERS ON THIS IORB
	AOS UDBSEK(P3)		;INCREMENT SEEKS (SEARCHES) DONE
	CALL PHYSDN		;INFORM PHYSIO SEARCH IS DONE
	HRROS K.SAVQ(P2)	;REMEMBER TO SCHEDULE MORE STUFF
	RET			;DONE
;HERE IF HAD AN ERROR ON A SEARCH OPERATON.  THE CURRENT WORKINGS OF PHYSIO
;DO NOT ALLOW SEEK ERRORS TO BE RETURNED FOR INDIVIDUAL UNITS OF A CONTROLLER.
;THEREFORE MAKE A SYSERR ENTRY, COUNT SEEK ERRORS, BUT MARK THE IORB AS
;SUCCESSFUL.  THE FOLLOWING TRANSFER WILL THEN DO AN IMPLIED SEEK, WHICH
;WILL CATCH THE ERROR IF IT WAS HARD.



SRCBAD:	AOS UDBSPE(P3)		;CALL THIS A SOFT POSITIONING ERROR
	CALL ERRSET		;ALLOCATE AN ERROR BLOCK
	SKIPN Q1,T1		;MOVE ADDRESS TO RIGHT AC
	JRST SRCDON		;IF NO SYSERR BLOCK, ACT LIKE SEARCH IS DONE
	MOVEM Q1,UDBERP(P3)	;SAVE ADDRESS IN THE UDB
	PUSH P,IRBSTS(P4)	;REMEMBER CURRENT STATUS OF IORB
	MOVX T1,IS.ERR!IS.DVE	;GET SOME ERROR FLAGS
	IORM T1,IRBSTS(P4)	;PLACE INTO IORB TEMPORARILY FOR LOGGING
	CALL REDALL		;READ ALL REGISTERS AND SENSE BYTES
	CALL RDRINI		;STORE THEM INTO SYSERR BLOCK
	CALL RDRFIN		;MAKE THEM THE FINAL REGISTERS ALSO
	CALL ERRFIN		;TERMINATE ERROR LOGGING
	POP P,IRBSTS(P4)	;RESTORE ORIGINAL FLAGS
	JRST SRCDON		;THEN ACT LIKE SEARCH WAS SUCCESSFUL
;ROUTINES TO HANDLE ON-LINE TRANSITIONS, EITHER FROM AN OLD EXISTENT DRIVE
;OR FROM A TOTALLY UNKNOWN DRIVE.  IF CALLED FOR AN UNKNOWN DRIVE, WE
;HAVE TO BUILD A UDB FOR THE UNIT.



NEWONL:	TXNE Q3,ES.DVE		;DEVICE END UP?
	TXNE Q3,ES.UNC!ES.UNE	;AND NO ERRORS DETECTED?
	RET			;NO, DON'T CREATE UDB THEN
	PUSH P,Q3		;PRESERVE DRIVE NUMBER AND STATUS
	PUSH P,P5		;SAVE SOME ACS
	PUSH P,Q1		;THAT WILL BE USED
	LOAD Q1,ASDRV,Q3	;GET UNIT NUMBER
	TLO Q1,-1		;CREATE AOBJN WORD FOR THIS UNIT ONLY
	MOVX P5,US.OFS		;BEGIN STATUS WITH DRIVE OFF-LINE
	SETZ Q3,		;PREVENT A BUGCHK ON SICK DRIVES
	CALL ISDRV		;TRY TO BUILD A NEW UDB FOR THIS DRIVE
	POP P,Q1		;RESTORE ACS
	POP P,P5		;THAT WE SAVED
	POP P,Q3		;RESTORE UNIT NUMBER AND STATUS
	LOAD T4,ASDRV,Q3	;GET DRIVE NUMBER
	CALL DRVSRC		;NOW SEE IF THE UDB GOT BUILT
	 RET			;NOPE, IGNORE IT
				;YES, NOW FALL INTO NORMAL ON-LINE CODE

ONLINE:	MOVX T1,US.WLK		;ASSUME DRIVE IS WRITABLE AGAIN
	ANDCAB T1,UDBSTS(P3)	;SO CLEAR WRITE LOCK AND GET OTHER BITS
	TXNE Q3,ES.DVE		;HAVE DEVICE END?
	TXNE Q3,ES.UNC!ES.UNE	;AND NO ERROR BITS?
	RET			;NO, DRIVE IS STILL OFF-LINE THEN
	TXNN T1,US.OFS		;DID WE THINK THE DRIVE WAS OFF-LINE?
	RET			;NO, IGNORE SPURIOUS STATUS THEN
	CALL PHYONL		;MARK THE DEVICE ON-LINE AGAIN
	HRROS K.SAVQ(P2)	;REMEMBER TO TELL PHYSIO TO SCHEDULE
	RET			;DONE
	SUBTTL	ROUTINE TO CHECK FOR DATA ERRORS




;ROUTINE TO SEE IF A TRANSFER OPERATION HAD ANY DRIVE OR CONTROLLER
;ERRORS.  CALL:
;  P1/	CDB
;  P2/	KDB
;  P3/	UDB
;  P4/	IORB
;RETURNS:
;  +1:	ERRORS FOUND AND FLAGGED
;  +2:	NO ERRORS FOUND OR OF NO IMPORTANCE



HARCHK:	MOVX T1,IS.DVE!IS.DTE	;GET IORB ERROR BITS
	ANDCAM T1,IRBSTS(P4)	;CLEAR THEM TO BEGIN
	MOVX T2,.DXERR		;SET UP TO READ ERROR REGISTER
	CALL RDREG		;READ IT
	TXNN T1,ER.ALL		;ANY ERRORS?
	RETSKP			;NO, SUCCESSFUL RETURN
	TXNE T1,ER.STP!ER.MPE	;DX20 HALTED OR HAS MEMORY PROBLEMS?
	JRST ISDEAD		;YES, GO MARK THE CONTROLLER DOWN
	TXNE T1,ER.ILF!ER.ILR	;ILLEGAL FUNCTION OR REGISTER?
	JRST ISHARD		;YES, CALL THIS A HARD ERROR
	TXNE T1,ER.RMR!ER.CPE!ER.DPE	;RMR ERROR OR BUS PARITY ERRORS?
	JRST ISFAIL		;YES, TRY THE OPERATION AGAIN
	LOAD T2,ERCOD,T1	;GET POSSIBLE ERROR CODE
	TXNE T1,ER.NEW		;NO ERROR CODE STORED?
	CAILE T2,ERCMAX		;OR CODE IS OUT OF RANGE?
	MOVEI T2,0		;YES, TURN INTO CODE ZERO FOR UNKNOWN
	CALL @ERCTAB(T2)	;DISPATCH ON ERROR CODE TYPE
	 RET			;ERROR RETURN
	RETSKP			;SUCCESSFUL RETURN
;DISPATCH TABLE FOR ERROR CLASS CODES.  CODE IS OBTAINED FROM THE
;ERCOD FIELD OF THE ERROR REGISTER (.DXERR).  THIS FIELD IS VALID ONLY
;IF THE ER.NEW FLAG IS SET IN THE ERROR REGISTER.



ERCTAB:	IFIW ISUNKN		;(0) ILLEGAL OR UNKNOWN ERROR
	IFIW ISUNUS		;(1) UNUSUAL DEVICE STATUS
	IFIW ISIGNR		;(2) RECORD LENGTH ERROR
	IFIW ISOFFL		;(3) DRIVE SELECTION ERROR
	IFIW ISFAIL		;(4) RECOVERABLE ERROR
	IFIW ISRETY		;(5) COMMAND RETRY REQUEST
	IFIW ISFAIL		;(6) NON-RECOVERABLE ERROR
	IFIW ISIGNR		;(7) RETRY LOG INFORMATION AVAILABLE
	IFIW ISDEAD		;(10) FATAL ERRORS

	ERCMAX==.-ERCTAB-1	;HIGHEST KNOWN ERROR CODE




;HERE WHEN THE ERROR IS UNKNOWN:


ISUNKN:	HRRZ T2,CDBADR(P1)	;GET CHANNEL
	BUG(DXBIEC,<<T2,CHAN>,<Q2,DX20>,<T1,STATUS>>)	;COMPLAIN
	JRST ISHARD		;AND MAKE THIS A HARD ERROR
;HERE TO HANDLE UNUSUAL DEVICE STATUS (ERROR CLASS 1).  THIS IS THE
;NORMAL ERROR CODE FOR TYPICAL DEVICE AND DATA ERRORS.  INFORMATION ON
;THE PARTICULAR ERROR IS OBTAINED FROM THE ENDING STATUS REGISTER AND
;THE SENSE BYTES.


ISUNUS:	MOVX T2,.DXEND		;WANT TO READ ENDING STATUS
	CALL RDREG		;DO IT
	TXNE T1,ES.BSY		;IS CONTROLLER BUSY?
	JRST ISFAIL		;YES, VERY OBSCURE, RETRY IT
	TXNN T1,ES.UNC!ES.UNE	;UNIT CHECK OR EXCEPTION?
	JRST ISIGNR		;NO, IGNORE SPURIOUS ERROR
	PUSH P,T1		;SAVE ENDING STATUS
	CALL REDALL		;READ ALL REGISTERS AND CLEAR ERRORS
	POP P,T1		;RESTORE STATUS
	MOVE T2,K.DEXS(P2)	;GET SENSE BYTES 0 THROUGH 3
	TXNE T2,S0.IVR		;INTERVENTION REQUIRED?
	JRST ISOFF2		;YES, MARK DRIVE OFF-LINE
	TXNE T2,S0.REJ		;COMMAND REJECTED?
	JRST ISRJEC		;YES, GO SEE IF WRITE-LOCKED
	TXNN T2,S0.BPE!S0.OVR!S0.ECK!S1.PRM!S1.ITF!S1.EOC!S1.NRF!S1.FPE!S1.OPI
	TXNN T2,S0.DCK		;DEVICE ERRORS OR NOT DATA CHECK ERROR?
	JRST ISFAI2		;YES, RETURN RETRIABLE ERROR
	JRST ISDATC		;GO FLAG A NORMAL DATA CHECK ERROR
;HERE IF THE DRIVE HAS GONE OFF-LINE:


ISOFFL:	CALL REDALL		;READ ALL REGISTERS AND CLEAR THE ERROR
ISOFF2:	CALL PHYOFL		;MARK THE DRIVE OFF-LINE
	JRST ISFAI2		;CALL IT A RECOVERABLE ERROR, SO ON THE
				;NEXT RETRY THE UNIT WILL BE MARKED AS
				;WAITING FOR OPERATOR INTERVENTION




;HERE IF THE COMMAND WAS REJECTED, PROBABLY DUE TO A WRITE-LOCKED DRIVE.
;IF SO, CLEAR THE ACTIVE STATUS OF THE IORB AND REQUEST ANOTHER SCHEDULER
;CYCLE, WHICH WILL THEN MARK THE DRIVE AS NEEDING OPERATOR INTERVENTION.
;THE DRIVE WILL STAY THAT WAY UNTIL RANDOM ASYCHRONOUS STATUS APPEARS DUE
;TO THE OPERATOR HITTING THE ATTN BUTTON OR POWERING UP THE DRIVE.  THIS
;ROUNDABOUT PROCEDURE IS NECESSARY BECAUSE YOU CAN'T TELL IF AN RP20 IS
;WRITE-LOCKED WITHOUT ACTUALLY TRYING TO WRITE ON IT!!!



ISRJEC:	TXNN T2,S1.WLK		;IS DRIVE REALLY WRITE-LOCKED?
	JRST ISFAI2		;NO, GO TO NORMAL ERROR STUFF
	SKIPE UDBERR(P3)	;YES, SEE IF IN MIDDLE OF ERROR RECOVERY
	JRST ISHAR2		;IF SO, DON'T CONFUSE THINGS!
	MOVX T1,IS.WLK		;GET FLAG READY
	IORM T1,IRBSTS(P4)	;REMEMBER IN IORB FOR STATUS WATCHERS
	MOVX T1,US.WLK		;GET WRITE-LOCK FLAG FOR UDB
	IORM T1,UDBSTS(P3)	;PREVENT ANY FURTHER WRITES FOR THIS UNIT
	CALL CLRACT		;MARK THE DRIVE IDLE
	SETZM P3		;FORGET ABOUT THIS UNIT
	SETOM P4		;REQUEST A SCHEDULER CYCLE
	RET			;AND STOP STACKED COMMAND BY DOING ERROR RETURN




;HERE ON A NORMAL DATA CHECK ERROR:


ISDATC:	MOVX T1,IS.DTE!IS.ERR	;GET ERROR FLAGS
	IORM T1,IRBSTS(P4)	;FLAG THAT A DATA ERROR OCCURRED
	MOVX T1,UE.DAT		;GET ANOTHER FLAG
	IORM T1,UDBERR(P3)	;REMEMBER IN UDB ALSO
	RET			;RETURN ERROR
;HERE IN CASES WHERE THE OPERATION FAILED DUE TO SOME DEVICE ERROR,
;BUT THERE IS REASON TO THINK THAT RETRYING THE OPERATION MIGHT WIN.



ISFAIL:	CALL REDALL		;READ IN REGISTERS AND CLEAR THE ERROR
ISFAI2:	MOVX T1,UE.DEV		;GET FLAG READY
	IORM T1,UDBERR(P3)	;REMEMBER THAT THIS IS A DEVICE ERROR
	MOVX T1,IS.ERR!IS.DVE	;GET IORB FLAGS
	IORM T1,IRBSTS(P4)	;MARK THE ERROR IN THE IORB
	RET			;DONE




;HERE FOR RETRY REQUEST ERROR (ERROR CLASS 5).  THE DX20 DOES NOT GIVE
;THIS ERROR IN ANY REASONABLE SITUATION, THEREFORE THE MONITOR MAKES NO
;USE OF IT.  WE CLEAR THE CONDITION AND DO RETRIES OURSELF.


ISRETY:	MOVX T2,.DXFLG		;WANT TO READ FLAG REGISTER SPECIALLY
	CALL RDREG		;GET IT
	PUSH P,T1		;SAVE FOR AWHILE
	MOVX T2,.DXFLG		;GET READY
	CALL WTREG		;CLEAR REGISTER BEFORE DOING ANYTHING ELSE
	CALL ISFAIL		;NOW SAFE TO READ SENSE BYTES
	POP P,K.DEXS+REG.22(P2)	;RESTORE TRUE VALUE OF REGISTER FOR SYSERR
	RET			;DONE




;HERE ON ERROR CONDITIONS WHICH ARE NOT REALLY ERRORS, OR WHICH ARE
;DETECTED ELSEWHERE (SUCH AS OVERRUNS).  JUST CLEAR THE ERROR REGISTER
;AND SAY THE OPERATION SUCCEEDED.


ISIGNR:	MOVX T2,.DXERR		;GET ERROR REGISTER
	CALL WTREG		;CLEAR IT OUT
	RETSKP			;RETURN SAYING NO ERRORS FOUND
;HERE ON FATAL ERRORS WHICH INDICATE THAT THE MICROPROCESSOR IS
;MALFUNCTIONING.  COMPLAIN AND MARK THE CONTROLLER AS HALTED.


ISDEAD:	CALL REGALL		;READ THE MASSBUSS REGISTERS ONLY
	MOVX T2,.DXMAI!MR.RES	;GET READY TO STOP THE PROCESSOR
	CALL WTREG		;MAKE SURE IT IS STOPPED
	MOVX T1,KS.HLT		;GET HALT FLAG
	IORM T1,KDBSTS(P2)	;REMEMBER THAT THE CONTROLLER IS DEAD
	HRRZ T1,CDBADR(P1)	;GET CHANNEL NUMBER
	BUG(DXBDIE,<<T1,CHAN>,<Q2,DX20>>)	;COMPLAIN THAT THE DX20 DIED
	JRST ISHAR2		;AND TREAT THIS AS A HARD ERROR





;HERE ON DEVICE ERRORS WHICH WE CANNOT EXPECT TO RECOVER FROM.  FLAG
;THE ERROR AS FATAL.


ISHARD:	CALL REDALL		;READ IN ALL REGISTERS
ISHAR2:	MOVX T1,IS.DVE!IS.ERR!IS.NRT	;GET FLAGS FOR FATAL ERROR
	IORM T1,IRBSTS(P4)	;MARK THEM IN THE IORB
	MOVX T1,UE.HRD!UE.DEV	;GET HARD ERROR AND DEVICE ERROR FLAGS
	IORM T1,UDBERR(P3)	;REMEMBER IN THE UDB
	RET			;ERROR RETURN
	SUBTTL	ERROR RECOVERY




;ROUTINE CALLED BY PHYSIO TO RECOVER FROM ERRORS.  THIS ROUTINE IS CALLED
;IN THE FOLLOWING CASES:
;  1.	WHEN A NEW OPERATION FAILED, AFTER THE INITIAL ERROR STATUS
;	AND CAUSE HAS BEEN SAVED.
;  2.	ON EACH RETRY OPERATION THEREAFTER, EVEN IF THE RETRY SUCCEEDED.
;	IS.ERR WILL BE SET IN THE IORB IF THE OPERATION FAILED.  IF IS.ERR
;	IS NOT SET, THEN WE HAVE RECOVERED AND THE ERROR WAS SOFT.
;RETURNS:
;  +1:	ERROR RECOVERY STILL IN PROGRESS
;  +2:	ERROR RECOVERY IS FINISHED (IS.ERR SET IF UNSUCCESSFUL)




DX2ERR:	SAVEQ			;SAVE REGISTERS
	HRRZ Q1,UDBERP(P3)	;GET ERROR BLOCK (IF ANY)
	MOVE Q2,UDBSTS(P3)	;GET UDB STATUS
	AOS Q3,UDBERC(P3)	;INCREMENT RETRY COUNTER
	CAIN Q3,1		;FIRST TIME?
	CALL RDRINI		;YES, COPY INITIAL REGISTERS
	MOVE T1,IRBSTS(P4)	;GET STATUS OF IORB
	TXNE T1,IS.IER!IS.NRT	;ERROR RECOVERY INHIBITED OR NO MORE RETRIES?
	JRST HRDERR		;YES, THIS ERROR IS HARD THEN
	TXNN T1,IS.ERR		;DID THE LAST RETRY SUCCEED?
	JRST SFTERR		;YES, THIS WAS A SOFT ERROR
	TXNE Q2,US.POS		;ERROR DURING A TRANSFER?
	JRST POSERR		;NO, GO HANDLE POSITIONING ERROR
	MOVE T1,UDBERR(P3)	;TRANSFER ERROR, GET ERROR STATUS
	TXNN T1,UE.DEV		;DEVICE ERROR?
	TXNN T1,UE.DAT		;OR NOT A DATA ERROR?
	JRST DEVERR		;YES, A DEVICE ERROR
	LDB T1,IRYFCN		;GET FUNCTION FROM IORB
	LOAD T2,SBECOD		;AND ERROR CLASS CODE FROM 7TH SENSE BYTE
	CAIN T1,IRFRED		;WAS THIS A VANILLA READ OPERATION?
	CAIE T2,FC.ECC		;AND DOES THE DX20 THINK IT'S ECC CORRECTABLE?
	JRST DATERR		;NO, JUST PLAIN DATA ERROR THEN
	LOAD Q3,SBEPOS		;YES, GET ECC POSITION FROM BYTES 18 AND 19
	CAILE Q3,BYTSEC		;REASONABLE ECC POSITION?
	JRST DATERR		;NO, TREAT AS NORMAL DATA ERROR
	JRST ECCERR		;YES, GO DO THE ECC CORRECTION
;HERE ON A DEVICE ERROR DURING A SEARCH OR SEEK OPERATION.  WE DO NOT
;GENERATE SEEK ERRORS, SO THAT SUPPOSEDLY IT IS IMPOSSIBLE TO GET HERE.
;THEREFORE, JUST GIVE A HARD ERROR WITHOUT RETRYING.


POSERR:	MOVX T1,UE.POS!UE.DEV	;MARK A POSITIONING ERROR
	IORM T1,UDBERR(P3)	;IN THE UDB ERROR LOCATION
	MOVX T1,IS.DVE		;MARK A DEVICE ERROR
	IORM T1,IRBSTS(P4)	;IN THE IORB STATUS
	JRST HRDERR		;AND CAUSE A HARD ERROR




;HERE TO HANDLE DEVICE OR SIMPLE DATA ERRORS FROM A DATA TRANSFER OPERATION.
;FLAG THE ERROR AND INITIATE THE NEXT STEP IN ERROR RECOVERY.  THE ALGORITHM
;IS SIMPLE, SINCE THERE ARE NO FANCY OFFSETS OR SUCH TO TRY.  JUST TRY THE
;OPERATION OVER AGAIN MANY TIMES, WITH OCCASIONAL RECALIBRATIONS.


DATERR:	MOVX T1,UE.DAT		;GET DATA ERROR FLAG
	MOVX T2,IS.DTE		;ANOTHER ONE TOO
	JRST ERR		;JOIN OTHER CODE

DEVERR:	MOVX T1,UE.DEV		;GET DEVICE ERROR FLAG
	MOVX T2,IS.DVE		;AND ANOTHER ONE

ERR:	IORM T1,UDBERR(P3)	;FLAG THE ERROR IN THE UDB
	IORM T2,IRBSTS(P4)	;AND IN THE IORB
	MOVE T1,UDBERC(P3)	;GET CURRENT RETRY COUNT
	IDIVI T1,RTYCNT		;SPLIT INTO TWO NUMBERS
	JUMPN T2,RETRY		;DO STRAIGHT RETRY MOST OF THE TIME
	CAIG T1,RCLCNT		;RECALIBRATION TIME, MORE TO DO?
	JRST RECAL		;YES, GO DO ONE
	JRST HRDERR		;NO, GIVE UP
;HERE ON A DATA ERROR WHICH IS ECC CORRECTABLE.  TO CORRECT AN ECC ERROR,
;THE DX20 PROVIDES THE FOLLOWING INFORMATION IN THE SENSE BYTES:
;  18-19  FIRST BYTE IN ERROR COUNTED FROM THE LAST BYTE IN THE SECTOR
;	  (FROM 0 TO ^D2304).  ZERO MEANS THE ERROR WAS IN THE ECC BYTE
;	  ITSELF, AND THEREFORE THE DATA IS ACTUALLY OK.
;  20-21  MASK WHICH WHEN XORED WITH THE DATA ACTUALLY READ WILL
;	  CORRECT THE DATA.
;SENSE BYTES 18-19 ARE ALREADY IN AC Q3.



ECCERR:	JUMPE Q3,ECCER2		;SKIP ON IF ERROR WAS IN ECC BYTE
	MOVNI T1,-BYTSEC(Q3)	;COMPUTE BYTES FROM START OF PAGE
	LSH T1,3		;CONVERT TO BITS
	TLZ T1,-1		;CLEAR JUNK
	IDIVI T1,^D36		;COMPUTE OFFSET IN SECTOR AND POSITION IN WORD
	MOVE Q3,T1		;SAVE OFFSET IN SECTOR
	MOVNI T2,-^D<36-16>(T2)	;COMPUTE SHIFT VALUE FOR MASK
	LOAD P5,SBEMSK		;GET THE ECC MASK FROM BYTES 20 AND 21
	SKIPN P5		;MASK SHOULD BE NONZERO
	BUG(DXBZEC)		;NOPE, THEN WHY WERE WE CALLED?
	SETZ P6,		;CLEAR SECOND WORD OF MASK
	LSHC P5,(T2)		;SHIFT MASK TO PROPER POSITION
	CALL ECCADR		;GET ADDRESS OF BEGINNING OF SECTOR TRANSFERED
	ADDI T1,(Q3)		;POINT TO ADDRESS OF FIRST WORD TO BE FIXED
	JUMPE P5,ECCER1		;SKIP SOME IF NO CORRECTION NECESSARY
	CALL PHYMOV		;GET THE DATA WORD
	XOR T2,P5		;APPLY THE ECC MASK TO THE WORD
	CALL PHYSTO		;AND STORE BACK THE CORRECTED DATA
ECCER1:	JUMPE P6,ECCER2		;SKIP SOME IF NO CORRECTION NECESSARY
	ADDI T1,1		;INCREMENT TO SECOND WORD
	CALL PHYMOV		;GET THE DATA WORD
	XOR T2,P6		;APPLY THE ECC MASK TO THE WORD
	CALL PHYSTO		;AND STORE BACK THE CORRECTED DATA
ECCER2:	CALL ECCUCL		;UPDATE CCW LIST, RETURN WORDS TRANSFERED OK
	 JRST ECCDON		;WAS IN LAST SECTOR, FINISH UP
	BUG(DXBMSR)		;NEVER GET HERE SINCE ALWAYS READING ONE SECTOR


ECCDON:	MOVX T1,UE.ECC		;GET ERROR FLAG
	IORM T1,UDBERR(P3)	;REMEMBER WE HAD AN ECC-CORRECTABLE ERROR
	MOVX T1,IS.ERR!IS.DTE	;GET ERROR BIT IN IORB
	ANDCAM T1,IRBSTS(P4)	;AND CLEAR IT SINCE WE RECOVERED
	AOS UDBSRE(P3)		;INCREMENT SOFT READ ERROR COUNTER
	RETSKP			;SKIP RETURN TO SAY ALL DONE
;HERE IF THE LAST RETRY SUCCEEDED SO THAT THE ERROR WAS SOFT:


SFTERR:	MOVEI T1,T1		;PROTECT IN CASE NOTHING MATCHES
	LDB T2,IRYFCN		;GET FUNCTION FROM IORB
	CAIE T2,IRFRVC		;READ VALIDITY CHECK?
	CAIN T2,IRFRED		;READ?
	MOVEI T1,UDBSRE(P3)	;YES, POINT TO SOFT READ COUNTER
	CAIE T2,IRFWVC		;WRITE VALIDITY CHECK?
	CAIN T2,IRFWRT		;WRITE?
	MOVEI T1,UDBSWE(P3)	;YES, POINT TO SOFT WRITE COUNTER
	TXNE Q2,US.POS		;WAS THIS A SEEK OPERATION?
	MOVEI T1,UDBSPE(P3)	;YES, POINT TO SOFT POSITION COUNTER
	AOS (T1)		;INCREMENT PROPER COUNTER
	CALL RDRFIN		;READ FINAL REGISTERS NOW
	RETSKP			;SKIP RETURN TO INDICATE RECOVERY FINISHED




;HERE ON A HARD ERROR WHEN NO AMOUNT OF RETRIES SUCCEEDED, OR IF
;ERROR RECOVERY WAS INHIBITED:


HRDERR:	MOVX T1,IS.ERR!IS.NRT	;INDICATE HARD ERROR WITH NO RETRIES
	IORM T1,IRBSTS(P4)	;PUT IN IORB
	MOVX T1,UE.HRD		;GET HARD ERROR FLAG
	IORM T1,UDBERR(P3)	;REMEMBER IN ERROR WORD
	MOVEI T1,T1		;PROTECT IN CASE OF NO MATCH
	LDB T2,IRYFCN		;GET FUNCTION FROM IORB
	CAIE T2,IRFRVC		;READ VALIDITY?
	CAIN T2,IRFRED		;READ?
	MOVEI T1,UDBHRE(P3)	;YES, POINT TO HARD READ COUNTER
	CAIE T2,IRFWVC		;WRITE VALIDITY CHECK?
	CAIN T2,IRFWRT		;WRITE?
	MOVEI T1,UDBHWE(P3)	;YES, POINT TO HARD WRITE COUNTER
	TXNE Q2,US.POS		;POSITIONING?
	MOVEI T1,UDBHPE(P3)	;YES, POINT TO HARD POSITIONING COUNTER
	AOS (T1)		;INCREMENT COUNTER
	CALL RDRFIN		;READ FINAL REGISTERS
	RETSKP			;SKIP RETURN TO INDICATE ALL DONE
;HERE TO RECALIBRATE A DRIVE AFTER MANY UNSUCCESSFUL OPERATIONS:



RECAL:	MOVEI T1,RCLNDX		;GET INDEX FOR RECALIBRATION
	HRRM T1,UDBERR(P3)	;REMEMBER FUNCTION FOR ERROR RECOVERY
	CALL SETIO		;MARK THE IORB ACTIVE
	HRRZ T1,UDBERR(P3)	;GET FUNCTION BACK
	LOAD Q1,TBFNC,(T1)	;TRANSLATE TO HARDWARE FUNCTION
	MOVE Q2,K.DXAD(P2)	;SET UP DX20 UNIT NUMBER
	CALL DX2CON		;CONNECT TO THE DRIVE
	 JRST HRDERR		;FAILED, CALL THE ERROR HARD
	CALL DOPOS		;START THE OPERATION
	 JFCL			;CHSTRT ALWAYS SKIPS
	RET			;DONE



;HERE ON THE ATTENTION INTERRUPT AFTER THE RECAL IS INITIATED.  SEE
;IF THE OPERATION IS TOTALLY FINISHED.  IF NOT, WE HAVE TO WAIT FOR
;THE ASYCHRONOUS STATUS TO APPEAR.



RTYRCL:	TXNE Q3,ES.DVE!ES.UNC!ES.UNE	;IS OPERATION COMPLETE?
	JRST RCLFIN		;YES, SKIP ONWARD
	MOVEI T1,RC2NDX		;NO, GET FUNCTION CODE READY
	HRRM T1,UDBERR(P3)	;CHANGE DISPATCH FOR NEXT INTERRUPT
	SETZM P4		;WANT TO WAIT FOR ASYCHRONOUS STATUS
	RET			;RETURN
;HERE WHEN DEVICE END IS UP, EITHER FROM THE INITIAL ATTENTION
;INTERRUPT, OR ON THE ASYCHRONOUS STATUS INTERRUPT.  GO BACK
;AND RETRY THE OPERATION WHICH FAILED.




RCLFIN:	MOVX T1,US.POS		;SEE IF ORIGINAL OPERATION WAS A SEARCH
	TDNN T1,UDBSTS(P3)	;WAS IT?
	JRST RETRY		;NO, THEN GO RESTART A TRANSFER OPERATION
	JRST HRDERR		;YES, CALL IT A HARD ERROR THEN





;HERE TO RETRY A TRANSFER OPERATION:


RETRY:	HRRZ Q1,UDBSTS(P3)	;GET ERROR BLOCK IF PRESENT
	LDB T1,IRYFCN		;GET ORIGINAL OPERATION
	HRRM T1,UDBERR(P3)	;REMEMBER FUNCTION FOR ERROR RECOVERY
	CALL SETIO		;MARK THE IORB ACTIVE
	HRRZ T1,CDBDSP(P1)	;GET CHANNEL DISPATCH
	CALL CDSSIO(T1)		;TRY TO RESTART THE I/O
	 JRST HRDERR		;FAILED, CALL IT HARD ERROR
	SETZM P4		;WANT TO DISMISS THIS INTERRUPT
	RET			;DONE
	SUBTTL	SUBROUTINES TO LOAD UP THE SYSERR BLOCKS



;RDRINI - ROUTINE CALLED ON FIRST ERROR TO LOAD UP THE SYSERR BLOCK.
;THE MASSBUSS REGISTERS AND DX20 SENSE BYTES HAVE ALREADY BEEN SAVED
;IN THE KDB.  THIS ROUTINE COPIES THEM INTO THE SYSERR BLOCK AS THE
;INITIAL STATUS FOR THE ERROR.



RDRINI:	JUMPE Q1,R		;CAN'T DO THIS IF NO ERROR BLOCK
	MOVE T1,Q1		;COPY ADDRESS OF BLOCK
	MOVE T2,[-NEDAT,,EDAT]	;SET UP POINTER TO CONTROL TABLE
	CALL SEBCPY		;COPY THE DATA
	 JFCL			;FAILURE
	CALL PHYBLK		;GET PHYSICAL ADDRESS
	MOVEM T2,SEBDAT+MB%LOC(Q1)	;SAVE IT
	MOVE T1,[REGNUM,,DX%MBR]	;GET COUNT AND OFFSET READY
	MOVEM T1,SEBDAT+DX%MBI(Q1)	;SAVE POINTER TO MASSBUS REGISTERS
	MOVE T1,[<<SNSNUM+3>/4>,,DX%ESR]	;GET OTHER COUNT READY TOO
	MOVEM T1,SEBDAT+DX%ESI(Q1)	;SAVE POINTER TO SENSE BYTE REGISTERS
	MOVEI T1,SEBDAT+DX%MBR(Q1)	;POINT TO STORAGE AREA FOR REGISTERS
	MOVE T2,T1		;COPY ADDRESS
	HRLI T1,K.DREG(P2)	;INSERT SOURCE ADDRESS FOR BLT
	BLT T1,REGNUM+<<SNSNUM+3>/4>-1(T2)	;MOVE TO SYSERR BLOCK
	RET			;DONE




;TABLE OF DATA TO COPY AT INITIAL ERROR RECOVERY TIME:


EDAT:	SEBPTR MB%CNI,SBTWD,K.DCNI(P2)	;CONI
	SEBPTR MB%D1I,SBTWD,K.DCS1(P2)	;TCR
	SEBPTR MB%D2I,SBTWD,K.DDBF(P2)	;BAR/DBF
	SEBPTR MB%CS0,SBTWD,CDBCS0(P1)	;CHANNEL STATUS 0
	SEBPTR MB%CS1,SBTWD,CDBCS1(P1)	;CHANNEL STATUS 1
	SEBPTR MB%CS2,SBTWD,CDBCS2(P1)	;CHANNEL STATUS 2
	SEBPTR MB%CC1,SBTWD,CDBCC1(P1)	;CCW 1
	SEBPTR MB%CC2,SBTWD,CDBCC2(P1)	;CCW 2
	SEBPTR MB%ICR,SBTWD,CDBICR(P1)	;INITIAL CONTROL REGISTER

	NEDAT==.-EDAT		;NUMBER OF ENTRIES IN TABLE
;RDRFIN - ROUTINE TO STORE REGISTERS AT END OF RETRY.  CALLED AT THE END
;OF ERROR RECOVERY TO COPY THE FINAL MASSBUSS REGISTER VALUES INTO THE
;SYSERR BLOCK ALONGSIDE THE INITIAL REGISTERS.  THE FINAL REGISTERS HAVE
;ALREADY BEEN READ INTO THE KDB.




RDRFIN:	HRRZ Q1,UDBERP(P3)	;GET ERROR BLOCK AGAIN IF ANY
	JUMPE Q1,R		;DONE IF NO BLOCK
	MOVEI T1,K.DREG(P2)	;POINT TO REGISTERS TO SAVE
	HRLI T1,-REGNUM		;INSERT COUNTER FOR LOOP
	MOVEI T2,SEBDAT+DX%MBR(Q1)	;POINT TO STORAGE IN SYSERR BLOCK
RDRFIL:	MOVE T3,(T1)		;GET REGISTER
	HRLM T3,(T2)		;SAVE AWAY ALONGSIDE INITIAL VALUE
	ADDI T2,1		;INCREMENT TO NEXT ADDRESS
	AOBJN T1,RDRFIL		;LOOP OVER ALL REGISTERS
	MOVE T1,K.DCNI(P2)	;GET FINAL CONI
	MOVEM T1,SEBDAT+MB%CIF(Q1)	;SAVE IN SYSERR BLOCK
	MOVE T1,K.DCS1(P2)	;GET FINAL TCR
	MOVEM T1,SEBDAT+MB%D1F(Q1)	;SAVE
	MOVE T1,K.DDBF(P2)	;GET FINAL BAR
	MOVEM T1,SEBDAT+MB%D2F(Q1)	;SAVE IT TOO
	RET			;DONE
	SUBTTL	ROUTINE TO CHECK FOR DUAL PORTING




;THIS ROUTINE IS CALLED TO SEARCH ALL CURRENTLY BUILT UDBS LOOKING FOR
;THE OTHER END OF A DUAL PORTED DRIVE.  (THIS ASSUMES THAT NO UDB IS YET
;BUILT FOR THIS SIDE OF THE DUAL PORTED DRIVE).
;CALL:
;  T1/	RP20 UNIT NUMBER
;RETURNS:
;  +1:	UNIT IS NOT YET KNOWN TO BE DUAL PORTED
;  +2:	UNIT DEFINITELY DUAL PORTED
;	  T1/  UDB OF THE UNIT
;
;NOTE:  THERE IS NO WAY TO ASK THE HARDWARE WHETHER OR NOT AN RP20 SPINDLE
;IS DUAL PORTED.  THEREFORE THE MONITOR DEPENDS ON THE FOLLOWING CONVENTION
;BASED ON THE DRIVE NUMBER TO MAKE ITS DECISIONS (WHICH SHOULD BE ENFORCED
;BY FIELD SERVICE!):
;
;  DRIVE NUMBER LESS THAN 20:  DRIVE IS SINGLE PORTED.
;  OTHERWISE:  IF TWO DIFFERENT COMBINATIONS OF RH20 AND DX20 EXIST
;   SUCH THAT THE SAME DRIVE NUMBER EXISTS ON EACH COMBINATION, THEN
;   THOSE TWO COMBINATIONS ARE THE TWO PORTS FOR THAT DRIVE.



CKDUAL:	CAIGE T1,DUADRV		;IS DRIVE NUMBER IN RANGE OF DUAL PORT DRIVES?
	RET			;NO, SAY DRIVE ISN'T DUAL PORTED
	SAVEPQ			;PRESERVE REGISTERS
	HRRZ Q1,T1		;SAVE DRIVE NUMBER AND SAY NO UDB FOUND YET
	MOVSI Q2,-CHNN		;GET READY FOR SEARCH OF ALL CHANNELS

CKDUA2:	SKIPN P1,CHNTAB(Q2)	;THIS CHANNEL EXIST?
	JRST CKDUA3		;NO, GO LOOK AT NEXT ONE
	CALL DGUMAP		;LOOP OVER ALL UNITS ON THE CHANNEL
	 CALL CKDUAU		;INSTRUCTION TO XCT FOR EACH UNIT
	TLNN Q1,-1		;CHECKED ALL UNITS ON CHANNEL, FIND THE UNIT?
	JRST CKDUA3		;NOPE, LOOK AT NEXT CHANNEL
	HLRZ T1,Q1		;YES, SET UP UDB ADDRESS
	RETSKP			;AND RETURN SAYING DRIVE IS DUAL PORTED

CKDUA3:	AOBJN Q2,CKDUA2		;LOOP OVER ALL CHANNELS
	RET			;DRIVE NOT FOUND, SO ISN'T DUAL PORTED YET
;SUBROUTINE CALLED BY DGUMAP TO CHECK EACH UDB.  IF THIS UDB IS FOR THE
;DRIVE WE ARE SEARCHING FOR, THE UDB ADDRESS IS SAVED IN LEFT HALF OF Q1.



CKDUAU:	LOAD T1,USTYP,(P3)	;GET UNIT TYPE
	CAIE T1,.UTP20		;IS THIS AN RP20?
	RET			;NO, IGNORE IT
	HRRZ T1,UDBSLV(P3)	;YES, GET SLAVE NUMBER
	CAIN T1,(Q1)		;IS THIS THE ONE WE ARE LOOKING FOR?
	HRL Q1,P3		;YES, REMEMBER UDB ADDRESS
	RET			;DONE
	SUBTTL	MISC ROUTINES




;DRVSRC - FIND THE UDB GIVEN THE DRIVE NUMBER OF A DRIVE.
;GIVEN THE 8-BIT DRIVE NUMBER, THIS ROUTINE RETURNS THE UDB FOR
;THAT DRIVE.  INDEXING INTO K.DUDB WILL NOT WORK SINCE A TABLE
;256 ENTRIES LONG WOULD BE NEEDED.
;  T4/	DRIVE NUMBER, P2/  KDB
;RETURNS:
;  +1:	NO UDB FOUND WITH THIS DRIVE NUMBER
;  +2:	P3/  ADDRESS OF THE FOUND UDB
;DESTROYS T1.




DRVSRC:	MOVSI T1,-NUMDRV	;BUILD AOBJN POINTER
	ADDI T1,K.DUDB(P2)	;POINT TO UDB TABLE
	SKIPE P3,(T1)		;ANY UDB THERE?
	CAME T4,UDBSLV(P3)	;YES, IS IT THIS UNIT?
	AOBJN T1,.-2		;NO, LOOK MORE
	JUMPL T1,RSKP		;GOOD RETURN IF FOUND IT
	RET			;ERROR RETURN




;HERE TO SEE IF A PARTICULAR UNIT EXISTS ON THIS DX20.
;  Q2/	UNIT TO CHECK
;RETURNS:
;  +1:	T1/ 0	UNIT IS ILLEGAL
;	T1/ -1	NONEXISTENT UNIT
;  +2:	UNIT EXISTS


DX2EXT:	SKIPL T4,Q2		;MOVE TO RIGHT AC AND CHECK LEGALITY
	CAIL T4,MAXDRV
	 JRST RFALSE		;UNIT NUMBER IS ILLEGAL
	CALL DRVSRC		;SEE IF THIS UNIT IS EXISTENT
	 JRST RTRUE		;NO
	RETSKP			;YES, SKIP RETURN
;DX2CNV - ROUTINE TO CONVERT A SECTOR NUMBER INTO CYLINDER NUMBER AND
;REMAINDER.  CALLED FROM PHYSIO.  CALL:
;  P3/	UDB
;  P4/	IORB
;RETURNS:
;  +1:	ALWAYS, WITH:
;	  T2/	CYLINDER NUMBER (UDBPS1)
;	  T3/	SECTOR WITHIN CYLINDER (UDBPS2)



DX2CNV:	CALL PHYBLK		;GET THE DISK ADDRESS INTO T2
	MOVE T3,UDBSIZ(P3)	;THEN GET POINTER TO DISK SIZE TABLE
	IDIV T2,SECCYL(T3)	;DIVIDE BY SECTORS/CYLINDER TO GET RESULTS
	RET			;DONE





;GTHWSC - ROUTINE TO CONVERT A SECTOR WITHIN A CYLINDER INTO THE FORMAT
;NEEDED FOR SETTING UP THE .DXREC REGISTER (HEAD AND RECORD NUMBERS,
;WITH THE PROPER REGISTER NUMBER).  CALL:
;  T3/	SECTOR IN CYLINDER
;RETURNS:
;  +1:	ALWAYS,	T1/  .DXREC DATA READY FOR USE
;PRESERVES T2.



GTHWSC:	MOVE T4,UDBSIZ(P3)	;GET ADDRESS OF SIZE TABLE
	IDIV T3,SECSRF(T4)	;SPLIT INTO SURFACE AND RECORD VALUES
	MOVX T1,.DXREC		;GET REGISTER NUMBER
	STOR T3,RCHED,T1	;INSERT HEAD NUMBER (SURFACE NUMBER)
	STOR T4,RCREC,T1	;AND RECORD WITHIN SURFACE
	RET			;DONE
;DX2CCK - CALLED ONCE A MINUTE FROM PHYCHK TO SEE IF THE DX20
;HAS HALTED, AND IF SO TO TRY AND RESTART IT.
;ARGUMENTS:
;  P1/	CDB
;  P2/	KDB
;  P3/	UDB (ANY ONE WILL DO)
;RETURNS:
;  +1:	ALWAYS
;TRASHES Q2



DX2CCK:	MOVE Q2,K.DXAD(P2)	;GET CONTROLLER NUMBER
	CALL HLTCHK		;IS MICROCODE STILL HAPPY?
	 CALL CHKMIC		;OR IS IT REAL SICK?
	  RET			;YES, NOTHING TO DO
	AOS T1,K.STCT(P2)	;INCREMENT RESTART COUNTER
	CAILE T1,STMAX		;TOO MANY UPS AND DOWNS?
	RET			;YES, LET DX20 REST IN PEACE THEN
	MOVEI T1,STADDR		;GET START ADDRESS
	CALL DXSTRT		;RESTART THE MICROCODE
	MOVX T1,KS.HLT		;GET HALT FLAG
	ANDCAM T1,KDBSTS(P2)	;SAY MICROCODE IS RUNNING AGAIN
	RET			;AND RETURN




;DX2HNG - ROUTINE CALLED ON A HUNG IORB.  FORCE THE DX20 TO RESTART.
;CALL:
;  P1/  CDB
;  P2/  KDB
;RETURNS:
;  +1:	ALWAYS



DX2HNG:	MOVE T1,KDBSTS(P2)	;GET STATUS
	TXNE T1,KS.HLT		;DID DX20 STOP RUNNING?
	RET			;YES, LEAVE IT ALONE THEN
	MOVE Q2,K.DXAD(P2)	;SET UP DX20 NUMBER
	MOVEI T1,STADDR		;GET START ADDRESS
	CALL DXSTRT		;CLEAR THE DX20 AND RESTART IT
	RET			;DONE
;DX2PCK - ROUTINE TO SKIP IF POSITIONING IS NEEDED.  CALLED FROM PHYSIO.
;CALL:
;  P3/	UDB
;  P4/	IORB
;RETURNS:
;  +1:	POSITIONING NOT NEEDED, SO APPEND REQUEST TO TWQ
;  +2:	POSITIONING NECESSARY, APPEND TO PWQ




DX2PCK:	MOVX T1,US.POS		;GET POSITIONING FLAG READY
	TDNE T1,UDBSTS(P3)	;UNIT ALREADY POSITIONING?
	RETSKP			;YES, THEN NEED TO POSITION THIS TOO
	CALL DX2CNV		;RETURN CYLINDER OF THIS REQUEST
	CAME T2,UDBPS1(P3)	;SAME AS CURRENT ONE?
	RETSKP			;NO, POSITIONING NECESSARY
	RET			;YES, NO POSITIONING NEEDED
	SUBTTL	SUBROUTINES FOR MANIPULATING THE DX20




;CHKMIC - CHECKS THE MICROCODE IN DX20, TO SEE IF IT IS THE RIGHT ONE
;BY LOOKING INTO CRAM LOCATIONS 7 AND 10.  CRAM LOCATION 7 MUST MATCH THE
;DRIVE TYPE REGISTER, AND CRAM LOCATION 10 MUST CONTAIN THE VALUE 220.
;(VALUE 220 IS FOR TOPS-20 FORMAT DISKS, VALUE 210 IS FOR TOPS-10 FORMAT)
;THIS ROUTINE LEAVES THE MICROCODE HALTED.
;CALL:
;  P1/	CDB
;  Q2/	DX20 UNIT NUMBER
;RETURNS:
;  +1:	MICROCODE IS BAD
;  +2:	MICROCODE IS VALID



CHKMIC:	MOVX T2,.DXMAI!MR.RES	;SET UP BIT AND REGISTER NUMBER
	CALL WTREG3		;RESET THE DX20
	MOVX T2,.DXDG1!DG.UIR!DG.PCS!DG.PCI+7	;GET FLAGS AND PC VALUE OF 7
	CALL WTREG3		;SET PC AND ENABLE MICROSTORE READING
	RDRG .DXDG0		;READ IR REG. SO TO GET LOCATION 7 OF CRAM
	PUSH P,T1		;SAVE IT
	RDRG .DXTYP		;GET DRIVE TYPE REG.
	POP P,T2		;RESTORE LOCATION 7 OF CRAM
	CAME T1,T2		;DRIVE TYPE MATCH?
	RET			;NO, ERROR RETURN
	RDRG .DXDG7		;NOW CHECK FOR PARITY ERRORS
	TXNE T1,DG.IRP		;IRPER=1B22
	RET			;ERROR
	RDRG .DXDG0		;READ AGAIN TO GET LOCATION 10 (AUTO-INCREMENTS)
	CAIE T1,220		;CONTAIN PROPER CONSTANT?
	RET			;NO, ERROR
	RDRG .DXDG7		;CHECK IR PARITY ERROR AGAIN
	TXNE T1,DG.IRP		;IRPER=1B22
	RET			;ERROR
	RETSKP			;GOOD RETURN
;HLTCHK - SEES IF THE MICROCODE IS STILL RUNNING IN THE DX20.
; P1/	CDB
; P2/	KDB
; P3/	UDB
; Q2/	DX20 UNIT NUMBER
;RETURNS:
;  +1:	MICROCODE HALTED, BUGINF PRINTED IF FIRST TIME NOTICED
;  +2:	MICROCODE IS RUNNING FINE
;
;PRESERVES T4.



HLTCHK:	RDRG .DXSTS		;READ THE STATUS REGISTER
	MOVX T2,KS.HLT		;GET HALTED FLAG READY
	TXNN T1,ST.RUN		;IS IT RUNNING?
	JRST DX2STP		;NO IT QUIT ON US
	ANDCAM T2,KDBSTS(P2)	;NOT HALTED, REMEMBER THAT
	RETSKP			;GOOD RETURN


DX2STP:	TDNE T2,KDBSTS(P2)	;ALREADY KNEW THAT IT WAS STOPPED?
	RET			;YES, JUST RETURN
	IORM T2,KDBSTS(P2)	;NOW WE KNOW
	PUSH P,T4		;SAVE T4
	PUSH P,T1		;SAVE STATUS
	RDRG .DXERR		;READ ERROR REGISTER
	HRLZ T4,T1		;SAVE ERROR REGISTER
	RDRG .DXES0		;FINALLY GET POSSIBLE ERROR REASON
	HRR T4,T1		;SAVE IT
	CALL FNDCKU		;SET UP CHANNEL AND CONTROLLER NUMBERS
	POP P,T3		;RESTORE STATUS REGISTER
	BUG(DXBHLT,<<T1,CHAN>,<T2,DX20>,<T3,REG1>,<T4,2AND26>>)	;COMPLAIN
	POP P,T4		;RESTORE AC
	RET			;ERROR RETURN
;ROUTINE TO START THE DX20 MICROCODE.  CALL:
;  T1/	PC TO START AT
;  P1/	CDB
;  P2/	KDB
;  Q2/  UNIT NUMBER OF DX20
;RETURNS:
;  +1:	ALWAYS



DXSTRT:	PUSH P,T1		;SAVE THE PC
	MOVX T2,.DXMAI!MR.RES	;GET MAINTAINANCE REGISTER AND FLAG
	CALL WTREG3		;STOP AND RESET THE DX20
	POP P,T2		;RESTORE PC
	ANDX T2,DG.PC		;MAKE SURE NOT TOO LARGE
	TXO T2,.DXDG1!DG.PCS!DG.PCI!DG.UIR	;SET REGISTER AND FLAGS
	CALL WTREG3		;LOAD STARTING ADDRESS INTO DX20
	MOVX T2,.DXMAI!MR.ST	;GET START FLAG
	CALLRET WTREG3		;START IT UP





;ROUTINE TO READ THE VERSION OF THE DX20 MICROCODE.  THIS IS STORED IN
;LOCATION 0 OF THE CRAM.  THIS ROUTINE STOPS THE DX20 MICROPROCESSOR.
;CALL:
;  P1/	CDB
;  Q2/	DX20 UNIT NUMBER
;RETURNS:
;  +1:	ALWAYS
;	  T1/	CONTENTS OF LOCATION 0 OF THE CRAM



GETVER:	MOVX T2,.DXMAI!MR.RES	;GET MAINTAINANCE REGISTER AND FLAG
	CALL WTREG3		;STOP AND RESET THE DX20
	MOVX T2,.DXDG1!DG.PCS!DG.UIR	;WANT TO CHANGE THE PC
	CALL WTREG3		;SET THE PC TO ZERO
	RDRG .DXDG0		;FINALLY READ IR REGISTER TO GET VALUE
	RET			;DONE
;GTSNS - DOES SENSE OPERATION FOR A DRIVE AND RETURNS SENSE BYTES.
;THIS ROUTINE RETURNS SENSE BYTES 0-3 IN AC T1 FOR A DRIVE.  CALL:
;  T4/	RP20 UNIT NUMBER
;  P1/	CDB
;  Q2/	DX20 NUMBER
;RETURNS:
;  +1:	FAILED TO READ THEM, BUGCHK GIVEN
;  +2:	SUCCESSFUL RETURN, BYTES IN T1




GTSNS:	CALL HLTCHK		;SEE IF MICROCODE IS STILL ALIVE
	 RET			;NO, FAIL
	MOVX T2,.DXDRV		;GET DRIVE NUMBER REGISTER READY
	STOR T4,DRVNM,T2	;INSERT DRIVE NUMBER
	CALL WTREG3		;SET IT
	MOVX T2,.DXFLG+<FLD(XS.SEN,DFCOD)>	;GET SUB-FUNCTION CODE
	CALL WTREG3		;PLACE INTO ARGUMENT REGISTER
	MOVX T2,.DXCTL+<FLD(XF.SEN,DXFNC)>	;GET FUNCTION FOR SENSE
	CALL WTREG3		;TELL DX20 TO GET THE BYTES NOW
	CALL ATTNWT		;WAIT FOR COMPLETION
	 JRST [	BUG(DXBFGS)	;FAILED, COMPLAIN
		RET]		;ERROR RETURN
	JRST RDSNS		;OK, JOIN COMMON CODE
;GETEXS - READ IN FOUR BYTES OF EXTENDED STATUS.
;THE SENSE BYTES ARE ASSUMED TO BE VALID FOR THIS CALL (EITHER AN
;ERROR JUST OCCURED ON A DRIVE AND SO THE DX20 AUTOMATICALLY READ
;THE NEW BYTES, OR ELSE A SENSE OPERATION WAS DONE EXPLICITLY).
;  T1/	INDEX FOR EXTENDED STATUS
;  P1/	CDB
;  Q2/  DX20 UNIT NUMBER
;RETURNS:
;  +1:	FAILED, BUGCHK DONE
;  +2:	SUCCESSFUL, 4 SENSE BYTES IN T1



GETEXS:	MOVX T2,.DXEND!ES.SUI	;GET BIT TO REQUEST NEW SENSE BYTES
	STOR T1,ESIDX,T2	;PLACE INDEX INTO WORD
	CALL WTREG3		;TELL DX20 TO GET THE BYTES NOW

RDSNS:	MOVX T4,100		;GET COUNTER TO TIME OUT COMPLETION
RDSNS1:	RDRG .DXEND		;READ BACK REGISTER
	TXNN T1,ES.SUI		;DX20 DONE READING SENSE BYTES YET?
	JRST RDSNS2		;YES, GO GET THEM
	SOJG T4,RDSNS1		;NO, KEEP LOOPING
	BUG(DXBFUS)		;TIMED OUT, COMPLAIN
	RET			;AND GIVE ERROR RETURN

RDSNS2:	RDRG .DXES0		;READ FIRST TWO SENSE BYTES
	MOVE T4,T1		;SAVE THEM
	RDRG .DXES1		;READ LAST TWO SENSE BYTES
	HRL T1,T4		;GET BACK FIRST TWO
	RETSKP			;DONE
;REDALL - ROUTINE CALLED ON ERRORS TO READ ALL REGISTERS AND EXTENDED
;STATUS INTO THE KDB, AND CLEAR THE ERROR.  CALL:
;  P1/	CDB
;  P2/	KDB
;  P3/	UDB
;RETURNS:  +1:	ALWAYS, WITH REGISTERS READ AND ERROR CLEARED




REDALL:	CALL REGALL		;FIRST READ THE DRIVE REGISTERS
	MOVX T2,.DXERR		;SET UP ERROR REGISTER NUMBER
	CALL WTREG		;CLEAR THE DX20 ERROR REGISTER
	CALL EXSALL		;THEN READ IN EXTENDED STATUS
	RET			;DONE
;REGALL - ROUTINE TO READ ALL RELEVANT DX20 REGISTERS INTO THE K.DREG
;AREA OF THE KDB.  ALSO SAVES THE CHANNEL CONI AND DATAI WORDS.  CALLED
;TO STORE INFORMATION ON AN ERROR.  CALL:
;  P1/	CDB
;  P2/	KDB
;  P3/	UDB
;RETURNS:  +1:	ALWAYS



REGALL:	SAVEQ			;SAVE REGISTERS
	MOVEI Q1,K.DREG(P2)	;SET UP POINTER TO STORAGE AREA
	MOVX Q2,SAVBIT		;GET BITS SAYING WHICH REGISTERS TO SAVE
	SETZ Q3,		;SET UP FIRST REGISTER NUMBER

REGALP:	JUMPGE Q2,REGALN	;SKIP IF NOT SAVING THIS REGISTER
	HLLZ T2,Q3		;GET REGISTER NUMBER INTO RIGHT AC
	CAMN T2,[.DXDG0]	;ABOUT TO READ REGISTER 30?
	TDZA T1,T1		;YES, READ IT LATER
	CALL RDREG		;READ IT
	MOVEM T1,(Q1)		;SAVE IN THE KDB
	ADDI Q1,1		;ADVANCE TO NEXT STORAGE LOCATION
REGALN:	ADD Q3,[EXP 1B5]	;INCREMENT TO NEXT REGISTER NUMBER
	LSH Q2,1		;SHIFT BIT REGISTER OVER
	JUMPN Q2,REGALP		;LOOP IF STILL MORE TO READ
	CALL ERRCNI		;READ CONI AND DATAI INFO FOR CHANNEL
	MOVEM T1,K.DCNI(P2)	;SAVE CONI
	MOVEM T2,K.DCS1(P2)	;AND PCTR
	MOVEM T3,K.DDBF(P2)	;AND PBAR
	DMOVE T1,K.DREG+REG.0(P2) ;GET CONTROL AND STATUS REGISTERS
	TXNN T1,DX.GO		;IS GO STILL UP?
	TXNE T2,ST.RUN		;NO, IS THE MICROPROCESSOR STILL RUNNING
	RET			;YES, CAN'T READ REGISTER 30 THEN
	MOVX T2,.DXDG1		;SETUP TO READ THE PC REGISTER
	CALL RDREG		;READ IT
	TXZ T1,DG.UIR		;TURN OFF IR ENABLE
	MOVX T2,.DXDG1		;SET TO WRITE PC REGISTER
	IOR T2,T1		;WITH ALL THE BITS BUT IR ENABLE
	CALL WTREG		;WRITE IT BACK
	MOVX T2,.DXDG0		;SETUP TO READ THE IR
	CALL RDREG		;READ IT NOW
	MOVEM T1,K.DREG+REG.30(P2) ;STORE IT WHERE IT BELONGS
	RET			;AND FINALLY RETURN
;EXSALL - ROUTINE TO READ IN ALL EXTENDED STATUS BYTES INTO THE K.DEXS
;AREA OF THE KDB.  CALLED AFTER AN ERROR OCCURS.  CALL:
;  P1/	CDB
;  P2/	KDB
;  P3/	UDB
;RETURNS:  +1:	ALWAYS




EXSALL:	SAVEQ			;SAVE REGISTERS
	MOVSI Q1,-<SNSNUM+3>/4	;SET UP AN AOBJN POINTER FOR SENSE BYTES
	MOVE Q2,K.DXAD(P2)	;GET DX20 UNIT NUMBER
	MOVEI Q3,K.DEXS(P2)	;POINT TO AREA IN KDB TO STORE THEM

EXSALP:	HRRZ T1,Q1		;GET CURRENT INDEX FOR EXTENDED STATUS
	CALL GETEXS		;READ IN THE NEXT FOUR SENSE BYTES
	 JRST EXSALF		;FAILED, GO CLEAR THE TABLE
	MOVEM T1,(Q3)		;SAVE THESE FOUR BYTES
	ADDI Q3,1		;INCREMENT TO NEXT STORAGE ADDRESS IN THE KDB
	AOBJN Q1,EXSALP		;CONTINUE IF HAVE TO GET MORE SENSE BYTES
	RET			;DONE

EXSALF:	HLL Q3,Q1		;CAN'T GET THE INFO, COPY REMAINING COUNT
	SETZM (Q3)		;CLEAR THIS WORD
	AOBJN Q3,.-1		;DO THE REST ALSO
	RET			;AND RETURN
;ATTNWT - ROUTINE TO WAIT FOR AN ATTENTION FROM THE DX20.
;CALLED WHEN A NO OPERATION OR SENSE IS DONE, TO WAIT UNTIL THE
;ATTENTION BIT COMES UP SAYING THE OPERATION IS COMPLETE.  THEN
;THE ATTENTION BIT IS CLEARED.  CALL:
;  P1/	CDB
;  Q2/	DX20 NUMBER
;RETURNS:
;  +1:	TIMED OUT WAITING FOR ATTENTION
;  +2:	ATTENTION SEEN AND CLEARED



ATTNWT:	PUSH P,P4		;SAVE A REGISTER
	MOVEI P4,AT.0		;GET BIT FOR UNIT 0
	LSH P4,(Q2)		;POSITION PROPERLY FOR THIS DX20
	MOVEI T4,4000		;SET UP COUNTER

ATTNW1:	RDRG .DXATN		;READ THE ATTENTION REGISTER
	TDNN T1,P4		;ATTENTION UP YET?
	SOJG T4,ATTNW1		;NO, LOOK SOME MORE
	MOVE T2,P4		;MOVE ATTENTION BIT
	POP P,P4		;RESTORE AC
	JUMPLE T4,R		;FAIL IF COUNTER REACHED ZERO
	TXO T2,.DXATN		;INSERT REGISTER NUMBER
	CALL WTREG3		;CLEAR ATTENTION
	RETSKP			;GOOD RETURN
	SUBTTL	DATA




;FUNCTION TABLE.  USED TO CONVERT PHYSIO'S FUNCTIONS TO THE ACTUAL RP20
;HARDWARE FUNCTIONS, AND TO REMEMBER DATA ABOUT EACH FUNCTION.  THE FF
;MACRO HAS THE FOLLOWING ARGUMENTS:
;
;	FF (FLAG,FUNCTION,DISPATCH,NAME)
;WHERE:
;	FLAG IS SET IF THE FUNCTION TRANSFERS DATA. LOAD WITH TBDTF
;	FUNC IS HARDWARE FUNCTION (XF.???).  LOAD WITH TBFNC
;	DISP IS ADDRESS TO PROCESS THE FUNCTION ON INTERRUPT.  LOAD WITH TBDSP
;	NAME IS USED TO DEFINE A VARIABLE FOR THIS OFFSET INTO THE TABLE



	DEFINE FF(FLAG,FUNC,DISP,NAME),<
	IFNB <NAME>,<NAME==.-RP2FNC>	;;DEFINE OFFSET
	EXP <FLD(FLAG,TBDTF)>+<FLD(FUNC,TBFNC)>+<FLD(DISP,TBDSP)>	;;DATA
>


RP2FNC:	EXP 0			;0 - ILLEGAL
	FF (1,XF.RED,RP2RED)	;1 - READ
	FF (1,XF.RDF,RP2RED)	;2 - READ FORMAT
	FF (1,XF.WRT,RP2WRT)	;3 - WRITE
	FF (1,XF.WRF,RP2WRT)	;4 - WRITE FORMAT
	FF (0,XF.SRC,HRDERR,SRCNDX)	;5 - SEEK (SEARCH, ACTUALLY)

	MXEXTF==.-RP2FNC-1	;HIGHEST EXTERNAL FUNCTION

	FF (0,XF.REC,RTYRCL,RCLNDX)	;6 - RECALIBRATE DURING ERROR RECOVERY
	FF (0,XF.REC,RCLFIN,RC2NDX)	;7 - SECOND STAGE OF RECALIBRATION

	MXFUNC==.-RP2FNC-1	;HIGHEST LEGAL FUNCTION

	EXP 0,0,0,0,0,0		;ILLEGAL FUNCTIONS

	MNRP2V==.-RP2FNC	;LOWEST LEGAL DATA VALIDITY FUNCTION

	FF (1,XF.WRT,RP2WRT)	;16 - WRITE VALIDITY CHECK
	FF (1,XF.RED,RP2RED)	;17 - READ VALIDITY CHECK
	TNXEND
	END