Google
 

Trailing-Edge - PDP-10 Archives - cust_sup_cusp_bb-x130c-sb - 10,7/unsmon/dsxkon.mac
There are 4 other files named dsxkon.mac in the archive. Click here to see a list.
TITLE	DSXKON - CONTROLLER ROUTINE FOR 3330/3331 DISK - V5
SUBTTL	JOSEPH A. DZIEDZIC/JAD (DEC) & JEFF GUNTER/JEG (ADP)	23-AUG-88
;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,5		;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)


;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

UNYHNM:	POINT	UNSHNM,UNISCY+1(U),UNNHNM	;HEAD NUMBER (SEEK/SEARCH)
UNYRNM:	POINT	UNSRNM,UNISCY+1(U),UNNRNM	;RECORD NUMBER (SEEK/SEARCH)
UNYSSC:	POINT	UNSSEC,UNISEC(U),UNNSEC		;SECTOR NUMBER

;$$*** MAKE MORE EXTENSIVE USE OF THE ATTACH CODE SO THAT SENSE'S, LONG
;$$*** MULTI_TRACK XFERS AND THE LIKE CAN BELIEVE THAT THEY'RE DOING IT
;$$*** LINEARLY.
;$$***
;$$*** MAKE ECC CORRECTION WORK BY STASHING INFO AT INT LEVEL, AND THEN
;$$*** MAKE THE WORK HAPPEN AT xxxECC TIME, AND ALWAYS RETURNS 0S BACK TO
;$$*** FILIO SO HE DOESN'T DO ANYTHING ON HIS OWN.
;$$***
;$$*** MAKE DSXPOS DO A READ/WRITE OF RECORD ZERO AFTER THE SEEK SO THAT
;$$*** WE CAN SEE WRITE-LOCK STATUS.  COPY INTERVENTION-REQUIRED AND LOCKED
;$$*** INTO UDB, AND THEN CHECK IT AT OPRED/OPWRT TIME SO WE DO A "CONECT"
;$$*** ROUTINE TO CATCH PROBLEMS AT "UUO LEVEL" ALA RPXKON ET AL.
	SUBTTL	DEVICE-DEPENDENT DEFINITIONS

;3330 DEVICE COMMANDS

O.SEEK==7		;SEEK
O.SIE==61		;SEARCH ID EQUAL
O.READ==6		;READ
O.WRIT==5		;WRITE
O.SNS==4		;SENSE I/O
O.SFM==37		;SET FILE MASK
O.RREL==244		;READ, RESET ERROR LOG
O.SSEC==43		;SET SECTOR
O.RCAL==23		;RECALIBRATE
O.NOP==3		;NO-OP
O.MTRD==206		;MULTI-TRACK READ
O.MTOP==200		;MULTI-TRACK OPERATION ('OR' WITH READ OR SEARCH)

FM.WRI==200		;FILE MASK TO ALLOW WRITES AND SEEKS
FM.WRF==100		;FILE MASK TO ALLOW WRITE HEADERS AND SEEKS

BLKMSK==777		;THIS PART OF DEVICE COMMAND WORD IS IGNORED
			;BY SA10 AND WE USE IT TO STORE RELATIVE BLOCK #,
			;USED TO CALCULATE # BLOCKS XFER'ED UP TO ERROR PC

;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
SN.TWE==1B31		;TERMINATION WORD ERROR
	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
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
DSXOVR:!BLOCK	1		;COUNT OF OVERRUNS REPORTED IN STATISTICS
				; LH=COMMAND RETRIES  RH=DATA RETRIES
DSXUCC:!BLOCK	1		;COUNT OF UNCORRECTABLE DATA CHECKS
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
UNICHP:!			;BEGINNING OF CHANNEL PROGRAM
UNISEK:!BLOCK	2		;SEEK PROGRAM(SEEK, IOWD TO DATA)
UNINOP:!BLOCK	1		;NO-OP TO FORCE WAIT FOR DEV-END OF SEEK
UNISTP:!BLOCK	1		;BMUX-HALT OR TIC.+1
UNISET:!BLOCK	2		;SET-SECTOR, IOWD TO SECTOR-DATA
UNISIE:!BLOCK	1		;SEARCH ID EQUALS
UNISID:!BLOCK	2		;POINTER TO ID DATA, TIC TO UNISIE
UNIIOT:!BLOCK	1		;TRANSFER TO XFER PART OF CHAN PROG
UNISNP:!BLOCK	3		;SENSE I/O PROGRAM, BMXEND
UNIRWC:!BLOCK	1		;PROTOTYPE DVW FOR CHANNEL PROGRAMS
UNISND:!BLOCK	SNSLEN		;SENSE DATA

;UNIFMW:!			;FILE MASK IN BITS 0-7
; UNNFMW==^D7			;RIGHTMOST BIT FOR FILE MASK
UNISEC:!BLOCK	1		;SECTOR NUMBER IN BITS 16-23
  UNSSEC==^D8			;SIZE OF SECTOR NUMBER FIELD
  UNNSEC==^D23			;RIGHTMOST BIT
UNISCY:!BLOCK	2		;CYLINDER, HEAD, RECORD NUMBER (SEEK, SEARCH)
  SCYLSH==4			;BITS TO SHIFT TO PUT CYLINDER NUMBER IN POSITION
  UNSHNM==^D16			;SIZE OF HEAD FIELD
  UNNHNM==^D15			;RIGHTMOST BIT (UNISCY+1)
  UNSRNM==^D8			;SIZE OF RECORD NUMBER FIELD
  UNNRNM==^D23			;RIGHTMOST BIT (UNISCY+1)

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
  UNPCUE==10			;     BIT SET IF RECEIVE CONTROL-UNIT-END
				;      OTHER UNITS MAY HAVE RECEIVED BUSY
  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
UNINUM:!BLOCK	1		;NUMBER OF BLOCKS TO TRANSFER (LIKE CHNNUM)
UNICSW:!BLOCK	1		;SAVED COPY OF SACSW FOR DEBUGGING
UNITCA:!BLOCK	1		;SAVED COPY OF SATCA FROM BEFORE SENSE COMMAND
UNICBN:!BLOCK	1		;CURRENT BLOCK NUMBER (USED WHILE BUILDING PROGRAM)
UNIECN:!BLOCK	1		;ERROR COUNTER BETWEEN RECALIBRATES
UNIRCT:!BLOCK	1		;RECALIBRATE COUNTER
UNILPC:!BLOCK	1		;PHYSICAL PC OF FAILING XFER CMD
UNICOE:!BLOCK	1		;COUNT OF CORRECTABLE ERRORS CORRECTED WITH ECC
UNITCW:!BLOCK	1		;EXPECTED TERMINAL COUNT,ADDR WORD
UNITNM:!BLOCK	1		;NUMBER OF GOOD BLOCKS IN PREVIOUS PARTS OF XFER
				; USED IF ECC APPLIED IN MIDDLE OF XFER
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
UNISDA:!BLOCK	1		;PHYSICAL ADDRESS OF SEARCH-ID DATA BUFFER
				;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 UDB
DSXUDB:	UDBBEG	(UNI,DSXULN)
	SETWRD	(UNIRCL,<F.XEC!F.CC!F.NMT!<O.RCAL>B15!<000>B23>)	;*
	SETWRD	(UNISEK,<F.XEC!F.CC!F.BYTE!<O.SEEK>B15!<000>B23>)	;*
	SETWRD	(UNISEK+1,<BYTE (1)1(DCSSIZ)-6(DCSADR)UNISCY>)		;&
	SETWRD	(UNINOP,<F.XEC!F.CC!F.NMT!<O.NOP>B15>)			;*
	SETWRD	(UNISET,<F.XEC!F.CC!F.BYTE!F.OFP!<O.SSEC>B15!<000>B23>) ;*
	SETWRD	(UNISET+1,<BYTE (1)1(DCSSIZ)-1(DCSADR)UNISEC>)		;&
	SETWRD	(UNISIE,<F.XEC!F.CC!F.BYTE!F.OFP!<O.SIE>B15!<000>B23>)	;*
	SETWRD	(UNISID,<BYTE (1)1(DCSSIZ)-5(DCSADR)UNISCY>)		;&
	SETWRD	(UNISID+1,<TIC UNISIE>)					;&
	SETWRD	(UNIIOT,<TIC 0>)
	SETWRD	(UNISNP,<F.XEC!F.CC!F.BYTE!<O.SNS>B15!<000>B23>)	;*
	SETWRD	(UNISNP+1,<BYTE (1)1(DCSSIZ)-^D24(DCSADR)UNISND>)	;&
	SETWRD	(UNISNP+2,<BMXEND>)
	SETWRD	(UNIRWC,<F.XEC!F.CC!F.SLI!<O.MTRD>B15!<000>B23>)	;*
	UDBEND

;ENTRIES TAGGED BY "*" HAVE THE DEVICE ADDRESS INSERTED BY DSXUNI (DSXCFG)
;ENTRIES TAGGED BY "&" HAVE THE UDB ADDRESS INSERTED BY DSXUNI (DSXCFG)
	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
;RETURN CPOPJ IF ECC WASN'T POSSIBLE (EG: ECC BAD BITS NOT IN DATA FIELD)
;	CPOPJ1 WITH ERROR ALREADY CORRECTED (BUT FILIO DOESN'T KNOW IT)
;		AND WITH T1 = -1 SO FILIO THINKS ECC WASN'T REALLY REQUIRED

DSXECC:	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
	JRST	DSXEC2		;TELL FILIO EVERYTHING ISOK
	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	DSXEC2		;BRANCH (WE WIN!) IF NO BAD DATA
	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
	SOJG	P3,DSXEC1	;LOOP FOR ALL NIBBLES NEEDING FIXING

DSXEC2:	MOVEI	T1,BLKSIZ##+1	;MAKE FILIO BELIEVE ERROR IS OUTSIDE DATA
	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 (?)
	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
;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
	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:	PUSH	P,M		;SAVE UUO
	PUSHJ	P,SETCHP	;SET UP CHANNEL PROGRAM
	POP	P,M		;RESTORE UUO
DSXGO1:	MOVEI	T2,SO.GO	;GO FLAG
	PUSHJ	P,CNOSET	;SET FLAG AND DO THE CONO
	MOVE	T1,UNIDRB(U)	;RESTORE DRB, IF ANY
	PJRST	CPOPJ1##	;GIVE OK (SKIP) RETURN

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	;SEE IF ALREADY TRIED SETTING GO FLAG AGAIN
	TDNE	T1,UNIFNC(U)	;SKIP IF NO
	JRST	DSXST1		;YES, DO THE RECOVERY THING
	IORB	T1,UNIFNC(U)	;NO, REMEMBER THAT WE HAVE
	PUSHJ	P,CUERS2	;SET TO RESTART CHANNEL PROG
	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,BMXWRD	;GET ADDR OF NULL CHANNEL 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
;$$ HERE WHEN SA10 IS SO "HUNG" IT WON'T EVEN START ANY CHANNEL PROGRAM
;$$ COME HERE TO PICK UP SOME USEFULL INFO, TAKE STOPCODE DUMP,
;$$ AND THEN RESET THE AFFLICTED CHANNEL.
;$$ ITEMS WITH "**" ARE SAVED IN THE ACS IN THE DUMP.
;$$ (MIGHT BE WIPED OUT ON RESET OR DURING I/O TO SA10 DISK)
	PUSHJ	P,SAVE1##	;SAVE P1
;$$ THIS CODE HERE IS INTENDED TO LOAD P1 WITH DEVL ENTRY
;$$ FOR WHATEVER UNIT LAST TALKED TO THE SA10.
;$$ TO DO THIS RIGHT, WE MUST FIND BEGINNING OF DEVL LIST FOR THE
;$$ APPROPRIATE SUBCHANNEL AND SCAN THE CU ADDRESS FIELD BY HAND FOR A
;$$ MATCH (AND REMEMBER THAT THERE MIGHT NOT BE A MATCH)
;$$	MOVE	T1,@DSXCSW(J)	;GET CHANNEL STATUS WORD
;$$	LDB	T1,[POINT CSSUNI,T1,CSNUNI] ;GET UNIT NUMBER
;$$	MOVE	P1,@DSXCHP(J)	;** P1=DEVICE LIST ENTRY FOR UNIT OF LAST INT
DSXST2:	TRO	T2,SO.CLK+SO.SDR+SO.DT0+DSKCHN## ;SA10 CONO TO STOP CLOCK
				; AND SET TO READ MICRO-PC
	XCT	DSXCO2(J)	;DO THE CONO
	XCT	DSXDTI(J)	;** T1=DATAI=SA10 MICRO-PC
	HLRZ	T2,T1		;T2=SUBCHANNEL NUMBER FROM DATAI
	HRRZ	T3,DSXSCH(J)	;T3=SUBCHANNEL WE WANT
	CAME	T2,T3		;SKIP IF OUR SUBCHANNEL
	JRST	DSXST2		;STEP CLOCK TILL GET TO OUR SUBCHANNEL
	XCT	DSXCI2(J)	;DO CONI INTO T2
	MOVE	T4,T2		;** T4=SA10 CONI
	TRO	T2,SO.CLK+SO.SET+DSKCHN## ;CONO TO START CLOCK AGAIN
	XCT	DSXCO2(J)	;START SA10 CLOCK AGAIN
	MOVEI	T2,@DSXCSW(J)	;T2=ADDR OF CHANNEL STATUS WORD
	MOVE	T3,1(T2)	;** T3=TERMINATING COUNT,ADDR WORD
	MOVE	T2,(T2)		;** T2=CHANNEL STATUS WORD
	STOPCD	.+1,DEBUG,SAH,	;++ SA10 HUNG
	MOVEI	T2,SO.RCH	;FUNCTION TO RESET SUBCHANNEL
	PUSHJ	P,CNOCLR	;DO IT - SHOULD GET SA10 STARTED AGAIN
	MOVE	T2,DSXGSN(J)	;GET GLOBAL SA10 SUBCHANNEL
	MOVE	T2,SAXDVP##(T2)	;GET START OF DEVICE LIST FOR SUBCHANNEL
	MOVEI	T3,DL.TRM	;MARK EVERYTHING TERMINATED SO WONT RESET AGAIN RIGHT AWAY
DSXST3:	PMOVE	T1,T2		;GET ANOTHER DEVICE LIST ENTRY
	JUMPE	T1,DSXST4	;BRANCH IF OUT OF DEVICE LIST ENTRIES
	DPB	T3,DSYDLC	;STORE TERMINATED CODE
	CAME	T2,DSXDVP(U)	;SKIP IF THIS IS THE AFFLICTED UNIT
	PMOVEM	T1,T2		;STORE CHANGED CODE BACK INTO DEVICE LIST
	AOJA	T2,DSXST3	;LOOP FOR DEVICE LIST FOR THIS SUBCHANNEL
DSXST4:	PUSHJ	P,DSXGO1	;SET GO FLAG - WILL GET DISK INTERRUPT
	  JFCL			;IGNORE SKIP RETURN
	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
	MOVE	T1,@DSXCSW(J)	;GET CHANNEL STATUS WORD
	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
	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	DEVERR		;CONSIDER THAT A DEVICE ERROR
	TLNE	T1,(S.CUE)	;SKIP UNLESS CONTROL-UNIT-END
	JRST	HCUE		;GO HANDLE THAT
	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
CHNERR:
DEVERR:	TRO	S,IODERR	;SOME ERROR DECTECTED
	TRO	F,SN.CDE	;CHANNEL DETECTED ERROR
;FALL INTO DSXRET
DSXRET:	MOVSI	T1,UNPCUE	;GET CONTROL-UNIT-END BIT
	TDNN	T1,UNIFNC(U)	;SKIP IF CONTROL-UNIT-END AFTER BUSY
	JRST	NOTCUE		;NO, NORMAL CASE
	ANDCAM	T1,UNIFNC(U)	;CLEAR IT NOW THAT IT'S PROCESSED
	PUSHJ	P,CUECHK	;SET UP UNITS WAITING BECAUSE BUSY
	JUMPE	T1,NOTCUE	;JUMP IF NO SUCH UNITS
	PUSHJ	P,DSXGO1	;RESTART THOSE UNITS
	  JFCL			;
NOTCUE:	MOVE	T1,UNIFNC(U)	;GET CURRENT FUNCTION
	TLNE	T1,UNPSNS	;SKIP IF NOT SENSE AFTER UNIT CHECK
	JRST	DSXSNS		;IT WAS
	MOVE	T2,@DSXCSW(J)	;GET CHANNEL STATUS WORD
	MOVEM	T2,UNICSW(U)	;SAVE FOR DEBUGGING
	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
	  JRST	HCUE1		;UNIT WAS BUSY, RETRY
	TRO	S,IODERR	;FORCE RETRY
	TRO	F,SN.RTR	;RETRY REQUESTED BY CONTROLLER
NOTCU1:	MOVEI	T2,@DSXCSW(J)	;GET ADDR OF CHANNEL STATUS WORD
	MOVE	T2,1(T2)	;GET TERMINAL COUNT AND COMMAND ADDR
	MOVEM	T2,UNITCA(U)	;SAVE FOR LATER IN CASE ANOTHER UNIT FINISHES
	PMOVE	T1,UNIDVP(U)	;GET DEVICE LIST ENTRY INTO T1
	LDB	T4,DSYDLC	;GET TERMINATING ADDRESS FROM DEVICE LIST
	CAME	T4,T2		;COMPARE WITH STATUS TERMINATING ADDR
	JRST	NOTCU2		;SHOULD BE THE SAME
	LDB	T4,DSYDLO	;GET SA10 STATUS FROM DEVICE LIST
	CAIE	T4,DL.TRM	;SKIP IF MARKED TERMINATED
NOTCU2:	AOS	DSXFST		;COUNT FUNNY STATUS CONDITIONS
	MOVE	T2,@DSXICW(J)	;GET INITIAL COMMAND WORD
;*** WRONG?
;***	CAME	T2,DSXSTA(J)	;SHOULDN'T HAVE CHANGED
;***	AOS	DSXFST		;FUNNY STATUS
	MOVE	T1,UNIFNC(U)	;RESTORE T1 AGAIN
	MOVE	T2,UNIDES(U)	;GET UNIT DESCRIPTION
	TLNE	T2,UNPOFL	;SKIP UNLESS UNIT WAS OFF-LINE
	JRST	DSXRT4		;MUST NOW BE ON-LINE
	MOVE	T2,UNISTS(U)	;GET UNIT STATUS
	CAIL	T2,OWCOD	;SKIP IF NOT OPERATOR WAIT
	JRST	DSXRT3		;OPERATOR WAIT
	JRST	DSXRT2		;SKIP THIS

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
DSXRT4:	MOVSI	T1,UNPONP	;BIT TO MARK POSITION FOR ON-LINE INTERRUPT
	MOVEM	T1,UNIFNC(U)	;SET UNIFNC TO HANDLE NEXT INTERRUPT
	MOVEI	T1,^D411	;GET A LARGE CYLINDER NUMBER
	LSH	T1,SCYLSH	;SHIFT INTO POSITION FOR SEEK ARGS
	MOVEM	T1,UNISCY(U)	;STORE FIRST WORD OF SEEK ARG
	SETZM	UNISCY+1(U)	;ZERO SECOND WORD OF SEEK ARG
	MOVEI	T1,UNISND-1(U)	;ADDR-1 OF PLACE TO PUT SEEK COMMAND
	PUSH	T1,UNISEK(U)	;PUT IN SEEK COMMAND
	PUSH	T1,UNISEK+1(U)	;AND DCW
	PUSH	T1,BMXWRD	;END OF CHAN PROG
	MOVEI	T2,UNISND(U)	;ADDR OF CHAN PROG
	PUSHJ	P,CPYWAT	;SET UP, START CHAN PROG, RETURN AFTER INT

;HERE AFTER SEEK

	TLNN	T1,(S.UC)	;SKIP IF UNIT CHECK CAME UP
	JRST	DSXR4L		;NO, MUST BE 3331 - GOOD THING WE CHECKED
	MOVEI	T2,UNISNP(U)	;ADDR OF SENSE CHAN PROG
	PUSHJ	P,CPYWAT	;SET UP, START CHAN PROG, RETURN AFTER INT

;HERE AFTER SENSE AFTER SEEK

	MOVE	T1,UNISND(U) ;GET SENSE DATA
	TLNN	T1,(S00REJ+S00IRQ) ;SKIP IF ILLEGAL SEEK
	JRST	DSXR4L		;MUST HAVE FAILED FOR SOME OTHER REASON

;HERE IF UNIT IS 3330
	MOVEI	T2,.UT330	;UNIT TYPE FOR 3330
	JRST	DSXR4A		;SET IT UP

;HERE IF UNIT IS 3331
DSXR4L:	MOVEI	T2,.UT331	;UNIT TYPE FOR 3331
DSXR4A:	MOVE	T1,BLKPRU(T2)	;GET BLOCKS/UNIT BASED ON UNIT TYPE
	MOVEM	T1,UNIBPU(U)	;STORE BLOCKS PER UNIT
	MOVEM	T1,UNIBPM(U)	;AND INCLUDING MAINTENANCE CYLINDERS
	DPB	T2,UNYUTP##	;STORE NEW UNIT TYPE
	SETZM	UNIFNC(U)	;MAKE SURE NEW INTS DONT DISPATCH
	PUSHJ	P,SNSCLR	;CLEAR OUT SENSE DATA TO PREVENT CONFUSION

DSXRT3:	MOVEI	T1,OPPOS	;MAKE LOOK LIKE FREE INTERUPT SINCE NOW ON-LINE
	MOVEI	T2,400000	;GET SIGN BIT
	MOVN	T4,T3		;MINUS UNIT NUMBER
	LSH	T2,(T4)		;POSITION AN ATTENTION FLAG
	TRO	T3,(T2)		;SO LOOKS LIKE RP10 FREE INTERRUPT
DSXRT2:	MOVE	T2,F		;NO SENSE DATA, USE INTERNAL FLAGS FOR "CONI"
	JRST	DSXRT1

	$LOW
DSXFST:	0			;COUNT OF TIMES FUNNY STATUS CAME UP
				;EITHER DEVICE NOT MARKED TERMINATED, OR
				;TERMINATING ADDR IN DEVICE LIST NOT SAME AS STATUS
				;ALSO IF INITIAL COMMAND WORD CHANGES

LOWZRO:	0			;A WORD CONTAINING ZERO
	$HIGH
; HERE WHEN SENSE COMMAND DONE

DSXSNS:	MOVSI	T2,UNPSNS	;BIT MARKING SENSE COMMAND
	ANDCAM	T2,UNIFNC(U)	;CLEAR THE BIT SINCE THE SENSE IS DONE
	MOVE	T2,UNISND+S02WRD(U) ;GET WORD WITH CORRECTION BIT
	TLNE	T2,(D02COR)	;SKIP IF NOT A KNOWN CORRECTABLE ERROR
	TRO	S,IOECCX	;LET FILIO KNOW ECC CORRECTION WOULD WORK
	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

DSXRT1:	TLZ	T1,-1		;CLEAR LH JUNK
	CAIE	T1,OPRED	;READ OR
	CAIN	T1,OPWRT	; WRITE OPERATION?
	PUSHJ	P,FIXCWA	;YES, FIXUP DF10'S ICWA+1 WORD
	HRL	T1,T3		;UNIT NUMBER TO LH T1
	MOVE	T3,@DSXCSW(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
	PUSH	P,T1		;SAVE T1
	PUSH	P,T2		;SAVE CONI
	PUSHJ	P,STSCLR	;CLEAR STATUS FLAG
	POP	P,T2		;RESTORE CONI
	POP	P,T1		; AND INTERRUPTING UNIT,,FUNCTION
	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 TO HANDLE UNIT CHECK - DO A SENSE I/O COMMAND

HUC:	MOVSI	T1,UNPSNS	;FLAG TO REMEMBER DOING A SENSE
	IORM	T1,UNIFNC(U)	;STORE IT
	MOVEI	T1,@DSXCSW(J)	;ADDR OF CHANNEL STATUS WORD
	MOVE	T1,1(T1)	;GET TERMINAL COUNT, ADDRESS WORD
	MOVEM	T1,UNITCA(U)	;SAVE FOR LATER SENSE PROCESSING
				;SINCE SENSE COMMAND WILL DESTROY
	MOVEI	T2,UNISNP(U)	;ADDR OF SENSE CHANNEL PROGRAM
	PUSHJ	P,SETDVL	;SET UP DEVICE LIST
	MOVEI	T2,SO.GO	;SET GO FLAG
	PUSHJ	P,CNOSET	;SET IT UP AND DO THE CONO
STSCLR:	MOVEI	T2,SO.STS	;STATUS FLAG
	PJRST	CNOCLR		;CLEAR STATUS FLAG


;HERE TO HANDLY BUSY - SET A FLAG TO RETRY LATER

HBUSY:	AOS	CNTBSY		;COUNT NUMBER OF TIMES GOT BUSY
	MOVSI	T1,UNPBSY	;SET FLAG TO REMEMBER BUSY OCCURED
	IORM	T1,UNIFNC(U)	;STORE IT
	PJRST	STSCLR		;CLEAR STATUS FLAG AND DISMISS

	$LOW
CNTBSY:	0
	$HIGH
;HERE TO HANDLE CONTROL UNIT END - SET UP TO RETRY UNITS THAT GOT A BUSY

HCUE:	AOS	CNTCUE		;COUNT NUMBER OF TIMES GO CUE
	MOVE	T2,U		;SAVE ORIGINAL UNIT
	TLNE	T1,(S.UC)	;UNIT-CHECK ALSO?
	JRST	HUC		;CU END + UNIT CHECK MEANS ERROR AFTER CHAN END
	TLNN	T1,(S.BUSY)	;BUSY ALSO?
	JRST	HCUE5		;NO, HANDLE CU END
	MOVSI	T1,UNPBSY	;YES, CU END + BUSY MEANS TEMPORARILY BUSY
	IORM	T1,UNIFNC(U)	;PRETEND NOT BUSY ANYMORE
HCUE5:	LDB	T1,UNYUST##	;GET STATE OF UNIT
	CAIE	T1,UNVDWN	;SKIP IF UNIT IS DOWN
	JRST	HCUE6		;OK AS IS
	MOVE	U,UNIKON(U)	;DOWN UNITS NOT IN KONTROLLER RING
	CAIE	U,(T2)		;SKIP IF BACK TO ORIGINAL SOME HOW
	JRST	HCUE5		;TRY TO FIND A UNIT IN THE RING
	JRST	HCUE7		;OH WELL

HCUE6:	PUSHJ	P,UCCHK		;CHECK IF ANY UNIT WAITING FOR SENSE
	  JRST	HCUE2		;YES, START IT FIRST
	MOVSI	T1,UNPCUE	;BIT SET IF ALREADY GOT C-U-E
	TDNN	T1,UNIFNC(U)	;SKIP IF SECOND TIME AROUND
	JRST	HCUE3		;NO, DO THE NORMAL THING
	PUSHJ	P,CUEONE	;TRY ONE OTHER UNIT IF ANY WAITING
	  JRST	HCUE2		;ONE UNIT SET UP
HCUE3:	PUSHJ	P,CUERES	;SET UP CHANNEL PROG FOR RESTART IF HAD BUSY
	  JRST	HCUE1		;YES, ONLY DO THIS UNIT NOW
HCUE4:	PUSHJ	P,CUECHK	;NO, SET UP ALL OTHERS
	JUMPN	T1,HCUE2	;JUMP IF ANY OTHER UNITS HAD BUSY
HCUE7:	AOS	CNTCUN		;COUNT TIMES CUE FOUND NO UNITS
	PJRST	STSCLR		;NO, JUST CLEAR STATUS FLAG

	$LOW
CNTCUE:	0			;TIMES GOT CUE
CNTCUN:	0			;TIMES CUE FOUND NO UNITS
	$HIGH


HCUE1:	MOVSI	T1,UNPCUE	;GET CUE BIT
	IORM	T1,UNIFNC(U)	;NOTE CONTROL-UNIT-END
HCUE2:	PUSHJ	P,DSXGO1	;START OPERATION AGAIN
	  JFCL			;FORGET PROBLEMS FOR NOW
	PJRST	STSCLR		;CLEAR STATUS FLAG AND DISMISS
;SUBROUTINE TO CHECK IF ANY UNIT WAITING FOR SENSE COMMAND

UCCHK:	PUSH	P,U		;SAVE FIRST UNIT
UCCHK1:	MOVSI	T1,UNPSNS	;GET BIT INDICATING WAITING FOR SENSE
	PUSHJ	P,CUERS1	;SEE IF THIS UNIT WAITING FOR SENSE
	  JRST	CUEON2		;YES, NOTE OTHER UNITS MAY BE WAITING FOR CUE
	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 SET UP ONE UNIT OTHER THAN THE ONE CAUSING THE INTERRUPT

CUEONE:	PUSH	P,U		;SAVE U
CUEON1:	MOVE	U,UNIKON(U)	;MOVE TO NEXT UNIT
	CAMN	U,(P)		;SKIP IF NOT BACK TO ORIGINAL UNIT
	PJRST	UPOPJ1##	;YES, NO OTHER UNITS
	PUSHJ	P,CUERES	;SEE IF WAITING
	  SKIPA			;YES
	JRST	CUEON1		;NO, LOOP THROUGH UNITS ON CONTROLLER
CUEON2:	MOVSI	T1,UNPCUE	;GET CUE BIT
	ORM	T1,UNIFNC(U)	;SET FOR THAT UNIT
	JRST	UPOPJ##		;RESTORE U AND RETURN

;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 WAITING FOR BUSY ELSE SET UP CH PRG
	  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
CUERS1:	TDNN	T1,UNIFNC(U)	;SKIP IF SET FOR THIS UNIT
	JRST	CPOPJ1##	;NO, UNIT NOT WAITING AFTER BUSY
	ANDCAB	T1,UNIFNC(U)	;CLEAR BIT AND GET THE OTHERS
CUERS2:	MOVEI	T2,UNICHP(U)	;NORMAL CHANNEL PROGRAM ADDR FOR THIS UNIT
	TLNE	T1,UNPSNS	;SKIP UNLESS WAS DOING A SENSE COMMAND
	MOVEI	T2,UNISNP(U)	;IN WHICH CASE CHANNEL PROG IS DIFFERENT
	TLNN	T1,UNPONP	;SKIP IF POSITION FOR ON-LINE INTERRUPT
	TLNE	T1,UNPCPY	;SKIP UNLESS WAS IN CPY ROUTINE
	MOVE	T2,UNIACP(U)	;GET ADDR REMEMBERED
	PJRST	SETDVL		;SET UP DEVICE LIST AGAIN
;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, WILL CAUSE CONTROL ERROR
				;NOTE NEXT REAL OP ON THIS UNIT WILL GET UNIT CHECK
				;EQUIPMENT CHECK WILL BE UP, HOPEFULLY RETRY WILL WIN

;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	CHNERR		;RETURN CHANNEL ERROR

;HERE WHEN INTERRUPT FOR UNKNOWN UNIT - COUNT AND IGNORE
;UNIT NUMBER IN T3

DSXIIN:	AOS	CNTIIN		;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
	PJRST	STSCLR		;CLEAR STATUS AND RETURN

	$LOW
CNTIIN:	0			;TIMES GOT INT FOR UNKNOWN UNIT
	$HIGH
	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
	MOVE	T1,UNISNP(U)	;GET SENSE COMMAND
	AND	T1,[<377>B23]	;ISOLATE DEVICE ADDR
	TLO	T1,(F.NMT!F.XEC!F.SLI!<O.NOP>B15) ;MAKE NO-OP
	MOVEM	T1,UNISND(U)	;STORE IN AVAILABLE PLACE FOR THIS UNIT
	MOVE	T1,BMXWRD	;END OF CHAN PROG
	MOVEM	T1,UNISND+1(U)	;STORE THAT
	MOVEI	T2,UNISND(U)	;ADDR OF CHAN PROG
	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
	MOVEI	T2,UNISNP(U)	;ADDR OF SENSE CHAN PROG
	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
	MOVEI	T1,^D411	;GET LARGE CYLINDER NUMBER
	LSH	T1,SCYLSH	;SHIFT INTO POSITION FOR SEEK CMD
	MOVEM	T1,UNISCY(U)	;STORE FIRST WORD OF SEEK ARGS
	SETZM	UNISCY+1(U)	;ZERO SECOND WORD OF SEEK ARGS
	MOVEI	T1,UNISND-1(U)	;ADDR-1 FOR PLACE TO PUT CHAN PROG
	PUSH	T1,UNISEK(U)	;STORE SEEK COMMAND
	PUSH	T1,UNISEK+1(U)	;AND DCW
	PUSH	T1,BMXWRD	;TERMINATE CHAN PROG
	MOVEI	T2,UNISND(U)	;ADDR OF CHAN PROG
	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
	MOVEI	T2,UNISNP(U)	;ADDR OF SENSE CHAN PROG
	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
	PJRST	STSCLR		;CLEAR THE STATUS FLAG 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:	MOVEM	T2,UNIACP(U)	;SAVE ADDR OF CHAN PROG IN CASE BUSY
	PUSHJ	P,SETDVL	;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
	PUSHJ	P,STSCLR	;CLEAR STATUS FLAG
	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

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
DSXERR:	MOVEI	T1,3		;GIVE UP
	POPJ	P,		;RETURN
	
DSXRDC:
DSXWTC:
DSXUNL:	STOPCD	.,STOP,DSX3IF,	;++ 3330 ISN'T FANCY
;SUBROUTINE TO CONSTRUCT CHANNEL PROGRAM
;ARGS:	T1=ADDRESS OF DRB IF XFER,  OR ZERO IF POS/RCL
;	T4=FILSER OPCODE
;	U=UNIT
;	DATA CHAIN LIST ALREADY SET UP

SETCHP:	HRRZM	T4,UNIFNC(U)	;REMEMBER FILSER FUNCTION
	MOVEM	T1,UNIDRB(U)	;SAVE DRB ADDRESS (IF ANY)
	MOVEI	T2,UNICHP(U)	;ADDR OF NORMAL CHANNEL PROGRAM
	PJUMPE	T1,SETPOS	;SETUP A POSITIONING PROGRAM
	PJRST	SETXFR		;SETUP AN XFER PROGRAM
;	ROUTINE TO SETUP A POSITION PROGRAM

SETPOS:	MOVE	T1,UNIBLK(U)	;PICK UP THE BLOCK NUMBER
	PUSHJ	P,SETSIE	;SETUP SEEK DATA
	MOVE	T1,[BMXEND]	;BMUX END
	MOVEM	T1,UNISTP(U)	;STOP PROGRAM AFTER SEEK
	MOVEI	T2,UNICHP(U)	;POINT AT SEEK PROGRAM
	CAIN	T4,OPCAL	;SKIP IF POSITION
	MOVEI	T2,UNIRCL(U)	;POINT AT RECAL PROGRAM
	PJRST	SETDVL		;SETUP TO RUN SEEK PROGRAM
;	ROUTINE TO SETUP A DATA XFER PROGRAM
SETXFR:
;*** FROM DRB
	MOVE	T3,DRBNUM##(T1)	;T3=# OF BLOCKS TO XFER
	SKIPN	T3		;SKIP IF WE FOUND SOMETHING TO DO
	STOPCD	SETDVL,INFORM,SETXF0,	;++ DRBNUM =0 IN DSXKON
	PUSHJ	P,MAPIO		;CALL LOCAL MAPIO TO CONVERT DF10C TO SA10B
	MOVE	T1,UNIPCP(U)	;GET ADDRESS OF CHANNEL PROGRAM AREA
	TLO	T1,(TIC)	;SET UP TRANSFER IN CHANNEL
	MOVEM	T1,UNIIOT(U)	;MAKE NORMAL CHANNEL PROGRAM BRANCH TO I/O PROG
	MAP	T1,UNISET(U)	;GET PHYSICAL ADDRESS OF SET-SECTOR CMD
	TLZ	T1,(MP.NAD)	;TURN OFF MAP ATTRIBUTE BITS
	TLO	T1,(TIC)	;FINALLY, T1="TIC .+1"
	MOVEM	T1,UNISTP(U)	;DON'T STOP PROGRAM AFTER SEEK!
	MOVEI	T2,UNICHP(U)	;RESET T2 TO POINT AT NORMAL CHANNEL PROGRAM
	PJRST	SETDVL		;SETUP TO RUN XFER PROGRAM
;FALL INTO SETDVL

;SUBROUTINE TO SET UP DEVICE LIST FOR A UNIT
;ARGS:	T2=VIRTUAL ADDRESS OF CHANNEL PROGRAM
;	STORE ADDRESS, THEN SET "START DEVICE" IN DEVICE LIST

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
	TDO	T3,T2		;INSERT THE PROGRAM ADRESS
	PMOVEM	T3,UNIDVP(U)	;INSERT NEW DEVICE LIST ENTRY IN LIST
	MOVEM	T3,UNIDVL(U)	;SAVE FOR DEBUGGING
	POPJ	P,		;RETURN
;SUBROUTINE TO COMPUTE AND STORE SEEK AND SEARCH DATA
;ARGS	T1=BLOCK NUMBER

SETSIE:	LDB	T2,UNYBPY##	;BLOCKS PER CYLINDER
	IDIV	T1,T2		;T1=CYLINDER NUMBER, T2=REM
	MOVEM	T1,UNICYL(U)	;REMEMBER OUR CYLINDER
	LSH	T1,SCYLSH	;SHIFT FOR CHANNEL PROGRAM DATA
	MOVEM	T1,UNISCY(U)	;STORE IN CHANNEL PROGRAM
	LDB	T3,UNYBPT##	;GET BLOCKS PER TRACK
	IDIV	T2,T3		;T2=TRACK (HEAD) NUMBER, T3=REC NUMBER
	DPB	T2,UNYHNM	;STORE HEAD NUMBER IN CHANNEL PROG
	AOJG	T3,SETSI1	;RECORD ID (FIRST REC IS REC 1)
	STOPCD	.+1,DEBUG,IR0,	;++ I/O TO RECORD R0
	MOVEI	T3,1		;SET A BETTER RECORD
SETSI1:	DPB	T3,UNYRNM	;STORE RECORD ID

	POPJ	P,		;RETURN

;SUBROUTINE TO COMPUTE AND STORE SECTOR NUMBER
;ARGS:	T3=RECORD ID

SIZGAP==^D135	;TOTAL NUMBER OF BYTES IN GAPS FOR A NORMAL RECORD
SIZR0==^D237	;TOTAL NUMBER OF BYTES IN R0 INCLUDING ITS GAPS, HOME, ETC

SECTRK==^D128	;SECTORS PER TRACK
BYTTRK==^D13440	;TOTAL NUMBER OF BYTES PER TRACK

SECERR:	STOPCD	.+1,DEBUG,ISN,	;++ ILLEGAL SECTOR NUMBER
	MOVEI	T3,1		;SET A BETTER SECTOR NUMBER

SETSEC:	SOJL	T3,SECERR	;JUMP IF ILLEGAL SECTOR NUMBER
	IMULI	T3,SIZGAP+BLKBYT ;(REC NUM -1) * (TOTAL BYTES FOR A NORMAL RECORD)
	ADDI	T3,SIZR0	;PLUS BYTES FOR R0 = BYTE NUM OF BEGINNING OF REC
	IMULI	T3,SECTRK	;TIMES SECTORS PER TRACK
	IDIVI	T3,BYTTRK	;THUS GET (BYTE NUM/TOTAL BYTES PER TRACK) * SECTORS
	DPB	T3,UNYSSC	;STORE SECTOR NUMBER
	POPJ	P,		;RETURN
;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.WRIT>B15-<O.MTRD>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,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)
	MOVSI	T1,KOPECA	;GET THE "I KNOW HOW TO DO ECC" BIT
	IORM	T1,KONECA(J)	;...AND SET IT IN OUR KDB
	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
	ADDM	U,UNISEK+1(U)
	ADDM	U,UNISET+1(U)
	ADDM	U,UNISID(U)
	ADDM	U,UNISID+1(U)
	ADDM	U,UNISNP+1(U)
	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, UNISET(U), DVNDVA]
	DPB	T1,[POINT DVSDVA, UNISIE(U), DVNDVA]
	DPB	T1,[POINT DVSDVA, UNISNP(U), DVNDVA]
	DPB	T1,[POINT DVSDVA, UNIRWC(U), DVNDVA]
	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
	HRRZ	T1,P2		;GET DCU ADDRESS
	LSH	T1,WIDUNA	;POSITION
	HRRZ	T2,P3		;GET PHYSICAL DRIVE NUMBER
	IOR	T2,T1		;BUILD FAKE DRIVE S/N SINCE IT CAN'T BE READ
	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:	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
;TABLES OF BLOCKS PER UNIT INDEXED BY UNIT TYPE

BLKPRU:	DEC	18*19*411	;TYPE 0 (3330)
	DEC	18*19*815	;TYPE 1 (3331)
;	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
;	P2=POINTER TO CURRENT SA10B CHANNEL PROGRAM WORD, USED ONLY BY MAPIO
;	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
	MOVE	P1,UNIDRB(U)	;PICK UP POINTER TO DRB FOR THIS UNIT
	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
	MOVE	P2,UNIPCP(U)	;PICK UP PHYSICAL POINTER TO SA10 PROGRAM
	MOVE	T2,P2		;PICK UP COPY OF PHYSICAL ADDR OF PROGRAM
	ADDI	T2,CPALEN-2	;COMPUTE FIRST AVAILABLE SID-DATA AREA
	MOVEM	T2,UNISDA(U)	;SAVE POINTER
	PUSHJ	P,SETSIE	;SETUP THE SEEK CMD IN THE UDB
	PUSHJ	P,SETSEC	;SETUP THE SET-SECTOR COMMAND IN THE UDB
	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
MAPIO1:
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,MAPIO0	;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 WHEN THE WORD COUNT WAS ZERO FOR A DF10 IOWD
MAPIO0:
;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
	MOVE	P2,R		;GO BACK TO THE BEGINNING OF THE CURRENT GROUP
	PUSHJ	P,STODVC	;STORE THE END CMD
	POPJ	P,		;ALL DONE

;HERE IF LAST BLOCK WASN'T FILLED.
MAPIOY:	SUBI	P2,1		;BACK UP "PC" TO POINT AT PARTIAL IOWD
	PMOVE	T1,P2		;GET PARTIAL IOWD
	TLO	T1,(1B0)	;SET "LAST IOWD" AFTER ALL
	PMOVEM	T1,P2		;STORE PARTIAL IOWD BACK AS A "LAST IOWD" INSTEAD
	ADDI	P2,1		;RESTORE PC TO CURRENT PLACE
	MOVE	T1,BMXWRD	;GET A HALT
	PJRST	STODVC
;	ROUTINES TO INSERT NON-IOWD CHANNEL COMMANDS INTO THE 3330/3331 PROGRAM

STOXFR:	MOVE	R,P2		;THIS LOCATION GETS "HALT" IF NO MORE TO DO
	TROE	W,1		;SKI IF FIRST BLOCK
	TLNN	S,IO		;SKIP IF READ COMMANDS NEEDED
	SKIPA
	PUSHJ	P,STOSID	;INSERT A SEARCH-ID-EQUAL FOR WRITES
	MOVE	T1,W		;GET THE READ/WRITE COMMAND
	PUSHJ	P,STODVC	;STORE DEVICE COMMAND
	AOS	UNICBN(U)	;ADVANCE BLOCK #
	POPJ	P,

STOSID:	MOVE	T1,UNISIE(U)	;GET VIRGIN SEARCH ID
	TLZ	T1,(F.OFP)	;TURN OFF "START AT BYTE 3" BIT
	PUSHJ	P,STODVC	;STORE DEVICE COMMAND
	MOVSI	T1,777300	;-5 BYTE COUNT (IE: C C H H R)
	OR	T1,UNISDA(U)	;ADD IN ADDRESS OF SEARCH DATA
	PUSHJ	P,STODVC	;STORE IOWD FOR SEARCH-ID-EQUAL
	PUSHJ	P,SETSID	;SETUP SEARCH DATA, UPDATE UNISDA
	MOVE	T1,P2		;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

STOIOW:	IMUL	T1,[-100,,0]	;POSITION THE COUNT FIELD
	TDO	T1,P4		;INSERT THE ADDRESS FIELD
STODVC:	PMOVEM	T1,P2		;STORE COMMAND IN THE CHANNEL PROGRAM
	AOJA	P2,CPOPJ##	;RETURN

;ROUTINE TO INSERT SEARCH ID DATA

SETSID:	MOVE	T1,UNICBN(U)	;GET CURRENT BLOCK NUMBER
	LDB	T2,UNYBPY##	;GET BLOCKS PER CYLINDER
	IDIV	T1,T2		;COMPUTE T1=CYL#, T2=REMAINDER
	ROT	T1,-^D16	;T1=LEFT JUSTIFIED CYL# IN 16 BIT FIELD
	LDB	T3,UNYBPT##	;GET BLOCKS PER TRACK
	IDIV	T2,T3		;COMPUTE T2=TRACK/HEAD, T3=RECORD
	ADDI	T3,1		;T3=RECORD NUMBER (START AT 1, NOT 0)
	DPB	T2,[POINT 16,T1,31];INSERT HEAD INFO INTO T1
	MOVE	T2,UNISDA(U)	;GET POINTER TO SEARCH DATA BUFFER
	PMOVEM	T1,T2		;INSERT 1ST WORD OF IT
	ADDI	T2,1		;POINT AT NEXT WORD
	ROT	T3,-^D8		;LEFT JUSTIFY RECORD NUMBER IN 8 BIT FIELD
	PMOVEM	T3,T2		;INSERT 2ND WORD OF IT
	SUBI	T2,3		;UPDATE T2 TO NEW UNISDA
	MOVEM	T2,UNISDA(U)	;UPDATE UNISDA
	POPJ	P,		;RETURN
;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:	PUSHJ	P,SAVT##		;SAVE ACS - INTERUPT LEVEL CARES
	JUMPE	S,FIXGUD		;BRANCH IF NO ERRORS
	PUSHJ	P,SAVR##		;SAVE
FIXCW0:	LDB	R,[POINT 22,UNITCA(U),35];GET THE UNDONE PC
	MOVE	T1,UNIPCP(U)		;GET THE PROGRAM POINTER
	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)
	CAMN	T1,R			;SKIP IF NOT THE UNDONE PC
	JRST	FIXCC1			;BRANCH IF UNDONE PC
	TRNE	T2,1			;CHECK FOR AN XFER COMMAND
	JRST	FIXCW2			;BRANCH IF AN XFER COMMAND
	ADDI	T1,3			;MUST BE SEARCH-ID, IOWD, TIC, 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:	MOVE	T1,UNIDRB(U)		;POINT AT DRB
	MOVE	T2,T4			;COPY WORD COUNT
	IDIVI	T2,^O200		;COMPUTE # OF GOOD BLOCKS
	MOVEM	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:	MOVE	T1,UNIDRB(U)		;POINT AT DRB
	MOVE	T2,DRBBLK##(T1)		;GET EXPECTED WORDS
	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

	$LIT
DSXEND:	END