Google
 

Trailing-Edge - PDP-10 Archives - bb-bt99q-bb - dsxkon.x23
There is 1 other file named dsxkon.x23 in the archive. Click here to see a list.
TITLE	DSXKON - CONTROLLER ROUTINE FOR 3330/3331 DISK - V7
SUBTTL	JOSEPH A. DZIEDZIC/JAD (DEC) & JEFF GUNTER/JEG (ADP)	25-APR-89
;OLD AUTHORS - CYPHERNETICS CORPORATION  18-MAR-76


	SEARCH	F, S, DEVPRM, SAXPRM

	$RELOC
	$HIGH

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED
;  OR COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1987,1988.
;ALL RIGHTS RESERVED.
.CPYRT<1987,1988>
XP	VDSXKN,7		;VERSION NUMBER FOR GLOB AND MAP

DSXKON::ENTRY	DSXKON
	EXTERN	SAXSER		;FORCE LOAD OF SERVICE ROUTINES


;MISCELLANEY UNTIL ELSEWHERE

.UT330==0
.UT331==1
;COMPUTE SIZE OF CHANNEL PROGRAM FROM CYLINDER SIZE (342) BLOCKS
;TIMES THE SIZE OF A WRITE (4 WORDS+2 DATA=6 WORDS) + 25% (BECAUSE 1 BLOCK
;OUT OF EVERY PAGE MIGHT REQUIRE A SPLIT IOWD)
;PLUS THE OVERHEAD OF A SEEK COMMAND, A SET-SECTOR COMMAND, AND THEIR ARGS
;(2 SEEK+2DATA+2 SECTOR+1DATA=7 WORDS)+A HALT
CPAOVR==10	;SEEK,IOWD,DATA(2); SECTOR,IOWD,DATA; HALT
CPAWRT==^D342*6	;ENOUGH TO WRITE 342 BLOCKS
CPASPL==^D342/4+1	;  INCASE EVERY PAGE (IE: EVERY 4TH BLOCK) IS SPLIT
CPALEN==CPAOVR+CPAWRT+CPASPL+1	;ENOUGH FOR MAXIMUM SIZED PROGRAM+A HALT
BLKBYT==<BLKSIZ##/2>*^D9 ;SIZE OF A DISK BLOCK IN BYTES (9 PER TWO WORDS)
DSXMAX==2	;MAX NUMBER OF CONSECUTIVE ERRORS ON A SINGLE BLOK#


;BYTE POINTERS

DSYDLO:	POINT	SASDLO,T1,SANDLO	;BYTE PTR TO STORE OPCODE IN DEVLST
DSYDLC:	POINT	SASDLC,T1,SANDLC	;POINTER TO ADDRESS FIELD
DSYDLD:	POINT	SASDLD,T1,SANDLD	;POINTER TO DEVICE-ADDRESS FIELD

DEFINE SIOW(LEN,ADR),<<-^D'LEN'>B11>	;GENERATE BYTE MODE "LAST" IOWD
	SUBTTL	DEVICE-DEPENDENT DEFINITIONS

;3330 DEVICE COMMANDS

O.RCL==23		;RECALIBRATE
O.NOP==03		;NO-OP
O.SEK==07		;SEEK
O.SEC==43		;SET SECTOR
O.SIE==61		;SEARCH ID EQUAL
O.RED==06		;READ
	O.MTR==206	;MULTI-TRACK READ
O.WRT==05		;WRITE
O.SNS==04		;SENSE I/O
O.RR0==26		;READ-RECORD-0

;THE FOLLOWING ARE NOT REALLY SENSE BITS, BUT ARE SET FOR SYSERR IN ERROR STATUS

SN.CDE==1B35		;CHANNEL DETECTED ERROR (ERROR BUT NO SENSE)
SN.RTR==1B34		;RETRY REQUESTED BY CHANNEL
SN.HNR==1B33		;HUNG DISK RECOVERY - ASSUME ERROR
SN.COE==1B32		;ERROR CORRECTED WITH ECC (ACTUAL NONZERO DATA FIELD CORRECTION)
	SUBTTL	AUTOCONFIGURATION TABLES

	SUBTTL	DEFINITIONS


;PARAMETERS TO CONTROL XXKON MACRO:

DSFIX==0			;NOT A FIXED-HEAD DEVICE
DSOFS==400000			;NO OFFSET CAPABILITY
DSRDC==400000			;NO 10/11 COMPATABILITY MODE
DSUNL==400000			;NO UNLOAD CAPABILITY
DSCPY==0			;CAN TELL UNIT TYPE EVEN IF KONTROLLER BUSY
DSMX==0				;CANNOT DO MULTIPLE TRANSFERS
DSDRB==KOPDRB			;USES DISK I/O REQUEST BLOCKS
DSBMX==KOPBMX			;IS A BLOCK MULTIPLEX KONTROLLER
DSECA==KOPECA			;KONTROLLER CAN HANDLE ECC FIRST
DSERNO==0			;NO DRIVE REGISTERS TO SAVE ON ERROR
DSXELG==DPEELG##		;ERROR LOG ROUTINE IS IN COMMOD

DSXDMX==MAXUNS			;MAX DRIVES/KONTROLLER
DSXHDN==DSXDMX-1		;HIGHEST DRIVE NUMBER

;DRIVER CHARACTERISTICS
;	DSX	= DSXCNF
;	DSK	= DISK
;	TYPDS	= KONTROLLER TYPE
;	DSXDMX	= MAXIMUM DRIVES PER KONTROLLER
;	DSXHDN	= HIGHEST DRIVE NUMBER
;	MDSEC0	= SECTION FOR KDB/UDB
;	MDSEC0	= SECTION FOR DDB
DRVCHR	(DSX,DSK,0,TYPDS,DSXDMX,DSXHDN,MDSEC0,MDSEC0,<DR.MCD!DR.UCK!DR.UCU>)
	SUBTTL	KDB EXTENSIONS

	.ORG	KONUDB		;START OF 3330/3331 SPECIFIC DATA
DSXUTB:!BLOCK	DSXDMX		;TABLE OF POINTERS TO UDBS
DSXEBK:!BLOCK	1		;DUMMY ERROR BLOCK
DSXBUC:!BLOCK	1		;COUNT OF BUSY UNITS RIGHT NOW
DSXBSY:!BLOCK	1		;COUNT OF ABSOLUTE BUSYS SEEN
DSXIUU:!BLOCK	1		;INTERRUPT UNKNOWN UNIT
DSXSUI:!BLOCK	1		;SPURIOUS UNIT INTERRUPTS
DSXSCH:!BLOCK	1		;LH = SA10 SUBCHANNEL IN CONO POSITION
				;RH = SA10 SUBCHANNEL NUMBER
DSXGSN:!BLOCK	1		;GLOBAL SUBCHANNEL NUMBER
DSXICW:!BLOCK	1		;BASE ADDR FOR SUBCHANNEL THIS KONTROLLER IS ON
DSXCSW:!BLOCK	1		;CHANNEL STATUS WORD ADDR FOR THIS KONTROLLER
DSXDVP:!BLOCK	1		;POINT TO DEVICE LIST FOR UNIT 0
DSXPTR:!BLOCK	1		;USED TO PICK UP UNIT DATA BLOCK ADDRESS
				; GIVEN PHYSICAL UNIT NUMBER IN T3 (@ USED)

DSXIOB:!			;BEGINNING OF I/O INSTRUCTIONS IN KDB
DSXCO2:!BLOCK	1		;CONO (T2)
DSXCI2:!BLOCK	1		;CONI T2
DSXDTI:!BLOCK	1		;DATAI T1
DSXIOE:!			;END OF I/O INSTRUCTIONS IN KDB

DSXIUM:! BLOCK	DSXDMW		;IGNORE DRIVE MASK
DSXNUM:! BLOCK	DSXDMW		;NEW DRIVE MASK

DSXKLN:!			;LENGTH OF KDB
	.ORG

;PROTOTYPE KDB
DSXKDB:	XXKON	(DS)
	SETWRD	(DSXCO2,<CONO  000,(T2)>)
	SETWRD	(DSXCI2,<CONI  000,T2>)
	SETWRD	(DSXDTI,<DATAI 000,T1>)
	SETWRD	(DSXPTR,<DSXUTB(T3)>)

	KDBEND
	SUBTTL	UDB EXTENSIONS

	.ORG	UNIEBK		;FOLLOW THE JUNK
UNIRCL:!BLOCK	1		;RECALIBRATE PROTOTYPE
UNISEK:!BLOCK	1		;SEEK-CMD PROTOTYPE
UNINOP:!BLOCK	1		;PROTOTYPE NOP COMES AFTER SEEK
	BLOCK	1		;...HALT AFTER NOP
UNISS:! BLOCK	1		;SET-SECTOR-CMD PROTOTYPE
UNISIE:!BLOCK	1		;SEARCH ID EQUALS PROTOTYPE
UNISNC:!BLOCK	1		;SENSE-I/O COMMAND PROTOTYPE
	BLOCK	1		;SENSE-I/O IOWD
	BLOCK	1		;SENSE-I/O HALT
UNIRWC:!BLOCK	1		;PROTOTYPE DVW FOR CHANNEL PROGRAMS
UNIRR0:!BLOCK	1		;PROTOTYPE READ-RECORD-0
UNISND:!BLOCK	SNSLEN		;SENSE DATA
UNIR0D:!BLOCK	2		;R0 DATA, 8 BYTES

UNIFNC:!BLOCK	1		;(RH) FILSER FUNCTION LAST ATTEMPTED
  UNPSNS==1			;(LH) BIT SET IF DOING SENSE I/O
  UNPBSY==2			;     BIT SET IF GOT BUSY RESPONSE FOR UNIT
  UNPHNS==4			;     BIT SET IF SET GO FLAG AFTER HUNG DISK
  UNPONP==20			;     BIT SET IF POSITIONING FOR UNIT COMING ON-LINE
  UNPCPY==40			;     BIT SET IF IN CPY ROUTINE FOR ATT CMD

  UNPCPD==400000		;(SIGN) BIT SET IF CPY ROUTINE DONE

UNIDRB:!BLOCK	1		;ADDRESS OF DRB FOR THIS UNIT
UNIBBK:!BLOCK	1		;"BAD BLOK" - LAST LBN COMPUTED BY DSXSNS
UNIRTC:!BLOCK	1		;NUMBER OF RETRIES INVESTED IN THIS OPERATION
UNICSW:!BLOCK	1		;SAVED COPY OF SACSW FOR DEBUGGING
UNIECW:!BLOCK	1		;TEMP COPY OF SATCA FROM LAST INTERRUPT
UNITCA:!BLOCK	1		;SAVED COPY OF SATCA FROM BEFORE SENSE COMMAND
UNICBN:!BLOCK	1		;CURRENT BLOCK NUMBER (USED WHILE BUILDING PROGRAM)
UNILPC:!BLOCK	1		;PHYSICAL PC OF FAILING XFER CMD
UNIDVL:!BLOCK	1		;SAVED COPY OF DEVICE LIST ENTRY WHEN SET UP
UNIDVP:!BLOCK	1		;POINTER TO DEVICE LIST ENTRY FOR THIS UNIT
UNIPCP:!BLOCK	1		;PHYSICAL ADDRESS OF CHANNEL PROGRAM
UNITCP:!BLOCK	1		;POINTER TO NEXT WORD IN CHANNEL PROGRAM
UNISDA:!BLOCK	1		;PHYSICAL ADDRESS OF SEARCH-ID DATA BUFFER
UNISUI:!BLOCK	1		;SPURIOUS UNIT INTERRUPTS
				;THE NEXT 5 WORDS ARE USED DURING ATTACH/KONCPY CALL
UNICCF:!BLOCK	1		;"ComCon Flag" NONZERO IF ATTACH CMD IN PROGRESS
UNICAD:!BLOCK	1		;"Continue ADdress" where to go at next DSXINT time
UNIACP:!BLOCK	1		;"Attach Channel Program" pointer to chan program
UNIPRU:!BLOCK	1		;COPY OF BLKPRU DATA FOR THIS DRIVE
UNITYP:!BLOCK	1		;FLAGS,,UNIT TYPE FOR ATTACH
DSXULN:!
	.ORG
;PROTOTYPE DEVICE-COMMANDS KEPT IN UDB, DSXUNI FILLS IN DEVICE-ADDRESS
DSXUDB:	UDBBEG	(UNI,DSXULN)
	SETWRD	(UNIRCL,<F.XEC!F.CC!F.NMT!<O.RCL>B15>)	;RE-CALIBRATE
	SETWRD	(UNISEK,<F.XEC!F.CC!F.BYTE!<O.SEK>B15>)	;SEEK Z,Z,C,C,H
	SETWRD	(UNINOP,<F.XEC!F.CC!F.NMT!<O.NOP>B15>)	;NO-OP
	SETWRD	(UNINOP+1,BMXEND)			;HALT FOR NO-OP
	SETWRD	(UNISS, <F.XEC!F.CC!F.BYTE!<O.SEC>B15>)	;SET-SECTOR S
	SETWRD	(UNISIE,<F.XEC!F.CC!F.BYTE!<O.SIE>B15>)	;SEARCH-ID C,C,H,H,R
	SETWRD	(UNISNC,<F.XEC!F.CC!F.BYTE!<O.SNS>B15>)	;SENSE-I/O 24 BYTES DATA
	SETWRD	(UNISNC+2,BMXEND)			;END OF SENSE PROGRAM
	SETWRD	(UNIRWC,<F.XEC!F.CC!F.SLI!<O.MTR>B15>)	;MULTI-TRAK READ
	SETWRD	(UNIRR0,<F.XEC!F.CC!F.SLI!F.BYTE!<O.RR0>B15>) ;READ-RECORD-0
	UDBEND
	SUBTTL	DRIVER DISPATCH TABLE

EQUATE	(LOCAL,0,<DSXICD,DSXICL,DSXULP,DSXULB>)
EQUATE	(LOCAL,CPOPJ##,<DSXINI,DSXRLD,DSXEDL>)

	DSXATK==ATTKON##	;ATTACH DISK KONTROLLER
	DSXDTK==DETKON##	;DETACH DISK KONTROLLER
	DSXCCM==CYLCM##

DSXDSP::DRVDSP	(DSX,DSKCHN##,DSKDDB##,DDBLEN##,DSKDIA##)

;DEFAULT MONGEN'ED DEVICE TABLE

DEFMDT:	MDKL10	(7,274,0,0,<MD.KON>)	;SA10 DEVICE CODE 274
	MDKL10	(7,270,0,0,<MD.KON>)	;SA10 DEVICE CODE 270
	MDTERM				;TERMINATE TABLE

DSXCKT:	EXP	TYPDS,	0		;COMPATIBLE KONTROLLER TABLE

	SAXDSP	(DSX)
	SUBTTL	START DRIVE POSITIONING


;ROUTINE TO CAUSE THE DRIVE TO DO A RECALIBRATE OPERATION.
;CALL:
;	J/KDB ADDRESS
;	U/UDB ADDRESS
;	PUSHJ	P,DSXRCL
;RETURN CPOPJ IF ERROR
;	CPOPJ1 WITH RECAL STARTED

DSXRCL:	SETZ	T1,		;NO DRB HERE
	MOVEI	T4,OPCAL	;RE-CAL REQUEST
	PJRST	DSXGO		;START IT AND RETURN


;ROUTINE TO START A POSITION OPERATION GOING.
;CALL WITH UNIBLK(U) CONTAINING THE DESIRED BLOCK NUMBER.
;CALL:
;	J/KDB ADDRESS
;	U/UDB ADDRESS
;	PUSHJ	P,DSXPOS
;RETURN CPOPJ IF ERRORS
;	CPOPJ1 WITH POSITION STARTED

DSXPOS:	SETZ	T1,		;NO DRB
	MOVEI	T4,OPPOS	;POSITION REQUEST
	JRST	DSXGO		;FINISH REQUEST
;ROUTINE TO CHECK FOR AND DO ECC CORRECTION
;CALLED FROM FILIO WITH:
;	J/KDB ADDRESS
;	U/UDB ADDRESS
;	PUSHJ	P,DSXECC
;ALWAYS RETURNS CPOPJ (=ECC NOT POSSIBLE) BECAUSE WE NEVER SET IOECCX

DSXECC:	POPJ	P,		;WON'T EVER GET HERE
;ROUTINE TO UPDATE THE RE-START PC FOR AN ECC ERROR
XXXECC:	PUSHJ	P,YYYECC	;GO DO THE CORRECTION
	  POPJ	P,		;PASS BACK THE ERROR
XXXEC1:	AOS	UNILPC(U)	;POINT AT NEXT IOWD
	PMOVE	T2,UNILPC(U)	;GET AN IOWD
	JUMPG	T2,XXXEC1	;BRANCH IF MORE IOWDS TO SKIP
	AOS	UNILPC(U)	;STEP TO FIRST UNDONE COMMAND
	POPJ	P,		;RETURN
;ROUTINE TO DO AN ECC CORRECTION ON THE LAST BLOK XFERED
YYYECC:	PUSHJ	P,SAVE4##	;SAVE SOME ACS
	LDB	T1,[POINTR (UNISND+S07WRD(U),D07FMC)] ;GET FORMAT/MESSAGE CODE
	CAIE	T1,FMCECC	;SKIP IF ECC IS POSSIBLE AND APPLIES TO DATA
	PJRST	CPOPJ1##	;EXIT IF ECC ERROR IS NOT IN THE DATA PART
	LDB	T1,[POINTR (UNISND+S15WRD(U),D15RD1)] 	;GET 1ST BYTE OF RESTART
	LDB	P1,[POINTR (UNISND+S16WRD(U),D16RD2)] ;GET REST OF RESTART DISP
	LSH	T1,^D16		;POSITION HIGH BYTE
	IOR	P1,T1		;COMBINE TO GET RESTART-DISPLACEMENT IN P1
	CAIE	P1,BLKBYT	;SKIP IF #BYTES IS EXACTLY THAT FOR 200 WORDS
	POPJ	P,		;GIVE ERROR RETURN IF BLOCK SIZE ISN'T NORMAL
	LDB	P2,[POINTR (UNISND+S16WRD(U),D16ERD)] ;P2=ERROR DISPLACEMENT
	CAILE	P2,BLKBYT+3	;SKIP IF ERROR BYTES ARE WITHIN DATA BYTES
	JRST	CPOPJ1##	;EXIT IF ERROR IS OUTSIDE DATA ERROR - WE WIN
	LSH	P1,1		;P1=RESTART NIBBLES
	LSH	P2,1		;P2=ERROR NIBBLES
	SUB	P1,P2		;NOW P1=FORWARD NIBBLES
	MOVEI	P3,6		;6 NIBBLES = 3 BYTES = #BYTES IN ERROR PATTERN
	CAIGE	P2,6		;SKIP IF ALL PATTERN NIBBLES ARE SIGNIFICANT
	MOVE	P3,P2		;...ELSE REDUCE # OF PATTERN NIBBLES TO USE
	MOVE	P4,[POINT 4,UNISND+S20WRD(U)] ;POINT AT PATTERN NIBBLES

DSXEC1:	ILDB	P2,P4		;GET A CORRECTION NIBBLE
	SKIPE	P2		;SKIP IF NO CHANGE REQUIRED
	PUSHJ	P,FIXNIB	;GO AND FIX A NIBBLE
	JUMPL	P3,CPOPJ##	;BRANCH IF WE FOUND AN XFER'ED NIBBLE NEEDING
				;CORRECTING, BUT IT FALLS IN THE TRUNCATED AREA
	SOJG	P3,[AOJA P1,DSXEC1] ;LOOP FOR ALL NIBBLES NEEDING FIXING
	JRST	CPOPJ1##	;THEN GIVE IT GOOD RETURN
;ROUTINE APPLY ECC ERROR CORRECTION TO A SINGLE NIBBLE

;	P1/ NIBBLE NUMBER NEEDING NEXT CORRECTION
;	P2/ CORRECTION NIBBLE
;	P3/ NUMBER OF REMAINING NIBBLES NEEDING CORRECTION
;	P4/ BYTE POINTER TO PATTERN NIBBLES

FIXNIB:	PUSHJ	P,NIBSET	;SETUP T3=PHYSICAL ADDRESS, T4=NIBBLE INDEX
	  POPJ	P,		;...RETURN IF NIBBLE # NOT IN IOWD LIST (?)
	MOVE	T1,UNISND+S23WRD(U) ;GET TRUNCATION BYTE
	TDNE	T1,[D23CHT]	;SKIP IF NOT TRUNCATED BY CHANNEL
	  JRST	[SETO P3,	;***UGH - BUT PASS BACK ERROR
		POPJ P,0]	;***GIVE ERROR RETURN
	PMOVE	T1,T3		;GET WHOLE WORD
	ROT	P2,@NIBTAB(T4)	;POSITION THE CORRECTION DATA
	XOR	T1,P2		;CORRECT THE BROKEN NIBBLE
	PMOVEM	T1,T3		;RESTORE THE CORRECTED WORD
	POPJ	P,		;RETURN

NIBTAB:	EXP	^D32,^D28,^D24,^D20,^D16,^D12,^D8,^D4,^D0	;1ST WORD
	EXP	^D0,^D32,^D28,^D24,^D20,^D16,^D12,^D8,^D4	;2ND WORD
;N.B.: THE LAYOUT OF NIBBLES COMING FROM THE DISK INTO MEMORY IS AS FOLLOWS:
;
;	0/ BYTE(4)0,1,2,3,4,5,6,7,8		;1st 8 NIBBLES GO INTO 1ST WORD
;	1/ BYTE(4)10,11,12,13,14,15,16,17,9	;9th NIBBLE IS LOW 4 BITS!
;
;NOTE: THAT 1ST 8 NIBBLES GO DIRECTLY INTO FIRST 8 MEMORY NIBBLES,
;	BUT THAT 9TH NIBBLE GOES INTO LOW ORDER MEMORY NIBBLE OF NEXT WORD,
;	LEAVING THE REMAINING 8 NIBBLES TO GO LEFT JUSTIFIED INTO 2ND WORD.
;(WHY DOESN'T DISK USE "NATURAL MODE" ?)

;ROUTINE TO CALCULATE PHYSICAL ADDRESS OF A NIBBLE GIVEN:
;
;	P1/ NIBBLE NUMBER NEEDING CORRECTION
;	UNILPC(U)		;POINTER TO CMD,IOWD LIST IN 4 1/2 PACKED MODE

NIBSET:	PUSHJ	P,SAVE2##	;SAVE P1, P2
	IDIVI	P1,^D9		;COMPUTE P1=WORD#, P2=NIBBLE INDEX
	TRNE	P1,1		;SKIP IF EVEN WORD OF PAIR
	ADDI	P2,^D9		;P2=NIBBLE INDEX BASED ON A WORD PAIR, 0-17
	SETZ	T1,		;T1=# WORDS SO FAR
	MOVE	T2,UNILPC(U)	;T2=POINTER TO CMD
	ADDI	T2,1		;NOW T2=POINTER TO IOWD LIST
NIBSE1:	PMOVE	T3,T2		;GET AN IOWD
	MOVE	T4,T3		;SAVE A COPY
	TLO	T3,(1B0)	;MAKE SURE IT APPEARS -VE
	ASH	T3,-^D24	;NOW T3=-VE WORDS IN THIS IOWD
	MOVNS	T3		;NOW T3=+VE WORDS IN THIS IOWD
	SUB	P1,T3		;COMPUTE P1=# WORDS REMAINING TO BE SKIPPED
	JUMPL	P1,NIBSE2	;BRANCH IF WE FINALLY FOUND RELAVENT IOWD
	JUMPG	T4,[AOJA T2,NIBSE1] ;LOOP FOR NEXT IOWD, IF THERE IS ONE
	POPJ	P,		;GIVE ERROR RETURN IF NOT THERE

;HERE WHEN WE FIND THE RELAVENT IOWD
NIBSE2:	ADD	P1,T3		;NOW P1=# WORDS INTO THIS IOWD
	LDB	T3,[POINT 22,T4,35] ;LOAD T3=BASE ADDRESS OF THIS IOWD
	ADD	T3,P1		;COMPUTE AFFLICTED ADDRESS
	MOVE	T4,P2		;LOAD UP NIBBLE NUMBER
	JRST	CPOPJ1##	;GIVE GOOD RETURN IF WE FIND IOWD
	SUBTTL	START DATA TRANSFERS


;ROUTINES TO START A DATA TRANSFER GOING ON A DRIVE.  ENTRY POINTS
;ARE:
;	DSXRED	- READ DATA, DON'T STOP ON ERROR
;	DSXRDS	- READ DATA, STOP ON ERROR
;	DSXRDF	- READ FORMAT
;	DSXWRT	- WRITE DATA, DON'T STOP ON ERROR
;	DSXWTS	- WRITE DATA, STOP ON ERROR
;	DSXWTF	- WRITE FORMAT
;CALL:
;	J/KDB ADDRESS
;	U/UDB ADDRESS
;	T1/ DRB ADDRESS
;	P1/CHANNEL DATA BLOCK ADDRESS
;	PUSHJ	P,ROUTINE
;RETURN CPOPJ IF ERRORS WITH:
;	T1/ERROR BITS
;	CPOPJ1 IF TRANSFER STARTED SUCCESSFULLY


;HERE TO START TRANSFER TO READ/WRITE FORMATS.

DSXRDF:
DSXWTF:	SETZ	T1,		;CAN'T DO FORMATTING YET
	POPJ	P,		;BAD UNIT RETURN
;HERE TO START TRANSFER TO READ/WRITE DATA, NOT STOPPING ON ERRORS.

DSXRED:	SKIPA	T4,[OPRED]	;GET FILSER READ FUNCTION
DSXWRT:	MOVEI	T4,OPWRT	;GET FILSER WRITE FUNCTION
	JRST	DSXGO		;START THE I/O

;HERE TO START TRANSFER TO READ/WRITE DATA, STOPPING ON ERRORS.

DSXRDS:	SKIPA	T4,[OPRED]	;GET FILSER READ FUNCTION
DSXWTS:	MOVEI	T4,OPWRT	;GET FILSER WRITE FUNCTION
	JRST	DSXGO		;START THE I/O
;HERE TO START ALL OPERATIONS GOING

DSXGO:	MOVEM	T1,UNIDRB(U)	;SAVE DRB, IF ANY
	SETZM	UNIRTC(U)	;# RETRIES FOR THIS LOGICAL OPERATION
	SETOM	UNIBBK(U)	;LAST BLOK THAT HAD AN ERROR
DSXGO0:	PUSHJ	P,SETCHP	;SET UP CHANNEL PROGRAM
	POPJ	P,		;GIVE ERROR RETURN, T1=KOPXXX BITS
DSXGO1:	PUSHJ	P,SETGOB	;SET GO BIT
	MOVE	T1,UNIDRB(U)	;RESTORE DRB, IF ANY
	PJRST	CPOPJ1##	;GIVE OK (SKIP) RETURN

;HERE TO RE-START AN INTERRUPTED XFER
DSXRGO:	MOVE	T1,UNIDRB(U)	;GET DRB, IF ANY
	JUMPN	T1,DSXRG1	;BRANCH IF DRB, (IE: IF XFER)

;HERE TO RE-START A CHANNEL PROGRAM WITH NO (FURTHER) CHANGE
DSXRG0:	PUSHJ	P,SETDVP	;RESET DEVL SLOT, ETC
	PJRST	SETGOB		;SET GO BIT AND EXIT

;HERE TO RE-START A TRANSFER
DSXRG1:	SKIPN	DRBGOD(T1)	;SKIP IF SOME GOOD BLOKS
	JRST	DSXRG0		;BRANCH IF NOTHING GOOD AT ALL
	PUSHJ	P,SETTCP	;SETUP PROGRAM POINTERS
	MOVE	T1,UNIBBK(U)	;GET THE OFFENDING BLOCK'S LBN + 1
	MOVEM	T1,UNICBN(U)	;SAVE AS CURRENT BLOCK #
	PUSHJ	P,STOSEK	;STORE SEEK
	PUSHJ	P,STOSS		;STORE SET-SECTOR
	PUSHJ	P,STOSID	;STORE SEARCH-ID-EQUAL
	MOVE	T1,UNILPC(U)	;GET PC OF OFFENDING XFER CMD
	TDO	T1,[TIC]	;MAKE A TIC
	PUSHJ	P,STODVC	;INSERT A TIC TO THE GOOD PART
	PJRST	DSXRG0		;JOIN CODE TO START XFER
;HERE TO DO FUNCTION RELATED CONO SET/CLEAR
CNOSET:	TRO	T2,SO.SET	;SET BIT TO SET THE DESIRED FLAG
CNOCLR:	HLRZ	T1,DSXSCH(J)	;GET SUBCHANNEL NUMBER IN CONO POSITION
	IORI	T2,DSKCHN##(T1)	;INCLUDE PI CHANNEL AND SUBCHANNEL NUMBER
	XCT	DSXCO2(J)	;DO THE CONO
	POPJ	P,		;RETURN
	SUBTTL	STOP TRANSFER FOR HUNG RECOVERY


;ROUTINE TO STOP THE CURRENT TRANSFER WHEN THE HUNG TIMER TIMES
;OUT.
;CALL:
;	J/KDB ADDRESS
;	PUSHJ	P,DSXSTP
;RETURN CPOPJ IF COULDN'T GET BUSY TO CLEAR
;	CPOPJ1 IF SUCCESSFUL

;SUBROUTINE TO STOP DEVICE FOR ERROR RECOVERY

DSXSTP:	MOVSI	T1,UNPHNS	;GET HUNG-RECOVERY-IN-PROGRESS BIT
	TDNE	T1,UNIFNC(U)	;SKIP IF 1ST HUNG SINCE LAST GOOD OPERATION
	JRST	DSXST1		;BRANCH IF 2ND (OR WORSE) TO RESET SA10 CHANNEL.
	IORM	T1,UNIFNC(U)	;REMEMBER WE'RE NOW IN HUNG-RECOVERY
	PUSHJ	P,SETDVR	;SETUP TO RUN LAST PROGRAM AGAIN
	AOS	(P)		;GIVE SKIP RETURN
	PJRST	DSXST4		;SET GO FLAG, RETURN PROPER AC'S

;HERE WHEN SET GO FLAG AND TIMED OUT AGAIN IN HUNG RECOVERY

DSXST1:	PMOVE	T1,UNIDVP(U)	;GET DEVICE LIST ENTRY
	PUSH	P,T1		;SAVE CURRENT DEVICE LIST ENTRY
	MOVEI	T2,UNINOP(U)	;POINT AT NULL NO-OP PROGRAM
	PUSHJ	P,SETDVL	;SET UP FOR THAT
	MOVE	T3,UNIFNC(U)	;RETURN UNIFNC AS DATAI WORD
	POP	P,T2		;RESTORE PREVIOUS DEVICE LIST ENTRY
	PMOVE	T1,UNIDVP(U)	;GET CURRENT DEVICE LIST ENTRY
	CAME	T1,T2		;SKIP IF DIDN'T CHANGE SINCE LAST TRY
	JRST	DSXST4		;OK
DSXST2:	STOPCD	.+1,DEBUG,SAH,	;++ SA10 HUNG
	MOVEI	T2,SO.RCH	;FUNCTION TO RESET SUBCHANNEL
	PUSHJ	P,CNOCLR	;DO IT - SHOULD GET SA10 STARTED AGAIN
	PUSHJ	P,SETDVP	;TRY TO RE-RUN THE OPERATION'S CHANNEL PROGRAM
DSXST4:	PUSHJ	P,SETGOB	;SET GO FLAG FOR THIS SUBCHANNEL
	MOVE	T2,UNISND(U)	;T2="CONI"=FIRST SENSE BYTES
	MOVE	T3,@DSXCSW(J)	;T3="DATAI"=CHANNEL STATUS WORD
	POPJ	P,		;GIVE NOT-RECOVERED RETURN
	SUBTTL	INTERRUPT HANDLER


;ROUTINE TO PROCESS SA10 INTERRUPTS.
;CALL:
;	T3/ CONI STATUS BITS
;	W/ KDB ADDRESS
;	PUSHJ	P,DSXINT
;EXITS CPOPJ ALWAYS THROUGH FILINT WITH:
;	T1/ COMMUNICATIONS WORD (ATN BITS+DRIVE,,ERROR+FUNCTION)
;	T2/ CONI
;	T3/ ENDING STATUS,,ERROR REGISTER
;CALLED FROM SA10 INTERRUPT HANDLER WHEN AN INTERRUPT HAS
;OCCURRED FOR THE SUBCHANNEL TO WHICH THIS KDB IS CONNECTED.

DSXINT:	SETZB	S,F		;CLEAR ERROR FLAGS (S), ALSO "CONI" FLAGS (F)
	MOVE	J,W		;COPY KDB ADDRESS TO FILSER AC
	TRNE	T3,SI.PAR!SI.NXM ;SKIP UNLESS MEMORY ERRORS
	JRST	MEMERR		;HANDLE MEMORY ERRORS
	DMOVE	T1,@DSXCSW(J)	;GET CHANNEL-STATUS, UNDONE-PC
	PUSH	P,[CLRSTS]	;CALL CLRSTS ON OUR WAY OUT OF INTERRUPT
	LDB	T3,[POINT CSSUNI,T1,CSNUNI] ;GET UNIT NUMBER
	MOVE	U,@DSXPTR(J)	;GET ADDR OF UNIT IF EXISTS
	JUMPE	U,DSXIIN	;JUMP IF NO SUCH UNIT, IGNORE INTERRUPT
	DMOVEM	T1,UNICSW(U)	;SAVE CHANNEL-STATUS, UNDONE-PC
	MOVE	T2,UNIFNC(U)	;GET CURRENT FUNCTION BITS
	TLNN	T2,UNPONP	;SKIP IF POSITION FOR ON-LINE INTERRUPT
	TLNE	T2,UNPCPY	;OR SEE IF CALLING CPY ROUTINE
	JRST	DSXDCH		;DISPATCH TO CONTINUING HANDLER
	TLNE	T1,(S.SE)	;SKIP UNLESS SELECT ERROR
	JRST	SELERR		;BRANCH IF SELECT ERROR (CU OFFLINE?)
	TLNE	T1,(S.CUE)	;SKIP UNLESS CONTROL-UNIT-END
	JRST	HCUE		;GO HANDLE THAT
	HRRE	T4,UNIFNC(U)	;GET FILSER OPERATION IN PROGRESS
	SKIPGE	T4		;SKIP IF IT LOOKS LEGAL
	JRST	DSXSPR		;BRANCH IF APPARENTLY SPURIOUS
	TLNE	T1,(S.BUSY)	;SKIP UNLESS BUSY 
	JRST	HBUSY		;GO HANDLE BUSY
	TLNE	T1,(S.UC)	;SKIP UNLESS UNIT CHECK
	JRST	HUC		;GO HANDLE THAT
	TLNE	T1,(S.CTLE)	;SKIP UNLESS CONTROL ERROR
	JRST	HCTLE		;GO HANDLE CONTROL ERROR
	TLNN	T2,UNPHNS	;SET AN ERROR IF HUNG RECOVERY
	JRST	DSXIN2		;NO, OK
	TRO	S,IODERR	;HUNG RECOVERY
	TRO	F,SN.HNR	;NOTE HUNG RECOVERY
DSXIN2:	TLNN	T1,(S.BIPE+S.LE+S.PCI+S.ATN+S.UE) ;SKIP IF SA10 REPORTS ERRORS
	JRST	DSXRET		;NO, OK
DEVERR:	TRO	S,IODERR	;SOME ERROR DECTECTED
	TRO	F,SN.CDE	;CHANNEL DETECTED ERROR
;FALL INTO DSXRET
;HERE ON NORMAL DEV-END + CHAN-END COMPLETION OF AN OPERATION
DSXRET:	MOVE	T1,UNIFNC(U)	;GET CURRENT FUNCTION
	TLNE	T1,UNPSNS	;SKIP IF NOT SENSE AFTER UNIT CHECK
	JRST	DSXSNS		;IT WAS
	MOVE	T2,UNICSW(U)	;GET CHANNEL-STATUS
	TLNE	T2,(S.DE)	;SKIP UNLESS DEVICE END ON
	TLNE	T2,(S.CE)	;SKIP IF DEVICE END, NO CHANNEL END
	JRST	NOTCU1		;OK, NO DEVICE END, OR DEV END AND CHN END
	SKIPN	UNISTS(U)	;SKIP UNLESS UNIT IDLE (PROBABLY COMING ON-LINE)
	JRST	NOTCU1		;WAS IDLE, PROBABLY COMING ON-LINE
	PUSHJ	P,CUERES	;DEVICE END ALONE MAY BE AFTER UNIT BUSY
	  PJRST	SETGOB		;UNIT WAS BUSY, SET GO BIT AND DISMISS
	TRO	S,IODERR	;FORCE RETRY
	TRO	F,SN.RTR	;RETRY REQUESTED BY CONTROLLER
NOTCU1:	MOVE	T2,UNIECW(U)	;GET RESIDUAL_COUNT & UNDONE_PC WORD
	MOVEM	T2,UNITCA(U)	;SAVE FOR LATER IN CASE ANOTHER UNIT FINISHES
	MOVE	T1,UNIFNC(U)	;RESTORE T1 AGAIN
	MOVE	T2,UNIDES(U)	;GET UNIT DESCRIPTION
	TLNE	T2,UNPOFL	;SKIP UNLESS UNIT WAS OFF-LINE
	PJRST	FREINT		;MUST BE ON-LINE INTERRUPT
	MOVE	T2,UNISTS(U)	;GET UNIT STATUS
	CAIL	T2,OWCOD	;SKIP IF NOT OPERATOR WAIT
	PJRST	FREINT		;BRANCH TO GIVE ON-LINE INTERRUPT
	MOVE	T2,F		;NO SENSE DATA, USE FLAGS FOR "CONI" DATA

DSXRT1:	HRRZ	T1,UNIFNC(U)	;GET FILSER OPERATION
	CAIE	T1,OPRED	;READ OR
	CAIN	T1,OPWRT	; WRITE OPERATION?
	PUSHJ	P,FIXCWA	;YES, FIXUP DF10'S ICWA+1 WORD
DSXRT0:	HRRZ	T1,UNIFNC(U)	;GET FILSER OPERATION
	HLLOS	UNIFNC(U)	;THEN INVALIDATE IT
	LDB	T3,[POINT CSSUNI,UNICSW(U),CSNUNI] ;UNIT NUMBER TO RH T3
	HRL	T1,T3		;UNIT NUMBER TO LH T1
	MOVE	T3,UNICSW(J)	;T3=CHANNEL-STATUS WORD FOR "DATAI"
;NOTE AFTER UNIT CHECK THIS WILL BE FROM AFTER THE SENSE COMMAND
	IOR	T1,S		;INCLUDE ERROR BITS IN T1 FOR FILSER
;*** SHOULDN'T DRREST DO THIS?  DOES ANYONE CARE?
	HRRM	U,KONCUA(J)	;MUST TRUST HARDWARE SINCE ALL DRIVES MAY BE GOING
	SKIPE	DSXBUC(J)	;SKIP IF NO BUSY UNITS
	PUSHJ	P,FAKCUE	;FAKE A C-U-E IF BUSY UNITS
	HLRZ	T4,T1		;GET UNIT WHICH INTERRUPTED
	TRNE	T1,OPPOS	;POSITIONING INTERRUPT?
	SKIPA	T4,BITTBL##(T4)	;YES, GET BIT FOR THAT (SINGLE) UNIT
	SETZ	T4,		;NO, NO UNITS HAVE POSITIONED
	SKIPN	UNIDRB(U)	;DRB PRESENT?
	PJRST	FLHTID##	;NO, KINDA EASY TO CALL FILINT
	EXCH	T3,UNIDRB(U)	;GET DRB ADDRESS, SAVE "DATAI" BITS
	MOVEM	T2,DRBCNI##(T3)	;SAVE "CONI"
	MOVE	T2,UNIDRB(U)	;GET "DATAI" BITS
	MOVEM	T2,DRBDTI##(T3)	;SAVE "DATAI"
	SETZM	UNIDRB(U)	;CLEAR MEMORY OF DRB
	PJRST	FLHTID##	;CALL FILINT
;HERE ON INTERRUPT DURING KONCPY
DSXDCH:	MOVEI	T2,@UNICAD(U)	;ADDRESS TO CONTINUE
	CAIL	T2,DSXKON	;MUST BE IN DSXKON
	CAILE	T2,DSXEND	;SKIP IF VALID
	STOPCD	CPOPJ##,DEBUG,DND, ;++DISPATCH NOT IN DSXKON
	JRST	@T2		;DISPATCH OK, DO IT

;HERE ON INTERRUPT SHOWING SELECT ERROR
SELERR:	MOVSI	T1,(S00IRQ)	;GET INTERVENTION REQUIRED
	IORM	T1,UNISND+S00WRD(U) ;SET IT IN UDB
	PJRST	DSXSNZ		;MAKE SURE BADUNI GETS CALLED TO SLOW US DOWN

;HERE ON "ON-LINE" INTERUPT (DEV END WHILE UNIT IS "OFFLINE")
FREINT:	PUSHJ	P,FJCONT##	;ENSURE WE TRY THIS UNIT AGAIN ASAP
	MOVEI	T1,OPPOS	;MAKE LOOK LIKE FREE INTERUPT SINCE NOW ON-LINE
	HRL	T1,UDBPDN(U)	;INSERT DRIVE #
	SETZB	T2,T3		;NO CONI ETC
	MOVE	T4,UDBPDN(U)	;GET DRIVE #
	MOVE	T4,BITTBL##(T4)	;GET "ATTENTION" BIT
	PJRST	FLHTID##	;GIVE FREE-INTERRUPT TO FILSER
; HERE WHEN SENSE COMMAND DONE

DSXSNS:	MOVSI	T1,UNPSNS	;GET SENSE-IN-PROGRESS BIT
	ANDCAM	T1,UNIFNC(U)	;CLEAR IT
	SKIPN	UNIDRB(U)	;SKIP IF AN XFER OPERATION
	JRST	DSXSNK		;BRANCH IF ERROR ON SEEK/RECAL
	PUSHJ	P,FIXBAD	;GO UPDATE UNILPC, ETC
	MOVE	T1,UNIDRB(U)	;GET POINTER TO DRB
	MOVE	T2,DRBBLK(T1)	;GET 1ST BLOK IN XFER
	ADD	T2,DRBGOD(T1)	;COMPUTE BLOK IN ERROR
	EXCH	T2,UNIBBK(U)	;REMEMBER THE BAD BLOK FOR THIS PASS
	CAMN	T2,UNIBBK(U)	;SKIP IF SAME OFFENDER
;HERE DIRECTLY ON SEEK/RECAL ERROR
DSXSNK:	AOSA	T2,UNIRTC(U)	;UP RETRY COUNT BY ONE
	SETZB	T2,UNIRTC(U)	;START OVER WITH NEXT BLOCK
	CAILE	T2,DSXMAX	;SKIP IF NOT TOO MANY CONSECUTIVE ERRORS
	JRST	DSXSNZ		;BRANCH TO JUST GIVE UP
	MOVE	T2,UNISND+S02WRD(U) ;GET WORD WITH CORRECTION BIT
	TDNE	T2,[D02COR]	;SKIP IF NOT AN ECC-CORRECTABLE ERROR
	PUSHJ	P,XXXECC	;GO DO ECC CORRECTION
	PJRST	DSXRGO		;RE-CALL "DSXGO" AND EXIT INTERRUPT

;HERE TO JUST GIVE UP ON THIS XFER
DSXSNZ:	SKIPN	UNIDRB(U)	;SKIP IF XFER REQUEST
	PJRST	DSXRT0		;POS IGNORES ERROR, XFER VIA CONECT CATCHS IT
	MOVE	T2,UNISND(U)	;GET SENSE DATA FOR "CONI"
	TLNE	T2,(S00DCK)	;SKIP IF NOT DATA CHECK
	TROA	S,IODTER	;NOTE DATA ERROR
	TRO	S,IODERR	;NOTE AN EQUIPMENT ERROR
	TDNE	T2,[D01ITF!D01EOC!D01NRF]
	TRO	S,IODERR!IODTER	;NOTE FORMAT ERROR IF BAD PACK
	PJRST	DSXRT0		;BRANCH OFF TO EXIT INTERRUPT AND TELL FILSER
;HERE TO HANDLE UNIT CHECK - DO A SENSE I/O COMMAND

HUC:	MOVE	T1,UNIECW(U)	;GET TERMINAL COUNT, UNDONE-PC WORD
	MOVEM	T1,UNITCA(U)	;SAVE FOR LATER SENSE PROCESSING
	MOVSI	T1,UNPSNS	;GET SENSE-IN-PROGRESS BIT
	IORM	T1,UNIFNC(U)	;SET IT
	MOVEI	T2,UNISNC(U)	;POINT AT SENSE PROGRAM
	PUSHJ	P,SETDVL	;SETUP TO RUN SENSE PROGRAM
	PJRST	SETGOB		;SET GO BIT AND EXIT INTERRUPT
;HERE TO HANDLY BUSY - SET A FLAG TO RETRY LATER

HBUSY:	AOS	DSXBSY(J)	;COUNT AN ABSOLUTE BUSY
	PJRST	SETBSY		;SET UNIT'S BUSY BIT AND DISMISS INTERRUPT
;HERE TO HANDLE CONTROL UNIT END - SET UP TO RETRY UNITS THAT GOT A BUSY
HCUE:	TLNE	T1,(S.UC)	;UNIT-CHECK ALSO?
	JRST	HUC		;CU END + UNIT CHECK MEANS ERROR AFTER CHAN END
	TLNE	T1,(S.BUSY)	;BUSY ALSO?
	PUSHJ	P,SETBSY	;SET BUSY BIT FOR THIS UNIT
HCUE6:	MOVE	T2,UNIFNC(U)	;GET STATUS OF THE UNIT THAT GOT CUE
	TLNE	T2,UNPSNS	;SKIP IF NOT SENSE-IN-PROGRESS
	JRST	HCUE9		;IF THAT UNIT IS SENSING, GIVE IT PRIORITY
	PUSHJ	P,UCCHK		;CHECK FOR UNITS THAT ARE SENSE-BUSY'ED
	  PJRST	SETGOB		;BRANCH TO SET GO BIT AND EXIT
HCUE4:	PUSHJ	P,CUECHK	;NO, SET UP ALL OTHERS
	PJRST	SETGOB		;SET GO BIT AND DISMISS INTERRUPT

;HERE TO FAKE A CUE WHEN WE EXIT AN INTERRUPT VIA FILINT
FAKCUE:	PUSHJ	P,SAVT##	;SAVE ACS, CUZ FILINT CARES
	PJRST	HCUE6		;GET THINGS GOING AGAIN

HCUE9:	PUSHJ	P,SETDVR	;SET TO RE-RUN CHANNEL PROGRAM
	PJRST	SETGOB		;SET GO BIT AND DISMISS CUE
;ROUTINE TO CHECK IF ANY UNIT(S) ARE WAITING FOR "SENSE"
UCCHK:	PUSH	P,U		;SAVE FIRST UNIT
UCCHK1:	MOVSI	T1,UNPSNS	;GET BIT INDICATING SENSE IN PROGRESS
	TDNN	T1,UNIFNC(U)	;SKIP IF SENSE IN PROGRESS
	JRST	UCCHK2		;BRANCH IF NOT SENSING
	PUSHJ	P,SETDVR	;RESTART THE SENSEING UNIT
	JRST	UPOPJ##		;RETURN TO CALLER, WHO SETS GO BIT AND DISMISSES
UCCHK2:	MOVE	U,UNIKON(U)	;STEP TO NEXT UNIT IN RING
	CAME	U,(P)		;SKIP IF BACK TO ORIGINAL UNIT
	JRST	UCCHK1		;NO
	PJRST	UPOPJ1##	;YES, NO UNITS WAITING FOR SENSE
;SUBROUTINE TO CHECK ALL UNITS AND SET UP TO RESTART 
;VAL:	T1=NUMBER OF UNITS SETUP TO RETRY BECAUSE BUSY

CUECHK:	PUSH	P,[0]		;PUT ZERO ON STACK
	PUSH	P,U		;SAVE U FOR END OF LOOP
BSYCHK:	PUSHJ	P,CUERES	;SKIP IF UNIT NOT BUSY, NORMAL IF RESTARTED
	  AOS	-1(P)		;COUNT UNITS WAITING BECAUSE BUSY
	MOVE	U,UNIKON(U)	;STEP TO NEXT UNIT
	CAME	U,(P)		;SKIP IF BACK TO FIRST UNIT
	JRST	BSYCHK		;NO, CHECK ALL UNITS ON CONTROLLER
	POP	P,U		;RESTORE U
	PJRST	TPOPJ##		;GET COUNT AND RETURN

;SUBROUTINE TO CHECK IF UNIT HAD BUSY RESPONSE, AND SET CHAN PROG FOR RESTART
;	NON-SKIP IF UNIT HAD BUSY RESPONSE, CHANNEL PROGRAM SET TO RESTART
;	SKIP RETURN IF DID NOT HAVE BUSY

CUERES:	MOVSI	T1,UNPBSY	;BIT SET IN UNIFNC IF UNIT GOT BUSY RESPONSE
	TDNN	T1,UNIFNC(U)	;SKIP IF SET FOR THIS UNIT
	JRST	CPOPJ1##	;NO, UNIT NOT WAITING AFTER BUSY
	PJRST	SETDVR		;RUN SAME PROGRAM AGAIN
;ROUTINE TO SET BUSY AND KEEP DSXBUC STRAIGHT
SETBSY:	PUSH	P,T1		;SAVE AN AC
	MOVSI	T1,UNPBSY	;GET THE BUSY BIT
	TDNE	T1,UNIFNC(U)	;SKIP IF NOT SET YET
	JRST	TPOPJ##		;EXIT NOW IF ALREADY SET
	IORM	T1,UNIFNC(U)	;SET THE BIT
	AOS	DSXBUC(J)	;BUMP THE COUNT
	JRST	TPOPJ##		;EXIT WITH BIT SET AND COUNT FIXED

;ROUTINE TO CLEAR BUSY AND DSXBUC STRAIGHT
CLRBSY:	PUSH	P,T1		;SAVE AN AC
	MOVSI	T1,UNPBSY	;GET THE BUSY BIT
	TDNN	T1,UNIFNC(U)	;SKIP IF ALREADY SET
	JRST	TPOPJ##		;EXIT WITH BIT ALREADY CLEARED
	ANDCAM	T1,UNIFNC(U)	;CLEAR BIT
	SOS	DSXBUC(J)	;FIX THE COUNT
	JRST	TPOPJ##		;EXIT WITH BIT CLEAR AND COUNT FIXED
;HERE TO HANDLE MEMORY ERRORS
MEMERR:	MOVEI	T2,SO.CME	;CLEAR MEMORY ERRORS
	PUSHJ	P,CNOCLR	;DO THE CONO
	MOVEI	T2,SO.SRS	;SELECTIVE RESET COMMAND
	PJRST	CNOCLR		;DO SELECTIVE RESET
;SELECTIVE RESET SHOULD CAUSE CONTROL ERROR AND INVOKE ERROR RECOVERY.
;AT WORST, IF NOTHING HAPPENS FROM RESET, DSXTMO WILL TIME OUT THE OPERATION.

;HERE TO HANDLE CONTROL ERROR
HCTLE:	HRRZ	T3,DSXGSN(J)	;GET GLOBAL SUBCHANNEL NUMBER
	MOVE	T2,SAMERR##(T3)	;GET MEMORY ERROR WORD
	TRNN	T2,SI.NXM!SI.PAR ;SKIP IF THIS IS AFTER MEMORY ERROR
	JRST	DEVERR		;NO, JUST RETURN ERROR
	TRNN	T2,SI.NXM	;SKIP IF WAS NXM
	TROA	S,IOCHMP!IODERR	;NO, MEMORY PARITY ERROR
	TRO	S,IOCHNX!IODERR	;YES, NXM
	SETZM	SAMERR##(T3)	;CLEAR FLAGS
	JRST	DEVERR		;RETURN CHANNEL DETECTED DEVICE ERROR

;HERE WHEN INTERRUPT FOR UNKNOWN UNIT - COUNT AND IGNORE
DSXIIN:	AOS	DSXIUU(J)	;COUNT TIMES GOT INT FOR UNKNOWN UNIT
	MOVE	T3,BITTBL##(T3)	;GET BIT FOR THE NEW UNIT
	IORM	T3,DSXNUM(J)	;SET IN BIT MASK
	HRROS	KDBNUM(J)	;FLAG FOR THE REST OF THE WORLD TO SEE
	POPJ	P,		;EXIT INTERRUPT

;HERE ON INTERRUPT FOR KNOWN BUT IDLE UNIT
DSXSPR:	TLNN	T1,(S.CE)	;NORMAL SPURIOUS?
	JRST	FREINT		;BRANCH IF DEV-END ONLY ON APPARENTLY IDLE UNIT
	AOS	DSXSUI(J)	;REMEMBER APPARENTLY SPURIOUS INTERRUPT
	AOS	UNISUI(U)	;AND KEEP PER UNIT COUNT TOO
	POPJ	P,
	SUBTTL	DETERMINE CAPACITY AND STATUS


;ROUTINE TO RETURN CAPACITY AND STATUS OF AN IBM DRIVE TO FILSER.
;CALL:	J/KDB ADDRESS
;	U/UDB ADDRESS
;	PUSHJ	P,DSXCPY
;RETURN CPOPJ IF ERROR
;	CPOPJ1 IF DRIVE EXISTS AND IS OK TO USE WITH:
;	T1/BLOCKS PER UNIT
;	T2/BLOCKS PER UNIT INCLUDING MAINTENANCE CYLINDERS
;	T3/BLOCKS PER UNIT IN 10/11 COMPATABILITY MODE
;	T4/STATUS BITS (KOPUHE,KOPNSU),,UNIT TYPE
;	W/BLOCKS PER TRACK,,BLOCKS PER CYLINDER

DSXCPY:	MOVSI	T1,UNPCPY	;BIT MARKING IN CPY ROUTINE
	TDNE	T1,UNIFNC(U)	;SKIP IF NOT HERE BEFORE
	JRST	DSXCPA		;YES, COMMAND TRIES EVERY TICK OR SO
	MOVEM	T1,UNIFNC(U)	;NOTE WE ARE IN CPY ROUTINE
	SETOM	UNICCF(U)	;SET COMCON FLAG SO HE KNOWS WE'RE STILL GOING
	PUSHJ	P,SETTCP	;SETUP UNITCP, UNISDA
	PUSHJ	P,STONOP	;INSERT ONLY A NO-OP
	PUSHJ	P,STOHLT	;INSERT A HALT
	PUSHJ	P,CPYWAT	;START NO-OP, RETURN A SKIP TO COMCON

;HERE AFTER NO-OP

DSXCP1:	TLNE	T1,(S.SE)	;SKIP UNLESS SELECT ERROR
	JRST	DSXCPD		;STILL DOWN
	PUSHJ	P,SETSNS	;SETUP A SENSE
	PUSHJ	P,CPYWAT	;SET UP, START CHAN PROG, RETURN AFTER INT
;HERE WHEN SENSE CMD DONE

	SETZ	T4,		;ZERO T4 IN CASE OFF-LINE
	TLNE	T1,(S.SE)	;SKIP UNLESS SELECT ERROR
	JRST	DSXCPD		;STILL DOWN
	MOVE	T1,UNISND(U)	;GET SENSE INFO
	TLNE	T1,(S00IRQ)	;SKIP IF UNIT OK
DSXCP2:	TLO	T4,KOPUHE	;NOTE UNIT OFF-LINE
	JUMPN	T4,DSXCPS	;ASSUME 3330 IF OFF-LINE
	PUSHJ	P,SETTCP	;SETUP UNITCP, UNISDA
	MOVE	T1,[^D411 * ^D342] ;T1=1ST BLOCK IN CYL 411
	MOVEM	T1,UNICBN(U)	;SET AS CURRENT BLOCK
	PUSHJ	P,STOSEK	;STORE IT
	PUSHJ	P,STONOP	;GET A NO-OP
	PUSHJ	P,STOHLT	;AND A HALT
	PUSHJ	P,CPYWAT	;SET UP, START CHAN PROG, RETURN AFTER INT

;HERE AFTER SEEK

	SETZ	T4,		;ZERO T4 SINCE NOT OFF-LINE
	TLNN	T1,(S.UC)	;SKIP IF UNIT CHECK
	JRST	DSXCPL		;NO, MUST BE 3331
	PUSHJ	P,SETSNS	;SETUP FOR A SENSE
	PUSHJ	P,CPYWAT	;SET UP, START CHAN PROG, RETURN AFTER INT

;HERE AFTER SENSE AFTER SEEK

	SETZ	T4,		;CLEAR T4 FOR ON-LINE UNIT
	MOVE	T1,UNISND(U)	;GET SENSE INFO
	TLNN	T1,(S00REJ+S00IRQ) ;SKIP IF ILLEGAL SEEK
	JRST	DSXCPL		;MUST HAVE FAILED FOR SOME OTHER REASON

;HERE IF 3330 UNIT

DSXCPS:	HRRI	T4,.UT330	;UNIT TYPE
	JRST	DSXCPC		;RETURN VALUES

;HERE IF 3331 UNIT

DSXCPL:	HRRI	T4,.UT331	;UNIT TYPE
DSXCPC:	MOVE	T1,BLKPRU(T4)	;GET BLOCKS/UNIT BASED ON UNIT TYPE
	MOVEM	T1,UNIPRU(U)	;SAVE VALUES
	MOVEM	T4,UNITYP(U)
	SETZM	UNICCF(U)	;CLEAR COMCON FLAG TO NOTE WE'RE FINISHED
	POPJ	P,		;EXIT INTERRUPT AND RETURN

DSXCPD:	MOVSI	T4,KOPNSU	;UNIT IS DOWN
	JRST	DSXCP2		;RETURN THAT
;HERE WHEN CALLED AGAIN FROM COMMAND - SEE IF FINISHED

DSXCPA:	SKIPE	UNICCF(U)	;SKIP IF FINISHED
	JRST	CPOPJ1##	;NO, STILL WAITING
	MOVSI	T1,UNPCPD	;FLAG TO NOTE CPY ROUTINE DONE
	MOVEM	T1,UNIFNC(U)	;SO COMMAND WILL CONTINUE
				;ALSO PREVENT INTS FROM DISPATCHING
	PUSHJ	P,SNSCLR	;CLEAR OUT SENSE BYTES
	MOVE	T1,UNIPRU(U)	;GET SIZE OF UNIT
	MOVE	T2,T1		;BLOCKS INCLUDING MAINT CYLS
	SETZ	T3,		;NO 10/11 COMPATIBILITY MODE
	MOVE	T4,UNITYP(U)	;AND UNIT TYPE
	MOVE	W,[^D18,,^D18*^D19] ;BLKS, PER TRK,,BLKS PER CYL
	TLNN	T4,-1		;ANY ERRORS?
	AOS	(P)		;NO, SET FOR SKIP RETURN
	POPJ	P,		;GIVE DOWN RETURN
;SUBROUTINE TO SET UP CHAN PROG, SET GO FLAG, AND MARK RETURN AFTER INT

CPYWAT:	PUSHJ	P,SETDVP	;SET UP DEVICE LIST ENTRY
	POP	P,UNICAD(U)	;SAVE RETURN ADDR FOR AFTER INT
IFN FTKL10,<			; IF KL10
	SKIPN	DINITF##	;NO NEED TO FLUSH CACHE IF ONCE ONLY
	PUSHJ	P,CSDMP##	;FLUSH CACHE
>;END IFN FTKL10
	PUSHJ	P,DSXGO1	;SET GO FLAG
	  JFCL			;DONT CARE ABOUT THIS
	CONSO	PI,PIPROG##	;SKIP IF PIS IN PROGRESS (CALLED AT INT LEVEL)
	SKIPN	DINITF##	;NOT INTERRUPT LEVEL, DURING ONCE-ONLY?
	POPJ	P,		;PIS IN PROGRESS OR NOT ONCE-ONLY, DISMISS
				; INTERRUPT AND WAIT FOR COMPLETION
	PJRST	DSXWAT		;ONCE-ONLY, WAIT A WHILE FOR COMMAND TO COMPLETE

SNSCLR:	SETZM	UNISND(U)	;CLEAR FIRST WORD
	MOVEI	T2,UNISND+1(U)
	HRLI	T2,-1(T2)	;MAKE BLT PTR
	BLT	T2,UNISND+5(U)	;CLEAR SENSE DATA
	POPJ	P,		;RETURN
	$INIT

;SUBROUTINE TO WAIT FOR DSX COMMAND FOR ONCE-ONLY

DSXWAT:	MOVSI	T1,ONCTIM##	;GET TIME TO WAIT FOR COMPLETION
	SKIPE	UNICCF(U)	;SEE IF CPY CHECK STILL IN PROGRESS
	SOJG	T1,.-1		;KEEP LOOPING
	JUMPG	T1,DSXCPA	;IF COMPLETED, FETCH DATA FROM UDB AND RETURN
	MOVSI	T4,KOPNSU!KOPUHE ;GET THE ERROR FLAGS
	POPJ	P,		;RETURN TO ONCE CALLER

	$HIGH
	SUBTTL	MISCELLANEOUS TO BE CLEANED UP

;*** TEST INTERRUPT ENABLED FLAG FOR SUBCHANNEL?
DSXALV:	XCT	KDBCNI(J)	;GET CONI
	TRNE	T1,7		;ALIVE?
	POPJ	P,		;YES, JUST RETURN
	MOVEI	T1,SAXCHN##	;GET THE CHANNEL THE SA10 BELONGS ON
	XCT	KDBCNO(J)	;INSIST ON A NEW PI CHANNEL
	STOPCD	.+1,INFORM,DSXPIA, ;++ DSXKON NOTICES SA10 HAS NO PI-ASSIGNMENT
	POPJ	P,		;RETURN
;HERE TO COPY SA10 DISK REGISTERS FOR SYSERR ET AL
DSXREG:	POPJ	P,
	SUBTTL	CHECK IF CONTROLLER IS UP/CHECK UNIT WRITE LOCK

	$INIT

DSXHWP:	POPJ	P,		;WRITE-ENABLED FOR NOW
REPEAT 0,<			;*** TEMP
	PUSHJ	P,GTHOM##	;TRY TO READ A HOME BLOCK
	  POPJ	P,		;IF BOTH BAD, DONT RISK WRITING, ASSUME OK
	JUMPL	T2,.+2		;JUMP IF FIRST HOME BLOCK BAD, USE SECOND
	SKIPA	T2,UNIHOM(U)	;FIRST IS OK, REWRITE IT
	HLRZ	T2,UNIHOM(U)	;SECOND IS OK, REWRITE IT
	HRRZS	T2		;JUST BLOCK NUMBER IN RH
	PUSHJ	P,OMNWRT##	;REWRITE THE GOOD BLOCK
	  SKIPA	T2,UNISND(U)	;ERROR, GET SENSE DATA FOR UNIT
	POPJ	P,		;OK, MUST NOT BE WRITE-PROTECTED
	TLNE	T2,(D01WRL)	;SKIP IF WRITE-INHIBIT OFF
	AOS	(P)		;WRITE INHIBIT - SKIP RETURN
	POPJ	P,		;RETURN
>; *** TEMP

DSXUPA:	PUSHJ	P,SAVE1##	;SAVE P1
	MOVEI	T1,7		;GET PI ASSIGNMENT
	XCT	KDBCNO(J)	;TRY TO GIVE IT ONE
	XCT	KDBCNI(J)	;READ IT BACK
	TRNN	T1,7		;DID IT WORK?
	POPJ	P,		;NO, ASSUME DEAD
	PUSHJ	P,DSXENB	;ENABLE INTERRUPTS FOR THIS CONTROLLER
	HRRZ	T1,DSXSCH(J)	;GET SUBCHANNEL
	MOVNS	T1		;NEGATIVE FOR RIGHT SHIFT
	MOVEI	T2,SI.ENB	;BIT FOR SUBCHAN ZERO
	LSH	T2,(T1)		;GET OUR INT ENABLE BIT
	XCT	KDBCNI(J)	;GET CONI STATUS
	TRNN	T1,(T2)		;INTERRUPTS ENABLED?
	POPJ	P,		;SA10 PROBABLY NOT WORKING
	SJSP	T1,CPOPJ1##	;FLAG WRITE-HEADER-LOCKOUT OK AND RETURN SUCCESS

	$HIGH
;ROUTINE TO TELL FILIO OF RETRY-STRATEGY ON ERROR.
;IF HARDWARE LOOKS OK AND ERROR IS PROBABLY MEDIA, GIVE UP.
;IF HARDWARE LOOKS BAD (OFFLINE, UNSAFE, ETC) TRY RECALS FOREVER.
DSXERR:	PUSHJ	P,CONECT	;SKIP IF WE LOST FAIR AND SQUARE
	SKIPA	T1,[5]		;TRY RECAL IF HARDWARE CROAKED IN MID XFER
	MOVEI	T1,3		;GIVE UP
	POPJ	P,		;RETURN
;READ PDP11 MODE (?)
;WRITE PDP11 MODE (?)
;UNLOAD UNIT
DSXRDC:
DSXWTC:
DSXUNL:	STOPCD	.,STOP,DSX3IF,	;++ 3330 ISN'T FANCY
	SUBTTL	ROUTINES TO BUILD CHANNEL PROGRAMS

;SUBROUTINE TO CONSTRUCT CHANNEL PROGRAM FOR FILSER OPERATION
;ARGS:	T4=FILSER OPCODE
;	U=UNIT

SETCHP:	HRRZM	T4,UNIFNC(U)	;REMEMBER FILSER FUNCTION AND CLEAR LH FLAGS
	PUSHJ	P,SETTCP	;SETUP UNITCP, UNISDA
	MOVE	T1,UNIDRB(U)	;GET DRB ADDRESS, IF ANY
	PJUMPE	T1,SETPOS	;SETUP A POSITIONING PROGRAM
	PJRST	SETXFR		;SETUP AN XFER PROGRAM
;	ROUTINE TO SETUP A POSITION PROGRAM

SETPOS:	SETZM	UNISND(U)	;DEFAULT TO NO SENSE DATA INFO
	MOVE	T1,UNIBLK##(U)	;PICK UP THE BLOCK NUMBER
	MOVEM	T1,UNICBN(U)	;SAVE AS CURRENT BLOCK #
	MOVE	T1,UNIRCL(U)	;GET PROTOTYPE RECAL
	CAIN	T4,OPCAL	;SKIP IF POSITION
	PUSHJ	P,STODVC	;INSERT A RE-CAL
	PUSHJ	P,STOSEK	;INSERT A SEEK
	PUSHJ	P,STONOP	;INSERT A NO-OP
	TLNE	S,IO		;SKIP IF READ
	PUSHJ	P,STORC0	;INSERT READ/WRITE RECORD 0
	PUSHJ	P,STOHLT	;INSERT A HALT
	AOS	(P)		;ALWAYS GIVE GOOD RETURN
	PJRST	SETDVP		;SETUP TO RUN SEEK PROGRAM
;	ROUTINE TO SETUP A DATA XFER PROGRAM
SETXFR:	PUSHJ	P,CONECT	;TRY TO CONECT TO DRIVE
	POPJ	P,		;  CANNOT CONECT TO DRIVE
	AOS	(P)		;GIVE GOOD RETURN
	SKIPN	T1,UNIDRB(U)	;T1=DRB
	XCT	DSXXWD		;++ XFER WITHOUT DRB
	MOVE	T3,DRBNUM##(T1)	;T3=# OF BLOCKS TO XFER
	SKIPN	T3		;SKIP IF WE FOUND SOMETHING TO DO
	STOPCD	SETDVP,INFORM,SETXF0,	;++ DRBNUM =0 IN DSXKON
	PUSHJ	P,MAPIO		;CALL LOCAL MAPIO TO CONVERT DF10C TO SA10B
	PJRST	SETDVP		;SETUP TO RUN XFER PROGRAM
;	ROUTINE TO CHECK STATUS OF DRIVE

CONECT:	SETZ	T1,		;DEFAULT TO NO-PROBLEMS
	MOVE	T2,S00WRD+UNISND(U) ;GET BYTE 0
	TDNN	T2,[S00OVR]	;SKIP IF OVERRUN
	TDNE	T2,[S00IRQ]	;SKIP IF NOT INTERVENTION REQUIRED
	TRO	T1,KOPOFL	;JUST PLAIN OFFLINE
	TDNE	T2,[S00REJ]	;SKIP IF NOT REJECTED COMMAND
	TDNN	T2,[D01WRL]	;SKIP IF CMD-REJ + WRITE-LOCKED
	SKIPA
	TRO	T1,KOPWLK	;WRITE-LOCKED
	TDNE	T2,[S00BOC]	;SKIP IF NOT BUS-OUT-CHECK
	TRO	T1,KOPUSI	;THEN STATUS-INCONSISTENT
	JUMPE	T1,CPOPJ1##	;BRANCH IF NO PROBLEMS SPOTTED
	LDB	T2,[POINTR (UNISND+S07WRD(U),D07FMC)] ;GET FORMAT/MESG CODE
	LSH	T2,-4		;DUMP MESG, EXTRACT FORMAT
	CAIE	T2,1		;SKIP IF FORMAT 1
	POPJ	P,		;RETURN WITH T1=DIAGNOSIS
	MOVE	T2,UNISND+S08WRD(U) ;GET 1ST WORD OF UNSAFE BITS
	MOVE	T3,UNISND+S12WRD(U) ;GET 2ND WORD OF UNSAFE BITS
	TDNN	T2,[D09PLU!D11HUS] ;SKIP IF PLO OR HEAD UNSAFES
	TDNE	T3,[D12DUS!D12SUS!D12PUS] ;DATA,SERVO,OR PAD UNSAFE
	TRO	T1,KOPFUS	;SET UNSAFE BIT
	POPJ	P,		;AND RETURN
;	ROUTINES TO RUN A CHANNEL PROGRAM
;
;	SETDVP TO RUN THE FILSER PROGRAM
;	SETDVL TO RUN THE VIRTUAL (T2) PROGRAM
;	SETDVR TO RUN THE PREVIOUS PROGRAM AGAIN

SETDVP:	SKIPA	T2,UNIPCP(U)	;JUST USE UDB PROGRAM
SETDVL:	MAP	T2,(T2)		;CONVERT VIRTUAL ADDRESS TO PHYSICAL
	TLZ	T2,(MP.NAD)	;CLEAR NON-ADDRESS BITS
	PMOVE	T3,UNIDVP(U)	;GET DEVICE LIST ENTRY
	AND	T3,[776000,,0]	;PRESERVE ONLY DEVICE ADDRESS
	TLO	T3,(<DL.STA>B<SANDLO>) ;INSERT THE "GO" CODE
	TDOA	T3,T2		;INSERT THE PROGRAM ADRESS
SETDVR:	MOVE	T3,UNIDVL(U)	;GET ORIGINAL DEVL SLOT CONTENTS
	PMOVEM	T3,UNIDVP(U)	;INSERT NEW DEVICE LIST ENTRY IN LIST
	MOVEM	T3,UNIDVL(U)	;SAVE FOR DEBUGGING
	PJRST	CLRBSY		;NOT BUSY NOW
;ROUTINE TO ACTUALLY SET GO BIT FOR A CHANNEL
SETGOB:	PUSHJ	P,SAVT##	;SAVE THE ACS
	MOVEI	T2,SO.GO	;GET GO FLAG FUNCTION
	PJRST	CNOSET		;GO DO CONO TO SET IT AND THEN EXIT

;ROUTINE TO CLEAR STATUS FLAG AT INTERRUPT TIME
CLRSTS:	PUSHJ	P,SAVT##	;SAVE THE ACS
	MOVEI	T2,SO.STS	;GET THE STATUS-FLAG FUNCTION
	PJRST	CNOCLR		;CLEAR FUNCTION AND THEN EXIT
;SUBROUTINE TO COMPUTE LATENCY - SHOULDN'T BE CALLLED

DSXLTM:	SETZ	T1,		;RETURN PERFECT EVERY TIME
	JRST	CPOPJ1##	;
;SUBROUTINE TO RETURN A PROTOTYPE DEVICE COMMAND WORD FOR SETTING UP CHANNEL PROGRAM
;REQUIRED BECAUSE IOWD IS MAPPED BEFORE CALLING DSX??? TO START READ OR WRITE

DSXDVC:	MOVE	T1,UNIRWC(U)	;GET PROTOTYPE READ DVW
	TLNE	S,IO		;SKIP IF READ IS DESIRED
	ADD	T1,[<O.WRT>B15-<O.MTR>B15] ;NO, CONVERT TO WRITE DVW
	POPJ	P,
;SUBROUTINE TO SET INTERRUPT ENABLE FOR A SUBCHANNEL

DSXENB:	MOVEI	T2,SO.ENB	;INTERRUPT ENABLE
	PJRST	CNOSET		;SET THE FLAG AND RETURN
	SUBTTL	AUTOCONFIGURE

;AUTCON ENTRY POINT

DSXCFG:	JRST	CPOPJ1##	;I'M NOT INTERESTED UNTIL SAXSER IS


;SAXSER ENTRY POINT
;M/ GLOBAL SA10 SUBCHANNEL NUMBER
;P1/ -N,,CONTROL UNIT ADDRESS (HIGH 'WIDCUA' BITS)
;P2/ SA10 SUBCHANNEL NUMBER
;P3/ AVAILABLE FOR USE
;P4/ ADDRESS OF SENSE CHANNEL PROGRAM FOR UNIT ZERO

DSXCFS:	PUSHJ	P,SAVW##	;FREE UP KDB ADDRESS AC
	SETZ	W,		;HAVEN'T SEEN A KDB YET
	MOVSI	P3,-DSXDMX	;INIT SEARCH FOR EXISTANT UNITS
DSXCF1:	HRRZ	T1,P1		;GET DCU ADDRESS
	LSH	T1,WIDUNA	;POSITION IN HIGH 'WIDCUA' BITS
	IORI	T1,(P3)		;INCLUDE UNIT ADDRESS
	PUSH	P,T1		;SAVE UNIT ADDRESS
	PUSHJ	P,DSXNOP	;DO A NO-OP ON THE UNIT
	 JRST	DSXCF5		;FAILED - GO SENSE AND STEP TO NEXT UNIT
	POP	P,T1		;GET UNIT ADDRESS
	PUSHJ	P,DSXSNU	;DO A SENSE TO GET SOME USEFUL INFORMATION
	  JRST	DSXCF4		;SENSE TIMED OUT, MUST NOT BE A UNIT THERE
	TLNE	T1,(S.SE)	;SELECT ERROR?
	JRST	DSXCF4		;YES
	SKIPE	SNSDAT+S16WRD(P4) ;ANY TCU REV LEVEL INFORMATION RETURNED?
	JRST	CPOPJ1##	;YES, MUST NOT BE A DISK CU
	JUMPN	W,DSXCF2	;JUMP IF HAVE ALREADY SEEN A UNIT
	PUSHJ	P,DSXBKD	;BUILD AND LINK THE KDB
	  POPJ	P,		;NO CORE, GIVE UP
DSXCF2:	LDB	T1,[POINT 8,SNSDAT+S04WRD(P4),7] ;GET SENSE BYTE 4
				; (PHYSICAL MODULE ID)
	ANDI	T1,77		;KEEP JUST GOOD STUFF
	JUMPN	T1,DSXCF3	;JUMP IF ANYTHING THERE
	SKIPN	SNSDAT+S08WRD(P4) ;HOW ABOUT BYTES 8-11?
	JRST	DSXCF4		;NOPE
DSXCF3:	PUSHJ	P,DSXUNI	;BUILD UDB
	  POPJ	P,		;NO CORE
DSXCF4:	AOBJN	P3,DSXCF1	;BUMP UNIT NUMBER AND KEEP LOOKING
	POPJ	P,		;RETURN

DSXCF5:	POP	P,T1		;GET UNIT ADDRESS
	PUSHJ	P,DSXSNU	;DO A SENSE TO UNLOAD UNIT-CHECK INFO
	  JFCL			;OH WELL
	JRST	DSXCF4		;THEN REJOIN CODE AND STEP TO NEXT UNIT
;BUILD AND LINK KDB
;M/ GLOBAL SA10 SUBCHANNEL NUMBER
;P1/ JUNK,,DCU ADDRESS (HIGH 'MAXCUA' BITS)
;P2/ SA10 SUBCHANNEL NUMBER

DSXBKD:	HRRZ	T1,P1		;GET DCU ADDRESS AS MASSBUS NUMBER
	TRO	T1,400000	;BUT MAKE IT LOOK NEGATIVE TO FAKE OUT AUTKDB
;***	MOVEI	T2,'ZZ '	;PREFERRED UNIT NAMES, IF ANY
;***	HRLM	T2,DSXKDB	;...SO FORCE THEM
	MOVEI	T2,TYPDS	;UNIT TYPE CODE
	PUSHJ	P,DSKKON##	;BUILD A DISK KDB
	  POPJ	P,		;GIVE UP IF NO CORE
	ADDM	J,DSXPTR(J)	;ADJUST DSXPTR
	MOVSI	T1,-<DSXIOE-DSXIOB> ;NUMBER OF WORDS TO CHECK
	XMOVEI	T2,DSXIOB(J)	;STARTING WORD
	HRRZ	T3,.CPDVC##	;<DEVICE CODE>/4
	PUSHJ	P,AUTDVC##	;SET DEVICE CODES
	MOVS	T1,P2		;GET SUBCHANNEL NUMBER
	IMULI	T1,SO.CHN	;POSITION FOR CONO
	HRR	T1,P2		;INCLUDE SUBCHANNEL NUMBER
	MOVEM	T1,DSXSCH(J)	;STORE IN KDB
	MOVEM	M,DSXGSN(J)	;SAVE GLOBAL SUBCHANNEL NUMBER
	MOVE	T1,SAXSBA##(M)	;GET BASE ADDRESS FOR THIS SUBCHANNEL
	MOVEI	T2,.CLICW(T1)	;GET ICW ADDRESS
	MOVEM	T2,DSXICW(J)
	MOVEI	T2,.CLCSW(T1)	;GET ECW ADDRESS
	MOVEM	T2,DSXCSW(J)
	MOVEI	T1,DSXDMX	;NUMBER OF DEVICE LIST ENTRIES WE'LL REQUIRE
	PUSHJ	P,SAXDVL##	;ASSIGN THEM
	  POPJ	P,		;THERE WEREN'T ENOUGH FREE?!
	MOVEM	T1,DSXDVP(J)	;SAVE ADDRESS OF START OF DEVICE LIST
	JRST	CPOPJ1##	;SKIP RETURN
;BUILD AND LINK UDB
;M/ GLOBAL SA10 SUBCHANNEL NUMBER
;J/ KDB ADDRESS
;P1/ JUNK,,DCU ADDRESS (HIGH 'MAXCUA' BITS)
;P2/ SA10 SUBCHANNEL NUMBER
;P3/ JUNK,,UNIT ADDRESS
;P4/ SENSE PROGRAM BUFFER ADDRESS

DSXUNI:	HRLZ	T1,P3		;PHYSICAL DRIVE NUMBER
	HRR	T1,P3		;UDB TABLE INDEX
	MOVEI	T2,.UT331	;ASSUME 3331 UNIT
	PUSHJ	P,DSKDRV##	;BUILD AND LINK THE UDB
	  POPJ	P,		;NO CORE
	HRRZ	T1,P1		;GET DCU ADDRESS
	LSH	T1,WIDUNA	;POSITION IN HIGH 'WIDCUA' BITS
	IORI	T1,(P3)		;INCLUDE UNIT NUMBER
	DPB	T1,[POINT DVSDVA, UNIRCL(U), DVNDVA] ;INSERT DEVICE ADDRESS
	DPB	T1,[POINT DVSDVA, UNINOP(U), DVNDVA]
	DPB	T1,[POINT DVSDVA, UNISEK(U), DVNDVA]
	DPB	T1,[POINT DVSDVA, UNISS(U), DVNDVA]
	DPB	T1,[POINT DVSDVA, UNISIE(U), DVNDVA]
	DPB	T1,[POINT DVSDVA, UNISNC(U), DVNDVA]
	DPB	T1,[POINT DVSDVA, UNIRWC(U), DVNDVA]
	DPB	T1,[POINT DVSDVA, UNIRR0(U), DVNDVA]
	MAP	T1,UNISND(U)	;POINT AT SENSE DATA BUFFER
	TLZ	T1,(MP.NAD)	;TURN OFF MAP BITS
	TLO	T1,(SIOW 24,0)	;TURN ON WORD COUNT
	MOVEM	T1,UNISNC+1(U)	;INSERT INTO PROGRAM
	MOVE	T1,DSXDVP(J)	;GET START OF DEVICE LIST
	ADDI	T1,(P3)		;INCLUDE UNIT ADDRESS
	MOVEM	T1,UNIDVP(U)	;SAVE POINTER TO DEVICE LIST ENTRY
	MOVEI	T1,CPALEN	;GET LENGTH OF CHANNEL PROGRAM AREA
	PUSHJ	P,SAXCOR##	;GO GET A CHUNK OF SA10 MEMORY
	  POPJ	P,		;DARN.
	MOVEM	T1,UNIPCP(U)	;SAVE ADDRESS OF CHANNEL PROGRAM BUFFER

;*** NOTE THAT IF THE "SERIAL NUMBER" WERE MADE UP OF
;*** THE "DCU ADDRESS" PLUS THE MODULE ID (IE: AND NOT THE UNIT NUMBER)
;*** THEN "DUAL PORTING" WOULD WORK EVEN IF LAP PLUGS WERE SWAPPED.
	HRRZ	T2,P1		;GET DCU ADDRESS
	LSH	T2,WIDUNA	;POSITION IT SO THAT T2=UNIT#0 ADDRESS
	IORI	T2,(P3)		;MERGE THIS UNIT NUMBER
	SETZ	T1,		;REALLY A ONE-WORD QUANTITY
	DMOVEM	T1,UDBDSN(U)	;SET SERIAL NUMBER IN UDB
	JRST	CPOPJ1##	;RETURN
;EXECUTE A SENSE OPERATION FOR A SPECIFIED ADDRESS.
;CALL:
;	T1/ DEVICE ADDRESS
;	P4/ CHANNEL PROGRAM BUFFER ADDRESS
;	PUSHJ	P,DSXSNU
;	  <ERROR RETURN>
;	<NORMAL RETURN>		;T1/ CHANNEL STATUS BITS

DSXSNU:	DPB	T1,[POINT DVSDVA, SNSDVW(P4), DVNDVA] ;INSERT DEVICE ADDRESS
	MOVE	T1,P4		;COPY CHANNEL PROGRAM ADDRESS
	ADDI	T1,SNSDVW	;POINT AT SENSE CMD
	PJRST	SAXRCP##	;RUN THE SENSE CHANNEL PROGRAM
;EXECUTE A NO-OP OPERATION FOR A SPECIFIED ADDRESS.
;CALL:
;	T1/ DEVICE ADDRESS
;	P4/ CHANNEL PROGRAM BUFFER ADDRESS
;	PUSHJ	P,DSXNOP
;	  <ERROR RETURN>
;	<NORMAL RETURN>		;T1/ CHANNEL STATUS BITS

DSXNOP:	DPB	T1,[POINT DVSDVA, SNSDVW(P4), DVNDVA] ;INSERT DEVICE ADDRESS
	DPB	T1,[POINT DVSDVA, SNSNOP(P4), DVNDVA] ;INSERT DEVICE ADDRESS
	MOVE	T1,P4		;COPY CHANNEL PROGRAM ADDRESS
	PJRST	SAXRCP##	;RUN THE NO-OP/SENSE CHANNEL PROGRAM
SUBTTL	ONCE A SECOND CODE

DSXSEC:	PUSHJ	P,DSXTMO	;GO TIME OUT DISKS IF NECESSARY
	SKIPL	@KDBCHN(J)	;CHANNEL BUSY?
	POPJ	P,		;LEAVE IT ALONE
	SKIPE	T1,DSXNUM(J)	;GET BIT MASK
	JFFO	T1,DSXSE1	;FIND FIRST UNIT NUMBER
	HRRZS	KDBNUM(J)	;INDICATE NO DRIVES TO CONFIGURE
	POPJ	P,		;DONE
DSXSE1:	PUSHJ	P,AUTLOK##	;GET AUTCON INTERLOCK
	  POPJ	P,		;TRY AGAIN NEXT TIME
	PUSHJ	P,SAVW##	;PRESERVE W
	MOVE	W,J		;COPY KDB ADDRESS TO W FOR AUTCON
REPEAT 0,<
	MOVSS	T2		;MASSBUS UNIT = DRIVE NUMBER FOR DSX DISKS
	PUSH	P,T2		;SAVE
	MOVE	T1,KDBDVC(J)	;DEVICE CODE
	XMOVEI	T2,DSXDSP	;DISPATCH
	MOVE	T3,KDBCHN(J)	;CHANNEL DATA BLOCK
	PUSHJ	P,AUTSET##	;SET UP CPU VARIABLES
	EXCH	P1,(P)		;SAVE P1, GET MASSBUS UNIT
	PUSH	P,KDBUNI(J)	;SAVE KDBUNI
	MOVEM	P1,KDBUNI(J)	;SET FOR THIS MASSBUS UNIT NUMBER (FOR RDMBR)
	PUSHJ	P,DSXUNI	;CONFIGURE A NEW UNIT
	  JFCL			;IGNORE ERRORS
	PUSHJ	P,AUTULK##	;RELEASE AUTCON INTERLOCK
	POP	P,KDBUNI(J)	;RESTORE KDBUNI
	PJRST	P1POPJ##	;RESTORE P1 AND RETURN
>; END REPEAT 0
	POPJ	P,		;*** FOR NOW
;ROUTINE TO INSPECT ALL ACTIVE DISKS ONCE-A-SECOND AND TIME THEM OUT IF NEEDED
;AS A BMUX KONTROLLER, FILIO WON'T DO THIS FOR US - AND THATS PROBABLY A FAVOR.
DSXTMO:	PUSH	P,U		;SAVE "REAL" U IN CASE CALLER CARES
	MOVE	T1,KDBIUN(J)	;GET POINTER TO UNIT LIST
	DSKOFF			;NO INTERRUPTS
DSXTM1:	SKIPE	U,(T1)		;PICK UP A U
	SKIPN	T3,UNITIM(U)	;SKIP AND PICK UP FILSER TIMER
	JRST	DSXTM2		;BRANCH IF NO SUCH UNIT
	HRRO	T4,UNIFNC(U)	;PICK UP FILSER OPERATION
	JUMPL	T4,DSXTM2	;BRANCH IF UNIT WASN'T REALLY ACTIVE
	SOSN	T3		;COUNT DOWN TIMER
	PUSHJ	P,DSXTM9	;BRANCH TO FIX UP A HUNG UNIT
	MOVEM	T3,UNITIM(U)	;PUT BACK INCREMENTED TIMER
DSXTM2:	AOBJN	T1,DSXTM1	;LOOP BACK FOR ALL UNITS
	DSKON			;TURN INTERRUPTS BACK ON
	PJRST	UPOPJ##		;RETURN TO CALLER

DSXTM9:	PUSHJ	P,SETBSY	;GO SET BUSY BIT FOR THIS UNIT TO RESTART IT
	PUSHJ	P,FAKCUE	;FAKE UP A CUE AND GET SOMETHING STARTED
	MOVEI	T3,DSKTIM##	;AND PREPARE TO RESTART TIMER
	POPJ	P,		;
;TABLES OF BLOCKS PER UNIT INDEXED BY UNIT TYPE

BLKPRU:	DEC	18*19*411	;TYPE 0 (3330)
	DEC	18*19*815	;TYPE 1 (3331)
	SUBTTL	MAPIO AND FRIENDS

;	ROUTINE TO SETUP UNITCP AND UNISDA

SETTCP:	MOVE	T1,UNIPCP(U)	;PICK UP PHYSICAL POINTER TO SA10 PROGRAM
	MOVEM	T1,UNITCP(U)	;SAVE AS FIRST FREE WORD
	ADDI	T1,CPALEN	;COMPUTE FIRST AVAILABLE SID-DATA AREA
	MOVEM	T1,UNISDA(U)	;SAVE POINTER
	POPJ	P,		;RETURN
;	ROUTINE TO TAKE A DF10C (IE: DEFAULT CHANNEL) CHANNEL IO LIST
;	AND CONVERT IT INTO A 3330/3331 CHANNEL PROGRAM.
;
;	PUSHJ	P,MAPIO
;	<ONLY RETURN, EVERYTHING DONE>
;
;	P1=POINTER TO CURRENT DF10 LIST, USED ONLY BY GETIOW ET AL
;
;	P3=COUNT OF CONSECUTIVE WORDS IN A SINGLE DF10 IOWD, RETURNED BY GETIOW
;	P4=ADDRESS OF CONSECUTIVE WORDS IN IOWD AS P3
;		N.B.: GETIOW COMBINES CONSECUTIVE CONTIGUOUS DF10 IOWDS

MAPIO:	PUSHJ	P,SAVE4##	;SAVE SOME ACS
	PUSHJ	P,SAVR##	;SAVE SOME MORE
	PUSH	P,M		;SAVE STILL MORE
	SKIPN	P1,UNIDRB(U)	;PICK UP POINTER TO DRB FOR THIS UNIT
	XCT	DSXXWD		;++ XFER WITHOUT DRB
	MOVE	T1,DRBBLK##(P1)	;GET 1ST BLOCK IN XFER
	MOVEM	T1,UNICBN(U)	;SAVE CURRENT BLOCK NUMBER
	MOVE	P1,DRBPRG##(P1)	;PICK UP POINTER TO DF10C LIST FOR THIS UNIT
	PUSHJ	P,DSXDVC	;COMPUTE CORRECT XFER COMMAND
	MOVE	W,T1		;SAVE COPY OF COMMAND IN W
	SETZ	M,		;M=THIS BLOCKS WORD COUNT SO FAR
MAPIO2:	PUSHJ	P,STOXFR	;INSERT A COPY OF THE XFER COMMAND
MAPIO3:	PUSHJ	P,GETIOW##	;GO GET AN IOWD PAIR T1=COUNT,T2=ADR
	JUMPE	T1,MAPIOD	;BRANCH IF "HALT"
	DMOVE	P3,T1		;P3=COUNT, P4=ADR

MAPIO4:	MOVEI	T1,BLKSIZ	;GET SIZE OF A BLOCK
	SUBI	T1,(M)		;COMPUTE NUMBER OF WORDS NEEDED
	CAILE	T1,(P3)		;SKIP IF NEEDED IS LESS THAN AVAILABLE
	JRST	MAPIO5		;BRANCH IF NEEDED IS MORE THAN AVAILABLE

;HERE WHEN #WORDS NEEDED TO FILL BLOCK IS LESS THAN WE GOT IN THIS IOWD
;IN THIS CASE, WE FINISH OUT THE BLOCK AND START ANOTHER.
	MOVE	T4,T1		;SAVE A COPY OF THE WORDS TAKEN FROM P3/P4
	PUSHJ	P,STOIOW	;STORE FINAL IOWD FOR THIS BLOCK
	PUSHJ	P,STOXFR	;INSERT A NEW COMMAND
	SUBI	P3,(T4)		;ADJUST DF10 COUNT
	ADDI	P4,(T4)		;ADJUST DF10 ADDRESS
	SETZ	M,		;NEXT BLOCK HAS 0 WORDS IN IT, SO FAR
	JUMPE	P3,MAPIO3
	JRST	MAPIO4

;HERE WHEN THE #WORDS IN THIS IOWD PAIR CANNOT FINISH THIS BLOCK.
;IN THIS CASE, WE INSERT ANOTHER PARTIAL SIOWD AND GET ANOTHER DF10 IOWD
MAPIO5:	MOVE	T1,P3		;COPY THE ACTUAL COUNT FOR THIS IOWD
	ADD	M,T1		;COUNT THESE WORDS
	TLO	T1,(1B0)	;TURN ON PARTIAL FLAG
	PUSHJ	P,STOIOW	;STORE PARTIAL IOWD FOR THIS BLOCK
	JRST	MAPIO3		;...GO GET ANOTHER DF10 IOWD
;HERE IF BOTH COUNT AND ADDRESS FIELDS WERE 0 - MUST BE A HALT
MAPIOD:	JUMPE	M,MAPIOX	;EXIT NOW IF NO PENDING BLOCK
;*** IBM DOCUMENT SAYS THAT STORAGE-CONTROL WILL TAKE CARE OF
;*** PADDING WITH ZEROES IF WE FEED IT LESS THAN "COUNT AREA DL" WORDS.
;*** ALSO, UUOCON/MONIO WILL HAVE ALREADY MADE SURE WE'RE A MULTIPLE OF
;*** THE SIZE OF A SINGLE BLOCK ANYWAY.
;***	MOVEI	T1,BLKSIZ	;GET SIZE OF A BLOCK
;***	SUBI	T1,(M)		;COMPUTE REMAINING WORDS IN BLOCK
	JUMPN	M,MAPIOY	;BRANCH IF LAST BLOCK WASN'T FILLED
MAPIOX:	MOVSI	T1,(BMXEND)	;GET A HALT
	MOVEM	R,UNITCP(U)	;GO BACK TO TOP OF CURRENT GROUP
	PUSHJ	P,STODVC	;STORE THE END CMD
	PJRST	MPOPJ##		;RESTORE M AND EXIT

;HERE IF LAST BLOCK WASN'T FILLED.
MAPIOY:	SOS	UNITCP(U)	;BACK UP "PC" TO POINT AT PARTIAL IOWD
	PMOVE	T1,UNITCP(U)	;GET PARTIAL IOWD
	TLO	T1,(1B0)	;SET "LAST IOWD" AFTER ALL
	PMOVEM	T1,UNITCP(U)	;STORE PARTIAL IOWD BACK AS A "LAST IOWD" INSTEAD
	AOS	UNITCP(U)	;PUT PC BACK TO CORRECT PLACE
	MOVE	T1,BMXWRD	;GET A HALT
	PUSHJ	P,STODVC	;INSERT THE HALT
	PJRST	MPOPJ##		;RESTORE M AND EXIT
;	ROUTINE TO INSERT THE I/O CMD INTO THE CHANNEL PROGRAM
STOXFR:	PUSHJ	P,SAVT##	;SAVE ACS
	MOVE	R,UNITCP(U)	;THIS LOCATION GETS "HALT" IF NO MORE TO DO
	PUSHJ	P,STOPOS	;GO INSERT ANY NEEDED POSITIONING
	MOVE	T1,W		;GET THE READ/WRITE COMMAND
	TRO	T1,1		;1 BIT USED BY FIXCWA
	PUSHJ	P,STODVC	;STORE DEVICE COMMAND
	AOS	UNICBN(U)	;ADVANCE BLOCK #
	POPJ	P,

;	ROUTINE TO INSERT SEEK, SET-SECTOR, OR SEARCH-ID CMDS AS NEEDED
STOPOS:	MOVE	T1,UNIDRB(U)	;POINT AT DRB
	MOVE	T1,DRBBLK(T1)	;GET 1ST BLOCK OF XFER
	CAMN	T1,UNICBN(U)	;SKIP IF NOT 1ST BLOCK OF XFER
	JRST	STOPO2		;NEEDED IF 1ST XFER OF PROGRAM
	TLNN	S,IO		;SKIP IF WRITE-CMD
	POPJ	P,		;EXIT IF READ CUZ READ CAN JUST READ-MULTITRAK
	PUSHJ	P,GETSSS	;GET T1=C, T2=H, T3=R, T4=S
	JUMPN	T3,STOPO1	;BRANCH IF NOT RECORD0 OF NEW TRACK
STOPO2:	PUSHJ	P,STOSEK	;STORE SEEK
	PUSHJ	P,STOSS		;STORE SET-SECTOR

STOPO1:	PUSHJ	P,STOSID	;ALWAYS STORE SEARCH-ID-EQUAL IF WRITE COMMAND
	POPJ	P,		;EXIT
;	ROUTINE TO INSERT A SEEK
STOSEK:	PUSHJ	P,GETSSS	;GET SEEK/SS/SID DATA
	MOVE	T4,UNISDA(U)	;POINT AT SEEK/SS/SID DATA AREA
	SUBI	T4,2		;WE WILL USE 1 WORDS
	MOVEM	T4,UNISDA(U)	;UPDATE BOTH T4 AND UNISDA
	LSH	T1,4		;T1=BYTE(8)0,0,C,C
	PMOVEM	T1,T4		;INSERT 1ST WORD OF SEEK DATA (0,0,C,C)
	ADDI	T4,1		;SETUP POINTER TO 2ND WORD
	ROT	T2,-^D 16	;POSITION SO T2=BYTE(8)H,H,0,0
	PMOVEM	T2,T4		;INSERT 2ND WORD OF SEEK DATA (H,H,-,-)
	MOVE	T1,UNISEK(U)	;GET VIRGIN SEEK CMD
	PUSHJ	P,STODVC	;INSERT SEEK CMD
	MOVE	T1,UNISDA(U)	;GET ADDRESS OF DATA
	TLO	T1,(SIOW 6,0)	;...CREATE 6 BYTE IOWD FOR BYTE(8)Z,Z,C,C,H,H
	PJRST	STODVC		;INSERT IOWD TO SEEEK DATA
;	ROUTINE TO INSERT A SET-SECTOR COMMAND AND IT'S DATA (BYTE(8)S,-,-,-)
STOSS:	MOVE	T1,UNISS(U)	;GET SET-SECTOR COMMAND
	PUSHJ	P,STODVC	;INSERT SET-SECTOR COMAND
	PUSHJ	P,GETSSS	;GET SEEK/SS/SID DATA
	MOVE	T1,UNISDA(U)	;POINT AT SECTOR DATA
	SUBI	T1,1		;BACKUP 1
	MOVEM	T1,UNISDA(U)	;UPDATE UNISDA
	DPB	T4,[POINT 8,T2,7] ;T2=BYTE(8)S,?,?,?
	PMOVEM	T2,T1		;STORE T2 INTO DATA AREA
	TLO	T1,(SIOW 1,0)	;GET "IOWD" FOR 1 BYTE OF DATA
	PJRST	STODVC		;STORE IOWD ROR SS DATA
;ROUTINE TO INSERT A SEARCH-ID-EQUAL COMMAND AND IT'S BACKWARDS-TIC
STOSID:	PUSHJ	P,GETSSS	;GET SEEK/SS/SID DATA
	MOVE	T4,UNISDA(U)	;POINT AT DATA BUFFER
	SUBI	T4,2		;NEED 2 WORDS FOR S-I-E ARGS
	MOVEM	T4,UNISDA(U)	;UPDATE UNISDA
	ROT	T1,-^D 16	;MAKE T1=C,C,0,0
	DPB	T2,[POINT 16,T1,31] ;MAKE T1=C,C,H,H
	PMOVEM	T1,T4		;INSERT DATA INTO BUFFER
	ADDI	T4,1		;ADVANCE TO 2ND WORD OF DATA
	ADDI	T3,1		;MAKE T3'S "R" START AT 1 NOT ZERO
	ROT	T3,-^D 8	;MAKE T3=R,0,0,0
	PMOVEM	T3,T4		;INSERT R INTO DATA BUFFER
	MOVE	T1,UNISIE(U)	;GET VIRGIN SEARCH ID
	PUSHJ	P,STODVC	;STORE DEVICE COMMAND
	MOVE	T1,UNISDA(U)	;GET ADDRESS OF DATA
	TLO	T1,(SIOW 5,0)	;5 BYTE WORD COUNT (IE: C,C,H,H,R)
	PUSHJ	P,STODVC	;STORE IOWD FOR SEARCH-ID-EQUAL
	MOVE	T1,UNITCP(U)	;GET ADDRESS OF NEXT CMD
	SUBI	T1,2		;BACK UP SO WE ARE POINTING AT SEARCH-EQUAL
	TLO	T1,(TIC)	;AND CREATE A TIC CMD THAT GOES TO SEARCH-EQUAL
	PUSHJ	P,STODVC	;INSERT TIC TO JUMP BACK TO SEARCH EQUAL
	POPJ	P,		;RETURN
;ROUTINES TO INSERT WORDS INTO THE CHANNEL PROGRAM
STOIOW:	IMUL	T1,[-100,,0]	;POSITION THE COUNT FIELD
	TDO	T1,P4		;INSERT THE ADDRESS FIELD
STODVC:	PMOVEM	T1,UNITCP(U)	;STORE COMMAND IN THE CHANNEL PROGRAM
	AOS	UNITCP(U)	;POINT AT NEXT WORD
	POPJ	P,		;RETURN
;	ROUTINE TO SETUP TO DO A SENSE
SETSNS:	PUSHJ	P,SETTCP	;SETUP UNITCP/UNISDA(U)
	MOVE	T1,UNISNC(U)	;GET SENSE CMD
	PUSHJ	P,STODVC	;STORE IT
	MOVE	T1,UNISNC+1(U)	;GET IOWD
	PUSHJ	P,STODVC	;STORE IT
	PUSHJ	P,STOHLT	;STORE A HALT
	PJRST	SETDVP		;SETUP DEVL ENTRY AND EXIT
;	ROUTINE TO INSERT A NO-OP
STONOP:	MOVE	T1,UNINOP(U)	;GET THE PROTOTYPE
	JRST	STODVC		;INSERT IT AND EXIT

;	ROUTINE TO INSERT A HALT
STOHLT:	MOVE	T1,BMXWRD	;GET THE HALT
	PJRST	STODVC		;INSERT IT AND EXIT
;	ROUTINE TO INSERT A READ/WRITE-RECORD-0
STORC0:	PUSHJ	P,STORR0	;STORE READ-RECORD-0
	PUSHJ	P,STOSR0	;STORE SEARCH-RECORD-0
	PUSHJ	P,STOWR0	;STORE WRITE-RECORD-0
	POPJ	P,		;EXIT
;	ROUTINE TO INSERT R-R-0
STORR0:	MOVE	T1,UNIRR0(U)	;GET CMD
	PUSHJ	P,STODVC	;STORE IT
	MAP	T1,UNIR0D(U)	;FIND ADDRESS OF R0 BUFFER
	TLZ	T1,(MP.NAD)	;TURN OFF MAP BITS
	TLO	T1,(SIOW 8,0)	;TURN ON WORD COUNT FIELD
	PJRST	STODVC		;STORE R-R-0
;	ROUTINE TO INSERT S-R-0
STOSR0:	PUSHJ	P,STOSID	;INSERT SEARCH-ID
	MOVE	T1,UNISDA(U)	;GET FREE ID WORD
	ADDI	T1,1		;POINT AT "R" WORD
	SETZ	T2,		;RECORD 0 DATA
	PMOVEM	T2,T1		;FORCE DATA TO BE C,C,H,H,0
	POPJ	P,		;
;	ROUTINE TO INSERT W-R-0
STOWR0:	PUSHJ	P,DSXDVC	;GET A WRITE COMMAND
	TDO	T1,[F.BYTE]	;SPECIFY BYTE MODE
	PUSHJ	P,STODVC	;STORE THE WRITE COMMAND
	MAP	T1,UNIR0D(U)	;POINT AT R0 BUFFER
	TLZ	T1,(MP.NAD)	;TURN OFF MAP BITS
	TLO	T1,(SIOW 8,0)	;TURN ON WORD COUNT FIELD
	PJRST	STODVC		;INSERT IT AND EXIT
;ROUTINE TO GET SEEK, SET-SECTOR, AND SEARCH-ID INFO
;	T1/ GETS CYLINDER
;	T2/ GETS HEAD
;	T3/ GETS RECORD BASED ON 0
;	T4/ SECTOR

SIZGAP==^D135	;BYTES IN GAPS OF NORMAL RECORD
SIZR0==^D237	;BYTES IN GAPS FOR R0 RECORD

SECTRK==^D128	;SECTORS FOR TRACK
BYTTRK==^D13440	;MAX BYTES PER TRACK

;*** CAN'T ACTUALLY USE UNYBPY/UNYBPT DURING ONCE ONLY ATTACH!
GETSSS:	MOVE	T1,UNICBN(U)	;GET CURRENT BLOCK NUMBER
;***	LDB	T2,UNYBPY##	;GET BLOCKS PER CYLINDER
	MOVEI	T2,^D342	;***
	IDIV	T1,T2		;COMPUTE T1=CYL#, T2=REMAINDER
;***	LDB	T3,UNYBPT##	;GET BLOCKS PER TRACK
	MOVEI	T3,^D18		;***
	IDIV	T2,T3		;COMPUTE T2=TRACK/HEAD, T3=RECORD
	PUSH	P,T3		;SAVE R
	IMULI	T3,SIZGAP+BLKBYT ;(REC_NUM-1) * (BYTES_DISK_BLOCK)
	ADDI	T3,SIZR0	;+ SIZE_OF_R0 = BYTE_#_OF_START_OF_BLOCK
	IMULI	T3,SECTRK	;* SECTORS_PER_TRACK
	IDIVI	T3,BYTTRK	;/ BYTES_PER_TRACK = SECTOR#
	MOVE	T4,T3		;GET INTO T4
	POP	P,T3		;RESTORE R INTO T3
	POPJ	P,		;RETURN

;*** GETSSS PROBABLY DEPENDS TOO MUCH ON PHYSICAL CONSTANTS OF THE 3330/3331
;*** PACK TO BE USEFUL AS/IS FOR OTHER DRIVES.
;ROUTINE TO TAKE THE SA10 UNDONE PC AND RESIDUAL COUNT
; AND USE IT TO CALCULATE AND UPDATE THE "CONTROL WORD" DATA FOR THE "DF10"
;SCAN THE SA10 PROGRAM FORWARDS UNTIL SIMULATED PC IS BEYOND THE DONE PC.
;KEEP TRACK OF EACH COMMAND AND COUNT BLKSIZ WORDS FOR EACH COMMAND.

FIXCWA:	JUMPE	S,FIXGUD		;BRANCH IF NO ERRORS
FIXBAD:	SKIPN	UNIDRB(U)		;SHOULD BE DRB IF XFER OP
	STOPCD	.+1,INFO,DSXXWD,	;++XFER WITHOUT DRB
	PUSHJ	P,SAVT##		;SAVE T ACS IF INTERRUPT LEVEL
	PUSHJ	P,SAVR##		;SAVE
FIXCW0:	LDB	R,[POINT 22,UNITCA(U),35];GET THE UNDONE PC
	MOVE	T1,UNIPCP(U)		;GET THE PROGRAM POINTER
	ADDI	T1,4			;MAKE IT POINT AT S-I-E CMD
	SETZ	T4,			;ASSUME 0 WORDS IF HEADER ERROR
FIXCW1:	PMOVE	T2,T1			;PICK UP THE "THING" THERE
	CAME	T2,BMXWRD		;SKIP IF A HALT (IE: A COMMAND)
	CAML	T1,R			;SKIP IF NOT YET THE UNDONE PC
	JRST	FIXCC1			;BRANCH IF UNDONE PC
	TLNN	T2,(F.XEC)		;SKIP IF EXECUTE
	AOJA	T1,[TLC T2,(TIC)	;TURN OFF TIC BIT IF TIC
		    CAML T2,T1		;SKIP IF BACKWARD TIC
		    MOVE T1,T2		;"EXECUTE" FORWARD TICS
		    JRST FIXCW1]	;
	TRNE	T2,1			;CHECK FOR AN XFER COMMAND
	JRST	FIXCW2			;BRANCH IF AN XFER COMMAND
	ADDI	T1,2			;MUST BE S-I-E, SS, SEEK, ETC, SO SKIP
	JRST	FIXCW1			;...OVER THEM AND TRY AGAIN
FIXCW2:	MOVEM	T1,UNILPC(U)		;REMEMBER LAST PC OF XFER CMD
FIXCW3:	ADDI	T1,1			;STEP TO NEXT IOWD
	PMOVE	T2,T1			;GET IOWD
	CAMN	T1,R			;SKIP IF NOT UNDONE PC
	JRST	FIXCR1			;BRANCH IF UNDONE PC
	MOVE	T3,T2			;COPY THE IOWD
	TLO	T2,(1B0)		;TURN ON SIGN BIT
	ASH	T2,-^D24		;T2=RIGHT JUSTIFIED -VE WORD COUNT
	SUB	T4,T2			;T4=+VE WORD COUNT SO FAR
	JUMPG	T3,FIXCW3		;BACK FOR MORE IF THIS IS PARTIAL IOWD
	AOJA	T1,FIXCW1		;FOUND LAST IOWD, NOW STEP TO NEXT CMD
;HERE IF WE FIND THE UNDONE PC POINTING AT AN IOWD, MUST BE A RESIDUAL
;T1=PC OF IOWD
;T2=THE IOWD
;T3=JUNK
;T4=TOTAL WORDS SO FAR
FIXCR1:	TLO	T2,(1B0)		;MAKE IT APPEAR -VE EVEN IF NOT
	ASH	T2,-^D24		;...SO THIS ASH WORKS
	LDB	T3,[POINT 12,UNITCA(U),11] ;GET RESIDUAL FROM SUBCHANNEL BLOCK
	ASH	T3,-^D24		;RIGHT JUSTIFY IT TOO
	SUB	T2,T3			;COMPUTE +VE XFER'ED ON THIS IOWD
	ADD	T4,T2			;ADD INTO TOTAL
	JRST	FIXCC1			;JOIN COMMAND CODE
;HERE IF WE FIND THE UNDONE PC POINTING AT A COMMAND
;NO RESIDUAL SINCE WE'RE POINTING AT A COMMAND, NOT IOWD
; NOW, SCAN DF10 PROGRAM UNTIL WE ENCOUNTER T4 WORDS
FIXCC1:
FIXCW5:	SKIPN	T1,UNIDRB(U)		;POINT AT DRB
	XCT	DSXXWD			;++XFER WITHOUT DRB ??
	MOVE	T2,T4			;COPY WORD COUNT
	IDIVI	T2,^O200		;COMPUTE # OF GOOD BLOCKS
	ADDM	T2,DRBGOD##(T1)		;SAVE GOOD COUNT IN DRB
	MOVE	T1,DRBPRG(T1)		;POINT AT DF10 PROGRAM

;SCAN DF10 PROGRAM UNTIL WE "XFER" (T4) WORDS
FIXCW6:	LDB	T2,[POINT 14,(T1),13]	;T2=WORD_COUNT
	LDB	T3,[POINT 22,(T1),35]	;T3=ADDRESS
	JUMPE	T2,FIXCW8		;BRANCH IF HALT OR GOTO
	IOR	T2,[-1,,740000]		;EXTEND THE SIGN OF T2 TO 36 BITS
	MOVNS	T2			;MAKE T2=+VE COUNT
	MOVE	R,T3			;COPY BASE ADDRESS OF THIS XFER
	CAMG	T2,T4			;SKIP IF THIS IOWD IS MORE THAN EENOUGH
	AOJA	T1,[ADD  R,T2		;...IF NOT ENUF, COMPUTE FINAL XFER ADR
		    SUB  T4,T2		;...IF NOT ENUF, ADJUST RUNNING REMAIN
		    JRST FIXCW6]	;...IF NOT ENUF, GO GET ANOTHER IOWD
	ADD	R,T4			;COMPUTE FINAL XFER ADDRESS
FIXCW7:	MOVE	T2,KDBICP(J)		;T2=POINTER TO "DF10 ICW PAIR"
	DPB	T1,[POINT 14,1(T2),13]	;INSERT FINAL CWAD
	DPB	R, [POINT 22,1(T2),35]	;INSERT FINAL XFER ADDRESS
	POPJ	P,

FIXCW8:	JUMPE	T3,FIXCW7		;BRANCH IF WE FOUND A DF10 HALT
	MOVE	T1,T3			;PERFORM A "JMP" IF ONE IS FOUND
	JRST	FIXCW6			;LOOP FOR MORE IOWD
;HERE IF THERE WASN'T AN ERROR, JUST USE COMPUTED DF10 WORD AND DRBGUD
FIXGUD:	PUSHJ	P,SAVT##		;SAVE T ACS IF INTERRUPT LEVEL
	SKIPN	T1,UNIDRB(U)		;POINT AT DRB
	XCT	DSXXWD			;++XFER WITHOUT DRB ??
	MOVE	T2,DRBNUM(T1)		;GET EXPECTED BLOCKS
	MOVEM	T2,DRBGOD(T1)		;MAKE IT ACTUAL BLOCKS
	MOVE	T2,KDBCHN(J)		;GET CHANNEL
	MOVE	T3,KDBICP(J)		;WHERE TO PUT IT
	MOVE	T2,CHNTCW(T2)		;GET WHAT FILIO WANTS
	MOVEM	T2,1(T3)		;PUT IT WHERE FILIO LOOKS
	POPJ	P,
	$LOW			;LOW SEG STUFF
BMXWRD:	BMXEND			;MUST BE LOW SEG SO SA10 GETS ACTUAL ADDR
	$HIGH
SUBTTL	THE END

DSXLIT:	$LIT
DSXEND:	END