Google
 

Trailing-Edge - PDP-10 Archives - BB-JR93N-BB_1990 - 10,7/unsmon/ts1kon.mac
There are 6 other files named ts1kon.mac in the archive. Click here to see a list.
TITLE	TS1KON - SA10/STC DEVICE DEPENDENT CODE FOR TAPSER	V20
SUBTTL	D. P. MASTROVITO/DPM (DEC) & JEFF GUNTER/JEG (ADP)	17-APRIL-90

;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,1990.
;ALL RIGHTS RESERVED.

.CPYRT<1987,1990>


XP VTS1KN,20
	SALL

TS1KON::ENTRY	TS1KON

;$$*** I DON'T SEE ANY REASON WHY TS1KON CAN'T BE A MULTI-BLOK DRIVER.
;$$*** I DON'T SEE ANY REASON NOT TO SUPPORT READ-BACKWARDS
;SA10 COMMANDS SPECIFIC TO MAGTAPES
MO.NOP==003		;NOP
MO.SNS==004		;SENSE COMMAND
MO.CTL==007		;CONTROL (TAPE MOTION COMMANDS)
MC.BSB==040		;BACKSPACE RECORD


;DEVICE COMMAND CODES (BITS 8-15 OF DEVICE COMMAND WORD)
O.WR==001		;WRITE
O.RD==002		;READ
O.RDB==014		;READ BACKWARDS
O.SNS==004		;SENSE
O.CTL==007		;CONTROL (TAPE MOTION OPERATIONS)
O.TIE==033		;REQUEST TRACK IN ERROR (9 TRACK NRZI ONLY)
O.7MS==003		;7 TRACK MODE SET
O.9MS==303		;9 TRACK MODE SET
O.NOP==003		;NO OPERATION


;TAPE MOTION OPERATIONS (OR'ED WITH O.CTL)
C.REW==000 !O.CTL	;REWIND
C.RUN==010 !O.CTL	;REWIND AND UNLOAD
C.ERG==020 !O.CTL	;ERASE GAP
C.WTM==030 !O.CTL	;WRITE TAPE MARK
C.BSB==040 !O.CTL	;BACK SPACE BLOCK
C.BSF==050 !O.CTL	;BACK SPACE FILE
C.FSB==060 !O.CTL	;FORWARD SPACE BLOCK
C.FSF==070 !O.CTL	;FORWARD SPACE FILE
C.DSE==220 !O.CTL	;DATA SECURITY ERASE


;9 TRACK MODE SETTINGS (OR'ED WITH O.9MS)
N.PE==000		;1600 BPI PHASE ENCODED
N.NRZI==010		;800 BPI NON RETURN TO ZERO INVERTED
N.GCR==020		;6250 BPI GROUP CODED RECORDING
N.STD==N.PE		;INSTALLATION STANDARD FOR 9 TRACK


;7 TRACK DENSITY SETTINGS (OR'ED WITH O.7MS)
D.200==000		;200 BPI
D.556==100		;556 BPI
D.800==200		;800 BPI


;7 TRACK MODE MODIFIERS (OR'ED WITH O.7MS)
M.DIAG==010		;FOR DIAGNOSTIC USE ONLY
M.DCON==020		;DATA CONVERTER ON
M.EPAR==040		;EVEN PARITY
M.TRON==010		;TRANSLATOR ON


;MODE DEFINITIONS
F.9TK==F.TAPC		;9 TRACK DEC COMPATIBLE (TAPE COMPATIBILITY MODE)
F.IND==F.BYTE		;9 TRACK IBM COMPATIBLE (BYTE MODE)
F.7TK==F.NAT		;7 TRACK ODD PARITY (NATURAL MODE)
F.7TE==F.BYTE		;7 TRACK EVEN PARITY (BYTE MODE)

NOPCMD==F.NMT!F.XEC!<O.NOP>B15	;A GENERIC NOP COMMAND
;BYTE POINTERS
CSYDVA:	POINT	CSSKON+CSSUNI,T1,CSNUNI	;DEVICE ADDR IN CSW
DVYDVA:	POINT	DVSDVA,T2,DVNDVA	;DEVICE ADDR IN DVW


;PROTOTYPE SENSE CHANNEL PROGRAM
SNSPGM:	PHASE	0
SNSNOP:! F.NMT!F.XEC!F.CC!<MO.NOP>B15		;PUT A CHAINED NOP BEFORE SENSE
SNSDVW:! F.XEC!F.BYTE!<MO.SNS>B15!<000>B23	;SENSE-I/O COMMAND
SNSDCW:! BYTE	(1)1(DCSSIZ)-^D24(DCSADR)0	;DCW FOR SENSE
SNSHLT:! EXP	0				;TERMINATE CHANNEL PROG (HALT)
SNSDAT:! BLOCK	SNSWDS				;SENSE BYTE STORAGE
SNSLEN:! DEPHASE				;LENGTH OF CHANNEL PROGRAM
	TS1DMX==20		;MAXIMUM DRIVES PER KONTROLLER
	TS1HDN==TS1DMX-1	;HIGHEST DRIVE NUMBER ON KONTROLLER
	TS1ELN==6		;SIZE OF FEP TABLE
	DCWMAX==^D40		;LENGTH OF CHANNEL PROGRAM AREA


;DRIVER CHARARCTERISTICS
;	TS1	= TS1CNF
;	MTA	= MAGTAPE
;	0	= MAXIMUM DEVICES IN SYSTEM (NO LIMIT)
;	K.TS1	= KONTROLLER TYPE
;	TS1DMX	= MAXIMUM DRIVES PER KONTROLLER
;	TS1HDN	= HIGHEST DRIVE NUMBER ON KONTROLLER
;	MDSEC0	= SECTION FOR KDB/UDB
;	MDSEC0	= SECTION FOR DDB
DRVCHR (TS1,MTA,0,K.TS1,TS1DMX,TS1HDN,MDSEC0,MDSEC0,<DR.XAD!DR.MCD!DR.DPU!DR.UCK!DR.GCC!DR.DDN>)

	.ORG	TKBUDB		;START OF SA10 SPECIFIC DATA
TS1UTB:! BLOCK	TS1DMX		;TABLE OF POINTERS TO UDBS
TS1SNS:! BLOCK	SNSLEN		;SENSE CHANNEL PROGRAM
TS1SUN:! BLOCK	1		;NON-EXISTANT DRIVE BEING SENSED
TS1IUM:! BLOCK	TS1DMW		;IGNORE UNIT MASK
TS1NUM:! BLOCK	TS1DMW		;NEW UNIT MASK
TS1UNI:! BLOCK	1		;SAVED UNIT NUMBER
TS1LCI:! BLOCK	1		;CONI OF SA10 AT LAST INTERRUPT
TS1ICC:! BLOCK	1		;SAVE INITIAL CSW AROUND SENSE CMD
TS1LST:! BLOCK	1		;CSW AT LAST INTERRUPT
TS1SCN:! BLOCK	1		;LH = SUBCHANNEL IN CONO POS, RH = SUBCHANNEL
TS1GSN:! BLOCK	1		;GLOBAL SUBCHANNEL NUMBER
TS1DVP:! BLOCK	1		;ADDRESS OF OUR DEVL SLOT
TS1PPC:! BLOCK	1		;PROGRAM PC DESIRED
TS1OFL:! BLOCK	1		;-1 IF WE THINK KON IS OFFLINE, 0 IF NOT
TTSCSW:! BLOCK	1		;ADDR OF CHANNEL STATUS WORD
TTSOFS:! BLOCK	1		;DEVICE ADDR FOR UNIT ZERO (@TTSOFS(W))
TTSTCA:! BLOCK	1		;TERMINAL COUNT,ADDR LAST IO INT (NOT SENSE)
TTSBUS:! BLOCK	1		;(U) THAT GOT BUS-OUT ON USER OPERATION
TTSBSM:! BLOCK	1		;BUS-OUT MAP FOR POLLER
TS1STI:! BLOCK	1		;(U) FOR UNIT THAT GOT DEFERED AT STIDON
TS1DUM:! BLOCK	1		;(U) FOR UNIT THAT GOT DEFERED AT TTSDMD
TTSOPU:! BLOCK	1		;(U) FOR UNIT THAT GOT BUSYED
TTSBCT:! BLOCK	1		;TIMER SET WHEN BUSY RECEIVED
TTSBRC:! BLOCK	1		;COUNT OF BUSYS,,COUNT OF TIMER EXPIRED
TTSDMD:! BLOCK	1		;ADDR OF UNIT WAITING FOR DUMMY STATUS
TTSMIO:! BLOCK	1		;VIRTUAL ADDRESS OF 1ST DCW IN TTSPRG
TTSPRG:! BLOCK	DCWMAX		;THE ACTUAL CHANNEL PROGRAM LIVES HERE
	  BLOCK	3		;THIS IS OVERWRITTEN WITH SENSE + HALT
TS1MDW:! BLOCK	1		;STORAGE FOR MDT DATA WORD
TS1KLN:!			;LENGTH OF KDB
	.ORG

	 .ORG	TUBLEN
TS1SND:!			;START OF SENSE DATA
TS1IST:! BLOCK	TS1ELN		;LAST ERROR INITIAL STATUS
TS1FST:! BLOCK	TS1ELN		;LAST ERROR FINAL STATUS
TS1SIP:! BLOCK	1		;SENSE IN PROGRESS - CONI WORD
	 BLOCK	1		;SENSE IN PROGRESS - CSW WORD
TS1TIE:! BLOCK	1		;SENSE BYTES 0,1,2,3 WHERE BYTE2=TIE BYTE
TS1ULN:!			;LENGTH OF UDB
	 .ORG


TS1KDB:	KDBBEG	(TS1,TS1KLN)
	SETWRD	(KDBNAM,<SIXBIT/MT/>)	;KONTROLLER NAME
	SETWRD	(KDBIUN,<TKBUDB>)	;INITIAL POINTER TO UDBS
	SETWRD	(KDBCUN,<TKBUDB>)	;CURRENT POINTER TO UDBS
	SETWRD	(KDBIUM,<TS1IUM>)	;OFFSET TO IGNORE UNIT MASK
	SETWRD	(KDBNUM,<TS1NUM>)	;OFFSET TO NEW UNIT MASK
	SETWRD	(KDBSTS,<INSVL.(1,KD.MPT)>) ;INITIALLY ONE PATH
	KDBEND


TS1UDB:	UDBBEG	(TS1,TS1ULN)
	SETWRD	(UDBNAM,<SIXBIT/MT/>)	;DRIVE NAME
	SETWRD	(UDBPCC,< TS1ELN,,TS1FST>) ;PHYSICALLY CONTIGUOUS CORE
	SETWRD	(TUBIEP,<-TS1ELN,,TS1IST>) ;INITIAL ERROR POINTER
	SETWRD	(TUBFEP,<-TS1ELN,,TS1FST>) ;FINAL ERROR POINTER
	UDBEND

EQUATE	(LOCAL,0,<TS1ICD,TS1ICL,TS1ULP,TS1ULB>)
EQUATE	(LOCAL,CPOPJ##,<TS1CMD,TS1EDL,TS1IDL,TS1LOD>)
EQUATE	(LOCAL,CPOPJ1##,<TS1CFG>)

TS1INT==TAPINT##		;INTERRUPT SERVICE
TS1ELG==TPELGX##		;MAKE AN ERROR LOG ENTRY

TS1DSP:	DRVDSP	(TS1,TAPCHN##,TDVDDB##,TDVLEN##,TPMDIA##)
	TPK	(TS1,NO,377777)	;SERVICE DEPENDENT DISPATCH

TS1CKT:	EXP	K.TX1,K.DX2,K.TS1,0 ;COMPATIBLE KONTROLLER TABLE

TS1NBF:	TPNBF	(RW,RU)		;NON-BLOCKING FUNCTION MASK

	SAXDSP	(TS1)		;GENERATE SA10 DRIVER DISPATCH

;DEFAULT MONGEN'ED DEVICE TABLE
DEFMDT:	MDKL10	(7,274,0,0,<MD.KON>)	;DEVICE CODE 274
	MDKL10	(7,270,0,0,<MD.KON>)	;DEVICE CODE 270
	MDTERM				;TERMINATE TABLE
SUBTTL TAPE INTERUPT SERVICE
;CALLED FROM TAPSER WITH W := KDB

TS1ISR:	PUSHJ	P,SAVE4##	;SAVE SOME ACS
	PUSHJ	P,TRK111	;$ GO TRACK "INT"
	XCT	KDBCNI(W)	;CONI SA10,T1
	DMOVE	T2,@TTSCSW(W)	;GET CSW & RESIDUAL COUNT
	EXCH	T1,T2		;SWAP AROUND
	MOVEM	T1,TS1LST(W)	;SAVE INTERRUPTING STATUS
	MOVEM	T2,TS1LCI(W)	;SAVE CONI
	TLNE	T1,(S.BUSY)	;SKIP IF NOT BUSY
	PJRST	WTCUE		;GO WAIT FOR CUE IF WE'RE BUSYED
	TLNE	T1,(S.CUE)	;CHECK FOR NOT-BUSY-ANYMORE
	PUSHJ	P,GOTCUE	;CALL IF NOT BUSY ANYMORE
	TRNE	T2,SI.PAR!SI.NXM ;SKIP UNLESS MEMORY ERROR
	JRST	STECHN		;GO HANDLE CHANNEL ERROR
	LDB	T4,[POINT 2,TS1LST(W),1]	;GET STATUS TYPE BYTE
	SETZM	TS1OFL(W)	;IF WE GET INT CAN'T BE COMPLETELY OFFLINE
	SKIPE	T1		;SKIP IF CSW WORD IS 0 (??)
	TLNE	T1,(S.SE)	;SKIP IF SELECT-ERROR IS CLEAR
	JRST	TS1IS3		;BRANCH IF CSW=0 OR IF SELECT-ERROR IS ON
	SKIPE	T2,TTSDMD(W)	;SKIP IF NO DUMMY DEVICE
	CAIE	T4,S.DUM	;SKIP IF DUMMY STATUS
	LDB	T2,CSYDVA	;GET DEVICE FROM STATUS
	SUBI	T2,@TTSOFS(W)	;GET UNIT NUMBER
	SETZB	U,P1		;DEFAULT TO NO UDB, NO IORB
	PUSHJ	P,SETUDB##	;SET UP UDB
	  PJRST	NEWUNI		;POSSIBLY A NEW DRIVE JUST POWERED UP
	PUSHJ	P,CHKIRB##	;CHECK FOR IORB AND LOAD UP T1 WITH IT
	  JRST	ONLUNI		;MAYBE THIS IS AN ONLINE INTERRUPT
	PUSHJ	P,SETACS	;LOAD UP VARIOUS PRESERVED STATUS
	SKIPE	TS1SIP(U)	;SKIP IF NOT SENSING
	JRST	[PUSHJ P,SNSANA	;GO ANALYZE SENSE DATA
		 PJRST TS1IS2]	;...THEN BRANCH TO COMPLETION ROUTINES
	MOVEM	T3,TTSTCA(W)	;SAVE FOR WORD COUNT CALCULATERS
	MOVE	T1,P1		;GET IORB INTO T1 INCASE WE EXIT HERE AND NOW
	CAIN	T4,S.DUM	;SKIP UNLESS DUMMY STATUS
	JRST	TS1IS4		;BRANCH IF WE GOT DUMMY INT FOR IDLE CTL
	JUMPE	P1,STSCLR
	MOVSI	T1,TUSBOT##	;GET BOT BIT
	ANDCAM	T1,TUBSTS(U)	;CLEAR SINCE IT WILL BE SET LATER IF NEEDED
	TLNE	P4,(S.UC)	;SKIP IF NO ERRORS DETECTED
	JRST	ERRSNS		;START SENSE, EXIT, RETURN LATER
	MOVE	T1,P1		;GET IORB ADDRESS
	LDB	T1,PRBFCN##	;AND THE FUNCTION CODE
	CAIE	T1,RB.FRW	;REWIND?
	CAIN	T1,RB.FRU	;UNLOAD?
	JRST	TS1IS2		;BRANCH IF INITIAL-STATUS FOR REW/RUN
	TLNE	P4,(S.DE)	;DEVICE END (COMMAND FINISHED)?
	JRST	TS1IS2		;YES
	PJRST	NXTWAT		;ODD, MUST BE MORE STATUS COMING
;HERE WHEN WE (FINALLY) GET DEVICE-END FOR AN IORB PROGRAM
TS1IS2:	MOVE	T1,P1		;WHERE TAPSER LIKES THE IORB
	LDB	T2,PRBFCN##	;GET DESIRED FCN
	CAIG	T2,STIMAX	;CHECK VALIDITY
	JUMPG	T2,@STIDSP(T2)	;DO IT
	JRST	TAPIFI##	;FUNCTION ERROR

;HERE IF SA10 OR TCU IS OFFLINE
TS1IS3:	MOVSI	T2,TKSOFL##	;GET THE OFFLINE BIT
	IORM	T2,TKBSTS##(W)	;SET IN KDB SINCE KON ITSELF IS OFFLINE
	SETOM	TS1OFL(W)	;REMEMBER TO OURSELVES IT IS OFFLINE
	HRROS	TS1SUN(W)	;UNLOCK THE POLLER IF WE DROP OFFLINE
	HRROS	KDBNUM(W)	;...
	SETZM	TTSOPU(W)	;NOTHING TO RESTART
	SETZM	TS1PPC(W)	;NOTHING TO START
	LDB	T2,CSYDVA	;TRY TO GET A VALID UNIT ADDRESS
	SUB	T2,TTSOFS(W)	;COMPUTE UNIT NUMBER
	SETZB	T1,P1		;NO IORB YET
	PUSHJ	P,SETUDB##	;FIND UNIT
	  PJRST	STSCLR		;EXIT INTERRUPT
	MOVSI	T1,TKSOFL##	;GET OFFLINE BIT AGAIN
	IORM	T1,TUBSTS##(U)	;MARK THE UNIT OFFLINE TOO
	PUSHJ	P,CHKIRB##	;GET IORB IF ANY
	  PJRST	STSCLR		;EXIT INTERRUPT
	MOVSI	T2,RB.SOL!RB.SNM ;OFFLINE, NO MOTION
	IORM	T2,TRBSTS##(T1)	;SET OFFLINE
	MOVE	P1,T1		;COPY IORB ADDRESS
	JRST	STIDON		;AND THEN SHORT CUT TO EXIT AND DISMISS INT

;HERE IF WE GET DUMMY INTERRUPT
TS1IS4:	SKIPE	TS1PPC(W)	;SKIP IF NO PROGRAMS IN PROGRESS
	JRST	TS1IS5		;BRANCH IF PROGRAM RUNNING FOR THIS KON
	SETZM	TS1DUM(W)	;NO LONGER A DEFERED DUMMY INTERRUPT
	SETZM	TTSDMD(W)	;NO LONGER A PENDING DUMMY INTERRUPT
	SKIPGE	TTSBCT(W)	;SKIP IF BUSY TIMER NOT RUNNING
	JRST	DISMIS		;BRANCH IF BUSY TIMER RUNNING
	PUSHJ	P,CKSTRT	;CHECK SELECTION STATE
	  POPJ	P,		;EXIT IF NEITHER STARTED NOR SELECTED. REQUEST SCHED
	JRST	STCSI1		;BRANCH TO SETUP COMMANDS AND START UNIT

;HERE IF WE GET DUMMY INTERRUPT, BUT POLLER IS BUSY WITH A UNIT
TS1IS5:	MOVEM	U,TS1DUM(W)	;REMEMBER THE UNIT THAT UUO LEVEL WANTS
	JRST	NXTWAT		;AND DISMISS TO WAIT FOR POLLER DONE INTERRUPT
;ROUTINE TO ANALYZE SENSE DATA AND SETUP VARIOUS UDB AND IORB THINGS
SNSANA:	PUSHJ	P,SNSUNI	;GO SETUP UDB THINGS
	PUSHJ	P,SNSIOR	;GO SETUP IORB THINGS
	POPJ	P,		;RETURN TO CALLER
;ROUTINE TO DO SENSE ANALYSIS AND SETUP UDB THINGS
SNSUNI:	PUSHJ	P,TRK333	;TRACK A SENSE DATA CALLBACK
	JUMPE	U,CPOPJ##	;EXIT IF NO UNIT (??????!!!!??!?!)

	MOVEI	T1,SNSWDS	;LOAD T1=# WORDS IN SENSE DATA
	XMOVEI	T2,TS1SND(U)	;POINT T2=SENSE DATA FROM SENSE CMD
	XMOVEI	T3,TS1IST(U)	;POINT T3="BEFORE" BUFFER FOR SENSE DATA
	EXTEND	T1,[XBLT]	;COPY DATA FROM UNIT BUFFER TO "BEFORE" BUFFER

	MOVSI	T1,TUSWTL##	;LOAD T1=UNIT BIT SAYING "WRITE-LOCKED-UNIT"
	TLNN	P2,(S01WLK)	;SKIP IF UNIT IS ACTUALLY LOCKED
	ANDCAM	T1,TUBSTS##(U)	;CLEAR BIT IF UNIT IF REALLY ENABLED
	TLNE	P2,(S01WLK)	;SKIP IF UNIT IS ACTUALLY ENABLED
	IORM	T1,TUBSTS##(U)	;SET BIT IF UNIT IS REALLY LOCKED

	TLNN	P2,(S00IRQ!S00BOC) ;SKIP IF UNIT HAS FALLEN OFFLINE
	TLNE	P2,(S01BOT)	;SKIP IF NOT BEGINNING-OF-TAPE
	PUSHJ	P,REWDON##	;LET TAPE-SERVICE KNOW WE HIT B-O-T

	MOVSI	T1,TKSOFL##	;LOAD T1=UNIT BIT SAYING "UNIT OFFLINE"
	TLNE	P2,(S00IRQ!S00BOC) ;SKIP IF ONLINE WITHOUT ERROR
	IORM	T1,TUBSTS(U)	;SET OFFLINE BIT IF OFFLINE OR BUS PARITY ERR

	TLNE	P2,(S00BOC)	;SKIP IF NOT BUS-OUT PARITY ERROR
	MOVEM	U,TTSBUS(W)	;REMEMBER LAST UNIT ON OUR KON HAVING ERROR

	TLNE	P2,(S00DCK)	;SKIP IF NOT DATA-CHECK
	MOVEM	P2,TS1TIE(U)	;SAVE T-I-E INFO IF DATA CHECK
; (N.B.: TIE ONLY VALID IF 800 NRZI 9 TRACK UNIT)
	TLNE	P3,(S067TK)	;SKIP IF 9-TRACK UNIT
	SETOM	TS1TIE(U)	;REJECT TIE INFO IF 7TRACK UNIT
	TRNE	P2,S03D16	;SKIP IF NOT 1600 BPI
	SETOM	TS1TIE(U)	;REJECT TIE INFO IF PE UNIT
	TRNE	P3,S06D62	;SKIP IF NOT 6250 BPI CAPABLE UNIT
	SETOM	TS1TIE(U)	;REJECT TIE INFO IF GCR UNIT
	POPJ	P,		;EXIT WHEN DONE WITH UDB STUFF
;HERE TO ANALYZE SENSE DATA AND SETUP IORB STUFF

SNSIOR:	JUMPE	P1,CPOPJ##	;EXIT IF NO IORB!
	SETZ	T1,		;START OFF WITH NO MODIFIER BITS
	TLNE	P2,(S00IRQ!S00BOC) ;SKIP IF OFFLINE AND NO BUS ERRORS
	TLO	T1,RB.SOL!RB.SNM ;SET OFFLINE AND NO-MOTION

	MOVE	T2,TS1SND+S04WRD(U) ;GET SENSE DATA
	TLNE	T2,(S04TPI)	;SKIP IF "INDICATE" LIGHT IS OFF
	TLO	T1,RB.SET	;SET "END-OF-TAPE" IF BEYOND EOT MARKER

	TLNE	P2,(S00DCK)	;SKIP IF DATA-CHECK
	TLO	T1,RB.SDE	;SET DATA-ERROR

	IORM	T1,TRBSTS(P1)	;SET BITS IN IORB

	MOVE	T1,P1		;POINT T1 AT IORB FOR PRBDEN
	TLNN	P2,(S01BOT)	;CAN'T UPDATE DENSITY IF HAVEN'T READ TAPE
	TLNE	P2,(S017TK)	;SKIP IF 9TRACK UNIT
	POPJ	P,		;7TRK DOESNT NEED UPDATE (AND WE CAN"T READ IT)
	MOVEI	T2,RB.D62	;IF 3600 SERIES, DEFAULT TO 6250
	TRNN	P3,S06D62	;SKIP IF 3600 SERIES DRIVE
	MOVEI	T2,RB.D8	;IF 3400 SERIES, DEFAULT 
	TRNE	P2,S03D16	;SKIP IF NOT 1600 BPI
	MOVEI	T2,RB.D16	;IF KNOWN TO BE 1600 BPI, MUST BE 1600 BPI!
	DPB	T2,PRBDEN##	;UPDATE IORB WITH CURRENT DENSITY INFO

	POPJ	P,		;EXIT WITH IORB INFO UPDATED
;ROUTINE TO LOAD THE INTERESTING ACS.
;	P1= IORB
;	P2= 1ST 4 SENSE BYTES, OR ZERO IF NOT SENSING
;	P3= 2ND 4 SENSE BYTES, OR ZERO IF NOT SENSING
;	P4= CSW WORD APLLYING TO THE ERROR, IF ANY

SETACS:	MOVE	P1,T1		;SAVE IORB IN A SAFER PLACE
	SETZB	P2,P3		;DEFAULT TO NO ERRORS
	SKIPE	TS1SIP(U)	;SKIP IF NOT SENSING
	DMOVE	P2,TS1SND+S00WRD(U)	;LOAD UP THE FIRST 8 SENSE BYTES
	SKIPE	TS1SIP(U)	;SKIP IF NOT SENSING
	SKIPA	P4,TS1SIP+1(U)	;LOAD UP THE SAVED CSW INFO
	MOVE	P4,TS1LST(W)	;LOAD UP THE "REAL" CSW INFO
	POPJ	P,
;HERE ON UN-EXPECTED INTERRUPT FOR KNOWN DRIVE (IE: UDB BUT NO IORB)
ONLUNI:	MOVE	T1,TS1LST(W)	;GET CSW WORD
	TLNE	T1,(S.CUE)	;SKIP IF NOT BUSY-CALL-BACK
	JRST	NXTWAT		;IGNORE INTERRUPT IF BUSY-CALL-BACK
	SKIPN	TS1SIP(U)	;SKIP IF WE'VE GOT SENSE DATA ALREADY
	PJRST	ERRSNS		;PICK UP SENSE DATA
	PUSHJ	P,SETACS	;SETUP THE ACS
	MOVE	P1,TUBSTS(U)	;FORGET IORB AND REMEMBER TUSREW INSTEAD
	PUSHJ	P,SNSUNI	;SETUP UDB DATA
	TLNN	P1,TUSREW##	;SKIP IF WE JUST REWINDING
	TLNE	P2,(S00IRQ)	;SKIP IF NOT OFFLINE
	SKIPA			;SKIP IF REWINDING OR OFFLINE
	PUSHJ	P,TPMONL##	;TELL MDC WHEN DRIVE COMES ONLINE
	SETZB	P1,T1		;MAKE SURE THERE IS NO IORB HANGING AROUND
	PJRST	STIDON		;FINISH INTERRUPT
;HERE ON AN UNSOLICITED INTERRUPT FROM AN UNKNOWN DRIVE
;IF NOT THE POLLER'S DRIVE, JUST IGNORE IT, AS POLLER WILL EVENTUALLY
;DO A SENSE AND CLEAR THE CONNECTION.
NEWUNI:	CAME	T2,TS1SUN(W)	;SKIP IF THE UNIT WE WERE EXPECTING
	JRST	NXTWAT		;BR IF BAD UNIT. IGNORE-WILL BE SENSED LATER.
	MOVE	T1,BITTBL##(T2)	;GET ASSOCIATED BIT
	TDNE	T1,TS1IUM(W)	;WANT TO IGNORE THIS DRIVE?
	JRST	NEWUN1		;BR IF WE WANT TO IGNORE IT (??!)
	MOVE	T3,TS1SNS+SNSDAT+S23WRD(W) ;GET LAST WORD
	TRNE	T3,17		;SKIP IF SENSE PROCEEDED CORRECTLY
	JRST	NEWUN3		;BR IF SENSE DIDN'T HAPPEN OR FAILED
	MOVE	T3,TS1SNS+SNSDAT+S01WRD(W) ;GET SENSE BYTE 1
	TLNE	T3,(S01TUA!S01TUB) ;DOES DRIVE EXIST?
	JRST	NEWUN2		;BRANCH IF DRIVE SEEMS TO EXIST

;HERE DRIVE DOESN'T (OR SHOULDN'T) EXIST.  UNFREEZE POLLER AND DISMISS INTRP
NEWUN1:	HRROS	TS1SUN(W)	;OK TO CHECK ANOTHER DRIVE NOW
	HRROS	KDBNUM(W)	;GIVE AUTCON A FLAG
	JRST	NEWUN4		;CHECK FOR PENDING UNIT, DISMISS

;HERE IF WE FIND A NEWLY EXISTING DRIVE
NEWUN2:	MOVE	T3,TS1SNS+SNSDAT+S06WRD(W) ;GET SENSE BYTE 6 ET AL
	TDNN	T3,[S06MOD]	;IS MODEL FIELD FILLED IN?
	JRST	NEWUN1		;HM - MUSTBE FALSE ALARM?
	IORM	T1,TS1NUM(W)	;TELL PER-SECOND LEVEL WHICH DRIVE TO FIND
	JRST	NEWUN4		;CHECK FOR PENDING UNIT, DISMISS

;HERE TO START A SENSE BECAUSE EXISTING FAILED OR DIDN'T HAPPEN
NEWUN3:	MOVEI	T3,TS1SNS+SNSDVW(W) ;POINT AT VIRTUAL ADDRESS OF PROGRAM
	MOVEM	T3,TS1PPC(W)	;SAVE FOR STARTUP/RESTARTS
	MAP	T3,TS1SNS+SNSDVW(W) ;POINT AT SENSE AFTER NO-OP CMD
	ADDI	T2,@TTSOFS(W)	;COMPUTE FULL UNIT ADDRESS
	DPB	T2,[POINT SASDLD,T3,SANDLD] ;INSERT IT
	MOVEI	T2,DL.STA	;GET START OPCODE
	DPB	T2,[POINT SASDLO,T3,SANDLO] ;INSERT OPCODE
	PMOVEM	T3,TS1DVP(W)	;CREATE NEW DEVL ENTRY
	SETZM	TS1SNS+SNSDAT+SNSWDS-1(W) ;MAKE IT LOOK LIKE IT WON THIS TIME
	JRST	SETGOB		;START SENSE-ONLY PROGRAM

;HERE WHEN SENSE COMPLETED.  DECLARE PENDING UUO LEVEL DONE.
NEWUN4:	SETZM	TS1PPC(W)	;NO MORE POLLING PROGRAM
	SKIPN	U,TS1STI(W)	;GET PENDING UNIT'S U
	JRST	NEWUN5		;BRANCH IF NO "STIDON" PENDING UNIT
	SETZM	TS1STI(W)	;NOTHING PENDING NOW
	PUSHJ	P,CHKIRB##	;GET IORB FOR IT
	SETZ	T1,		;VERY STRANGE
	MOVE	P1,T1		;GET P1=IORB FOR STIDON ET AL
	PJRST	STIDON		;END IT

;HERE WHEN SENSE COMPLETED BUT NO "STIDON" IS PENDING
NEWUN5:	SKIPN	TS1DUM(W)	;SKIP IF PENDING DUMMY INTERRUPT
	JRST	NXTWAT		;BRANCH IF NOTHING MORE TO DO
	MOVE	U,TS1DUM(W)	;GET THE UNIT WE'RE DEFERRING
	SETZM	TS1DUM(W)	;NO LONGER DEFERRING IT
	JRST	STCSI1		;START IT UP
;$ TRACK SUPPORT

;$ TRACK 111111 - TRACK AN INTERRUPT
;$
TRK111:	MOVSI	T1,111000	;$ TYPE 1
	IOR	T1,TIME##	;$ MERGE WITH TIME STAMP
	PUSHJ	P,SHOVE
	DMOVE	T1,@TTSCSW(W)	;$ T1=CSW, T2=WC
	PUSHJ	P,SHOVE		;$
	MOVE	T2,T1		;$ T1=WC
	PUSHJ	P,SHOVE		;$
	CONI	274,T1		;$
	PUSHJ	P,SHOVE
	POPJ	P,

;$ TRACK 222222 - TRACK AN INTENT TO SET GO FLAG
;$
TRK222:	PUSHJ	P,SAVT##	;$ SAVE ALL ACS
	PUSHJ	P,TRKLOK	;$ DON'T PERMIT AN INTERRUPTION
	PUSH	P,SHVPTR	;$ SAVE THE SHOVE POINTER
	MOVE	T1,[222222,,3]	;$ INITIAL RECORD TYPE 2
	PUSHJ	P,SHOVE		;$ REMEMBER IT
	MOVE	T1,UDBNAM(U)	;$ PICK UP THE UNIT NAME
	PUSHJ	P,SHOVE		;$ SAVE IT
	PMOVE	T1,TS1DVP(W)	;$ GET DEVL POINTER
	MOVE	T2,T1		;$ COPY DEVL POINTER
	PUSHJ	P,SHOVE		;$ SAVE DEVL POINTER
	MOVSI	T3,-50		;$
	LDB	T1,[POINT 4,T2,11]	;$ GET STATE
	CAIN	T1,17		;$ SKIP IF NOT STOPPED
	JRST	TRK22B		;$ BRANCH IF STOPPED
	AND	T2,[17,,-1]	;$ ISOLATE ADDRESS ONLY
TRK22A:	PMOVE	T1,T2		;$ PICK UP A PROGRAM INSTRUCTION
	PUSHJ	P,SHOVE		;$
	TLZ	T1,400000	;$ TURN OFF MUX BIT
	SKIPE	T1		;$ SKIP IF WE HALTED
	AOBJN	T3,[AOJA T2,TRK22a] ;$ LOOP FOR ALL ISNTRUCTIONS
TRK22B:	POP	P,T1		;$ GET SHOVE POINTER
	MOVE	T3,SHVPTR	;$ GET SHOVE POINTER
	SUB	T3,T1		;$ GET TRUE COUNT
	SKIPE	T1		;$ SKIP IF POINTER WAS BOGUS TO START WITH
	HRRM	T3,(T1)		;$ FIXUP HEADER
	POPJ	P,		;$ RETURN

TRK333:	PUSHJ	P,TRKLOK	;$ DON'T PERMIT INTERRUPTION
	MOVE	T1,[333333,,10]	;$ 
	PUSHJ	P,SHOVE
	MOVE	T1,UDBNAM(U)	;$
	PUSHJ	P,SHOVE
	MOVE	T1,TS1SND(U)	;$
	PUSHJ	P,SHOVE
	MOVE	T1,TS1SND+1(U)	;$
	PUSHJ	P,SHOVE
	MOVE	T1,TS1SND+2(U)	;$
	PUSHJ	P,SHOVE
	MOVE	T1,TS1SND+3(U)	;$
	PUSHJ	P,SHOVE
	MOVE	T1,TS1SND+4(U)	;$
	PUSHJ	P,SHOVE
	MOVE	T1,TS1SND+5(U)	;$
	PUSHJ	P,SHOVE
	POPJ	P,

;$ TRACK A CONO
TRK444:	PUSHJ	P,TRKLOK	;$ DON"T PERMIT INTERRUPTION
	PUSH	P,T1
	MOVE	T1,[444444,,3]
	PUSHJ	P,SHOVE
	MOVE	T1,UDBNAM(U)
	PUSHJ	P,SHOVE
	POP	P,T1
	PUSHJ	P,SHOVE
	POPJ	P,

TRK555:	PUSHJ	P,TRKLOK	;$ DON'T PERMIT INTERRUPTION
	PUSH	P,T1		;$ SAVE IORB POINTER
	MOVE	T1,[555555,,6]	;$ 5=SETCMD TIME
	PUSHJ	P,SHOVE		;$
	MOVE	T1,UDBNAM(U)	;$
	PUSHJ	P,SHOVE
	MOVE	T1,0(P1)	;$IORB
	PUSHJ	P,SHOVE
	MOVE	T1,1(P1)
	PUSHJ	P,SHOVE
	MOVE	T1,2(P1)
	PUSHJ	P,SHOVE
	MOVE	T1,3(P1)
	PUSHJ	P,SHOVE
	POP	P,T1
	POPJ	P,
SHOVE:	SKIPE	SHVPTR
	SKIPN	SHVCTR
	POPJ	P,
	MOVEM	T1,@SHVPTR
	AOS	SHVPTR
	SOSLE	SHVCTR
	POPJ	P,
	PUSH	P,T1
	MOVEI	T1,6000
	MOVEM	T1,SHVCTR
	MOVEI	T1,SHVBFR
	MOVEM	T1,SHVPTR
	POP	P,T1
	POPJ	P,

	$LOW
SHVPTR:	EXP	SHVBFR
SHVCTR:	6000
SHVBFR:	BLOCK	6001
	$HIGH

TRKLOK:	CONSZ	PI,PI.IPA-PI.IP7	;SKIP IF UUO LEVEL
	POPJ	P,			;$ EXIT IF ALREADY AT INTRP LEVEL
	TAPOFF				;$ DISABLE TAPE INTERRUPTS
	PUSHJ	P,@(P)			;$ CALL CALLER
	SKIPA				;$ NO SKIP RETURN
	AOS	-1(P)			;$ PASS ALONG SKIP
	POP	P,(P)			;$ WASTE CALLER'S PC
	TAPON				;$ RE-ENABLE TAPE INTERRUPTS
	POPJ	P,			;$ RETURN TO CALLER'S CALLER
STIDSP:	IFIW	TAPIFI##	;(00) ILLEGAL
	IFIW	STIRD		;(01) READ
	IFIW	STIWT		;(02) WRITE
	IFIW	TAPIFI##	;(03) READ BACKWARDS (ILLEGAL)
	IFIW	STISR		;(04) SKIP RECORD
	IFIW	STIBR		;(05) BACKSPACE RECORD
	IFIW	STISF		;(06) SKIP FILE
	IFIW	STIBF		;(07) BACKSPACE FILE
	IFIW	STIEG		;(10) ERASE GAP
	IFIW	STIDSE		;(11) DATA SECURITY ERASE
	IFIW	STIRW		;(12) REWIND
	IFIW	STIUN		;(13) REWIND / UNLOAD
	IFIW	STITM		;(14) WRITE TAPE MARK
	IFIW	STISTS		;(15) STATUS REQUEST
	IFIW	STIRD		;(16) CORRECTION READ
	IFIW	STIRD		;(17) READ LOW THRESHOLD
STIMAX==.-STIDSP-1
;HERE IF NO IORB - MAKE SURE DONT LOSE CUE OR DE

STCIN1:	SKIPE	TTSOPU(W)	;SKIP UNLESS WAITING FOR CUE/DE
	PUSHJ	P,SETGOP		;SET UP COMMAND WAITING FOR
	JRST	TAPDIS##	;DISMISS THIS INT

;HERE TO CHECK FOR DUMMY STATUS INTERUPT - SEE IF ALREADY SELECTED
;	NON-SKIP IF NOT SELECTED
;	SKIP RETURN IF ALREADY SELECTED

CKSTRT:	MOVSI	T2,TKSSEL##
	TDNN	T2,TKBSTS(W)	;SELECTED?
	PJRST	CLRCTL		;NOT STARTED OR SELECTED, CLEAR CONTROL
	MOVSI	T2,TKSSTD##	;SELECTED, SEE IF ALSO STARTED
	TDNE	T2,TKBSTS(W)	;SKIP IF NOT STARTED
	PJRST	CPOPJ1##	;SELECTED AND STARTED, CONTINUE
	PJRST	CLRCTL		;CLEAR CONTROL, ASK FOR SCHEDULING

;ROUTINE TO MAKE CONTROLLER IDLE

CLRCTL:
STCIDL:	SETOM	TS1UNI(W)	;NO UNIT SELECTED
	PJRST	STSCLR		;CLEAR STATUS FLAG AND RETURN

;HERE TO RESET ACTIVE I/O

TS1RES:	MOVEI	T2,SO.RCH	;RESET SUBCHANNEL
	PUSHJ	P,CNOCLR	;DO IT
	PUSHJ	P,CLRCTL	;CLEAR WORLD
	SETZM	TTSBCT(W)	;AND CLEAR BUSY TIMER
	SETZM	TTSOPU(W)	;AND NO BUSY UNIT
	HRROS	TS1SUN(W)	;FREE UP POLLER IF RUNNING
	HRROS	KDBNUM(W)	;FREE AUTO MASK
	SETZM	TS1PPC(W)	;FORGET PROGRAM IN PROGRESS
	HLRZ	T1,TS1SCN(W)	;SUBCHANNEL IN CONO POSITION
	TRO	T1,SO.CME+SO.RCH ;CLEAR SUBCHANNEL AND MEMORY ERRORS
	XCT	KDBCNO(W)	;CONO SA10,(T1)
	MOVEI	T2,SO.ENB	;ENABLE INTERRUPTS
	PUSHJ	P,CNOSET	;DO IT
	XCT	KDBCNI(W)	;CONI SA10,T1
	HRRZ	T3,TS1SCN(W)	;GET SA10 SUBCHANNEL
	MOVNS	T3		;T3=MINUS SUBCHANNEL NUMBER
	MOVEI	T2,SI.ENB	;INTERRUPT ENABLE FLAG FOR CHAN 0
	LSH	T2,(T3)		;T2=INTERRUPT ENABLE FLAG FOR OUR CHAN
	TDNE	T1,T3		;SKIP IF ENABLE DIDNT SET
	POPJ	P,		;OK
;ROUTINE TO CAUSE AN INTERUPT TO FORCE A SCHEDULE CYCLE

STCRSL:				;ALSO REQUEST SELECTION INTERRUPT
TS1SCH:	PUSHJ	P,SAVE4##	;SAVE ACS FOR CALLER
	MOVE	T1,UDBPDN(U)	;GET PHYSICAL DRIVE NUMBER
	ADDI	T1,@TTSOFS(W)	;MAKE DEVICE ADDR
	MOVEM	T1,TTSDMD(W)	;SAVE AS LAST DUMMY DEVICE
	MOVSI	T2,(KD.SIR)	;SOFT-INTERUPT-REQUEST BIT
	IORM	T2,KDBSTS(W)	;SET IT FOR SAXSER
	MOVEI	T2,SO.SRQ	;STATUS REQUEST
	PUSHJ	P,CNOSET	;REQUESTS AN INTERRUPT
	POPJ	P,
;ROUTINE TO CHECK IF KONTROLLER IS ON LINE
;CALLED ONCE A SECOND.
;TAKE THIS OPPORTUNITY TO CHECK BUSY TIMER

TS1ONL:	SKIPE	TTSBCT(W)	;SKIP IF TIMER NOT SET
	SOSLE	TTSBCT(W)	;SKIP IF TIMER EXPIRES
	JRST	TS1ON1		;GO CHECK/POLL FOR NEW UNITS
	SETOM	TTSBCT(W)	;MAKE SURE NEGATIVE FOR ENSUING INTERRUPT
	AOS	TTSBRC(W)	;COUNT TIMES TIMER EXPIRED
	MOVE	U,KDBCUN(W)	;GET CURRENT UNIT
	MOVE	U,(U)		;ADDR OF UDB
	PUSHJ	P,TS1SCH	;REQUEST SCHEDULING INTERRUPT

TS1ON1:	PUSHJ	P,TS1BOC	;GO REPORT BUS OUT CHECK IF NEEDED
	SKIPE	TS1OFL(W)	;SKIP IF WE THINK WE'RE ONLINE
	JRST	TS1ON0		;BRANCH IF WE'RE OFFLINE
;$	SKIPL	@KDBCHN(W)	;CHANNEL BUSY?
;$	JRST	CPOPJ1##	;LEAVE IT ALONE
	SKIPE	T1,TS1NUM(W)	;GET BIT MASK
	JFFO	T1,TS1ON4	;FIND FIRST UNIT NUMBER
	HRRZS	KDBNUM(W)	;INDICATE NO DRIVES TO CONFIGURE
	SKIPL	T1,TS1SUN(W)	;GET NON-EXISTANT UNIT BEING SENSED
	JRST	CPOPJ1##	;DO NOTHING IF SENSE IN PROGRESS
	PUSHJ	P,SAVE4##	;SAVE SOME ACS
	AOS	T1		;ADVANCE TO NEXT UNIT
	HRRZS	T1		;CLEAR LH FLAG
	CAILE	T1,TS1HDN	;OVERFLOW?
	MOVEI	T1,0		;START WITH DRIVE ZERO
	ADD	T1,KDBIUN(W)	;INDEX INTO TABLE

TS1ON2:	SKIPN	(T1)		;KNOWN UNIT?
	JRST	TS1ON3		;NO
	CAMGE	T1,KDBFUN(W)	;END OF TABLE?
	AOJA	T1,TS1ON2	;TRY ANOTHER
	SETOM	TS1SUN(W)	;RESET COUNTER
	JRST	CPOPJ1##	;AND INDICATE CONTROLLER ONLINE

TS1ON3:	SUB	T1,KDBIUN(W)	;REDUCE TO A DRIVE NUMBER
	MOVEM	T1,TS1SUN(W)	;SAVE FOR NEXT TIME
	SKIPL	@KDBCHN(W)	;SKIP IF CHANNEL IS FREE
	JRST	CPOPJ1##	;BRAND (AND RETURN LATER) IF CHANNEL BUSY
;HERE FROM INTERRUPT LEVEL WHEN WE COMPLETE AN IORB
TS1ONI:	PUSH	P,M		;SAVE M
	MOVE	M,TS1GSN(W)	;GET GLOBAL SUBCHANNEL NUMBER
	LDB	P1,[POINT WIDCUA,KDBUNI(W),17] ;GET TCU NUMBER
	XMOVEI	P4,TS1SNS(W)	;POINT TO SENSE PROGRAM
	HRRO	P3,T1		;GET MASSBUS UNIT (NONE),,DRIVE NUMBER
	PUSHJ	P,TS1DCK	;QUEUE UP SENSE REQUEST
	  JFCL			;SHOULD NEVER SKIP
	JRST	MPOPJ1##	;RETURN
TS1ON4:	PUSHJ	P,AUTLOK##	;GET AUTCON INTERLOCK
	  JRST	CPOPJ1##	;TRY AGAIN NEXT TIME
	PUSHJ	P,SAVE4##	;SAVE SOME ACS
	PUSH	P,M		;SAVE M TOO
	LDB	P1,[POINT WIDCUA,KDBUNI(W),17] ;GET TCH
	HRRO	P3,T2		;GET MASSBUS UNIT (NONE),,DRIVE NUMBER
	XMOVEI	P4,TS1SNS(W)	;POINT TO SENSE PROGRAM
	MOVE	T1,KDBDVC(W)	;DEVICE CODE
	XMOVEI	T2,TS1DSP	;DISPATCH
	MOVE	T3,KDBCHN(W)	;CHANNEL DATA BLOCK
	PUSHJ	P,AUTSET##	;SET UP CPU VARIABLES
	PUSHJ	P,TS1DRS	;TRY TO CONFIGURE A DRIVE
	  JFCL			;IGNORE ERRORS
	PUSHJ	P,AUTULK##	;RELEASE AUTCON INTERLOCK
	HRROS	TS1SUN(W)	;RELEASE THE POLLER (IE: UNLOCK THE SENSE BFR)
	HRROS	KDBNUM(W)	;GIVE AUTCON A FLAG
	JRST	MPOPJ1##	;TELL TAPSER THE CONTROLLER IS ALIVE

;HERE IF WE THINK WE'RE OFFLINE
TS1ON0:	MOVE	T1,KDBIUN(W)	;POINT AT UNIT TABLE
	SOS	(P)		;(UGH!) UNDO SKIP RETURN
	JRST	TS1ON3		;GO FORCE A POLL FOR UNIT #0
TS1BOC:	SKIPN	TTSBUS(W)	;SKIP IF ANY UNITS GOT BUS OUT
	POPJ	P,		;EXIT IF OK
	MOVE	U,OPRLDB##	;POINT U FOR OUTPUT
	PUSHJ	P,INLMES##	;
	ASCIZ	*
% BUS-OUT Parity Error for *	;
	SETZ	T2,
	EXCH	T2,TTSBUS(W)	;GET OFFENDING UNIT
	SKIPE	T2		;SKIP IF UNIT SOMEHOW DISAPPEARS
	SKIPN	T2,UDBNAM(T2)	;GET UNIT NAME
	MOVSI	T2,'MTA'	;THIS CAN'T HAPPEN
	PUSHJ	P,PRNAME##	;TYPE UNIT NAME
	MOVE	T2,.CPLOG##	;GET CPU NAME
	LSH	T2,-6		;GET A LEADING SPACE
	PUSHJ	P,PRNAME	;TYPE CPU NAME
	PUSHJ	P,INLMES##	;
	ASCIZ	* SA*		;
	LDB	T1,[POINT 9,KDBCNO(W),11]	;GET DEVICE CODE
	TRZ	T1,3		;TURN OFF OPCODE
	PUSHJ	P,PRTDI8##	;TYPE DEVICE CODE
	PUSHJ	P,INLMES##	;
	ASCIZ	* SC*		;
	HRRZ	T1,TS1SCN(W)	;GET LOCAL SUBCHANNEL #
	PUSHJ	P,PRTDI8##	;TYPE IT
	PUSHJ	P,INLMES##	;
	ASCIZ	* TCU*		;
	HRRZ	T1,TTSOFS(W)	;GET CU BASE ADDRESS
	PUSHJ	P,PRTDI8##	;
	PJRST	CRLF##		;EXIT
SUBTTL	AUTOCONFIGURE

;CONFIGURE A SINGLE TCU
;P1/ TCU NUMBER
;P2/ JUNK,,LOCAL SUBCHANNEL #
;P4/ ADDRESS OF CHANNEL PROGRAM
;M/  GLOBAL SUBCHANNEL NUMBER
TS1CFS:	PUSHJ	P,SAVE4##	;SAVE SOME ACS
	PUSHJ	P,SAVW##	;SAVE FOR SAXSER
	PUSH	P,M		;SAVE M TOO
	SETZ	W,		;DON'T HAVE A KDB YET
	MOVSI	P3,-TS1DMX	;SET UP POINTER
	PUSHJ	P,TS1DC1	;DO A SENSE TO GET SOME USEFUL INFORMATION
	  JFCL			;IGNORE POSSIBLE TIMEOUT (RELY ON SENSE BYTES)
	MOVE	T1,S17WRD+SNSDAT(P4) ;WORD WITH SENSE BYTE 17
	AND	T1,[S17TCE]	;ISOLATE TCU EC LEVEL
	JUMPE	T1,MPOPJ1##	;TCU ALWAYS HAS A NON-ZERO VALUE
	HRRZ	T1,P1		;GET "TCU" NUMBER
	TRO	T1,400000	;DON'T LET TAPINT THINK THIS IS RH
	PUSHJ	P,AUTKDB##	;BUILD A KDB
	  JRST	MPOPJ##		;GIVE UP IF NO CORE
	SKIPE	TS1SNS(W)	;ALREADY HAVE A SENSE PROGRAM?
	JRST	TS1CF1		;YES--EXISTING KDB
	XMOVEI	T1,TS1MDT##	;POINT TO MONGEN'ED DEVICE (SUB-)TABLE
	SETZ	T2,		;IMPLICIT DEFAULT MDT
	HRLOI	T3,(P2)		;SUB-CHANNEL,,ALL DRIVES
	MOVEI	T4,MD.KON	;KONTROLLER INFORMATION DESIRED
	PUSHJ	P,AUTMDT##	;SEE IF WE HAVE ANY DATA
	  SETZ	T1,		;NO DATA
	TRNN	T1,-1		;IF NO PORTING INFORMATION,
	HRRI	T1,-1		;SET UP TO TRUST THE TX
	MOVEM	T1,TS1MDW(W)	;SAVE THE MDT DATA WORD
	MOVEI	T1,SNSLEN	;WORDS TO XFER
	XMOVEI	T2,SNSPGM	;SENSE CHANNEL PROGRAM
	XMOVEI	T3,TS1SNS(W)	;DENSTINATION
	EXTEND	T1,[XBLT]	;COPY
	MOVEI	T1,1		;NUMBER OF DEVICE LIST ENTRIES NEEDED
	PUSHJ	P,SAXDVL##	;FILL IN THE TABLE
	  JRST	MPOPJ##		;FAILED
	MOVEM	T1,TS1DVP(W)	;SAVE FOR LATER REFERENCE

TS1CF1:	MOVEI	T1,(P2)		;GET SUBCHANNEL
	IMULI	T1,SO.CHN	;POSITION
	HRLZS	T1		; FOR CONI
	HRRI	T1,(P2)		;PUT SUBCHANNEL IN RH TOO
	MOVEM	T1,TS1SCN(W)	;SAVE
	MOVEM	M,TS1GSN(W)	;SAVE GLOBAL SUBCHANNEL NUMBER TOO
	SETOM	TS1SUN(W)	;NOT SENSING NON-EXISTANT UNITS
	MOVE	T2,SNSDVW(P4)	;GET SENSE COMMAND (FOR DRIVE#0)
	LDB	T1,DVYDVA	;GET ADDRESS FOR DRIVE#0
	MOVEM	T1,TTSOFS(W)	;REMEMBER DRIVE0 ADDRESS FOR LATER
	MOVE	T1,SAXSBA##(M)	;GET GLOBAL SUBCHANNEL DATA BLOCK ADDRESS
	ADDI	T1,.CLCSW	;ADD OFFSET TO COMPUTE ENDING STATUS WORD
	MOVEM	T1,TTSCSW(W)	;SAVE ADDRESS OF ENDING STATUS WORD
	MOVE	T1,KDBCSO(W)	;GET CONSO SKIP ADDRESS
	MOVEI	P3,(P1)		;GET TCU ADDRESS
	MOVSI	P3,-TS1DMX	;START WITH DRIVE ZERO

TS1CF2:	PUSHJ	P,TS1DRS	;BUILD UDB
	  JFCL			;PROBABLY NO DRIVE SWITCHED IN
	AOBJN	P3,TS1CF2	;LOOP BACK FOR EACH POSSIBLE DRIVE

;$$*** DO WE WANT TO CALL TS1RES?
;$$*** DOESN'T IT ENABLE INTS?
;$$*** WE WOULD NEED TO INSERT WAIT TIMING SINCE
;$$*** ANY OTHER CU'S OF ANY KIND ON THE SAME SUBCHANNEL
;$$*** WILL REFUSE TO ANSWER OR RETURN BUSY'S WHILE RESET IS GOING.
	PUSHJ	P,TS1RES	;RESET SUBCHANNEL
	JRST	MPOPJ##		;RESTORE M AND RETURN
;AUTOCONFIGURE A SINGLE DRIVE FROM "UUO" LEVEL

TS1DRV:	PUSHJ	P,SAVE4##	;SAVE SOME ACS
	PUSH	P,M		;SAVE SOME MORE
	LDB	P1,[POINT WIDCUA,KDBUNI(W),17] ;GET TCU# FROM KDB
	HRRO	P3,U		;LOAD P3=<NO-MASS-BUS>,,DRIVE#
	XMOVEI	P4,TS1SNS(W)	;POINT P4 AT SENSE PROGRAM AND BUFFER
	MOVE	M,TS1GSN(W)	;GET GLOBAL SUBCHANNEL NUMBER FROM KDB
	PUSHJ	P,TS1DRS	;CALL INTERNAL ROUTINE TO CONFIGURE DRIVE
	PJRST	MPOPJ##		;NORMAL RETURN
	PJRST	MPOPJ1##	;SKIP RETURN
;AUTOCONFIGURE A SINGLE DRIVE VIA AN INTERNAL CALL

TS1DRS:	PUSHJ	P,TS1DCK	;DOES DRIVE EXIST?
	  POPJ	P,		;NO
	LDB	T1,[POINTR (SNSDAT+S13WRD(P4),S13TCH)] ;HIGH BYTE TX S/N
	LDB	T2,[POINTR (SNSDAT+S14WRD(P4),S14TCL)] ;LOW BYTE TX S/N
	ROT	T2,-10		;LEFT JUSTIFY
	LSHC	T1,10		;MERGE HIGH + LOW BYTES
	SKIPN	T1		;HAVE A NON-ZERO S/N?
	MOVE	T1,P1		;NO, USE TCU NUMBER INSTEAD
	SETZM	KDBSER+0(W)	;NO HIGH ORDER S/N WORD
	MOVEM	T1,KDBSER+1(W)	;SAVE LOW ORDER S/N WORD
	MOVE	T1,SNSDAT+S17WRD(P4) ;SENSE BYTES 16-19
	TLNN	T1,(S172CH)	;TWO CHANNEL SWITCH CAPABILITY?
	TLZ	T1,(S17SWF)	;NO, IGNORE PORTING
	LDB	T1,[POINTR (T1,S17SWF)] ;GET SWITCH FEATURES
	ANDI	T1,3		;STRIP OFF HI/LO UNIT BIT
	AOS	T1		;GET MAXIMUM NUMBER OF PORTS
	LSH	T1,1		;ACCOUNT FOR A/B PATH PER TCU
	HRRZ	T2,TS1MDW(W)	;GET PORT COUNT FROM MDT DATA WORD
	CAILE	T1,(T2)		;IF THE MDT KNOWS BETTER THAN WE DO,
	MOVE	T1,T2		;THEN TRUST IT
	DPB	T1,[POINTR (KDBSTS(W),KD.MPT)] ;SAVE
	MOVSI	T2,(KD.MPD)	;GET A BIT
	CAIE	T1,1		;IF MORE THAN ONE PATH,
	IORM	T2,KDBSTS(W)	;THEN SAY MULTI-PORTED DEVICE

TS1DR1:	MOVSI	T1,(S05NSS)	;BIT TO TEST
	TDNN	T1,S05WRD+SNSDAT(P4)	;NEW SUBSYSTEM?
	JRST	TS1DR2		;MUST DUMMY UP S/N IF A TX01
	MOVE	T1,S12WRD+SNSDAT(P4) ;SENSE BYTES 12-15
	MOVE	T2,S16WRD+SNSDAT(P4) ;SENSE BYTES 16-19
	LSH	T1,-4		;POSITION HIGH ORDER S/N BITS
	ANDI	T1,377		;ISOLATE HIGH ORDER S/N BITS
	LSHC	T1,-34		;MERGE HIGH AND LOW BITS
	JUMPN	T2,TS1DR3	;CONTINUE IF S/N IS NON-ZERO

TS1DR2:	DMOVE	T1,KDBSER(W)	;GET TCU SERIAL NUMBER
	IMULI	T2,^D100	;TIMES 100
	ADDI	T2,(P3)		;INCLUDE DRIVE NUMBER

TS1DR3:	DMOVEM	T1,.CPTSN##	;SAVE TEMPORARILY
	HRRZ	T3,P3		;GET PHYSICAL DRIVE NUMBER
	PUSHJ	P,AUTDPU##	;LINK UP DUAL PORTED DRIVES
	  JFCL			;MUST PROCEED EVEN IF DUAL PORTED
	MOVEI	T1,TUCD80##+TUCD16## ;ASSUME TU70
	MOVE	T2,S13WRD+SNSDAT(P4) ;SENSE BYTE 13
	TLNN	T2,(S13FET)	;SKIP IF WE HAVE NRZI FEATURE
	MOVEI	T1,TUCD16##	;ASSUME 1600 BPI ONLY
	MOVE	T2,S06WRD+SNSDAT(P4) ;SENSE BYTE 6
	TLNE	T2,(S067TK)	;7 TRACK?
	MOVEI	T1,TUCD20##+TUCD55##+TUCD80##+TUC7TK##
	TRNE	T2,S06D62	;6250 DRIVE?
	MOVEI	T1,TUCD16##+TUCD62##
	TRO	T1,TUCIRD##+K.TS1 ;INCLUDE REWIND INTERRUPT AND KONT TYPE
	MOVE	T2,T1		;COPY DRIVE INFO
	HRRZ	T1,P3		;GET PHYSICAL DRIVE NUMBER
	HRLS	T1		;MAKE IT THE TABLE INDEX TOO
	PUSH	P,P1		;SAVE P1
	HRRZ	P1,P3		;GET PHYSICAL DRIVE NUMBER
	LDB	T3,[POINTR (SNSDAT+S06WRD(P4),S06MOD)] ;GET TU MODEL BITS
	SETZ	T4,		;INCASE WE CAN'T FIGURE IT OUT
	CAIN	T3,5		;TU70/71?
	MOVE	T4,['TU70  ']	;YES
	TRNE	T2,TUC7TK##	;BUT IS IT 7-TK?
	TRO	T4,10000	;MAKE IT A TU71
	CAIN	T3,14		;TU72?
	MOVE	T4,['TU72  ']	;YES
	CAIN	T3,15		;TU73?
	MOVE	T4,['TU73  ']	;YES
	XMOVEI	T3,HNGTBL	;POINT TO HUNG TIMER TABLE
	PUSHJ	P,TAPDRV##	;BUILD AND LINK UP UDB AND DDB
	  JFCL			;FAILED
	POP	P,P1		;RESTORE
	DMOVE	T1,.CPTSN##	;RETRIEVE DRIVE SERIAL NUMBER
	DMOVEM	T1,UDBDSN(U)	;SAVE IN UDB
	JRST	CPOPJ1##	;RETURN SUCCESS
;CHECK FOR DRIVE EXISTANCE
TS1DCK:	JUMPE	W,TS1DC2	;JUMP IF NO KDB
	HRRZ	T1,P3		;GET DRIVE NUMBER
	MOVE	T1,BITTBL##(T1)	;AND IT'S BIT
	TDNE	T1,TS1IUM(W)	;WANT TO IGNORE THIS DRIVE?
	POPJ	P,		;SAY IT DOESN'T EXIST

TS1DC1:	JUMPE	W,TS1DC2	;JUMP IF NO KDB
	SKIPL	T1,TS1SUN(W)	;FULL AUTOCONFIGURE?
	JRST	TS1DC2		;NO
	HRRZS	T1		;ISOLATE POSSIBLE DRIVE NUMBER
	HRRZ	T2,P3		;AND THE ONE BEING CONFIGURED NOW
	CAIN	T1,(T2)		;MATCH?
	JRST	CPOPJ1##	;THEN INTERRUPT LEVEL TOLD US IT EXISTED

TS1DC2:	MOVE	T1,P1		;GET TCU #
	LSH	T1,WIDUNA	;POSITION IN DEVICE ADDRESS
	ADDI	T1,(P3)		;INCLUDE DRIVE NUMBER
	MOVE	T2,SNSPGM+SNSDVW ;GET SENSE COMMAND
	DPB	T1,DVYDVA	;INSERT DEVICE ADDRESS
	MOVEM	T2,SNSDVW(P4)	;STORE DVW
	MOVE	T2,SNSPGM+SNSNOP ;GET NOP COMMAND
	DPB	T1,DVYDVA	;INSERT ADDRESS
	MOVEM	T2,SNSNOP(P4)	;STORE NOP
	MOVE	T2,SNSPGM+SNSDCW ;GET THE -VE BYTE COUNT FOR SENSE DATA
	HRRI	T2,SNSDAT(P4)	;FILL IN ADDRESS OF SENSE DATA
	MOVEM	T2,SNSDCW(P4)	;STORE DCW
	SETZM	SNSHLT(P4)	;INSERT A HALT
	MOVSI	T2,SNSHLT(P4)	;SET UP TO CLEAR SENSE DATA
	HRRI	T2,SNSDAT(P4)	; (CONVENIENT WE CLEARED WORD 2, HUH?)
	BLT	T2,SNSDAT+SNSWDS-2(P4)	;ZERO THE SENSE DATA AREA
	SETOM	SNSDAT+SNSWDS-1(P4) ;LEAVE LAST BYTES -1 FOR VALIDITY CHECK
	JUMPE	W,TS1DC3	;IF FIRST TIME HERE, NO KDB
	SKIPGE	TS1SUN(W)	;FULL AUTOCONFIGURE?
	JRST	TS1DC3		;GLET SAXSER RUN THE CHANNEL PROGRAM
	MOVEI	T2,TS1SNS+SNSNOP(W) ;GET VIRTUAL PC OF SENSE PROGRAM
	MOVEM	T2,TS1PPC(W)	;WHERE TO START/RESTART
	MAP	T2,TS1SNS+SNSNOP(W) ;GET THE PHYSICAL PC OF THE SENSE'S NOP
	MOVE	T1,TTSOFS(W)	;GET UNIT#0 ADDRESS
	ADD	T1,P3		;COMPUTE OUR UNIT #
	DPB	T1,[POINT SASDLD,T2,SANDLD]	;INSERT THE DEVICE ADDRESS
	MOVEI	T1,DL.STA	;GET THE "START" OPCODE
	DPB	T1,[POINT SASDLO,T2,SANDLO]	;INSERT THE OPCODE
	PMOVEM	T2,TS1DVP(W)	;CREATE NEW DEVL ENTRY
	PJRST	SETGOB		;SET "GO" BIT AND RETURN

TS1DC3:	MOVE	T1,P4		;COPY CHANNEL PROGRAM ADDRESS
	ADDI	T1,SNSDVW	;SKIP OVER THE NO-OP -- CALLER ALREADY DID IT
	PUSHJ	P,SAXRCP##	;RUN THE CHANNEL PROGRAM
	  POPJ	P,		;ERROR
	MOVE	T1,S17WRD+SNSDAT(P4) ;GET THE TCU EC WORD
	AND	T1,[S17TCE]	;ISOLATE THE LEVEL
	JUMPE	T1,CPOPJ##	;BRANCH IF UNIT IS NOT A TAPE UNIT
	MOVSI	T1,(S01TUA!S01TUB) ;BITS TO TEST
	TDNN	T1,S01WRD+SNSDAT(P4) ;DRIVE EXIST?
	POPJ	P,		;NO
;$$	MOVE	T1,S06WRD+SNSDAT(P4) ;GET SENSE BYTE 6 (MODEL ID)
;$$	TDNN	T1,[S06MOD]	;SKIP IF MODEL TYPE KNOWN
;$$	POPJ	P,		;RETURN IF NOT A GOOD DRIVE AFTER ALL
	JUMPE	W,CPOPJ1##	;GIVE GOOD RETURN IF NO KDB TO FIX UP
	HRRZ	T1,P3		;GET DRIVE NUMBER
	SETCA	T1,BITTBL##(T1)	;TRANSLATE DRIVE NUMBER TO A BIT
	SETZ	T2,		;ASSUME NO OTHER NEW DRIVES
	SYSPIF			;AVOID RACE WITH INTERRUPT LEVEL
	ANDB	T1,TS1NUM(W)	;CLEAR SINCE DRIVE IS NO LONGER "NEW"
	SKIPE	T1		;WORK PENDING FOR OTHER DRIVES?
	MOVNI	T2,1		;YES
	HLLM	T2,KDBNUM(W)	;UPDATE FLAG
	SYSPIN			;RELEASE INTERLOCK
	JRST	CPOPJ1##	;SAY DRIVE EXISTS
SUBTTL START I/O

;ROUTINE TO START I/O
;USER JOB MUST BE NAILED DOWN - CAUSE SELECTION INTERUPT
;OR JUST START I/O IF UNIT ALREADY SELECTED

TS1SIO:	PUSHJ	P,SAVE1##	;SAVE P1
	MOVE	T1,UDBPDN(U)	;PHYSICAL DEVICE NUMBER
	CAME	T1,TS1UNI(W)
	PJRST	STCRSL		;REQUEST SELECTION INTERRUPT
STCSI1:	PUSHJ	P,CHKIRB##	;GET IORB
	  JRST	STSCLR		;BRANCH IF NO IORB AND DISMISS INTERRUPT
	TAPOFF			;PREVENT POLLER INTERRUPT
	SKIPE	TS1PPC(W)	;SKIP IF POLLER IS IDLE
	JRST	TS1SI2		;BRANCH TO DEFER STARTUP TILL POLLER IS DONE
	TAPON			;ALLOW INTERRUPT AGAIN
	MOVE	P1,T1		;LOAD UP IORB IN A SAFER PLACE
	PUSHJ	P,SETCMD	;SET-UP THE COMMAND LIST
	  JRST	STIDON		;JUST EXIT IF ILLEGAL REQUEST
SETGOP:	SKIPN	TS1PPC(W)	;SKIP IF A PROGRAM TO DO
	JRST	NXTWAT		;PUNT IF NO PROGRAM TO RUN
	PUSHJ	P,SETDVP	;SETUP DEVL FROM PPC
SETGOB:	MOVEI	T2,SO.GO	;GO FLAG
	PUSHJ	P,CNOSET	;SET IT
	PUSHJ	P,TRK222	;$ DO A GO TRACK
NXTWAT:	PUSHJ	P,STSCLR	;CLEAR STATUS FLAG
	MOVEI	T1,0		;MORE INTS COMING
	POPJ	P,		;RETURN
TS1SI2:	MOVEM	U,TS1DUM(W)	;REMEMBER TO START UNIT WHEN POLLER IS DONE
	TAPON			;ALLOW POLLER TO COMPLETE
	JRST	NXTWAT		;TILL UUO LEVEL THAT IT IS IN PROGRESS

;HERE IF NO STATUS WORTH REMEMBERING

STCOFL:	MOVSI	T2,RB.SOL	;SET OFF-LINE STATUS
	IORM	T2,TRBSTS(P1)
	PUSHJ	P,CLRCTL	;RESET CTL
	PJRST	STIDON		;AND DISMISS INTERRUPT
SUBTTL COMMAND SETUP
;ROUTINE TO SET UP CHANNEL PROGRAM HEADER
;T1 := IORB FOR THIS REQUEST

SETCMD:	MOVE	T1,P1		;WHERE TAPSER NEEDS IORB ADDRESSES
	PUSHJ	P,TRK555	;$ TRACK IORB REQUEST
	PUSHJ	P,SAVR##	;SAVE R
	MOVEI	R,TTSPRG(W)	;POINT AT PROGRAM
	LDB	T4,PRBFCN##	;GET DESIRED FCN
	CAIG	T4,IOFMAX	;SKIP IF NEW FUNCTION
	SKIPN	T2,IOFTAB(T4)	;CHECK LEGAL
	JRST	SETILG		;NOPE - ERROR
	TRNE	T2,IOXLST	;SKIP IF NOT DATA TRANSFER
	JRST	SETXFR		;HANDLE DATA XFER
	TRNE	T2,IOXDSE	;SKIP IF NOT DATA-SECURITY-ERASE
	JRST	SETDSE		;BRANCH TO HANDLE DATA-SECURITY-ERASE
	TRNE	T2,IOXXCW	;SKIP IF NOT "XCW HAS A SPACING COUNT"
	JRST	SETXCW		;BRANCH TO HANDLE SPACING-OPERATIONS
	JRST	SETCTL		;BRANCH TO HANDLE RANDOM CONTROL COMMAND

SETEND:	MOVE	T2,[BMXEND]	;GET THE END OF PROGRAM
	MOVEM	T2,(R)		;SAVE IT
	MOVEI	T4,TTSPRG(W)	;GET INITIAL ADDRESS OF PROGRAM
	MOVEM	T4,TS1PPC(W)	;SAVE PROGRAM-PC
	JRST	CPOPJ1##	;GIVE GOOD RETURN
;HERE TO BUILD A COMMAND LIST FOR DATA-SECURITY-ERASE
SETDSE:	PUSH	P,T2		;SAVE THE DSE OPCODE
	HLLZ	T2,IOFTAB+RB.FLG ;GET ERG COMMAND
	PUSHJ	P,SETDVW	;SET A DEVICE-WORD INTO THE PROGRAM
	POP	P,T2		;GET BACK THE DSE CMD
	PUSHJ	P,SETDVW	;INSERT DSE INTO PROGRAM
	JRST	SETEND		;SET UP END-OF-PROGRAM
;HERE TO BUILD A COMMAND LIST FOR A MULTI-REC SPACING OPERATION
; N.B.:	TAPSER DOES THIS TO IMPLEMENT FORWARD/BACKWARD FILE.
;	TAPSER DOES NOT NECESSARILY EXPECT THE TRUE COUNT OF OPS
;	TO ACTUALLY TAKE PLACE, SO LONG AS WE RETURN IF WE FAIL.

SETCTL:	SKIPA	T4,[1]		;FAKE UP A COUNT OF 1 IF RANDOM-CTL COMMAND
SETXCW:	HRRZ	T4,@IRBACC(P1)	;GET COUNT
	TRZN	T2,IOXWRT	;SKIP IF READ OPERATION
	JRST	SETXC1		;BRANCH IF A READ OPERATION
	PUSHJ	P,SETMOD	;SETUP MODE-SET-1/2
	  JRST	SETILG		;BRANCH IF ILLEGAL SET REQUESTED
SETXC1:	CAIN	T4,1		;SKIP IF NOT LAST OPERATION
	TLZ	T2,(F.CC)	;TURN OFF CHAINING FOR LAST OPERATION
	PUSHJ	P,SETDVW	;INSERT THE REQUESTED OPERATION
	SOJLE	T4,SETEND	;EXIT WHEN LAST OP INSERTED, SKIP TRAILING NOP
	PUSHJ	P,SETNOP	;INSERT A CHAINED NOP
	JRST	SETXC1		;LOOP BACK FOR MORE OPS

SETNOP:	PUSH	P,T2		;SAVE AN AC
	MOVE	T2,[F.CC!NOPCMD] ;GET A CHAINED NO-OP
	PUSHJ	P,SETDVW	;INSERT THE NO-OP INTO THE PROGRAM
	POP	P,T2		;RESTORE THE AC
	POPJ	P,		;RETURN TO CALLER

;HERE AT INTERRUPT LEVEL TO LOAD T1=COUNT OF XCW OPS THAT WON
FNDXCW:	LDB	T1,[POINT 22,TTSTCA(W),35]	;GET UNDONE PC
;*** N.B.: THIS WORKS BECAUSE THE PROGRAM IS MAPPED 1:1 S0 WITH PHYSICAL
	SUBI	T1,TTSPRG(W)	;COMPUTE OFFSET INTO PROGRAM
	LSH	T1,-1		;DIVIDE BY TWO
	POPJ	P,		;EXIT WITH WIN-COUNT IN T1
;HERE TO BUILD A PROGRAM TO DO ACTUAL USER-REQUEST DATA I/O
SETXFR:	TRNE	T2,IOXTIE	;SKIP IF NOT TRYING TIE RECOVERY
	SKIPGE	TS1TIE(U)	;SKIP IF TRYING TIE, AND WE HAVE VALID TIE DATA
	PUSHJ	P,SETMOD	;INSERT THE MODE-SET AS NEEDED
	  JFCL	SETILG		;BRANCH IF MODE-SET REQUESTED IS ILLEGAL
	TRZE	T2,IOXTIE	;SKIP IF TIE CMD NOT NEEDED
	PUSHJ	P,SETTIE	;SETUP THE TRACK-IN-ERROR IF NEEDED
	PUSHJ	P,SETDVW	;INSERT THE I/O COMMAND INTO THE PROGRAM
	PUSHJ	P,MAPIO		;FINALLY, CONVERT DF10 LIST INTO SA10 LIST
	JRST	SETEND		;SET-UP THE END OF PROGRAM
;ROUTINE TO POSSIBLY SETUP A TRACK-IN-ERROR-REQUEST COMMAND
;  IF THIS COMMAND REQUIRES IT

SETTIE:	SKIPGE	TS1TIE(U)		;SKIP IF TIE DATA TO USE
	POPJ	P,			;RETURN IF NO TIE DATA
	PUSHJ	P,SAVT##		;DON'T DESTROY CONTEXT
	MOVE	T2,[F.NMT!F.CC!F.SLI!F.OFP!<O.TIE>B15]	;TIE COMMAND
	PUSHJ	P,SETDVW		;INSERT IT
	MAP	T2,TS1TIE(U)		;GET PHYSICAL ADDRESS OF TIE BYTE
	TLO	T2,777700		;INSERT BYTE COUNT OF 1
	PUSHJ	P,SETDCW		;INSERT DCW
	SETOM	TS1TIE(U)		;NO MORE TIE DATA
	POPJ	P,			;RETURN
;ROUTINE TO INSERT A MODE-SET-1 OR MODE-SET-2 INTO PROGRAM

SETMOD:	PUSHJ	P,SETFLG	;SET PROPER MODE BITS IN COMMAND IN T2
	PUSH	P,T2		;SAVE THE I/O COMMAND
	MOVE	T1,P1		;WHERE TAPSER DEMANDS IORB ADDRESSES
	LDB	T3,PRBDEN##	;GET DENSITY BYTE
	MOVSI	T2,RB.PAR	;CHECK PARITY
	TDNE	T2,TRBFNC(P1)	;ON FOR EVEN PARITY
	TRO	T3,20		;REMEMBER EVEN PARITY
	MOVEI	T2,TUC7TK##	;7-TRACK BIT
	TDNE	T2,TUBCNF##(U)	;SKIP IF 9-TRACK UNIT
	TRO	T3,10		;REMEMBER 7-TRACK
	ROT	T3,-1		;DIVIDE BY TWO, REMAINDER TO SIGN BIT
	HLLZ	T2,MODTAB(T3)	;ASSUME WANT LH
	SKIPGE	T3		;SKIP IF WAS EVEN
	HRLZ	T2,MODTAB(T3)	;THATS ODD, WANT RH
	JUMPE	T2,T2POPJ##	;BRANCH IF ILLEGAL SET REQUEST
	TLO	T2,(F.NMT!F.CC)	;NO MEMORY XFER, CHAINED
	PUSHJ	P,SETDVW	;SET MODE-SET INTO PROGRAM
	JRST	T2POJ1##	;GIVE GOOD RETURN

;THE FOLLOWING TABLE IS INDEXED BY THE 5 BIT QUANTITY FORMED BY
;PARITY,CHAN7,DENSITY,DENSITY,DENSITY
;THEN DIVIDED BY 2 TO ACCOUNT FOR HALF WORDS.
;(INDUSTRY-COMPATIBLE MODE IS FILTERED OUT BEFORE GET THIS FAR).

MODTAB:	XWD	<O.9MS+N.STD>_2		,<O.9MS+N.NRZI>_2	;0,,1
	XWD	<O.9MS+N.PE>_2		,<O.9MS+N.NRZI>_2	;2,,3
	XWD	<O.9MS+N.PE>_2		,<O.9MS+N.GCR>_2	;4,,5
	XWD	0			,0			;6,,7
	XWD	<O.7MS+D.556+M.DCON>_2	,<O.7MS+D.200+M.DCON>_2	;10,,11
	XWD	<O.7MS+D.556+M.DCON>_2	,<O.7MS+D.800+M.DCON>_2	;12,,13
	XWD	0			,0			;14,,15
	XWD	0			,0			;16,,17
	XWD	0			,0			;20,,21
	XWD	0			,0			;22,,23
	XWD	0			,0			;24,,25
	XWD	0			,0			;26,,27
	XWD	<O.7MS+M.EPAR+D.556>_2	,<O.7MS+M.EPAR+D.200>_2	;30,,31
	XWD	<O.7MS+M.EPAR+D.556>_2	,<O.7MS+M.EPAR+D.800>_2	;32,,33
	XWD	0			,0			;34,,35
	XWD	0			,0			;36,,37
;SET IMPROPER MODE AND RETURN

SETILG:	MOVSI	T2,RB.SIL!RB.SER ;ILLEGAL OP
	IORM	T2,TRBSTS(P1)	;SET IN IORB
	POPJ	P,		;ERROR RETURN

;SUBROUTINE TO STORE DEVICE ADDR IN T2

SETDVA:	MOVE	T3,UDBPDN(U)	;PHYSICAL DEVICE NUMBER
	ADDI	T3,@TTSOFS(W)	;MAKE PROPER DEVICE ADDRESS
	DPB	T3,[POINT DVSDVA,T2,DVNDVA]	;STORE IT
	POPJ	P,		;RETURN

;SUBROUTINE TO SET SA10 MODE FLAGS

SETFLG:	MOVEI	T3,TUC7TK##	;7-TRACK FLAG
	TDNN	T3,TUBCNF##(U)	;SKIP IF 7-TRACK DRIVE
	JRST	SETF.9		;NO, 9-TRACK
	MOVSI	T3,RB.PAR
	TDNE	T3,TRBFNC(P1)	;SKIP IF ODD PARITY
	TLOA	T2,(F.7TE)	;7-TRACK, EVEN PARITY FLAGS
	TLO	T2,(F.7TK)	;7-TRACK, ODD PARITY FLAGS
	POPJ	P,		;RETURN

SETF.9:	MOVE	T1,P1		;WHERE TAPSER REQUIRES IORB ADDRESSES
	LDB	T3,PRBMOD##	;GET MODE
	CAIN	T3,RB.MBY	;SKIP UNLESS INDUSTRY-COMPATIBILITY
	TLOA	T2,(F.IND)	;9-TRACK, INDUSTRY-COMPATIBLE FLAGS
	TLO	T2,(F.9TK)	;9-TRACK, DEC-MODE
	POPJ	P,

;SUBROUTINE TO INSERT A DEVICE COMMAND IN T2 INTO THE PROGRAM BUFFER.
;ALWAYS INSERT THE DEVICE-ADDRESS, ALWAYS TURN ON COMMAND-CHAINING.
SETDVW:	PUSHJ	P,SETDVA	;ADDRESS DEVICE ADDRESS FIELD
	TLO	T2,(F.XEC)	;EXECUTE, NOT HALT.
SETDCW:	MOVEM	T2,(R)		;INSERT INTO THE PROGRAM
	AOJA	R,CPOPJ##	;EXIT
;TABLE FOR I/O FUNCTIONS - ALREADY IN POSITION

DEFINE IOT(CMD,BITS) <
	BITS+<CMD>
>

;ALLOWABLE SPECIAL FCN BITS

IOXTIE==1B31		;OPERATION REQUIRES TRACK-IN-ERROR REQUEST FIRST
IOXLST==1B32		;DATA XFER LIST REQUIRED
IOXXCW==1B33		;SPACING OPERATION FOR WHICH COUNT IMPLEMENTED
IOXDSE==1B34		;DATA SECURITY ERASE
IOXWRT==1B35		;WRITE OPERATION

IOFTAB:	0					;0 - ILLEGAL
	IOT(<<O.RD>B15>,IOXLST)			;1 - READ
	IOT(<F.SLI!<O.WR>B15>,IOXLST!IOXWRT)	;2 - WRITE
	0					;3 - ILLEGAL
	IOT(<F.NMT!F.CC!<C.FSB>B15>,IOXXCW)	;4 - FORWARD SPACE BLOCK
	IOT(<F.NMT!F.CC!<C.BSB>B15>,IOXXCW) 	;5 - BACKWARD SPACE BLOCK
	IOT(<F.NMT!<C.FSF>B15>)			;6 - FORWARD SPACE FILE
	IOT(<F.NMT!<C.BSF>B15>)			;7 - BACKSPACE FILE
	IOT(<F.NMT!F.CC!<C.ERG>B15>,IOXWRT!IOXXCW) ;10 - WRITE LONG GAP
	IOT(<F.NMT!<C.DSE>B15>,IOXWRT!IOXDSE) 	;11 - DATA SECURITY ERASE
	IOT(<F.NMT!<C.REW>B15>)			;12 - REWIND
	IOT(<F.NMT!<C.RUN>B15>)			;13 - REWIND / UNLOAD
	IOT(<F.NMT!<C.WTM>B15>,IOXWRT)		;14 - WRITE TAPE MARK
	IOT(<<O.NOP>B15>)			;15 - "YELLOW BALL" STATUS
	IOT(<<O.RD>B15>,IOXTIE!IOXLST)		;16 - CORRECTION READ
	IOT(<<O.RD>B15>,IOXLST)			;17 - READ LOW THRESHOLD
IOFMAX==.-IOFTAB				;REMEMBER MAX FCN NUMBER
;HUNG TIMER TABLE
	EXP	^D480			;MAXIMUM TIMEOUT VALUE
HNGTBL:	BYTE(9)	^D000,^D046,^D046,^D046	;IL,RD,WT,RB
	BYTE(9)	^D046,^D046,^D480,^D480	;SR,BR,SF,BF
	BYTE(9)	^D046,^D480,^D135,^D135	;LG,SE,RW,RU
	BYTE(9)	^D046,^D046,^D046,^D046	;TM,YB,CR,RL
;ROUTINE TO CONVERT A DF10 IOWD LIST INTO AN SA10 LIST FOR A TAPE XFER
;ENTER:
;	T1/	IORB ADDRESS
;	U/	UDB ADDRESS
;	W/	KDB ADDRESS
;BUT THEN:
;	P1/	POINTER TO DF10 LIST
;	P2/	POINTER TO SA10 LIST
;	P3/	COUNT AS RETURNED IN T1 FROM GETIOW
;	P4/	PA AS RETURNED IN T2 FROM GETIOW

MAPIO:	PUSHJ	P,SAVE4##	;SAVE SOME ACS
	HLRE	M,TRBRCT(P1)	;GET -VE COUNT
	MOVMS	M		;MAKE IT +VE COUNT
	HRRZ	T3,TRBRCT(P1)	;GET UVA OR 0 IF M IS BYTES
	MOVE	T1,P1		;GET IORB ADDRESS
	MOVEI	T4,1		;MULTIPLIER IS 1
	LDB	T2,PRBMOD##	;GET BYTE CONTAINING MODE INFO
	CAIE	T2,RB.MBY	;SKIP IF "MTIND." INDUSTRIAL MODE
	SKIPN	T3		;SKIP IF WORD COUNT IS IN WORDS
	MOVEI	T4,4		;IF A BYTE MODE, WE ONLY SUPPORT 4 BYTES PER WORD
	HRRZ	P1,@IRBACC(P1)	;POINT AT DF10 IOWD LIST
	MOVEI	P2,(R)		;POINT AT SA10 COMMAND LIST
	MOVEM	P2,TTSMIO(W)	;SAVE POINTER TO 1ST IOWD WORD

;HERE TO FETCH SOME MORE DF10 IOWDS
MAPIO1:	PUSH	P,T4		;PROTECT THE MODE INFO
	PUSHJ	P,GETIOW##	;GET AN EXPANDED IOWD FROM DF10 LIST
	POP	P,T4		;RESTORE THE MODE INFO
	JUMPE	T1,MAPIO4	;BRANCH WHEN WE GET A HALT
	IMULI	T1,(T4)		;POSSIBLY CONVERT WORD COUNT TO BYTE COUNT
	DMOVE	P3,T1		;COPY "IOWD" INTO P3/P4

;HERE TO SPLIT A DF10 EXPANDED IOWD INTO AS MANY SA10 DCWs AS NEEDED
MAPIO2:	CAIG	P3,^D2048	;SKIP IF WE CAN'T FIT INTO ONE DCW
	JRST	MAPIO3		;BRANCH IF ONLY ONE DCW FOR THIS IOWD

	MOVE	T1,P4		;COPY ADDRESS PORTION (CUZ COUNT FIELD IS 0S)
	MOVEM	T1,(P2)		;INSERT THE DCW WORD WITH COUNT OF 2048
	SUBI	P3,^D2048	;ADJUST THE COUNT REGISTER
	MOVEI	T1,^D2048	;GET POSSIBLE COUNT
	IDIVI	T1,(T4)		;COMPUTE THE NUMBER OF ADDRESS UNITS
	ADD	P4,T1		;COMPUTE NEW ADDRESS
	AOJA	P2,MAPIO2	;LOOP TO FURTHER SPLIT UP THIS DF10 IOWD

;HERE WHEN THE REST OF A DF10 EXPANDED IOWD CAN EXACTLY FIT IN ONE SA10 DCW
MAPIO3:	IMUL	P3,[-100,,0]	;MAKE COUNT -VE AND POSITION
	TLZ	P3,(1B0)	;TURN OFF "THIS IS LAST DCW" BIT
	OR	P3,P4		;INSERT THE ADDRESS PORTION
	MOVEM	P3,(P2)		;INSERT THE LAST ONE FOR THIS DF10 IOWD
	AOJA	P2,MAPIO1	;BRANCH BACK TO GET ANOTHER DF10, IF ANY

;HERE WHEN WE ENCOUNTER THE DF10 HALT
MAPIO4:	MOVSI	T1,(1B0)	;GET THE "THIS IS LAST DCW" BIT
	IORM	T1,-1(P2)	;TURN IT ON
	MOVEI	R,(P2)		;PUT NEW PC INTO R
	POPJ	P,		;RETURN
SUBTTL	CHECK FOR KONTROLLER BUSY


;CHECK FOR KONTROLLER BUSY
;CALL:	MOVE	W, KDB ADDRESS
;	MOVE	U, UDB FOR DRIVE TRYING TO SCHEDULE
;	PUSHJ	P,TS1BSY
;	  <NON-SKIP>		;BUSY
;	<SKIP>			;NOT-BUSY
;
;USES T1 AND T2

TS1BSY:	PUSHJ	P,SAVE2##	;SAVE P1 AND P2
	PUSH	P,U		;SAVE U
	MOVE	P1,KDBIUN(W)	;POINT TO START OF DRIVE TABLE
	MOVSI	P2,-<TS1DMX/2>	;ACTUAL RANGE OF DRIVES PER KONT
	MOVE	T1,UDBPDN(U)	;GET PHYSICAL DRIVE NUMBER
	TRZ	T1,<TS1DMX/2>-1	;MASK OUT DRIVE LEAVING KONTROLLER OFFSET
	ADDI	P1,(T1)		;ADJUST STARTING TABLE INDEX

TS1BS1:	SKIPE	U,(P1)		;GET A TUB ADDRESS
	PUSHJ	P,CHKIRB##	;SEE IF IT HAS A VALID IORB
	  JRST	TS1BS2		;TRY ANOTHER DRIVE
	LDB	T2,PRBFCN##	;GET FUNCTION CODE
	MOVE	T2,BITTBL##	;NOT PICK UP THE ASSOCIATED BIT
	TDNN	T2,TS1NBF	;NON-BLOCKING FUNCTION?
	POPJ	P,		;NO--KONT IS BUSY

TS1BS2:	AOBJN	P2,TS1BS1	;LOOP BACK IF MORE DRIVES TO CHECK
	JRST	UPOPJ1##	;RESTORE U AND RETURN KONT NOT BUSY
SUBTTL	STATUS REQUEST ("YELLOW BALL")

STISTS:	SKIPN	TS1SIP(U)	;SKIP IF WE GOT SENSE DATA
	JRST	ERRSNS		;CAUSE SNSANA TO BE INVOKED
	JRST	STIDON		;EXIT BECAUSE SNSANL DID OUR WORK
;HERE ON DONE INTERRUPT FOR DSE

STIDSE:	PUSHJ	P,CHKWLK	;CHECK FOR WRITE-LOCK
	PUSHJ	P,STCKMV	;CHECK FOR MOTION ERROR
	  JRST	MOTERR		;PASS ALONG MOTION ERROR TO TAPSER
	PUSHJ	P,CHKEOT	;CHECK FOR AND SET EOT
	PJRST	STIDON		;ALL DONE
SUBTTL READ
;HERE ON DONE INTERUPT FOR READ

STIRD:	SKIPN	TS1SIP(U)	;SKIP IF WE HAVE SENSE DATA
	PUSHJ	P,STISNS	;GO FORCE A SENSE IF RECORD 0
	PUSHJ	P,STCKMV	;SEE IF TAPE MOVED
	  PJRST	MOTERR		;BRANCH IF TAPE DID NOT MOVE
	AOS	TUBREC(U)	;ONE MORE RECORD
	PUSHJ	P,CHRCT		;GET CHARACTER COUNT
	  JRST	[MOVSI T4,RB.STL
		 IORM T4,TRBSTS(P1)
		 JRST .+1]
	MOVEM	T2,TRBRCT(P1)	;SAVE COUNT IN IORB
	MOVEM	T2,TUBCCR##(U)	;AND IN UDB
	TLNE	P4,(S.UE)	;SKIP IF NO TAPE MARK
	PUSHJ	P,NXTFIL	;ADJUST TUBFIL AND TRBSTS FOR ONE MORE FILE
	MOVE	T2,TUBCCR##(U)	;GET CHAR COUNT FOR THIS RECORD
	ADDM	T2,TUBCRD##(U)	;AND UPDATE STATISTICS
	ADDM	T2,TKBCRD(W)
	ADDM	T2,.CPTFI##
;	PJRST	STIDON		;DISMISS INTERRUPT
;HERE TO FINISH INTERUPT AND EXIT TO TAPSER

STIDON:	SETZM	TS1SIP(U)	;NO LONGER SENSING
	SETZM	TS1PPC(W)	;NO MORE PROGRAM TO RUN
	MOVE	T1,P1		;COPY IORB ADDRESS FOR TAPINT
	JUMPE	T1,STIDO3	;BRANCH IF NO IORB TO UPDATE
	MOVE	T3,UDBPDN(U)	;PHYSICAL DEVICE NUMBER
	MOVEM	T3,TS1UNI(W)	;AND SAVE IT FOR LATER CHECK
	HLRZ	T3,TRBSTS(P1)	;LOOK AT FLAGS
	JUMPE	T3,STIDO2	;RETURN IF NONE
	MOVSI	T2,RB.EXC	;ELSE SET EXCEPTION
	IORM	T2,TRBFNC(P1)	;IN IORB
STIDO2:
STIDO3:	SKIPN	TS1NUM(W)	;SKIP IF POLLER HAS INPUT ALREADY
	SKIPGE	TS1SUN(W)	;SKIP IF POLLER IS WAITING FOR A UNIT
	JRST	DISMIS		;DISMIS IF NO POLLING TO BE DONE
	MOVE	T1,TS1SUN(W)	;GET T1 NOW WE KNOW WE DON'T NEED IORB IN T1
	PUSHJ	P,TS1ONI	;STARTUP PENDING POLLING NOW
	JFCL			;SO?
	MOVEM	U,TS1STI(W)	;REMEMBER UNIT NEEDING STIDON'ING
	SETZ	T1,		;TELL TAPSER THAT UUO ISN'T DONE YET
	POPJ	P,		;(IE: TELL IT LATER WHEN POLL IS COMPLETED)

DISMIS:	SETZM	TTSBCT(W)	;MAKE SURE STOP BUSY TIMER IF GET INT
	SKIPE	TTSOPU(W)	;SKIP IF NOTHING NEEDS RESTARTING
	JRST	SETGOP
	MOVN	T2,TS1SCN(W)	;GET NEGATIVE SUBCHANNEL NUMBER
	MOVEI	T3,SI.GO	;GO FLAG FOR SUBCHANNEL ZERO
	LSH	T3,(T2)		;GET OUR GO FLAG
	MOVEI	T2,SO.GO	;BIT TO START THE SUBCHANNEL
	TDNN	T3,TS1LCI(W)	;SKIP IF GO FLAG WAS ON
	PJRST	STSCLR		;CLEAR STATUS FLAG AND EXIT INTERRUPT
	PUSHJ	P,TRK222	;$ TRACK GO
	PJRST	CNOSET		;$ SET THE GO BIT AND EXIT
SUBTTL WRITE
;HERE ON DONE INTERUPT FOR WRITE

STIWT:	PUSHJ	P,CHKWLK	;CHECK FOR AND SET WLK
	PUSHJ	P,STCKMV	;DID IT MOVE
	  PJRST	MOTERR		;IF NO MOTION, BRANCH TO SET ERRORS AND EXIT
	AOS	TUBREC(U)	;ONE MORE RECORD FORWARD
	TLNE	P4,(S.UC)	;SKIP IF NO OTHER ERRORS PRESENT
	  PUSHJ	P,STEANL	;BRANCH TO ANALYZE ERRORS AND SETUP TRBSTS
	PUSHJ	P,CHRCT		;GET CHARACTER COUNT
	  JFCL			;DON'T CARE
	ADDM	T2,TUBCWR##(U)	;ADD TO STATS
	ADDM	T2,TKBCWR(W)
	ADDM	T2,.CPTFO##
	PUSHJ	P,CHKEOT	;CHECK FOR AND SET EOT
	PJRST	STIDON		;AND EXIT
;HERE ON DONE INTERUPT FOR WRITE TAPE MARK

STITM:	PUSHJ	P,CHKWLK	;CHECK FOR AND SET WLK
	PUSHJ	P,STCKMV	;SKIP IF TAPE REALLY MOVED
	  PJRST	MOTERR		;BRANCH IF NO MOTION
	SETZM	TUBREC(U)	;ZEROTH RECORD
	AOS	TUBFIL(U)	;AND 1 MORE FILE
	TLNE	P4,(S.UC)	;SKIP IF NO "SERIOUS" ERROR
	PUSHJ	P,STEANL	;UPDATE TRBSTS
	PUSHJ	P,CHKEOT
	PJRST	STIDON		;EXIT
SUBTTL REWIND/REWIND-UNLOAD

;HERE ON INTERUPT AFTER STARTING UNLOAD OR REWIND
;FINAL STATUS IS SEEN BY AND HANDLED BY "ONLUNI", NOT HERE
STIRW:
STIUN:	TLNN	P4,(S.UC)	;SKIP IF WE HAD PROBLEMS
	PJRST	STIDON		;EXIT IF NO PROBLEMS, LET TAPUUO DISCARD IORB
	SKIPN	TS1SIP(U)	;SKIP IF WE HAVE SENSE DATA ALREADY (?)
	PJRST	ERRSNS		;BRANCH TO GET SENSE DATA
	PUSHJ	P,REWDON##	;FAKE DONE TO AVOID HANG (SNSANL MIGHT NOT)
	PJRST	STIDON		;AND THEN EXIT INTERRUPT
;HERE ON DONE INTERUPT FROM BACKSPACE RECORD

STIBR:	PUSHJ	P,FNDXCW	;FIND THE XCW COUNT
	MOVNS	T1		;MAKE THE XCW COUNT -VE (CUZ BACKWARDS MOTION)
	ADDB	T1,TUBREC(U)	;UPDATE TUBREC
	SKIPGE	T1		;SKIP IF STILL POSSIBLE
	SETZB	T1,TUBREC(U)	;OOPS
	TLNE	P4,(S.UE)	;SKIP IF UNIT-EXCEPTION IS OFF
	PUSHJ	P,LSTFIL	;IF U-E ON, WE BSB'D BACK OVER TAPE MARK
STIBR2:	TLNN	P3,(S01BOT)	;SKIP IF SENSE BYTES SAY WE'RE AT BOT
	PUSHJ	P,STEANL	;UPDATE TRBSTS
	JRST	STIDON		;EXIT INTERRUPT
;HERE ON DONE INTERRUPT FROM BACKSPACE FILE

STIBF:	SKIPN	TS1SIP(U)	;SKIP IF WE HAVE SENSE DATA
	PUSHJ	P,STISNS	;GO GET SENSE BYTES
	TLNN	P4,(S.UC)	;UNIT CHECK?
	JRST	STIBF2		;NO
	SKIPN	TUBFIL(U)	;SHOULD BE AT BOT
	SKIPE	TUBREC(U)	;ARE WE?
	JRST	STIBF3		;NO
	MOVSI	T2,RB.SNM!RB.SBT ;NO MOTION + BOT BIT
	JRST	STIBF4		;FINISH UP

STIBF2:	SOS	TUBFIL(U)	;PREVIOUS
	SETZM	TUBREC(U)	;..

STIBF3:	TLNN	P3,(S01BOT)	;LOAD POINT?
	JRST	STIDON		;NO
	MOVSI	T2,RB.SBT	;BOT BIT

STIBF4:	IORM	T2,TRBSTS(P1)	;UPDATE IORB
	PUSHJ	P,UNIBOT##	;TELL TAPSER WE'RE AT THE LOAD POINT
	JRST	STIDON		;GO DISMISS INTERRUPT
SUBTTL SKIP RECORD/FILE/ERASE GAP

;HERE ON DONE INTERUPT FROM SKIP FORWARD RECORD

STISR:	PUSHJ	P,FNDXCW	;FIND XCW COUNT (IE: # OF RECORDS THAT WON)
	ADDM	T1,TUBREC(U)	;UPDATE TUBREC
	TLNE	P4,(S.UE)	;SKIP IF NOT A TAPE MARK ABORT
	PUSHJ	P,NXTFIL	;COUNT A FILE IF WE GOT IT
	JRST	STIDON		;EXIT FROM INTERRUPT
;HERE ON DONE INTERRUPT FROM SKIP FORWARD FILE

STISF:	SKIPN	TS1SIP(U)	;SKIP IF WE HAVE SENSE DATA
	PUSHJ	P,STISNS	;GO GET SENSE BYTES
	TLNE	P4,(S.UC)	;UNIT CHECK?
	PJRST	MOTERR		;YES, MUST BE A MOTION ERROR
	PUSHJ	P,NXTFIL	;COUNT ONE MORE FILE
	JRST	STIDON		;AND EXIT
;HERE ON DONE INTERUPT FROM ERASE GAP

STIEG:	PUSHJ	P,CHKWLK	;CHECK FOR WRITE-LOCK
	PUSHJ	P,STCKMV	;SKIP IF MOTION TOOK PLACE
	  JRST	MOTERR		;BRANCH IF NO MOTION
	PUSHJ	P,CHKEOT	;CHECK FOR END-OF-TAPE
	PJRST	STIDON		;EXIT INT
;HERE FOR CHANNEL ERROR

STECHN:	PUSHJ	P,SAVE1##	;SAVE P1
	HRRZ	P1,KDBCHN(W)	;PTR TO CHANNEL DATA BLOCK
	MOVEI	T3,CHENXM##	;ASSUME NXM
	TRNE	T2,SI.PAR	;SKIP UNLESS MEMORY PARITY ERROR
	MOVEI	T3,CHEMPE##	;NO - PARITY ERROR
	MOVEI	T1,@TTSCSW(W)	;ADDR OF CSW
	MOVE	T1,1(T1)	;GET TERMINAL COUNT,ADDR WORD
	PUSHJ	P,0(T3)		;EXERCISE ROUTINE
	MOVEI	T2,SO.CME	;CLEAR MEMORY ERROR
	PUSHJ	P,CNOCLR	;DO IT
	MOVEI	T2,SO.SRS	;SELECTIVE RESET REQUIRED TO GET OUT OF THIS
	PUSHJ	P,CNOCLR	;DO IT
	MOVE	U,KDBCUN(W)	;GET CURRENT UNIT
	MOVE	U,(U)		;ADDR OF UDB
	PUSHJ	P,CHKIRB##	;GET IORB PTR
	  JRST	TAPDIS##	;NO USE CONTINUING IF NO IORB
	MOVE	P1,T1		;PUT IORB IN A SAFER PLACE
	MOVSI	T2,RB.SER!RB.SED
	IORM	T2,TRBSTS(P1)	;TELL HIM WORLD ENDED
	PJRST	STIDON		;RETURN - NO RETRIES

;ROUTINE TO DETERMINE IF TAPE MOVED (DIVINATION)

STCKMV:	SKIPN	TS1SIP(U)		;SKIP IF WE HAD AN ERROR
	JRST	CPOPJ1##	;MUST HAVE MOVED IF NO ERROR
	TLNE	P2,(S00REJ!S00IRQ)	;SKIP IF MOTION HAPPENED
	POPJ	P,			;RETURN ERROR IF NO MOTION
	JRST	CPOPJ1##

;HERE FOR MOTION ERROR

MOTERR:	MOVSI	T2,RB.SER!RB.SNM	;NOTE NO MOTION
	IORM	T2,TRBSTS(P1)		;STASH INTO IORB
	PJRST	STIDON			;TELL TAPSER
;ROUTINE TO ANALYSE THE GENERIC SENSE BYTE ERRORS

STEANL:	MOVEI	T2,0		;SET INITIAL ZERO
	TLNE	P2,(S00IRQ)	;SKIP IF NOT INTERVENTION REQUIRED
	PUSHJ	P,STCOFL	;SET UNIT OFF-LINE
	TLNE	P2,(S00REJ!S00IRQ);SKIP IF NO MOTION
	TLO	T2,RB.SNM!RB.SED;SET NO MOTION
	TLNE	P2,(S00OVR)
	TLO	T2,RB.SED	;SOME ERROR DETECTED (IE: CHANNEL OVERRUN)
	TLNE	P2,(S00DCK)
	TLO	T2,RB.SDE!RB.SED ;DATA ERROR
	IORM	T2,TRBSTS(P1)	;SET SELECTED BITS
	POPJ	P,		;RETURN

LSTFIL:	SOSA	TUBFIL(U)	;ONE LESS FILE
NXTFIL:	AOS	TUBFIL(U)	;ONE MORE FILE
	SETZM	TUBREC(U)	;ASSUME AT ZEROTH RECORD
	MOVSI	T2,RB.STM	;"SAW TAPE MARK"
	IORM	T2,TRBSTS(P1)	;FLAG IN IORB
	POPJ	P,

CHKEOT:	MOVSI	T1,RB.SET	;"SAW END(OF)TAPE"
	ANDCAM	T1,TRBSTS(P1)	;START OFF WITH IT OFF FOR SURE
	TLNE	P4,(S.UE)	;GOT UNIT EXCEPTION ON A WRITE COMMAND
	IORM	T1,TRBSTS(P1)	;SO REMEMBER WE SAW THE TAPE MARK GO BY
	POPJ	P,

CHKWLK:	MOVSI	T1,RB.SLK	;BITS FOR ABORTED FOR WRITE-LOCK
	TLNE	P2,(S01WLK)	;SKIP IF RING-PRESENT
	TLNN	P2,(S00REJ)	;SKIP IF NOT REJECTED
	SKIPA
	IORM	T1,TRBSTS(P1)	;REJECTED, AND RING-NOT-PRESENT
	POPJ	P,
SUBTTL COMPUTE CHARACTER COUNTS
;ROUTINE COMPUTES CHARACTER COUNT
;RETURNS COUNT IN T2 , T1 SAVED

CHRCT:	PUSHJ	P,WRDCTS	;GET WORD COUNT
	  SKIPA			;LENGTH ERROR RETURN
	AOS	(P)		;SET FOR SKIP RETURN
	MOVE	T2,T1		;WORD COUNT INTO T2
	SETZ	T4,
CHRCT1:	DPB	T4,PMTNCR##	;NOTE NO RESIDUE FOR MTCHR.
	HRLM	T2,TUBCHR##(U)	;STORE LAST WORD COUNT FOR MTCHR.
	MOVE	T1,P1		;GET IORB FOR PRBMOD
	LDB	T1,PRBMOD##	;GET MODE
	IMUL	T2,TMODTB##(T1)	;WORDS TIMES CHARS PER WORD
	DPB	T4,PMTCRC##	;STORE ZERO FOR CRC FOR MTCHR.
	POPJ	P,		;RETURN
;ROUTINE TO COMPUTE NUMBER OF WORDS TRANSFERRED ON READ OPERATIONS
;RETURNS WORD COUNT IN T1
;CALLED WITH P1 := IORB

WRDCTS:	SETZ	T1,		;ACCUMULATE COUNT IN T1
	MOVE	T3,TTSMIO(W)	;GET 1ST DCW ADDRESS FROM MAPIO
	LDB	T4,[POINT DCSADR,TTSTCA(W),DCNADR] ;GET TERMINATION ADDRESS
				; (THIS IS EITHER THE ADDRESS OF LAST
				; DCW EXECUTED +1, OR, +2 IF FINISHED
				; LIST AND LAST WORD)
WRD.1:	LDB	T2,[POINT DCSSIZ,(T3),DCNSIZ] ;LOAD NEGATIVE COUNT IN THIS DCW
	ORCMI	T2,<1_DCSSIZ>-1	;CONVERT TO A TRUE NEGATIVE
	SUB	T1,T2		;ACCUMULATE BY SUBTRACTION TO FORM
				;A POSITIVE COUNT
	SKIPL	(T3)		;TEST FOR END OF LIST BY LOOKING, FIRST,
	CAIL	T3,-1(T4)	;AT HIGH-ORDER BIT OF DCW; THEN, BY
				;COMPARING WITH TERMINATION ADDRESS
	JRST	WRD.2		;END, T3 POINTS TO LAST DCW EXECUTED
	AOJA	T3,WRD.1	;NOT END, LOOP FOR NEXT DCW
WRD.2:	LDB	T2,[POINT DCSSIZ,TTSTCA(W),DCNSIZ] ;ADJUST COUNT BY TERMINATION
	JUMPE	T2,WRD.3	;COUNT (RESIDUAL COUNT) IF IT IS NONZERO
	ORCMI	T2,<1_DCSSIZ>-1	;MAKE INTO A TRUE NEGATIVE
	ADD	T1,T2		;BEFORE SUBTRACTING
WRD.3:	PUSH	P,T1		;SAVE COUNT ON STACK
	PUSHJ	P,CHKMOD	;IF NOT WORD MODE,
	  TDZA	T4,T4		;CONVERT BYTE COUNT TO WORD COUNT
	SETO	T4,		;WORD MODE
	POP	P,T1		;RESTORE WORD COUNT
	JUMPN	T4,WRD.4	;JUMP IF WORD MODE
	ADDI	T1,3		;BYTE MODE,
	LSH	T1,-2		;CONVERT
WRD.4:	SKIPGE	(T3)		;TEST FOR LENGTH ERROR IF
	SKIPE	T2		;FINISHED DATA CHAIN LIST INCLUDING LAST WORD
	JRST	CPOPJ1##	;DIDN'T FINISH LIST, RETURN
	TLNN	P4,(S.LE)	;TEST FOR LENGTH ERROR
	AOS	(P)		;NO LENGTH ERROR, SKIP RETURN
	POPJ	P,		;RETURN
WTCUEP:	POP	P,(P)		;WASTE RETURN
WTCUE:	SETZM	TTSOPU(W)	;UNIT NOT BUSY ANYMORE (YET)
	TLNE	T1,(S.CUE+S.DE)	;SKIP UNLESS NOT REALLY BUSY ANY MORE
	JRST	SETGOP		;START PROGRAM AND DISMISS INTERRUPT
	LDB	T2,CSYDVA	;GET UNIT ADDRESS FROM STATUS
	SUBI	T2,@TTSOFS(W)	;COMPUTE UNIT NUMBER
	PUSHJ	P,SETUDB##	;PICK UP U
	  SETZ	U,		;HM - COUNT BE NXM UNIT FROM POLLER
	SKIPN	TS1PPC(W)	;SKIP IF WE HAVE A PROGRAM TO RESTART
	  JRST	NXTWAT		;ULP - JUST TRY TO IGNORE IT
	HRROM	U,TTSOPU(W)	;REMEMBER UNIT THAT GOT BUSY
	MOVEI	T2,^D25		;WAIT FOR 25 SECONDS (LESS THAN HUNG TIMER)
	MOVEM	T2,TTSBCT(W)	;WHEN TIMER EXPIRES, RETRY EVEN IF NO CUE
	MOVSI	T2,1		;COUNT BUSYS IN LH
	ADDM	T2,TTSBRC(W)	;COUNT BUSYS
	PJRST	NXTWAT		;EXIT INT QUICKLY FOR NOW

GOTCUE:	TLNE	T1,(S.UC+S.DE+S.UE) ;SKIP UNLESS SOMETHING MORE IMPORTANT
	POPJ	P,		;JUST RETURN IF SOMETHING MORE IMPORTANT
	SKIPE	TTSOPU(W)	;SKIP IF NO UNIT KNOWN TO BE BUSY
	SKIPN	TS1PPC(W)	;SKIP IF KNOWN UNIT WITH KNOWN PROGRAM
	JRST	WTCUEP		;ODD, BUT PRETEND IT IS A BUSY
	PUSHJ	P,SAVT##	;SAVE THE ACS
	HRRZ	U,TTSOPU(W)	;GET THE U WHICH IS PENDED
	SETZM	TTSOPU(W)	;NO MORE U
	SETZM	TTSBCT(W)	;NO MORE TIMER
	PUSHJ	P,SETDVP	;SETUP DEVL ENTRY, ETC
	MOVEI	T2,SO.GO	;GET GO BIT
	PUSHJ	P,CNOSET	;SET GO BIT
	PUSHJ	P,TRK222	;$ TRACK GO BIT
	POPJ	P,		;RETURN TO HANDLE REAL INTERRUPT

SETDVP:	MAP	T2,@TS1PPC(W)	;GET PC
	MOVE	T1,TS1SUN(W)	;ASSUME POLLER'S UNIT
	SKIPE	U		;SKIP IF AN UNKNOWN UNIT (IE: POLLER'S UNIT)
	MOVE	T1,UDBPDN(U)	;GET DRIVE NUMBER FROM UDB IF KNOWN UNIT
	ADD	T1,TTSOFS(W)	;COMPUTE ADDRESS OF UNIT
	DPB	T1,[POINT SASDLD,T2,SANDLD] ;INSERT UNIT ADDRESS INTO DEVL ENTRY
	MOVEI	T1,DL.STA	;GET "START" CODE
	DPB	T1,[POINT SASDLO,T2,SANDLO] ;INSERT OPCODE INTO DEVL ENTRY
	PMOVEM	T2,TS1DVP(W)	;INSERT DEVL ENTRY INTO PROPER SLOT
	POPJ	P,		;RETURN
;ROUTINE TO POSSIBLY CALL ERRSNS IF WE'RE AT INTERRUPT LEVEL
; ON A FORWARD MOTION AT RECORD 0

STISNS:	SKIPN	TUBFIL(U)	;SKIP IF NOT FILE 0
	SKIPE	TUBREC(U)	;SKIP IF BOTH FILE 0 AND RECORD 0
	POPJ	P,		;RETURN
	POP	P,(P)		;WASTE THE RETURN AS SNSANL WILL DO IT
;	JRST	ERRSNS		;ARRANGE A SENSE-DATA-CALLBACK FOR OUR CALLER
;ROUTINE TO DO SENSE COMMAND AT INTERRUPT LEVEL

ERRSNS:	MOVE	T1,TS1LCI(W)	;GET THE CONI
	MOVE	T2,TS1LST(W)	;GET THE CSW WORD
	DMOVEM	T1,TS1SIP(U)	;REMEMBER A SENSE IN PROGRESS
	PUSHJ	P,SETSNS	;SETUP FOR SENSE OPERATION
	PJRST	SETGOP		;START THE SENSE COMMAND
;ROUTINE TO SET UP FOR SENSE OPERATION

SETSNS:	MOVE	T2,UDBPDN(U)	;GET PHYSICAL DRIVE NUMBER
	ADDI	T2,@TTSOFS(W)	;COMPUTE ACTUAL DEVICE ADDRESS
	DPB	T2,[POINT DVSDVA,TS1SNS+SNSDVW(W),DVNDVA]	;UPDATE SENSE
	MAP	T1,TS1SND(U)	;GET PHYSICAL ADDRESS OF SENSE BYTES
	DPB	T1,[POINT DCSADR,TS1SNS+SNSDCW(W),DCNADR]	;UPDATE IOWD
	MOVEI	T2,TS1SNS+SNSDVW(W) ;GET VIRTUAL ADDRESS OF SENSE PROGRAM
	MOVEM	T2,TS1PPC(W)	;SAVE PC WE WANT TO RUN
	SETZM	TS1SND(U)	;INVALIDATE OLD SENSE
	POPJ	P,
;SUBROUTINE TO DECIDE IF CURRENT MODE IS BYTE MODE OR WORD MODE
;ARGS	P1 := IORB

CHKMOD:	MOVE	T1,P1		;GET IORB ADDRESS
	LDB	T1,PRBMOD##	;GET CURRENT MODE
	CAIN	T1,RB.MBY	;SKIP UNLESS INDUSTRY COMPATIBLE 9-TRACK
	POPJ	P,		;BYTE MODE
	MOVEI	T1,TUC7TK##	;7-TRACK BIT
	TDNN	T1,TUBCNF##(U)	;SKIP IF 7-TRACK DRIVE
	JRST	CPOPJ1##	;9-TRACK WORD MODE
	MOVE	T1,TRBFNC(P1)	;GET PARITY BIT
	TLNN	T1,RB.PAR	;SKIP IF EVEN PARITY
	AOS	(P)		;7-TRACK ODD PARITY IS WORD MODE
	POPJ	P,		;7-TRACK EVEN PARITY IS BYTE MODE
	SUBTTL	SA10 FUNCTIONS
;SUBROUTINE TO CLEAR STATUS FLAG

STSCLR:	C