Google
 

Trailing-Edge - PDP-10 Archives - BB-JR93N-BB_1990 - 10,7/mon/tapser.mac
There are 11 other files named tapser.mac in the archive. Click here to see a list.
TITLE	TAPSER - COMMON MAGTAPE PHYSICAL IO DRIVER  V1111
SUBTTL  L.BOSACK/TAH/TW/DPM	17-APRIL-90

	SEARCH	F,S,DEVPRM
	$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
; 1974,1975,1976,1977,1978,1979,1980,1982,1984,1986,1988,1990.
;ALL RIGHTS RESERVED.

.CPYRT<1974,1990>


XP VTAPSR,1111			;FOR LINKEDIT MAP
	SALL

TAPSER::ENTRY	TAPSER
;BIT DEFINITIONS

;IN TKBSTS & TUBSTS

TKSOFL==:(1B17)		;OFFLINE
TKSSEL==:(1B16)		;SELECTED
TKSSTD==:(1B15)		;STARTED
TKSSCH==:(1B14)		;REQUESTED SCHED INTERUPT
TKSSIL==:(1B13)		;REQUEST SILENCE ABT OFF-LINE CONDITION
TKSMNT==:(1B12)		;CTL IS IN MAINT MODE
TKSCHE==(1B11)		;JOB HAS SWEPT CACHE FOR QUEUED REQUEST
TKSCHX==:(1B10)		;NOT YET SWEPT FOR (CPU0)
TKSCHA==(77B10)		;TKSCHX BITS FOR ALL CPUS

;IN TUBSTS

TUSNS==:(1B0)		;DO NOT SCHEDULE THIS UNIT (SIGN BIT)
TUSBOT==:(1B1)		;BEGINNING OF TAPE
TUSWTL==:(1B2)		;WRITE LOCKED
TUSREW==:(1B3)		;TAPE REWINDING
TUSFLT==:(1B4)		;TAPE UNIT FAULT (BROKEN DRIVE)
TUSBOF==:TUSBOT!TKSOFL

;IN TKBSTS

TKSNS==:(1B3)		;DON'T SCHEDULE THIS KONTROLLER (DIAG.)

;IN TUBCNF

TUC7TK==:1B18		;SEVEN TRACK
TUCIRD==:1B19		;INTERUPTS WHEN REWIND DONE
TUCDMS==:1B20		;DIAG MODE SET
TUCSNS==:1B21		;FORCE SENSE
;TUCD??==:1B22		;THIS BIT LEFT FOR EXPANSION
TUCD62==:1B23		;DRIVE CAN DO 6250 BPI
TUCD16==:1B24		;DRIVE CAN DO 1600 BPI
TUCD80==:1B25		;DRIVE CAN DO 800 BPI
TUCD55==:1B26		;DRIVE CAN DO 556 BPI
TUCD20==:1B27		;DRIVE CAN DO 200 BPI
;  THE FOLLOWING SYMBOLS ARE FOR THE CONVENIENCE OF MACRO V50
TUCDR8==:TUCD20+TUCD55+TUCD80	;TM10 9 TRACK DENSITIES
TUCDR7==:TUC7TK+TUCDR8		;7 TRACK TAPE + DENSITIES
TUCDR6==:TUCD80+TUCD16		;TM02 AND TU70 DENSITIES
TUCDR5==:TUCD16+TUCD62		;TU72 DENSITIES
TUCDIG==:1B28		;DIAG. UUO ON THIS DRIVE
TUCFOI==:1B29		;FILE OPERATIONS ILLEGAL

;IN TUBERR

NXTLSE==:(1B0)		;NEXT TO LAST ERROR RETRY
;ERROR RECOVERY PARAMETERS

ERPNCS==5		;NUMBER OF BACKSPACE/SPACE OP IN TAPE CLEANER SEQ.
IOSCP2==:(1B11)		;MUST BE SAME AS TKSCHE
;TAPSBF DETERMINES WHETHER REAL FILE POSITIONING IS DONE OR MULTIPLE
; SKIP/BACKSPACE RECORD OPERATIONS ARE USED TO SIMULATE FILE POSITIONING.

TAPSBF:	EXP	MTFILE## ;DEFAULT TO MONGEN'ED VALUE


;BYTE POINTERS

;INTO UDB

TUYKTP::POINT	4,TUBCNF(U),35		;CONTROLLER TYPE
TUYECT:	POINT	ERCCTS,TUBERR(U),17	;POINTER TO RETRY COUNTER
TUYEC1:	POINT	ERCCTS,TUBERR(U),17-ERCCTS ;SECONDARY RETRY COUNTER

;INTO IORB

PRBRQS:: POINT RB.RQS,TRBFNC(T1),RB.RQP		;REQUEST STATUS
PRBMOD:: POINT RB.MDS,TRBFNC(T1),RB.MDP		;REQUEST MODE
PRBMD2:: POINT RB.MDS,TRBFNC(P1),RB.MDP
PRBFCN:: POINT RB.FNS,TRBFNC(T1),RB.FNP		;REQUEST FUNCTION
PRBDEN:: POINT RB.DNS,TRBFNC(T1),RB.DNP		;DENSITY
PRBBYT:: POINT RB.BYS,TRBFNC(T1),RB.BYP		;REQUEST INFO
SUBTTL	PROTOTYPE INTERRUPT ROUTINE

TAPICD::PHASE	0
IFN FTKL10,<
	CONSO	000,0		;(00) NEED SERVICE?
	JRST	.		;(01) TRY NEXT DEVICE ON CHAIN
TAPCNI::!CONI	000,21		;(02) GET CONI BITS
	JSR	PIERR##		;(03) SAVE ACS
	SKIPA	W,.+1		;(04) SETUP DATA BLOCK POINTER
	EXP	0		;(05) DATA BLOCK ADDRESS
	XJRST .+1		;(06) GO SERVICE INTERRUPT
	EXP	0		;(07) INTERRUPT HANDLER ADDRESS
	EXP	0		;(10) OLD PC FLAGS
	EXP	0		;(11) OLD PC
	EXP	IC.UOU		;(12) NEW PC FLAGS
	EXP	.+1		;(13) NEW PC
	XCT	2		;(14) GET CONI BITS
	XCT	3		;(15) SAVE ACS
	DMOVE	T1,10		;(16) COPY OLD PC DOUBLE WORD
	DMOVEM	T1,-1		;(17) FAKE UP PI CHANNEL INTERRUPT
	JRST	4		;(20) GO CALL INTERRUPT HANDLER
TAPCII::!EXP	-1		;(21) CONI BITS AT INTTERUPT
> ;END FTKL10

IFN FTKS10,<
	CONSO	000,0		;(00) NEED SERVICE?
	JRST	.		;(01) TRY NEXT DEVICE ON CHAIN
	JSR	PIERR##		;(02) SAVE ACS
	SKIPA	W,.+1		;(03) SETUP DATA BLOCK POINTER
	EXP	0		;(04) DATA BLOCK ADDRESS
	JRST @.+1		;(05) GO SERVICE INTERRUPT
	EXP	0		;(06) INTERRUPT HANDLER ADDRESS
	EXP	0		;(07) OLD PC FLAGS
	EXP	0		;(10) OLD PC
	EXP	IC.UOU		;(11) NEW PC FLAGS
	EXP	.+1		;(12) NEW PC
	XCT	2		;(13) SAVE ACS
	DMOVE	T1,7		;(14) COPY OLD PC DOUBLE WORD
	DMOVEM	T1,-1		;(15) FAKE UP PI CHANNEL INTERRUPT
	JRST	3		;(16) GO CALL INTERRUPT HANDLER
> ;END FTKS10
	DEPHASE
TAPICL==:.-TAPICD		;LENGTH OF CODE
SUBTTL	AUTOCONFIGURATION


;ROUTINE TO SETUP CONI ADDRESS IN CONTROLLER'S CSO (TAPICD) BLOCK
;CALL:	MOVE	W, KDB ADDRESS
;	PUSHJ	P,TAPCSA
;	<ALWAYS RETURN HERE>
;DESTROYS T1,T2

IFN FTKL10,<
TAPCSA::MOVE	T1,KDBCSO(W)	;GET CSO BLOCK ADDRESS
	MOVEI	T2,TAPCII(T1)	;GET CONI DESTINATION ADDRESS
	HRRM	T2,TAPCNI(T1)	;PUT ADDRESS IN RH OF INSTRUCTION
	SETZM	TAPCII(T1)	;CLEAR THE CONI DESTINATION
	POPJ	P,
>
;ROUTINE TO DO COMMON TAPE DRIVE AUTOCONFIGURATION
;CALL:	MOVE	T1, PHYSICAL DRIVE #,,UDB TABLE INDEX
;	MOVE	T2, TUBCNF WORD
;	MOVE	T3, HUMG TIMER TABLE ADDRESS
;	MOVE	T4, SIXBIT DRIVE MODEL
;	PUSHJ	P,TAPDRV
;	  <NON-SKIP>		;NO CORE FOR UDB OR DDB
;	<SKIP>			;DATA STRUCTURES CREATED AND LINKED UP

TAPDRV::PUSH	P,T2		;SAVE TUBCNF BITS
	PUSH	P,T3		;SAVE HUNG TIMER TABLE ADDRESS
	PUSH	P,T4		;SAVE DRIVE MODEL
	PUSHJ	P,AUTUDB##	;BUILD A UDB
	  JRST	[POP  P,(P)	;PHASE STACK
		 JRST TTPOPJ##]	;FAILURE RETURN
	POP	P,TUBMDL(U)	;STORE SIXBIT DRIVE MODEL
	POP	P,T1		;GET HUNG TIMER TABLE ADDRESS
	MOVE	T2,-1(T1)	;GET MAXIMUM TIMEOUT VALUE
	MOVEM	T2,TUBMTV(U)	;SAVE INCASE NO ACCESS TO SOME PORT
	DMOVE	T2,0(T1)	;WORDS 0 & 1
	DMOVEM	T2,TUBHTT(U)	;SAVE THEM
	DMOVE	T2,2(T1)	;WORDS 2 & 3
	DMOVEM	T2,TUBHTT+2(U)	;DITTO
	POP	P,TUBCNF(U)	;STORE CONFIGURATION WORD
	SKIPE	F,UDBDDB(U)	;ALREADY HAVE A DDB?
	JRST	TAPDR1		;DON'T CREATE ANOTHER
	HLLZ	T1,KDBNAM(W)	;GENERIC DEVICE NAME,,LOCAL DEVICE
	HRR	T1,P1		;GET PHYSICAL DRIVE NUMBER
	SETZ	T2,		;LOCAL DEVICE
	PUSHJ	P,AUTDDB##	;CREATE A DDB
	  SKIPA	T1,.CPDRV##	;NO CORE
	JRST	TAPDR1		;ONWARD
	MOVE	T1,DRVULN(T1)	;UDB LENGTH
	MOVE	T2,U		;UDB ADDRESS
	PJRST	GIVWDS##	;RELEASE CORE AND RETURN

TAPDR1:	MOVEM	F,UDBDDB(U)	;LINK UDB TO DDB
	MOVEM	U,TDVUDB##(F)	;LINK DDB TO UDB
	MOVE	T1,KDBDSP(W)	;DRIVER DISPATCH
	MOVEM	T1,DEVDRV(F)	;SO WE CAN FIND IT GIVEN A DDB
	MOVSI	T1,TUBQUE(U)	;INIT QUEUE HEAD
	MOVEM	T1,TUBQUE(U)	; TO POINT TO EMPTY QUEUE
	MOVE	T1,UDBPDN(U)	;PHYSICAL DRIVE NUMBER
	MOVEI	T2,KDBNUM	;STATE,,MASK WORD OFFSET
	PUSHJ	P,AUTMSK##	;CLEAR NEW UNIT FOR THIS DRIVE
	  JFCL			;CAN'T FAIL HERE
	MOVE	T1,UDBPDN(U)	;PHYSICAL DRIVE NUMBER
	MOVEI	T2,KDBIUM	;STATE,,MASK WORD OFFSET
	PUSHJ	P,AUTMSK##	;CLEAR IGNORE UNIT FOR THIS DRIVE
	  JFCL			;CAN'T FAIL HERE
	SYSPIF			;INTERLOCK
	MOVSI	T1,TKSNS	;DIAG BIT
	TDNN	T1,TKBSTS(W)	;KONTROLLER STOPPED?
	TDZA	T1,T1		;NO DIAG JOB
	HLRZ	T1,TKBJOB(W)	;GET MAINTENANCE JOB NUMBER
	SYSPIN			;RELEASE INTERLOCK
	JUMPN	T1,TAPDR2	;CAN'T ATTACH IF DIAG
	LDB	T1,PJOBN##	;GET DDB OWNER
	MOVEI	T2,ASSCON	;BIT TO TEST
	TDNE	T2,DEVMOD(F)	;ASSIGNED TO JOB ZERO?
	JUMPN	T1,TAPDR2	;NO, DON'T NEED TO ATTACH IT
	PUSHJ	P,DDBATT##	;MAKE SURE ATTACHED
	  POPJ	P,		;FAILED
IFN FTMDA,<PUSHJ P,TAPMPA>	;INDICATE TAPE DRIVE ATTACHED

TAPDR2:	PUSHJ	P,SAVE3##	;SAVE P1-3
	PUSH	P,W		;SAVE W
	HLLZ	P1,TUBIEP(U)	;GET INITIAL ERROR POINTER LENGTH
	HLLZ	P2,TUBFEP(U)	;GET FINAL ERROR POINTER LENGTH
	LDB	P3,[POINTR KDBSTS(W),KD.MPT] ;GET OUR MAXIMUM PORT COUNT
	XMOVEI	T1,UDBKDB(U)	;POINT TO KDB TABLE FOR DRIVE
	MOVSI	T2,-MXPORT	;AOBJN POINTER

TAPDR3:	SKIPE	W,(T1)		;GET A KDB
	CAMN	W,(P)		;FOUND OURSELVES?
	JRST	TAPDR4		;IGNORE IT
	MOVE	T3,KDBDSP(W)	;GET DISPATCH
	MOVE	T3,DRVUDB(T3)	;AND PROTOTYPE UDB
	HLLZ	T4,TUBIEP(T3)	;GET INITIAL (PROTOTYPE) ERROR POINTER
	CAMLE	P1,T4		;CURRENT THE LARGER OF THE TWO?
	MOVE	P1,T4		;NO--MAKE IT SO
	HLLZ	T4,TUBFEP(T3)	;GET FINAL (PROTOTYPE) ERROR POINTER
	CAMLE	P2,T4		;CURRENT THE LARGER OF THE TWO?
	MOVE	P2,T4		;NO--MAKE IT SO
	LDB	T3,[POINTR KDBSTS(W),KD.MPT] ;GET THIS KDB'S MAX. PORT COUNT
	CAIGE	T3,(P3)		;IF HIS IS BETTER,
	MOVE	P3,T3		;UPDATE OUR VALUE
	DPB	P3,[POINTR KDBSTS(W),KD.MPT] ;SAVE POSSIBLY UPDATED VALUE IN KDB

TAPDR4:	AOS	T1		;ADVANCE POINTER
	AOBJN	T2,TAPDR3	;LOOP FOR ALL KDBS
	HLLM	P1,TUBIEP(U)	;UPDATE INITIAL ERROR POINTER
	HLLM	P2,TUBFEP(U)	;UPDATE FINAL ERROR POINTER
	POP	P,W		;RESTORE OUR KDB ADDRESS
	DPB	P3,[POINTR KDBSTS(W),KD.MPT] ;SAVE BEST VALUE IN OUR KDB
	JRST	CPOPJ1##	;RETURN SUCCESS
IFN FTMDA,<

;SEND A DEVICE ATTACHED MESSAGE TO [SYSTEM]MDA
TAPMPA::MOVE	T1,UDBNAM(U)	;PRIMARY PORT NAME
	SETZ	T2,		;NO SECONDARY PORT NAME
	PJRST	ATTMPA##	;INFORM [SYSTEM]MDA


;SEND A DEVICE DETACHED MESSAGE TO [SYSTEM]MDA
TAPMPD::SETZ	T1,		;NO NEW PRIMARY PORT NAME
	PJRST	DETMPA##	;INFORM [SYSTEM]MDA

> ;END IFN FTMDA
SUBTTL INTERRUPT SERVICE.

;HERE WITH AC'S SAVED AND PDL SETUP.
;C(W) := KDB ADDRESS
;AFTER CALL TO DEVICE DEPENDENT ROUTINE OR ERROR PROCESSOR
;C(T1) := IORB ADDRS IF COMPLETED
;	  0 IF ANOTHER INTERUPT COMING - NO FURTHER PROCESSING
;	  -1 IF SCHED CYCLE REQUESTED
;C(TUBERR) NON-ZERO IF ERROR RECOVERY IN PROGRESS

TAPINT::SKIPG	KDBUNI(W)	;MULTIPLE KONTROLLERS ON A SINGLE CHANNEL?
	JRST	TAPIN3		;NO
IFN FTKL10,<
	MOVE	T1,KDBCHN(W)	;GET ADDRESS OF CHANNEL DATA BLOCK
	MOVE	T1,CHNTYP(T1)	;GET CHANNEL TYPE BITS
	TLNE	T1,(CP.SAX)	;NON-SA10 CHANNEL?
	JRST	TAPIN3		;SA10 DOESN'T HAVE RH10/20 CONI BITS
	MOVE	T1,KDBCSO(W)	;GET TAPICD BLOCK ADDRESS
	MOVE	T1,TAPCII(T1)	;GET CONI BITS AT INTERRUPT
	TRNE	T1,CI.DON	;IS DONE IS ON?
	JRST	TAPIN3		;YES, THE INTERRUPT IS FOR THIS KDB
	PUSH	P,T1		;SAVE INTERRUPT CONI BITS FOR THE RIGHT KDB
	MOVSI	T2,(.DIASR)	;ATTEN INTERRUPT. READ ATTN SUMMARY
	XCT	KDBDTO(W)	; REGISTER
	IMULI	P,1
	IMULI	P,1		;STALL IN CASE AN RH10
	XCT	KDBDTI(W)	;READ ATTENTION BITS
> ;END IFN FTKL10
IFN FTKS10,<
	MOVE	T2,KDBDVC(W)	;CSR ADDRESS
	RDIO	T1,.DOSR(T2)	;READ DRIVE STATUS REGISTER
	TRNN	T1,DS.ATA	;ATTENTION BITS AVAILABLE?
	JRST	TAPIN3		;NO--INTERRUPT FOR INTERRUPTING KDB
	RDIO	T2,.DOAS(T2)	;READ ATTENTION BITS
> ;END IFN FTKS10
	ANDI	T2,377		;GET JUST ATTENTION BITS
	MOVE	T1,KDBCHN(W)	;POINT TO CHANNEL DATA BLOCK
	MOVE	T1,CHNTBP(T1)	;AND TO THE TABLE OF KDBS ON THIS CHN

TAPIN1:	SKIPN	W,(T1)		;GET A KDB ADDRESS
	JRST	TAPIN2		;NOTHING THERE??
	TDNE	T2,KDBUNI(W)	;INTERRUPT FOR THIS KDB?

IFN FTKS10,<JRST TAPIN3>	;YES. HANDLE IT
IFN FTKL10,<
	JRST [MOVE  T1,KDBCSO(W) ;GET TAPICD BLOCK ADDRESS
	      POP   P,TAPCII(T1) ;PUT IN CORRECT CONI BITS
	      JRST  TAPIN3]
> ;END IFN FTKL10

TAPIN2:	AOBJN	T1,TAPIN1	;TRY ANOTHER KDB
IFN FTKL10,<
	HRLI	T2,(.DIASR!DO.LDR!DO.DRE) ;NOT FOR ANY KDB WE KNOW OF
	XCT	KDBDTO(W)	; SO CLEAR THE INTERRUPT
	POP	P,(P)		;SYNCH STACK
> ;END IFN FTKL10
IFN FTKS10,<
	MOVE	T1,KDBDVC(W)	;GET CSR ADDRESS FOR CONTROLLER
	WRIO	T2,.DOAS(T1)	;CLEAR ATTENTION BITS
> ;END IFN FTKS10
	POPJ	P,		;AND GO AWAY

TAPIN3:	MOVE	T1,KDBDSP(W)	;GET XFER VECTOR
	PUSHJ	P,@TPKISR(T1)	;CALL DEV DEP INT RTN
TAPAGO:	JUMPE	T1,CPOPJ##	;IF ZERO, ANOTHER INT IS COMING
	JUMPL	T1,TAPIN4	;NO COMPLETION - DO SCHED CYCLE
	MOVSI	T2,TKSSTD	;CHANNEL NO LONGER STARTED
	ANDCAM	T2,TKBSTS(W)	;SO CLEAR STATUS
	ANDCAM	T2,TUBSTS(U)	;AND UNIT STATUS
	SKIPE	T2,TUBERR(U)	;IS ERP IN PROGRESS?
	PUSHJ	P,ERPINT	;YES - CALL INTERRUPT PART
	JUMPE	T1,CPOPJ##	;TERMINATE NOW?
	JUMPL	T1,TAPIN4	;NO COMPLETION
	MOVEI	T2,RB.DUN	;MARK IORB AS DONE
	DPB	T2,PRBRQS	;...
	MOVSI	T2,TUSREW	;BIT TO TEST
	TDNN	T2,TUBSTS(U)	;REWIND OR UNLOAD INTERRUPT PENDING?
	PUSHJ	P,TAPCTM	;CLEAR HUNG TIMER FOR ALL OTHER OPERATIONS
	PUSHJ	P,TPMDON##	;YES - NOTIFY UPPER LEVEL
	HRRZ	T2,TKBSTS(W)	;SEE IF A SCHED CYCLE IS DUE
	JUMPG	T2,CPOPJ##	;NO - DISMISS
	PUSHJ	P,TAPDSL	;DE-SELECT KONTROLLER
TAPIN4:	SETZB	S,F		;MAKE SURE IOSCP2 IS OFF
	MOVSI	T2,TUSREW	;BIT TO TEST
	TDNN	T2,TUBSTS(U)	;REWIND OR UNLOAD INTERRUPT PENDING?
	PUSHJ	P,TAPCTM	;CLEAR HUNG TIMER FOR ALL OTHER OPERATIONS
	PUSHJ	P,TAPSCH	;SEE IF THERE IS MORE WORK
	  POPJ	P,		;NO MORE TO DO
	PJRST	TPMSIO##	;START IO
;ROUTINE TO DE-SELECT A KONTROLLER AND CLEAR I/O
TAPDSL::MOVE	T1,KDBDSP(W)	;GET XFER VECTOR
	PUSHJ	P,@TPKIDL(T1)	;MARK IDLE
	HLLZS	TKBSTS(W)	;SET QUANTUM TO ZERO
	MOVSI	T1,TKSSEL	;CLEAR SELECTION BITS
	ANDCAM	T1,TUBSTS(U)	;  IN UDB
	ANDCAM	T1,TKBSTS(W)	;  AND KDB
	SETOM	T1,@KDBCHN(W)	;INDICATE CHANNEL IS IDLE
	POPJ	P,		;RETURN

;GENERAL INTERUPT ERROR HALT

TAPIFI::STOPCD	.,STOP,IFI,	;++ ILLEGAL FUNCTION AT INTERUPT
SUBTTL CHANNEL SCHEDULER


;W/ KDB TO CONTINUE SCANNING.
;SKIP RETURNS WITH W AND U SETUP IF ANOTHER REQUEST IS FOUND
;NON SKIP RETURN IF NOTHING TO DO
TAPSCH:	TAPOFF			;TRY TO DEFEND AGAINST RACES
	PUSH	P,W		;SAVE INITIAL KDB
	HRRZS	(P)		;SAVING ONLY RIGHT HALF
	MOVE	T4,KDBCUN(W)	;INDEX INTO UNIT TABLE
	PUSH	P,T4		;SAVE START
	MOVSI	T2,TKSOFL!TKSMNT!TKSNS ;CHECK IF HE IS AMOUNG US
	TDNE	T2,TKBSTS(W)	;...
	JRST	TPSCH2		;NO, DON'T SCHEDULE FOR HIM
	CAML	T4,KDBFUN(W)	;FINAL UDB?
	JRST	TPSCH2		;YES
	AOS	T4		;START LOOKINT AT NEXT UDB
TPSCH1:	SKIPN	U,(T4)		;GET UDB
	JRST	TPSCHN		;NONE PRESENT
	HRRZ	T1,TUBQUE(U)	;GET QUEUE POINTER
	JUMPE	T1,TPSCHN	;GO IF NONE
	SKIPL	T2,TUBAKA(U)	;GET ACTIVE KDB ADDRESS
	CAMN	W,T2		;KONTROLLER ALREADY PICKED?
	CAIA			;FOUND RIGHT KDB OR NONE PICKED YET
	JRST	TPSCHN		;IGNORE THIS TUB
IFE FTMP,<SKIPL	T2,TUBSTS(U)>	;NO SCHED?
IFN FTMP,<
	SKIPGE	T2,TUBSTS(U)	;NO SCHED ON THIS CPU
	TLNE	T2,TKSCHE
>
	TLNE	T2,TKSSEL!TKSCHA ;OR SELECTED BY ANOTHER?
	JRST	TPSCHN		;YES
IFN FTMP,<
	MOVE	T3,TRBFNC(T1)	;GET IORB FLAGS
	TLNN	T2,TKSCHE	;QUEUED REQUEST ON UDB?
	TLNN	T3,RB.PCL	;YES, QUEUED IORB REQUEST?
	TDCA	T2,S		;CLEAR IOSCP2(=TKSCHE) IF REQUEST TYPES MATCH
	JRST	TPSCHN		;CANT DO IT IF CHE=0, PCL=1
	TLNN	T2,IOSCP2	;CP2=CHE=PCL?
>
	JRST	TPSCH3		;FOUND 1 - USE IT
TPSCHN:	CAMN	T4,(P)		;BACK TO ORIGINAL UNIT?
	JRST	TPSCHY		;NOTHING TO DO - NON SKIP RETURN
	CAMGE	T4,KDBFUN(W)	;FINAL UDB?
	AOJA	T4,TPSCH1	;LOOP FOR MORE
TPSCH2:	MOVSI	T2,TKSSCH
	ANDCAM	T2,TKBSTS(W)
TPSC2A:	PUSHJ	P,NXTKDB	;ADVANCE TO NEXT KDB ON CHANNEL
	  JFCL			;IGNORE WRAP AROUND
	MOVSI	T4,TKSOFL!TKSMNT!TKSNS	;BE SURE HE'S REALLY HERE
	TDNE	T4,TKBSTS(W)	;IS HE?
	JRST	[CAMN	W,-1(P)	;NO, BACK TO ORIGINAL KDB ALREADY?
		 JRST	TPSCHY	;YES, EXIT
		 JRST	TPSC2A ];NO, STEP ON
IFN FTMP,<
	MOVE	T1,KDBCAM(W)	;GET ACCESSABILITY BIT
	TDNN	T1,.CPBIT##	;CAN WE DO I/O FOR THIS KDB?
	JRST	TPSC2A		;NO, LOOK FOR ANOTHER CANDIDATE
> ;END IFN FTMP
	IORM	T2,TKBSTS(W)
	MOVE	T4,KDBIUN(W)	;RESET POINTER
	JRST	TPSCH1		;CONTINUE SEARCH
;HERE WHEN A REQUEST IS FOUND
TPSCH3:
IFN FTMP,<
	SKIPE	T3,CPUTPQ	;ANY QUEUED REQUESTS?
	MOVSI	T3,TKSCHE	;YES
	TDNE	T3,TKBSTS(W)	;REQUEST FOR THIS CONTROL?
	SOS	T3,TKBFCT(W)	;YES, TIME TO BE FAIR?
	JUMPL	T3,TPSCHY	;YES, LET TAPTIC HAVE IT
>
	MOVSI	T2,RB.AIO	;IS THIS REQUEST SPECIAL?
	TDNE	T2,TRBFNC(T1)	;?
	PUSHJ	P,UUOLVL##	;YES, ARE WE AT UUO LEVEL?
	  JRST	TPSCH4		;AT INTERRUPT LEVEL OR NOT SPECIAL
	PUSH	P,F		;SAVE F
	MOVE	F,TUBCUR(U)	;GET DDB ADDR
	LDB	T2,PJOBN##	;GET JOB NUMBER
	POP	P,F		;RESTORE F
	CAMN	T2,.CPJOB##	;CURRENT JOB (CURRENTLY SWAPPED USER)?
	  JRST	TPSCH4		;YES, OK TO START IT
	MOVE	T2,KDBDSP(W)	;CAN'T START NOW, CLANK THE
	PUSHJ	P,@TPKSCH(T2)	;SCHEDULER TO COME BACK @INTERRUPT LEVEL
	JRST	TPSCHY		;FIX STACK AND RETURN

TPSCH4:	MOVEM	T4,KDBCUN(W)	;STORE CURRENT UNIT
	MOVSI	T2,TKSSEL	;FLAG AS SELECTED
	IORM	T2,TKBSTS(W)	;IN KDB
	IORM	T2,TUBSTS(U)	;AND IN UDB
	AOS	T2,@KDBCHN(W)	;INDICATE CHANNEL IS BUSY
	MOVEI	T2,MQUANT##	;SET SLICE
	HRRM	T2,TKBSTS(W)	; ..
	MOVE	T3,UDBPDN(U)	;GET PHYSICAL DRIVE NUMBER
	MOVEM	W,TUBAKA(U)	;STORE AS ACTIVE KDB ADDRESS
	AOS	-2(P)		;SET FOR SKIP RETURN
TPSCHY:	POP	P,(P)
	POP	P,(P)
TPSCHX:	MOVSI	T2,TKSSCH	;CLEAR FACT THAT WE SCHEDULED
	ANDCAM	T2,TKBSTS(W)	;  FOR THIS KDB
	JRST	TPONPJ		;RESTORE INTERRUPTS AND RETURN


NXTKDB:	PUSHJ	P,SAVE1##	;SAVE P1
	MOVE	P1,KDBCHN(W)	;POINT TO THE CHANNEL DATA BLOCK
	MOVE	P1,CHNTBP(P1)	;AND GET THE AOBJN POINTER TO KDBS
NXTKD1:	CAME	W,(P1)		;FOUND THE CURRENT KDB YET?
	AOBJN	P1,.-1		;KEEP SEARCHING
	JUMPGE	P1,CPOPJ##	;NOT THERE??
	AOBJN	P1,NXTKD2	;ADVANCE TO NEXT SLOT
	MOVE	P1,KDBCHN(W)	;RAN OUT SO MUST RESET W TO THE
	SKIPA	P1,CHNTBP(P1)	;FIRST KDB ON THE CHANNEL
NXTKD2:	AOS	(P)		;FORCE SKIP
	MOVE	W,(P1)		;PICK UP A KDB ADDRESS
	POPJ	P,		;AND RETURN
SUBTTL	CHECK FILE OPERATIONS


;ROUTINE TO SEE IF IT IS LEGAL TO USE A FILE OPERATION
;CALL:	MOVE	U, UDB ADDRESS
;	PUSHJ	P,TAPFOL
;	  <NON-SKIP>		;LEGAL
;	<SKIP>			;ILLEGAL

TAPFOL::PUSH	P,T1		;SAVE T1
	MOVE	T1,TUBCNF(U)	;GET DRIVE CONFIG WORD
	ANDI	T1,TUCFOI	;KEEP ONLY "FILE OPERATIONS ILLEGAL" BIT
	SKIPE	TAPSBF		;TAPSER SAY OK?
	JUMPE	T1,TPOPJ##	;YES, BUT ONLY IF DRIVER AGREES
	JRST	TPOPJ1##	;ELSE DO MULTIPLE RECORD OPS
SUBTTL	HUNG TIMER CONTROL


;SET HUNG TIMER
;CALL:	MOVE	U, UDB ADDRESS
;	PUSHJ	P,TAPSTM
;
;ALL ACS PRESERVED

TAPSTM::PUSH	P,T1		;SAVE T1
	MOVE	T1,KDBSTS(W)	;GET STATUS WORD
	TLNN	T1,(KD.APC)	;ALL PORTS CONFIGURED (KNOWN)?
	TLNN	T1,(KD.MPD)	;NO, BUT IS IT A MULTI-PORTED DEVICE?
	JRST	TAPST2		;NORMAL HUNG TIMER WILL DO
	PUSH	P,T2		;SAVE T2
	XMOVEI	T1,UDBKDB(U)	;POINT TO DRIVE'S KDB TABLE
	MOVSI	T2,-MXPORT	;AOBJN POINTER
	PUSH	P,[EXP 0]	;INIT A COUNTER

TAPST1:	SKIPE	(T1)		;HAVE A KDB ADDRESS?
	AOS	(P)		;COUNT IT
	AOS	T1		;ADVANCE POINTER
	AOBJN	T2,TAPST1	;LOOP FOR ALL KDBS
	POP	P,T1		;GET COUNT
	LDB	T2,[POINTR (KDBSTS(W),KD.MPT)] ;AND MAXIMUM NUMBER OF PORTS
	CAIE	T1,(T2)		;ALL PORTS KNOWN?
	JRST	TAPST3		;NO, USE MAXIMUM HUNG TIMER
	POP	P,T2		;RESTORE T2
	MOVSI	T1,(KD.APC)	;HERE IF ALL PORTS ARE KNOWN
	IORM	T1,KDBSTS(W)	;UPDATE TO MINIMIZE OVERHEAD NEXT TIME

TAPST2:	HRRZ	T1,TUBQUE(U)	;GET CURRENT IORB
	LDB	T1,PRBFCN	;AND THE FUNCTION CODE
	ADJBP	T1,[POINT 9,TUBHTT(U),8] ;INDEX TO THE PROPER BYTE
	LDB	T1,T1		;GET THE FUNCTION-SPECIFIC TIMER VALUE
	JRST	TAPST4		;GO STORE

TAPST3:	POP	P,T2		;RESTORE T2
	MOVE	T1,TUBMTV(U)	;GET MAXIMUM HUNG TIMER VALUE
TAPST4:	MOVEM	T1,TUBTIM(U)	;STORE IN THE TUB
	JRST	TPOPJ##		;RESTORE T1 AND RETURN


;CLEAR HUNG TIMER
;CALL:	MOVE	U, UDB ADDRESS
;	PUSHJ	P,TAPCTM
;
;ALL ACS PRESERVED

TAPCTM::PUSH	P,T1		;SAVE T1
	SETZM	TUBTIM(U)	;CLEAR TIMER WORD IN THE TUB
	MOVSI	T1,(1B0)	;GET A BIT
	IORM	T1,TUBAKA(U)	;MARK ACTIVE KONTROLLER INVALID
	JRST	TPOPJ##		;RESTORE ACS AND RETURN
SUBTTL START IO
;HERE WHEN UPPER LEVEL WANTS TO START IO. THE USER JOB IS LOCKED
;AND THE IORB HAS A VALID DATA XFR LIST IF ONE IS NEEDED

TAPSIO::
IFN FTMP,<
	MOVE	T2,KDBCAM(W)	;GET CPU ACCESSIBILITY MASK FOR KONTROLLER
	TDNN	T2,.CPBIT##	;ON THE RIGHT CPU?
	STOPCD	.,STOP,TIO,	;++ TAPE I/O TO WRONG CPU
> ;END IFN FTMP
	TAPOFF			;GUARD AGAINST
	MOVE	T2,TKBSTS(W)	;KONTROLLER STATUS
	TLNE	T2,TKSOFL	;CONTROLLER INTERRUPTS
	JRST	TPONPJ		;WHICH SET CONTROLLER OFFLINE
	TLOE	T2,TKSSEL	;SELECTED?
	TLZE	T2,TKSSTD	;AND NOT STARTED
	STOPCD	TAPSI3,INFO,KSW, ;++KONTROLLER STATUS WRONG
TAPSI1:	MOVE	T2,TUBSTS(U)	;UNIT STATUS
	TLOE	T2,TKSSEL	;SELECTED?
	TLZE	T2,TKSSTD	;AND NOT STARTED (ON ANOTHER KONTROL)?
	STOPCD	TAPSI4,INFO,USW, ;++UNIT STATUS WRONG
TAPSI2:	MOVEI	T2,RB.ACT	;SET IORB STATUS TO ACTIVE
	DPB	T2,PRBRQS	; ...
IFN FTMP,<
	LDB	T2,DEYPCL##	;DON'T SET NON-ZERO IF
	JUMPE	T2,TPSI2A	;IT WASN'T
	SKIPN	T2,.CPCPN##	;TELL THW WORLD
	MOVEI	T2,PCLCP0##	; WHAT CPU IS DOING THE IO
	DPB	T2,DEYPCL##
TPSI2A:>
	PUSHJ	P,TAPFLT	;CHECK IF FORCING HUNG DEVICE
	  JRST	TAPSI5		;WILL HANG, DON'T START THE IORB

;FOR ERROR LOGGING, RECORD SOME DATA IN THE TUB
	SKIPGE	T2,KDBUNI(W)	;MASSBUS UNIT NUMBER
	TDZA	T2,T2		;NOT A MULTI-UNIT DEVICE
	HLRZS	T2		;PUT IN RH
	ROT	T2,-3		;POSITION IN BITS 0-2
	IOR	T2,KDBDVC(W)	;INCLUDE THE DEVICE CODE/UNIBUS ADDRESS
	MOVEM	T2,TUBDVC(U)	;SAVE
	MOVE	T2,KDBDSP(W)	;DRIVER DISPATCH
	LDB	T2,[POINTR (DRVCF2(T2),DR.KTY)] ;KONTROLLER TYPE
	MOVEM	T2,TUBKTY(U)

	MOVSI	T2,TKSSTD	;MARK AS STARTED
	IORM	T2,TKBSTS(W)	;IN KDB
	IORM	T2,TUBSTS(U)	;AND UDB
	PUSHJ	P,TAPSTM	;SET HUNG TIMER
	MOVE	T2,KDBDSP(W)	;GET KONTROLLER DISPATCH
	PUSHJ	P,@TPKSIO(T2)	;CALL START IO
TAPSI5:	TAPON			;SAFE NOW
	POPJ	P,0

;HERE AFTER KSW STOPCD, TRY TO RECOVER
TAPSI3:	MOVEM	T2,TKBSTS(W)	;STORE REASONABLE STAUS
	JRST	TAPSI1		;GO CHECK UNIT STAUS

;HERE AFTER USW STOPCD
TAPSI4:	MOVEM	T2,TUBSTS(U)	;STORE REASONABLE STATUS
	JRST	TAPSI2		;GO START THE IO

;HERE TO SETUP FOR HUNG DEVICE.
;SKIP IF OPERATION OK, NON-SKIP IF FORCING DEVHNG.
TAPFLT::MOVSI	T2,TUSFLT	;TU FAULT BIT
	TDNN	T2,TUBSTS(U)	;IS THE DRIVE BROKEN?
	JRST	CPOPJ1##	;NO, GIVE OK RETURN
	LDB	T2,PRBFCN	;GET THE IORB FUNCTION
	CAIE	T2,RB.FRW	;IS IT REWIND?
	CAIN	T2,RB.FRU	;OR UNLOAD?
	JRST	CPOPJ1##	;YES, THIS IS SAFE
	MOVEI	T2,1		;A SMALL HUNG TIME LIMIT
	MOVEM	T2,TUBTIM(U)	;LIMIT FOR TAPSEC
	POPJ	P,		;GIVE FORCED DEVHNG RETURN
SUBTTL	CHECK FOR KONTROLLER BUSY


;THIS IS COMMON CODE TO SUPPORT THOSE KONTROLLERS WHICH ARE INCAPABLE
;OF HANDLING NON-BLOCKING FUNCTION.  THAT IS, A GIVEN FUNCTION TIES UP
;THE KONTROLLER UNTIL AN INTERRUPT IS RECEIVED.
;CALL:	MOVE	W, KDB ADDRESS
;	MOVE	U, UDB FOR DRIVE TRYING TO SCHEDULE
;	PUSHJ	P,TX1BSY
;	  <NON-SKIP>		;BUSY
;	<SKIP>			;NOT-BUSY
;
;USES T1 AND T2

TAPBSY::MOVSI	T1,TKSOFL!TKSMNT!TKSNS ;INTERESTING BITS
	TDNE	T1,TKBSTS(W)	;OFFLINE, MAINTENANCE MODE, OR NOT SCHEDULING?
	POPJ	P,		;RETURN BUSY
	MOVE	T1,KDBDSP(W)	;POINT TO DRIVER DISPATCH
	PUSHJ	P,@TPKBSY(T1)	;IS KONTROLLER BUSY?
	  POPJ	P,		;IT IS
	JRST	CPOPJ1##	;KONTROLLER IS FREE

;HERE IF FOR DUMB KONTROLLERS TO DO TRADITIONAL CHECKING
	MOVSI	T1,TKSSEL!TKSSTD!TKSSCH ;BITS TO TEST
	SKIPGE	@KDBCHN(W)	;CHANNEL IN USE (USER MODE DIAG)?
	TDNE	T1,TKBSTS(W)	;OR CHANNEL ALREADY GOING?
	POPJ	P,		;THEN IT'S BUSY
	JRST	CPOPJ1##	;ELSE RETURN CHANNEL FREE
SUBTTL ONLINE CHECK


;HERE ONCE A SECOND/MINUTE..  GROVEL THROUGH THE KDBS AND SEE IF THERE ARE
;ANY OFFLINE.  IF SO, COMPLAIN THROUGH TPMOFL.  IF ANY CLAIM TO BE OFFLINE
;AND ARE NOW ONLINE, INITIALIZE AND OPERATE A SCHEDULE CYCLE.

CHKKON:	SKIPN	W,KDBTAB##+.TYMTA ;GET FIRST KDB
	JRST	CPOPJ1##	;NO TAPES

CHKKO1:	MOVSI	T1,TKSMNT	;BIT TO TEST
IFN FTMP,<
	MOVE	T2,KDBCAM(W)	;CPU ACCESSIBILITY MASK
	TDNE	T2,.CPBIT##	;ON OUR CPU?
>
	TDNE	T1,TKBSTS(W)	;SEE IF WE SHOULD CHECK
	JRST	CHKKO7		; IF HE IS ONLINE
	MOVSI	T1,TKSSEL!TKSSTD!TKSSCH ;BITS TO TEST
	SKIPGE	@KDBCHN(W)	;CHANNEL IN USE (USER MODE DIAG)?
	TDNE	T1,TKBSTS(W)	;OR CHANNEL ALREADY GOING?
	JRST	CHKKO2		;THEN IT'S ONLINE
	MOVE	T1,KDBDSP(W)	;GET XFR VECTOR
	PUSHJ	P,@TPKONL(T1)	;SEE IF ONLINE
	  JRST	CHKKO4		;NOT ON LINE - COMPLAIN

CHKKO2:	MOVSI	T1,TKSOFL	;NOW ONLINE - SEE IF
	TDNN	T1,TKBSTS(W)	;IF FORMERLY OFFLINE.
	JRST	CHKKO7		;NO
	TLO	T1,TKSSCH!TKSSTD!TKSSEL!TKSSIL!IFN FTMP,<TKSCHE!TKSCHA>
	ANDCAM	T1,TKBSTS(W)	;CLEAR LOTS OF BITS
	MOVSI	T2,TKSSTD!TKSSEL!IFN FTMP,<TKSCHE!TKSCHA> ;BITS TO CLEAR IN UDB
	MOVE	T3,KDBIUN(W)	;POINTER TO UDB TABLE
	SETZ	U,		;FLAG FOR CALLING TPKRES ROUTINE

;KONTROLLER WAS OFFLINE BEFORE, BUT IT'S ONLINE NOW, SO FIXUP UDBS
CHKKO3:	SKIPE	0(T3)		;UNIT PRESENT?
	SKIPA	U,(T3)		;YES, GET ITS ADDRESS
	CAIA			;NO, SKIP THIS ONE
	ANDCAM	T2,TUBSTS(U)	;YES - CLEAR BITS
	CAMGE	T3,KDBFUN(W)	;FINAL UDB?
	AOJA	T3,CHKKO3	;LOOP FOR MORE
	JUMPE	U,CHKKO7	;IF NO UNITS, THEN NO ACTIVE I/O TO RESET
	MOVE	T1,KDBDSP(W)	;GET XFR VECTOR
	PUSHJ	P,@TPKRES(T1)	;RESET CONTROLLER
	JRST	CHKKO7		;GO FINISH THIS ONE

;KONTROLLER HAS BEEN NOTICED OFFLINE FOR THE FIRST TIME
CHKKO4:	MOVE	T1,KDBIUN(W)	;IF A KON BUT NO UNITS
CHKKO5:	SKIPN	(T1)		;HAVE A UDB?
	JRST	CHKKO6		;YES
	CAMGE	T1,KDBFUN(W)	;FINAL UDB?
	AOJA	T1,CHKKO5	;LOOP 'TIL A UDB IS FOUND
	JRST	CHKKO7		;ONTO THE NEXT KONTROLLER
CHKKO6:	PUSHJ	P,@0(P)		;CALL CO-ROUTINE
	JRST	CHKKO7		;CONTINUE LOOP

;END OF KONTROLLER LOOP
CHKKO7:
IFN FTMP,<
	MOVEI	T1,TKBICT##	;INITIAL FAIRNESS COUNT
	SKIPGE	TKBFCT(W)	;FAIRNESS COUNT GONE OFF?
	MOVEM	T1,TKBFCT(W)	;ABJECT PARANOIA
>
	SKIPE	W,KDBNXT(W)	;GET NEXT KDB
	JRST	CHKKO1		;IF THERE IS ONE, CHECK IT
	JRST	CPOPJ1##	;ELSE SKIP OVER ARG
;ONCE A SECOND - JUST CHECK AND UPDATE INFO

TAPSEC::MOVSI	T1,(CR.ATO)	;BIT TO TEST
	TDNE	T1,.CPRUN##	;WAITING FOR AUTCON TO RUN?
	POPJ	P,		;THEN DO NOTHING
	PUSHJ	P,CHKKON	;CRAWL THROUGH DATA BASE
	  JRST	SETOFL		;WHERE TO GO FOR OFFLINE CTL
	SKIPN	W,KDBTAB##+.TYMTA ;START AT FIRST KONTROLLER
	POPJ	P,		;NO TAPES
	PUSHJ	P,SAVE1##	;SAVE P1
TAPSE1:
IFN FTMP,<
	MOVE	T1,KDBCAM(W)	;CPU ACCESSIBILITY MASK
	TDNN	T1,.CPBIT##	;OUR CPU?
	JRST	TAPSE3		;NO
> ;END IFN FTMP
	MOVE	P1,KDBIUN(W)	;GET START ADDR OF DRIVE TABLE

TAPSE2:	SKIPE	U,(P1)		;GET A TUB
	PUSHJ	P,UTIMER	;CHECK ITS HUNG TIMER
	CAMGE	P1,KDBFUN(W)	;LOOKED AT ALL TUBS YET?
	AOJA	P1,TAPSE2	;LOOP BACK FOR ANOTHER

TAPSE3:	SKIPE	W,KDBNXT(W)	;STEP TO NEXT KONTROLLER
	JRST	TAPSE1		;GO CHECK IT
	POPJ	P,		;DONE - RETURN


SETOFL:	MOVSI	T1,TKSOFL
	TDNE	T1,TKBSTS(W)	;ON LINE?
	POPJ	P,		;NO - JUST RETURN
	MOVE	T1,KDBDSP(W)	;GET DISPATCH TABLE ADDRESS
	PUSHJ	P,@TPKLOD(T1)	;TRY TO LOAD MICROCODE
	  SKIPA			;CAN'T OR WON'T LOAD
	POPJ	P,		;KONTROLLER BACK ON-LINE
	MOVSI	T1,TKSOFL	;BIT TO SET
	IORM	T1,TKBSTS(W)	;MARK IT OFF-LINE
	PJRST	TPMOFL##	;TELL WORLD AND RETURN

;ONCE A MINUTE - SEE IF STILL OFF LINE

TAPMIN::PUSHJ	P,CHKKON	;LOOK AT THINGS
	  JRST	TPMOFL##	;JUST COMPLAIN IF OFF LINE
	POPJ	P,		;RETURN
IFN FTMP,<
;HERE ONCE A TICK
TAPTIC::SKIPN	W,KDBTAB##+.TYMTA ;START AT 1ST KDB
	POPJ	P,		;NO TAPES
	SKIPN	CPUTPQ		;ANY QUEUED REQUESTS FROM ANY CPU?
	JRST	TAPTI4		;NO
TAPTI1:	MOVSI	T2,TKSCHE	;USE 1 BIT PER CPU
	MOVE	T1,KDBCAM(W)	;CPU ACCESSIBILITY MASK
	TDNE	T1,.CPBIT##	;ON THIS CPU?
	TDNN	T2,TKBSTS(W)	;YES, DOES IT HAVE A QUEUED REQUEST?
	JRST	TAPTI3		;NO, TRY NEXT KDB
	MOVE	T3,.CPQPC##	;YES, CLEAR THE DOORBELL
	ANDCAM	T3,DOORBL##	; SINCE WE HAVE SERVICED THE REQUEST
	TLO	S,IOSCP2	;INDICATE WE ARE LOOKING FOR A QUEUED REQUEST
	MOVEI	T2,TKBICT##	;IF FAIRNESS HAS GONE OFF
	SKIPGE	TKBFCT(W)
	MOVEM	T2,TKBFCT(W)	;RESET IT
	PUSHJ	P,CKSIO2	;TRY TO SELECT SOME UNIT ON KDB
	MOVSI	T1,TKSCHE
	MOVE	T2,KDBIUN(W)	;POINTER TO UDB TABLE
	SYSPIF
	TLNN	S,IOSCP2	;DID WE SELECT A UNIT?
	ANDCAM	T1,TUBSTS(U)	;YES, CLEAR THE QUEUED REQUEST BIT
TAPT1A:	SKIPE	T3,(T2)		;GET A UDB
	TDNN	T1,TUBSTS(T3)	;DOES IT HAVE A QUEUED REQUEST?
	JRST	TAPT1B		;NO, TRY NEXT
	HRRZ	T4,TUBQUE(T3)	;MAYBE
	JUMPN	T4,TAPT1C	;IS THERE AN IORB?
	ANDCAM	T1,TUBSTS(T3)	;NO. IT GOT DESELECTED
TAPT1B:	CAMGE	T2,KDBFUN(W)	;FINAL UDB?
	AOJA	T2,TAPT1A	;LOOP FOR MORE
	ANDCAM	T1,TKBSTS(W)	;NO, CLEAR BIT IN THE KDB
TAPT1C:	SYSPIN
	TLZN	S,IOSCP2	;DID WE SELECT A UDB?
	SOSA	CPUTPQ		;YES, DECREMENT NO OF OUTSTANDING REQUESTS
	JRST	TAPTI3		;NO, TRY NEXT KDB
	HRRZ	T1,TUBQUE(U)	;POINT AT IORB
	MOVE	F,TUBCUR(U)	;SET UP NEEDED ACS
	MOVE	S,DEVIOS(F)
	LDB	J,PJOBN##	;JOB WHICH OWNS THE UDB
	MOVE	T2,TRBFNC(T1)	;GET IORB FLAGS
	TLNE	T2,RB.PCL	;THERE IS A RACE WITH ^C WHICH COULD GET
	JRST	TAPTI2		; US HERE FOR A NON-QUEUED REQUEST
	MOVE	T1,J		;THE JOB IS WAITING AT KONWAT
	PUSHJ	P,EWAKE##	;SO JUST TAKE IT OUT OF EW
	JRST	TAPTI3		;AND KEEP ON
TAPTI2:	PUSHJ	P,TAPTIX	;"REGULAR" ROUTE - GO START UP THE IO
TAPTI3:	SKIPE	W,KDBNXT(W)	;STEP TO NEXT KDB
	JRST	TAPTI1		;AND GO TEST IT
;HERE AFTER STARTING WHAT WE COULD
TAPTI4:	SKIPN	T1,.CPTAP##	;ANY REQUEST FOR THIS CPU
	POPJ	P,		;NO, GO AWAY
IFN FTKL10,<
	SKIPG	T1		;YES, DID FILIO ALREADY SWEEP?
	PUSHJ	P,CSDMP##	;NO, SWEEP CACHE NOW
>
	MOVE	W,KDBTAB##+.TYMTA ;START AT BEGINNING
	MOVE	T1,.CPCHX##	;NEEDS-A-SWEEP BIT
	MOVSI	T2,TKSCHE	;SWEEP-WAS-DONE BIT
TAPTI5:	TDNN	T1,TKBSTS(W)	;DOES THIS KDB NEED A SWEEP?
	JRST	TAPTI8		;NO
	ANDCAM	T1,TKBSTS(W)	;YES, CLEAR THAT BIT
	SYSPIF
	IORM	T2,TKBSTS(W)	;AND LIGHT SWEEP-DONE
	MOVE	T3,KDBIUN(W)	;POINTER TO UDB TABLE
TAPTI6:	SKIPE	T4,(T3)
	TDNN	T1,TUBSTS(T4)	;CLEAR SWEEP-NEEDED, SET SWEEP-DONE
	JRST	TAPTI7
	ANDCAM	T1,TUBSTS(T4)	; FOR ALL UDBS WHICH NEEDED A SWEEP
	IORM	T2,TUBSTS(T4)
TAPTI7:	CAMGE	T3,KDBFUN(W)	;FINAL UDB?
	AOJA	T3,TAPTI6	;LOOP FOR MORE
	SYSPIN
TAPTI8:	SKIPE	W,KDBNXT(W)	;STEP TO NEXT KDB
	JRST	TAPTI5		;GO SWITCH THE BITS FOR IT TOO
	MOVM	T1,.CPTAP##	;GET NO OF REQUESTS WE JUST SATISFIED
	ADDM	T1,CPUTPQ	;AND ADD TO REQUESTS FOR ALL CPUS
	SETZM	.CPTAP##	;CLEAR THIS-CPU-NEEDS-A-SWEEP
	POPJ	P,		;AND EXIT


;HERE TO GET A QUEUED REQUEST STARTED
;WILL GET TO START THE IO VIA TPMDON
TAPTIX:	PUSHJ	P,SVEUB##	;MAKE ADDRESSABLE
	PUSHJ	P,SPCS##	;AND SET PCS
	JRST	TAPAGO		;CALL TAPAGO TO GET TO TPMDON TO START IO
>;END IFN FTKL10
SUBTTL	CHECK HUNG TIMERS


UTIMER:	CAMN	W,TUBAKA(U)	;CURRENTLY ACTIVE KDB?
	PUSHJ	P,CHKIRB	;GET IORB (ONLY REALLY CHECKING ACTIVE STATE)
	  SKIPA	T2,TUBAKA(U)	;NOT ACTIVE, GET KDB
	JRST	UTIMR1		;GO CHECK THE TIMER
	TLZ	T2,(1B0)	;IN CASE KDB WAS INACTIVE
	MOVSI	T3,TUSREW	;REWIND/UNLOAD BIT
	CAMN	T2,W		;WAS THIS THE LAST ACTIVE KDB?
	TDNN	T3,TUBSTS(U)	;AND IS THE DRIVE REWINDING NOW?
	POPJ	P,		;NO TO EITHER

UTIMR1:	DDBSRL			;PREVENT LABEL DDBS FROM BEING DELETED
	SKIPN	F,TUBCUR(U)	;PICK UP CURRENT DDB
	MOVE	F,UDBDDB(U)	;OR THE "REAL" DDB IF NONE
	JUMPE	F,UTIMR6	;AND PUNT IF IMPOSSIBLE HAPPENS
	MOVE	S,DEVIOS(F)	;GET I/O STATUS WORD
	MOVSI	T1,DVOFLN	;BIT TO TEST
	HRRZ	T2,DEVSER(F)	;UUO-LEVEL DISPATCH TABLE ADDRESS
	TDNE	T1,DEVCHR(F)	;DRIVE OFFLINE?
	PUSHJ	P,DOFL(T2)	;YES, SEE IF ITS STILL OFFLINE
	  JRST	UTIMR2		;IT IS
	MOVSI	T1,DVOFLN	;GET THE BIT AGAIN
	ANDCAM	T1,DEVCHR(F)	;CLEAR OFFLINE STATE SINCE IT'S ONLINE NOW
	PUSHJ	P,PSIONL##	;TELL THE USER ITS ONLINE IF HE'S INTERESTED

UTIMR2:	HRRZ	T1,TUBQUE(U)	;GET IORB BACK
	MOVE	T2,TUBTIM(U)	;GET THE TIMER
	SOJL	T2,UTIMR3	;ZERO MEANS IGNORE THE DEVICE
	MOVEM	T2,TUBTIM(U)	;UPDATE COUNTER
	JUMPN	T2,UTIMR6	;JUMP IF COUNTER NOT EXPIRED YET
	JUMPN	T1,UTIMR4	;ELSE IF AN IORB, THEN SOME OPERATION HUNG

UTIMR3:	MOVSI	T3,TUSREW	;BIT TO TEST
	TDNE	T3,TUBSTS(U)	;REWIND OR UNLOAD INTERRUPT PENDING?
	JRST	[PUSHJ P,REWDON	;MISSED AN INTERRUPT, CLEAR REW/UNL, ETC.
		 JRST  UTIMR6]	;DON'T STOP JOB FOR REWIND OR UNLOAD
	JUMPL	T2,UTIMR6	;IGNORE DEVICE IF TIMER WAS ZERO

UTIMR4:	JUMPE	T1,UTIMR6	;CAN DO NOTHING WITHOUT AN IORB NOW
	MOVSI	T2,RB.AIO	;IF ASYNCHRONOUS I/O, THEN THERE'S NO TIMER
	TDNE	T2,TRBFNC(T1)	; TO COUNT DOWN SINCE THE USER'S PROGRAM IS
	JRST	UTIMR6		; TIMING THE I/O (IF IT CARES)
	MOVSI	T1,TUSFLT	;FAULTY DRIVE BIT
	TDNE	T1,TUBSTS(U)	;DID THE DRIVE GO BAD?
	JRST	UTIMR5		;YES, IGNORE SPECIAL CASE FOR SCHEDULE
	MOVSI	T1,TKSSCH	;GET SCHEDULER BIT
	TDNN	T1,TKBSTS(W)	;HUNG TRYING TO FORCE A SCHEDULE?
	JRST	UTIMR5		;NO, MUST BE A REAL TAPE OPERATION
	PUSHJ	P,TAPSTM	;SET HUNG TIMER
	MOVE	T1,KDBDSP(W)	;TRY AGAIN
	PUSHJ	P,@TPKSCH(T1)	;BY REQUESTING ANOTHER INTERRUPT
	JRST	UTIMR6		;FINISH UP

UTIMR5:	PUSH	P,U		;SAVE TUB ADDRESS
	PUSH	P,W		;AND KDB ADDRESS
	HRRZ	T1,DEVSER(F)	;UUO-LEVEL DISPATCH TABLE
	PUSHJ	P,DHNG(T1)	;CALL TAPUUO TO UNHANG THE DEVICE
	  PUSHJ	P,DEVHNG##	;PRINT ERROR MESSAGE AND STOP JOB
	POP	P,W		;RESTORE TKB
	POP	P,U		;RESTORE TUB ADDRESS

UTIMR6:	DDBSRU			;RESUME DDB SCANNING
	POPJ	P,		;RETURN
SUBTTL HUNG DEVICE

TAPHNG::TAPOFF			;WHO KNOWS WHAT EVIL...
	MOVE	W,TUBAKA(U)	;GET ACTIVE KDB ADDRESS
	TLZ	W,(1B0)		;CLEAR POSSIBLY INACTIVE BIT
	MOVE	T2,KDBDSP(W)	;CALL KON RESET ENTRY
	MOVSI	T1,TKSSTD
	TDNN	T1,TUBSTS(U)	;IF TAPE HAS BEEN STARTED
	JRST	TAPHN1
	PUSHJ	P,@TPKRES(T2)	; ...
	MOVSI	T1,(1B2)
	IORM	T1,TUBTRY(U)	;TELL DAEMON ITS A HUNG
	HRRZ	P1,TUBQUE(U)	;SETUP FOR TPEMOV
	PUSHJ	P,TPEMOV##	;PUT STATS IN DAEMON'S PART OF TUB
	PUSHJ	P,TPELOG##	;AND GO LOG IT

TAPHN1:	MOVSI	T1,TKSSTD!TUSREW ;CLEAR STARTED AND REWIND
	ANDCAM	T1,TUBSTS(U)	;IN UDB
	TLC	T1,TKSSCH^!TUSREW ;ALSO CLEAR SCH AND STARTED
	ANDCAM	T1,TKBSTS(W)	;IN KDB
IFN FTMP,<
	MOVSI	T1,TKSCHE	;SWEEP-NEEDED OR SWEEP-DONE
	IOR	T1,.CPCHX##
	TDNE	T1,TUBSTS(U)	;IF EITHER IS ON
	SOS	CPUTPQ		;DECREMENT NO OF OUTSTANDING QUEUED REQUESTS
	ANDCAM	T1,TUBSTS(U)	;AND CLEAR THE BITS IN THE TUB
> ;END IFN FTMP
	CAIA			;PRUNE QUEUE, DESELECT,PION

TAPKIL::TAPOFF			;FIGHT RACE
	MOVE	W,TUBAKA(U)	;GET ACTIVE KDB ADDRESS
	TLZ	W,(1B0)		;CLEAR POSSIBLY INACTIVE BIT
	HRRZ	T1,TUBQUE(U)	;RETURN HEAD OF LIST
	MOVSI	T2,TUBQUE(U)	;RESET POINTERS
	MOVEM	T2,TUBQUE(U)	; ...
	SETZM	TUBERR(U)	;STOP ERROR RECOV
	MOVSI	T2,TUSNS	;CLEAR HOLD
	ANDCAM	T2,TUBSTS(U)
	PUSH	P,T1		;SAVE PNTR
	PUSHJ	P,TPCSEL	;DESELECT IF NECESSARY
	POP	P,T1		;RESTORE PNTR
	PUSHJ	P,TAPCTM	;CLEAR HUNG TIMER
	PJRST	TPONPJ		;TURN ON PI AND EXIT

SUBTTL HOLD/CONTINUE
;HERE TO SET NS BIT ON A UNIT
TAPHLD::MOVSI	T1,TUSNS	;SET NO SCHEDULE
	IORM	T1,TUBSTS(U)	;...
IFN FTMP,<
	PUSHJ	P,CHKCPI##
	  JRST	[MOVEI	T1,1
		 HRRM	T1,TKBSTS(W)
		 POPJ	 P,]
>
	MOVSI	T1,TKSSTD	;SEE IF STARTED
	TDNE	T1,TUBSTS(U)	;...
	JRST	[HLLZS TKBSTS(W)	;GRNTEE NO MORE
		 POPJ P,]	;RETURN
TPCSEL:	MOVSI	T1,TKSSEL	;SEE IF SELECTED
	TDNN	T1,TUBSTS(U)
	POPJ	P,		;NOT SELECTED - EXIT
	PUSHJ	P,TAPDSL	;YES - DESELECT
	PJRST	CKSIO1		;CLANK SCHED IF NECESSARY

;ROUTINE CALLED TO ALLOW UNIT TO SCHEDULE AGAIN
TAPCNT::MOVSI	T1,TUSNS
	ANDCAM	T1,TUBSTS(U)	;CLEAR NS BIT
	PUSHJ	P,TPMACC##	;KDB/UDB ACCESSIBILE?
	  SKIPA			;NO
	JRST	CKSIO2		;WE CAN DO I/O OURSELVES
CKSIO1:	JUMPE	W,CPOPJ##	;IF NONE, PUNT
IFN FTMP,<
	MOVE	T1,KDBCAM(W)	;CPU ACCESSIBILITY MASK
	PUSHJ	P,CAMCPU##	;FIND ANY (HOPFULLY RUNNING) CPU
	CAME	T1,.CPCPN##	;IS IT US?
	PJRST	PCLTAP		;MUST QUEUE THE REQUEST
> ;END IFN FTMP
	TLZ	S,IOSCP2	;MAKE SURE IOSCP2 IS OFF
CKSIO2:	MOVSI	T2,TKSSEL!TKSSCH!TKSOFL
	SYSPIF
	SKIPGE	@KDBCHN(W)	;IS CHANNEL BUSY?
	TDNE	T2,TKBSTS(W)	; ARE ANY OF THESE ON?
	JRST	ONPOPJ##	;YES - SOMETHING WILL HAPPEN
	MOVSI	T2,TKSSCH	;SET SCHED REQUESTED
	IORM	T2,TKBSTS(W)	;...
	AOS	@KDBCHN(W)	;INDICATE CHANNEL IS BUSY
	SYSPIN
	PUSH	P,U		;SAVE U
	PUSHJ	P,TAPSCH	;TRY TO START SOMETHING
	  JRST	CKSIO3		;NOTHING TO DO, RETURN FOR NOW
IFN FTMP,<
	TLZE	S,IOSCP2
	JRST	CKSIO4
>
	PUSH	P,F		;SAVE F
	HRRZ	T1,TUBQUE(U)	;CURRENT IORB
	PUSHJ	P,TPMSIO##	;CRANKUP THE I/O
	MOVE	U,-1(P)		;WHICH UDB WE WERE USING
	PUSHJ	P,TAPACC	;RESTORE KDB ADDRESS
	  JFCL			;IGNORE OFF-CPU RETURN
	JRST	FUPOPJ##	;RESTORE F,U,AND RETURN

CKSIO3:	MOVE	U,(P)		;RESTORE U
	PUSHJ	P,TAPACC	;AND KDB ADDRESS
	  JFCL			;IGNORE OFF-CPU RETURN
CKSIO4:	SOS	T1,@KDBCHN(W)	;INDICATE CHANNEL IS NOT BUSY
	JRST	TPOPJ##		;MAKE STACK RIGHT AND RETURN
;SUPPORT FOR THE TAPOFF MACRO
TAPLOK::CONO	PI,PI.OFF	;TURN OFF THE PI SYSTEM WHILE TESTING
	AOSE	.CPTPN##	;BUMP LEVEL OF NESTING
	JRST	[CONO PI,PI.ON	;JUST NESTING, RETURN, ALREADY HAVE INTERLOCK
		 POPJ P,]	;...
	CONI	PI,.CPTPP##	;SAVE CHANNEL ENABLES
;	CONO	PI,PI.ON+TAPPIF## ;TURN PI SYSTEM ON LEAVING TAPCHN OFF
	CONO	PI,PI.ON+TPIOFF## ;TURN TAPCHN AND LOWER CHANNELS OFF
IFN FTMP,<
	SKIPGE	INTRTP##	;GIVE OTHER CPUS A CHANCE
	AOSE	INTRTP##	;WAIT FOR THE SYSTEM-WIDE INTERLOCK
	JRST	.-2		;WAIT
	APRID	INTOTP##	;INFORM THE WORLD WHO OWNS THIS
>; END IFN FTMP
	POPJ	P,		;RETURN


;SUPPORT ROUTINE FOR THE TAPON MACRO
TAPULK::PUSH	P,T1		;SAVE A REGISTER
	SOSL	T1,.CPTPN##	;DECREMENT LEVEL OF NESTING, SEE IF DONE
	PJRST	TPOPJ##		;STILL NESTING, JUST RETURN
	EXCH	T1,.CPTPP##	;GET CHANNEL ENABLE STATES
	ANDI	T1,177		;KEEP JUST THE CHANNELS WHICH WERE ON
	TRO	T1,PI.TNP	;TURN THEM BACK ON
	EXCH	T1,.CPTPP##	;RESTORE T1 AND SAVE ARGUMENT FOR CONO PI,
IFN FTMP,<
	SETOM	INTOTP##	;CLEAR INTERLOCK OWNER
	EXCH	T1,INTRTP##	;RESET THE INTERLOCK
	SKIPGE	T1		;MAKE SURE IT WAS OWNED
	STOPCD	.,CPU,TIU,	;++ TAPE INTERLOCK UNOWNED
>; END IFN FTMP
	CONO	PI,@.CPTPP##	;PUT THE PI SYSTEM BACK THE WAY IT WAS
	PJRST	TPOPJ##		;RESTORE T1 AND RETURN
IFN FTMP,<
PCLTAP:	HRRZ	T1,TUBQUE(U)	;GET IORB ADDRESS
	JUMPE	T1,CPOPJ##	;NONE??????
	MOVE	T1,TRBFNC(T1)	;GET IORB FLAGS
	TLNE	T1,RB.PCL	;IS THIS A QUEUED REQUEST?
	JRST	PCLTP1		;YES
	PUSHJ	P,TSLEE1##	;NO, LET THE NON-QUEUED STUFF FINISH
	JRST	CKSIO1		; BEFORE GETTING INTO QUEUED REQUESTS
PCLTP1:	MOVE	T1,UDBCAM(U)	;GET ACCESSABILITY MASK
	PUSHJ	P,CPUOK##	;RETURN CDB OFFSET
	  JRST	[PUSHJ P,HNGSTP## ;"PROBLEM ON DEVICE"
		 JRST	PCLTAP]	;TRY AGAIN
	IMULI	T1,.CPLEN##	;GET CPU OFFSET
	MOVE	T2,.CPCHX##	;INDICATE WE NEED CACHE SWEEP
	SYSPIF
	IORM	T2,TUBSTS(U)	;DONT SCHED UNIT TILL SWEEP
	IORM	T2,TKBSTS(W)
	SKIPLE	.CPTAP##	;COUNT UP A REQUEST
	AOSA	.CPTAP##
	SOS	.CPTAP##	; (.CPTAP COULD BE NEGATIVE)
	SYSPIN
	PJRST	SETQP1##	;RING THE DOORBELL ON THE OWNING CPU
;SUBROUTINE TO GET JOB ONTO ANY CPU WHICH CAN ACCESS THE DEVICE
;CALL:	MOVE	U, UDB ADDRESS
;	PUSHJ	P,TAPCPU

TAPCPU::PUSHJ	P,SAVT##	;SAVE SOME ACS

TAPCP1:	PUSHJ	P,TAPACC	;FIND CPU--KDB--UDB PATH
	  JUMPE	W,TAPCP2	;IF NO KDB, THEN HUNG DEVICE
	HLRZS	T1		;ELSE GET ACCESSIBILE CPU NUMBER
	PUSHJ	P,ONCPUS##	;GET US ONTO THAT CPU
	  JRST	TAPCP2		;CPU DEAD??
	POPJ	P,		;RETURN

TAPCP2:	PUSH	P,F		;SAVE F
	SKIPN	F,TUBCUR(U)	;GET CURRENT DDB
	MOVE	F,UDBDDB(U)	;POINT AT USER IF NONE
	PUSHJ	P,HNGSTP##	;CALL IT A HUNG DEVICE
	POP	P,F		;RESTORE F
	JRST	TAPCP1		;TRY AGAIN
> ;END IFN FTMP
;ROUTINE TO CHECK FOR KDB/UDB ACCESSIBILITY ON THE CURRENT CPU
;CALL:	MOVE	U, UDB ADDRESS
;	PUSHJ	P,TAPACC
;	  <NON-SKIP>		;KDB OR UDB INACCESSIBLE
;	<SKIP>			;W POINTS TO THE KDB WHICH CAN DO I/O
;
;NOTE:	THE NON-SKIP RETURN MIGHT BE TAKEN IF THE KONTROLLER IS OFFLINE

TAPACC::PUSHJ	P,SAVE4##	;SAVE SOME ACS
	SETZB	P3,P4		;CLEAR OFFLINE KDB AND BEST KDB
	MOVSI	P1,-1		;ASSUME ACTIVE KDB
	SKIPLE	W,TUBAKA(U)	;GET THE KDB
	JRST	TAPAC2		;ENTER LOOP WITH ACTIVE KDB SETUP
	MOVSI	P1,-MXPORT	;ELSE SCAN ALL DRIVE'S KDBS
	XMOVEI	P2,UDBKDB(U)	;POINT TO KDB TABLE IN UDB

TAPAC1:	SKIPN	W,(P2)		;GET A KDB
	JRST	TAPAC3		;SLOT EMPTY
TAPAC2:	MOVE	T1,KDBCAM(W)	;GET ASSESSIBILITY BITS
	TDNN	T1,UDBCAM(U)	;CAN IT TALK TO THE DRIVE IN QUESTION?
	JRST	TAPAC3		;NO, MUST TRY ANOTHER KDB
	MOVSI	T1,TKSOFL	;OFFLINE BIT
	TDNE	T1,TKBSTS(W)	;IS KONTROLLER OFFLINE?
	JRST	TAPAC3		;YES, TRY ANOTHER KDB
IFE FTMP,<HRRZ	T1,KDBCAM(W)>	;GET CPU0,,MASK
IFN FTMP,<
	MOVE	T1,KDBCAM(W)	;GET KONTROLLER'S CPU ACCESSIBILITY MASK
	PUSHJ	P,CPUOK##	;FIND A RUNNING CPU
	  JRST	TAPAC3		;CAN'T DO I/O FROM THIS KONTROLLER
	HRLZS	T1		;PUT OWNING CPU IN LH
	HRR	T1,KDBCAM(W)	;GET CPU MASK
	TDNN	T1,.CPBIT##	;OUR CPU?
	JRST	TAPAC4		;NO, KEEP LOOKING
> ;END IFN FTMP
	JRST	CPOPJ1##	;RETURN WITH USABLE KDB ADDRESS IN W

TAPAC3:	SKIPN	P3		;ALREADY HAVE A KDB INCASE ALL OFFLINE?
	MOVE	P3,W		;NO, SAVE OFFLINE KDB ADDRESS
	SKIPA			;THIS ONE'S NOT ONLINE
TAPAC4:	MOVE	P4,W		;SAVE BEST ONLINE KDB SO FAR
	AOS	P2		;ADVANCE POINTER TO NEXT KDB SLOT
	AOBJN	P1,TAPAC1	;LOOP BACK AND CHECK IT OUT
	SKIPN	W,P4		;SEE IF WE FOUND AN ACCESSIBLE ONLINE KDB
	JRST	TAPAC5		;THERE AREN'T ANY
IFE FTMP,<HRRZ	T1,KDBCAM(W)>	;GET CPU0,,MASK
IFN FTMP,<
	MOVE	T1,KDBCAM(W)	;GET KONTROLLER'S CPU ACCESSIBILITY MASK
	PUSHJ	P,CAMCPU##	;TRANSLATE TO CPU NUMBER
	TSO	T1,KDBCAM(W)	;ALL IS WELL, PUT MASK IN LH
	MOVSS	T1		;SET RETURNED VALUE TO CPU,,MASK
> ;END IFN FTMP
	JRST	CPOPJ1##	;RETURN GOODNESS

;HERE WHEN THERE ARE NO ONLINE KDBS
TAPAC5:	SKIPE	W,P3		;GET OFFLINE KDB
	POPJ	P,		;TAKE "NO ACCESS" RETURN
	MOVSI	P1,-MXPORT	;AOBJN POINTER TO FIND ANY KDB
	XMOVEI	P2,UDBKDB(U)	;POINT TO KDB TABLE IN UDB

TAPAC6:	SKIPE	W,(P2)		;PICK ANY EXISTANT KONTROLLER
	POPJ	P,		;TAKE OFFLINE RETURN
	AOS	P2		;ADVANCE PONTER
	AOBJN	P1,TAPAC6	;LOOP 'TILL WE FIND ONE
	POPJ	P,		;RETURN


;ROUTINE TO RETURN AN ACCESSIBILITY MASK FOR A GIVEN DDB
;MUST PRESERVE T2, T3, & W!

TAPCPI::SETZ	T1,		;JUST IN CASE
	SKIPN	TDVUDB##(F)	;CHECK FOR PROTOTYPE
	POPJ	P,		;IT IS
	SE1ENT			;ENTER SECTION ONE
	PUSH	P,U		;SAVE U
	PUSH	P,T2		;AND POSSIBLE BUFFER ADDRESS
	PUSH	P,W		;AND UUOCON'S SAVED IOACT
	MOVE	U,TDVUDB##(F)	;POINT TO UDB
	PUSHJ	P,TAPACC	;CAN WE GET TO IT?
	  JRST	TAPPI1		;NO
	MOVE	T1,KDBCAM(W)	;GET MASK
	TDNE	T1,.CPBIT##	;IF WE CAN USE IT,
	AOS	-3(P)		;SKIP
	SKIPA			;RETURN THE MASK
TAPPI1:	SETZ	T1,		;NOT ACCESSIBLE
	POP	P,W		;RESTORE FOR UUOCON
	POP	P,T2		;RESTORE POSSIBLE BUFFER ADDRESS
	POP	P,U		;RESTORE U
	POPJ	P,		;AND RETURN
SUBTTL QUEUE MANIPULATION

;HERE TO INSERT AN IORB AT THE HEAD OF A UNIT QUEUE
;U/ UDB, T1/ IORB

CPURQH::
IFN FTMP,<PUSHJ	P,TAPCPU>	;GET ON THE RIGHT CPU

TAPRQH::MOVEI	T2,RB.RPN	;SET IORB TO PENDING
	DPB	T2,PRBRQS	; ...
	TAPOFF			;FIGHT RACE
	HRRZ	T2,TUBQUE(U)	;GET HEAD POINTER
	HRRM	T2,IRBLNK(T1)	;FORWARD LINK
	HRRM	T1,TUBQUE(U)	;NEW HEAD
	MOVEM	F,TUBCUR(U)	;POINT AT THE RIGHT CULPRIT
	PUSHJ	P,TAPCNT	;POSSIBLY SCHEDULE I/O
	PJRST	TPONPJ		;RESTORE INTS AND EXIT

;HERE TO INSERT AN IORB AT THE TAIL OF A UNIT QUEUE
;U/UDB, T1/IORB

CPURQT::
IFN FTMP,<PUSHJ	P,TAPCPU>	;GET ON THE RIGHT CPU

TAPRQT::MOVEI	T2,RB.RPN	;SET IORB AS PENDING
	DPB	T2,PRBRQS	; ...
	TAPOFF			;FIGHT RACE
	HLRZ	T2,TUBQUE(U)	;GET TAIL POINTER
	HRRM	T1,IRBLNK(T2)	;FORWARD LINK AT OLD TAIL
	HRLM	T1,TUBQUE(U)	;NEW TAIL POINTER
	CAIN	T2,TUBQUE(U)	;IF FIRST IN Q,
	MOVEM	F,TUBCUR(U)	;TELL TPMSIO WHO WE ARE
	PUSHJ	P,TAPCNT	;POSSIBLY SCHEDULE I/O
	PJRST	TPONPJ		;RETURN

;HERE TO REMOVE THE IORB FROM THE HEAD OF A UNIT QUEUE

TAPREM::TAPOFF			;FIGHT RACE
	HRRZ	T1,TUBQUE(U)	;GET CURRENT HEAD
	JUMPE	T1,TPONPJ	;EXIT IF NONE
	MOVE	T2,IRBLNK(T1)	;FOLLOW FORWARD LINK
	HRRM	T2,TUBQUE(U)	;NEW HEAD POINTER
	HLRZ	T3,TUBQUE(U)	;CHECK IF QUEUE ONLY HAD ONE ELEMENT
	CAIE	T3,(T1)		;...
	JRST	TAPRM1		;NO - LEAVE
	MOVSI	T2,TUBQUE(U)	;MAKE TAIL POINT TO QUEUE  HEAD
	MOVEM	T2,TUBQUE(U)	; ...
	SETZM	TUBCUR(U)	;NO CURRENT DDB
	JRST	TPONPJ		;NO DDB TO FIND IF NO NEXT IORB
TAPRM1:	SKIPE	T3,TUBDDL(U)	;SEE IF HAVE A LABEL DDB
	CAME	T2,TDVIOR##(T3)	;IS THIS THE RIGHT DDB FOR THIS IORB?
	MOVE	T3,UDBDDB(U)	;NO, POINT AT USER
	CAMN	T2,TDVIOR##(T3)	;LAST CHANCE TO GET IT WRONG
	MOVEM	T3,TUBCUR(U)	;FOR TPMSIO TO FIND THE RIGHT JOB
TPONPJ:	TAPON			;ALLOW INTS
	POPJ	P,
;HERE WHEN UPPER LEVEL DECIDES THAT A SELECTED IORB IS NOT TO
;BE INITIATED AFTER ALL

TAPFLS::PUSHJ	P,TAPDSL	;FLUSH HDWRE
	PUSHJ	P,TAPREM	;AND PRUNE QUEUE
	PUSHJ	P,UUOLVL##	;IF NOT ON UUO LEVEL (DIAG)
	   POPJ	P,		; RETURN WITHOUT STARTING ANYTHING
	PUSH	P,T1		;SAVE T1
	PUSHJ	P,CKSIO1	;SEE IF SCHED CYCLE NEEDED
	MOVSI	T1,TKSSEL
	TDNN	T1,TUBSTS(U)	;DID THIS UNIT GET SELECTED AGAIN?
	TDON	T1,TKBSTS(W)	;NO, DID SOME OTHER UNIT GET SEL'D?
	JRST	TPOPJ##		;NO, RETURN
	TLNE	T1,TKSSTD	;YES, WAS IT ACTUALLY STARTED?
	JRST	TPOPJ##		;YES, RETURN
	PUSH	P,U		;NO, SAVE U
	MOVE	U,@KDBCUN(W)	;SET U TO RIGHT UNIT
	MOVSI	T1,TKSSEL	;NO UNIT IS ACTUALLY STARTED
	ANDCAM	T1,TUBSTS(U)	; NOW
	MOVSI	T1,TKSSCH	;INDICATE WE'RE REQUESTING A SCHEDULE
	IORM	T1,TKBSTS(W)	;FOR THIS KONTROLLER
IFN FTMP,<
	MOVEI	T1,TKBICT##	;CALL BACK ROUTINE
	MOVEM	T1,TKBFCT(W)	;ENSURE TAPSCH FINDS IT
> ;END IFN FTMP
	SETOM	@KDBCHN(W)	;MARK CHANNEL AVAILABLE
	MOVE	T1,KDBDSP(W)
	PUSHJ	P,@TPKSCH(T1)	;GO CAUSE AN INTERRUPT ON THAT UNIT
	POP	P,U		;RESTORE U
	JRST	TPOPJ##		;RESTORE T1 AND EXIT
SUBTTL NOTIFY OPERATOR OF KONTROLLERS WITH DOWN-LEVEL MICROCODE

;HERE TO TYPE OUT MESSAGE FOR WRONG MICROCODE
;CALL WITH T1/	MICROCODE VERSION,,[ASCIZ PREFIX STRING]
;OR WITH T1/	-1,,[ASCIZ TEXT]
TAPREV::PUSHJ	P,SAVT##	;SAVE ACS FOR OUR CALLERS
	PUSHJ	P,SAVE1##	;FREE UP A PRESERVED AC
	MOVE	P1,T1		;COPY TEXT POINTER THERE
	PUSHJ	P,CTYERM##	;SET FOR ERROR MESSAGE TO CTY
	PUSHJ	P,INLMES##	;TYPE MESSAGE
	ASCIZ	/
%Tape controller /		;SAY WHAT DEVICE
	MOVE	T2,KDBNAM(W)	;GET KON NAME
	PUSHJ	P,PRNAME##	;TELL OPERATOR
	PUSHJ	P,INLMES##	;FINISH THIS LINE
	ASCIZ	/ is below required microcode revision levels
% /				;AND FLAG THE NEXT LINE
	HRRZ	T1,P1		;RETRIEVE CALLER'S TEXT POINTER
	PUSHJ	P,CONMES##	;TYPE THAT TOO
	JUMPL	P1,TAPRV1	;JUMP IF VERSION NOT GIVEN
	PUSHJ	P,PRSPC##	;SPACE OVER FOR NUMBER
	HLRZ	T1,P1		;GET REVISION NUMBER
	PUSHJ	P,PRTDI8##	;TYPE THE NUMBER (OCTAL)
TAPRV1:	PJRST	CRLF##		;TYPE THE CRLF AND RETURN
SUBTTL ERROR RECOVERY PROCEDURE.

;CALL HERE FROM TPMDON WHEN ERROR RECOVERY IS TO BE INITIATED
;SELECT MUST BE ON FOR KDB AND UDB. IORB IN T1.

TAPERP::MOVSI	T1,(1B0)	;KDB IS NOT INVALID DURING ERROR PROCESSING
	ANDCAM	T1,TUBAKA(U)	;CLEAR FLAG
	MOVEI	T1,TKBERB(W)	;ENQUEUE ERP IORB
	HLLOS	TKBSTS(W)	;SET INFINITE QUANTUM
	PUSHJ	P,TAPRQH	;THIS IS CHEATING SLIGHTLY
	MOVE	T1,IRBLNK+TKBERB(W) ;RECOVER ORIGINAL IORB
	LDB	T2,PRBFCN	;GET FUNCTION CODE
	LDB	T3,ERYCNT	;GET INITIAL RETRY COUNT
	DPB	T3,TUYECT	;SET IN TUBERR
	SETZM	TUBTRY(U)	;CLEAR ATTEMPT COUNTER
	PUSHJ	P,ERPCMV	;CHECK IF TAPE MOVED
	  TDZA	T4,T4		;NO MOVE
	MOVNI	T4,1		;MOVED - ADJUST POSITION
	SKIPL	ERCTAB(T2)	;WAS OP FORWARD MOTION?
	MOVNS	T4		;NO - FIX COUNT
	ADD	T4,TUBREC(U)	;GET CORRECTED COUNT
	JUMPL	T4,[SETOM TUBPBE(U)	;IF NEG REC COUNT
		    JRST ERP0]	;THEN WE WON'T POS CHECK RIGHT
	HRL	T4,TUBFIL(U)	;ALSO COMPARE FILE POSITION
	MOVEM	T4,TUBPBE(U)	;STORE POSITION BEFORE ERROR
	JRST	ERP0		;START ERP

;HERE ON AN INTERRUPT WHILE THE ERP IS RUNNING

ERPINT:	CAIE	T1,TKBERB(W)	;BE REAL SURE ALL IS WELL
	STOPCD	.,STOP,ERF,	;++ERP REALLY FOULED UP
	HRRZ	T2,TUBERR(U)	;GET DISPATCH ADDR
	SKIPE	T2		;CODE ZERO IS ILLEGAL
	CAIL	T2,XERPMX	;SMALLER THAN MAX?
	STOPCD	.,STOP,RFU,	;++RECOVERY FOULED UP
	MOVE	F,TUBCUR(U)	;SET UP F AND S
	MOVE	S,DEVIOS(F)
	JRST	@ERPSTB(T2)	;THE LEAP OF FAITH
;HERE TO SEE IF ANYTHING OFFENSIVE HAS HAPPENED TO
;THE IORB IN T1

ANYERR:	SKIPL	TRBFNC(T1)	;IS EXCEPTION UP?
	POPJ	P,		;NO - ALL IS WELL.
	MOVE	T2,TRBSTS(T1)	;GET STATUS WORD
	TLNN	T2,RB.SER!RB.SED!RB.SLK!RB.SOL!RB.STL	;...
	POPJ	P,		;NOTHING TO OFFEND
	JRST	CPOPJ1##	;LOSE

;HERE TO COPY NEEDED THINGS TO THE ERP IORB

SETERB:	MOVE	T1,TKBERB+IRBLNK(W) ;GET OLD IORB

	MOVE	T3,TRBFNC(T1)	;GET MODE ETC
	TLZ	T3,RB.EXC	;CLEAR EXCEPTION BIT
	MOVEM	T3,TKBERB+TRBFNC(W)

	XMOVEI	T3,TKBERB+IRBCCW(W) ;ERP BASE ADDR OF CCW LIST
	MOVEM	T3,TKBERB+IRBACC(W) ;SAVE TEMPORARILY
	MOVEI	T3,IRBCCW(T1)	;IORB BASE ADDR OF CCW LIST
	SUB	T3,IRBACC(T1)	;COMPUTE OFFSET INTO LIST
IFN FTXMON,<HRRZS T3>		;AVOID XADDR DISASTER
	ADDM	T3,TKBERB+IRBACC(W) ;RELOCATE ERP CCW POINTER

	MOVSI	T3,IRBCCW(T1)	;POINT TO ADDRS OF CCWS
	HRRI	T3,TKBERB+IRBCCW(W) ;AND TO DESTINATION
	BLT	T3,TKBERB+IRBCCW+MXPORT-1(W) ;COPY

	MOVE	T3,TRBRCT(T1)	;COPY WORD COUNT
	MOVEM	T3,TRBRCT+TKBERB(W)

	POPJ	P,

;HERE TO SETUP ERP IORB FOR A NON DATA XFR OP

STERBS:	PUSHJ	P,SETERB	;SETUP ERB
	MOVEI	T1,1		;SET OP COUNT
	MOVEM	T1,@TKBERB+IRBACC(W) ;SET OP COUNT
	XMOVEI	T1,TKBERB(W)	;ERR REC IORB
	POPJ	P,

;HERE TO CLEAN UP WHEN LEAVING THE ERP FOR THE LAST TIME

ERPDON:	MOVE	T2,IRBLNK+TKBERB(W)	;PNTR TO ACTUAL IORB
	MOVE	T3,TRBRCT+TKBERB(W)	;LAST BYTE COUNT
	MOVEM	T3,TRBRCT(T2)	;INTO IORB
	PUSHJ	P,TAPREM	;REMOVE ERP IORB
	MOVE	T2,TUBERR(U)	;SAVE STATE WORD AT TERMINATION
	MOVEM	T2,TUBFES(U)	;AS FINAL ERROR STATE
	SETZM	TUBERR(U)	;CLEAR STATE INFO
	HLLZS	TKBSTS(W)	;CLEAR CHANNEL QUANTUM
	POPJ	P,
;ERP ACTION 0 - REPOSITION IF NEEDED. IORB IN T1
;FCN CODE IN T2

ERP0:	PUSHJ	P,ERPCMV	;CHECK MOVEMENT
	  JRST	ERP2		;NO - NO NEED TO REPOSITION
	MOVSI	T3,(ERCRPS)	;SHOULD REPOSITIONING BE DONE?
	TDNN	T3,ERCTAB(T2)	; ...
	JRST	ERP2		;NO - GO RETRY
	PUSHJ	P,STERBS	;YES - SETUP ERP IORB
	MOVEI	T4,RB.FBR	;ASSUME FORWARD MOTION, REPOS W/ BS
	SKIPL	ERCTAB(T2)	;WAS OP FORWARD MOTION?
	MOVEI	T4,RB.FSR	;NO-REPOS WITH FWD SPACE
	DPB	T4,PRBFCN	;STORE FUNCTION CODE
	MOVEI	T2,XERP1	;SET NEXT STATE

;HERE TO EXIT AND START IO ON ERP IORB

ERPX0:	HRRM	T2,TUBERR(U)	;STORE NEXT STATE
	MOVEI	T1,TKBERB(W)	;GET ERP IORB
	PUSHJ	P,TAPSIO	;GO INITIATE OP
	SETZ	T1,		;RETURN ZERO
	POPJ	P,

;HERE AT TERMINATION OF A REPOSITION

ERP1:	MOVSI	T2,RB.SNM!RB.SOL	;MOVED OR OFF-LINE?
	TDNN	T2,TRBSTS(T1)
	JRST	ERP1A		;SEE IF SPECIAL ACTION

;HERE WHEN RECOVERY HAS FAILED

ERP4:	MOVE	T3,IRBLNK+TKBERB(W)	;ORIG IORB
	MOVSI	T2,RB.SOL	;OFFLINE BIT
	TDNE	T2,TRBSTS(T1)	;CHECK STATUS
	IORM	T2,TRBSTS(T3)	;COPY BIT TO ORIG. IORB
	PUSHJ	P,ERPDON	;CLEAN UP
	HRRZ	T1,TUBQUE(U)	;GET FORMER IORB
	MOVSI	T2,RB.SER	;FLAG NON REC ERROR
	IORM	T2,TRBSTS(T1)	;IN  IORB
	POPJ	P,		;RETURN FAILING IORB

;SEE IF THE LAST OP SHOULD BE ERASED.

ERP1A:	MOVE	T1,IRBLNK+TKBERB(W)	;GET ORIGINAL OP
	LDB	T2,PRBFCN	; ...
	CAIE	T2,RB.FWT	;WRITE?
	CAIN	T2,RB.FTM	;OR WRITE TAPE MARK?
	JRST	ERP5		;YES
	JRST	ERP2		;NO - DO RETRY NOW.
;MORE ERROR RECOVERY PROCEDURE

;HERE TO RETRY A FAILING OPERATION. THE TAPE HAS BEEN
;REPOSITIONED IF NECESSARY

ERP2:	MOVE	T3,TUBPBE(U)	;GET POSITION BEFORE ERROR
	JUMPL	T3,ERP2A	;SKIP CHECK IF NEG.
	MOVE	T2,TUBREC(U)	;CHECK THAT REPOSITIONING
	HRL	T2,TUBFIL(U)	;HAS BROUGHT US
	CAME	T2,T3		;BACK TO BEFORE THE ERROR
	JRST	ERP4
ERP2A:	PUSHJ	P,SETERB	;COPY NEEDED GOODIES
	MOVE	T1,IRBLNK+TKBERB(W)	;GET OLD IORB
	LDB	T2,PRBFCN	;GET FUNCTION
	LDB	T3,ERYFCN	;GET FUNCTION TO RETRY
	MOVEI	T1,TKBERB(W)	;GET ERP IORB
	DPB	T3,PRBFCN	;STORE NEW OP.
	AOS	TUBTRY(U)	;INCREMENT ATTEMPT COUNT
	MOVEI	T2,XERP3	;SET NEXT STATE
	JRST	ERPX0

;HERE ON THE TERMINATION OF A RETRY OPERATION

ERP3:	PUSHJ	P,ANYERR	;ANY ERRORS TO SPEAK OF?
	  JRST	ERP3B		;NO - WIN WIN WIN
	MOVSI	T2,RB.SOL!RB.SER ;OFFLINE OR NON-RECOVERABLE
	TDNE	T2,TRBSTS(T1)	;???
	JRST	ERP4		;YES - LOSAGE
	LDB	T2,TUYECT	;GET RETRY COUNTER
	SUBI	T2,1		;DECREMENT
	JUMPLE	T2,ERP4		;SIGH - LOSE
	DPB	T2,TUYECT	;RESTORE COUNT
	MOVSI	T1,NXTLSE	;GET NEXT TO LAST RETRY BIT
	ANDCAM	T1,TUBERR(U)	;CLEAR IT
	CAIN	T2,2		;IS THIS NEXT TO LAST RETRY?
	IORM	T1,TUBERR(U)	;YES--THEN SET IT
	MOVE	T1,IRBLNK+TKBERB(W)	;GET OLD IORB
	LDB	T2,PRBFCN	;GET FUNCTION
	MOVE	T4,TUBTRY(U)	;RE-TRY COUNTER
	MOVSI	T3,(ERCTCS)	;THIS OP USE TCS?
	TDNE	T3,ERCTAB(T2)	;??
	TRNE	T4,3		;TCS EVERY 4 RE-TRIES
	JRST	ERP3A		;NO TCS NOW
	JRST	ERP7		;TCS TIME NOW
;HERE TO PERFORM REPOS/RETRY

ERP3A:	MOVEI	T1,TKBERB(W)	;SET UP ERROR IORB
	LDB	T2,PRBFCN	;AND FUNCTION
	JRST	ERP0		;AND TRY THE WHOLE THING AGAIN

;HERE WHEN THE ERROR IS RECOVERED. THE WORLD IS AMAZED.

ERP3B:	MOVSI	T2,RB.SNM	;CHECK MOVEMENT
	TDNE	T2,TRBSTS(T1)	;DID IT?
	JRST	ERP3C		;NO - TRY AGAIN
	PUSHJ	P,ERPDON	;GO CLEAN UP
	HRRZ	T1,TUBQUE(U)	;GET OLD IORB
	MOVSI	T2,RB.SRE	;RECOVERY SUCCEEDED
	IORM	T2,TRBSTS(T1)	;SET IN IORB
	POPJ	P,		;RETURN AND REJOICE

;HERE WHEN RETRY DIDN'T MOVE TAPE TRY AGAIN

ERP3C:	LDB	T2,TUYECT	;GET COUNTER
	SUBI	T2,1		;DECREMENT
	JUMPLE	T2,ERP4		;ALL THROUGH IF ZERO
	DPB	T2,TUYECT	;PUT COUNT BACK
	JRST	ERP2		;AND RETRY OP



;HERE TO DO AN ERG BEFORE RETRY

ERP5:	PUSHJ	P,STERBS	;SETUP ERB
	MOVEI	T2,RB.FLG	;GET ERG FUNCTION
	DPB	T2,PRBFCN	;STORE IN ERP IORB
	MOVEI	T2,XERP6	;NEXT STATE
	JRST	ERPX0		;EXIT AND SET STATE

;HERE AT THE TERMINATION OF AN OP BEFORE RETRY.

ERP6:	PUSHJ	P,ANYERR	;ERRORS?
	  JRST	ERP2		;NO - DO RETRY
	JRST	ERP4		;YES - FAIL
;ERROR RECOVERY TAPE CLEANER SEQUENCE (TCS)
;FCN CODE IN T2

;HERE TO STARTUP TCS

ERP7:	MOVEI	T1,TKBERB(W)	;GET RETRY IORB
	MOVSI	T3,RB.SBT!RB.SAP ;AT BOT OR ALREADY REPOSITIONED?
	TDNE	T3,TRBSTS(T1)	; ???
	JRST	ERP3A		;YES - NO TCS
	MOVEI	T4,ERPNCS	;NUMBER OF SPACE OPS IN TCS
	SKIPL	ERCTAB(T2)	;WAS OP FORWARD MOTION?
	SUBI	T4,1		;NO - USE ONE LESS BACKSPACE
	DPB	T4,TUYEC1	;STORE IN SECONDARY COUNTER

;HERE TO DO A BACKSPACE AS PART OF A TAPE CLEANER SEQ

ERP8:	PUSHJ	P,STERBS	;SETUP IORB
	MOVEI	T2,RB.FBR	;BACKSPACE FUNCTION
	DPB	T2,PRBFCN	;STORE
	MOVEI	T2,XERP9	;NEXT STATE
	JRST	ERPX0		;LEAVE

;HERE WHEN A TCS BACKSPACE HAS COMPLETED

ERP9:	LDB	T2,TUYEC1	;GET COUNT
	SOSL	T2		;DECREMENT
	DPB	T2,TUYEC1	;SAVE NEW VALUE
	HLRZ	T3,TRBSTS(T1)	;GET STATUS
	TRNE	T3,RB.SBT	;DONE?
	JRST	ERP9B		;YES - RECOMPUTE SPACE COUNT
	TRNE	T3,RB.SNM	;DID IT MOVE AT ALL
	JRST	ERP4		;NO -******SHOULD FIX POSITION****
	JUMPG	T2,ERP8		;TRY AGAIN IF STILL MORE TO GO
	MOVEI	T4,ERPNCS-1	;NUMBER OF SPACE OPS
	JRST	ERP9C

ERP9B:	MOVEI	T4,ERPNCS-2	;COMPUTE NUMBER OF SPACE OPS
	LDB	T2,TUYEC1	; ...
	SUB	T4,T2		; ...
ERP9C:	MOVE	T1,IRBLNK+TKBERB(W)	;GET ORIG IORB
	LDB	T2,PRBFCN	;ORIG FCN
	MOVEI	T1,TKBERB(W)	;RESTORE ERROR IORB
	SKIPL	ERCTAB(T2)	;WAS OP FORWARD MOTION?
	ADDI	T4,1		;NO - ONE MORE THEN
	DPB	T4,TUYEC1	;STORE COUNT
	JUMPLE	T4,ERP2		;NONE - GO TRY AGAIN

;FALL THROUGH INTO ERP10.
;FALL THROUGH HERE FROM ERP9C.
;HERE TO DO A SPACE RECORD AS PART OF A TCS

ERP10:	PUSHJ	P,STERBS	;SETUP IORB
	MOVEI	T2,RB.FSR	;SPACE OP
	DPB	T2,PRBFCN	;STORE
	MOVEI	T2,XERP11	;NEXT STATE
	JRST	ERPX0		;START IO AND EXIT

;HERE ON THE TERMINATION OF A TCS SPACE OP

ERP11:	HLRZ	T2,TRBSTS(T1)	;GET STATUS
	TRNE	T2,RB.SNM	;MOVED?
	JRST	ERP4		;NO ****SHOULD FIX POSN****
	LDB	T2,TUYEC1	;GET COUNT
	SUBI	T2,1		;DECREMENT
	DPB	T2,TUYEC1	;RESTORE
	JUMPLE	T2,ERP2		;CONTINUE RETRY
	JRST	ERP10		;MORE SPACES TO DO

;TABLE RELATING STATE CODES TO ROUTINE ADDRS.

ERPSTB:	PHASE	0
	XWD	0,-1		;ILLEGAL
XERP1:!	IFIW	ERP1		;REPOSITION TERMINATION
XERP3:!	IFIW	ERP3		;RETRY TERMINATION
XERP6:!	IFIW	ERP6		;ERASE GAP TERMINATION
XERP9:!	IFIW	ERP9		;TCS BACKSPACE TERMINATION
XERP11:!IFIW	ERP11		;TCS SPACE TERMINATION
XERPMX:!			;HIGHEST STATE
	DEPHASE
;TABLE USED TO GUIDE THE ERROR RECOVERY PROCEDURE

;BITS
ERCFWD==1B0	;OPERATION MOVES TAPE FOREWARD
ERCRPS==1B1	;OPERATION NEEDS REPOSITION BEFORE RETRY
ERCCSZ==1B2	;THIS OP IGNORE "NOISE" RECORDS
ERCTCS==1B3	;THIS OP USES TCS

;BYTES AND BYTE POINTERS
ERCCTP==^D17		;INITIAL RETRY COUNT POSITION
ERCCTS==6		;INITIAL RETRY COUNT SIZE
ERYCNT:	POINT	ERCCTS,ERCTAB(T2),ERCCTP

ERCFNP==^D35		;RETRY FUNCTION POSITION
ERCFNS==5		;RETRY FUNCTION SIZE
ERYFCN:	POINT	ERCFNS,ERCTAB(T2),ERCFNP

;MACRO TO BUILD ERCTAB ENTRY

DEFINE X(FLGS,CNT,FCN) <
	EXP	FLGS+<CNT>B<ERCCTP>+<FCN>B<ERCFNP>
>

;THE TABLE
ERCTAB:	EXP	-1		;ILLEGAL
	X(ERCFWD+ERCRPS+ERCCSZ+ERCTCS,^D40,RB.FCR) ;READ FORWARD
	X(ERCFWD+ERCRPS,^D14,RB.FWT)		;WRITE
	X(ERCRPS+ERCCSZ+ERCTCS,^D40,RB.FRB)	;READ BACKWARDS
	X(ERCFWD+ERCRPS,6,RB.FSR)		;SKIP RECORD
	X(ERCRPS,6,RB.FBR)			;BACKSPACE RECORD
	X(ERCFWD+ERCRPS,6,RB.FSF)		;SKIP FILE
	X(ERCRPS,6,RB.FBF)			;BACKSPACE FILE
	X(ERCFWD,6,RB.FLG)			;ERASE GAP
	X(ERCFWD,6,RB.FSE)			;DATA SECURITY ERASE
	X(0,6,RB.FRW)				;REWIND
	X(0,6,RB.FRU)				;REWIND AND UNLOAD
	X(ERCFWD+ERCRPS,^D14,RB.FTM)		;TAPE MARK
	X(ERCFWD,1,RB.FYB)			;YELLOW BALL
	X(ERCFWD+ERCRPS+ERCCSZ,6,RB.FCR)	;CORRECTION READ (ONLY IF USER REQD)
	X(ERCFWD+ERCRPS+ERCCSZ+ERCTCS,^D40,RB.FCR) ;READ LOW THRESHOLD
;ROUTINE CHECK IF TAPE MOVED OR ERROR ON NOISE RECORD
;CALL:	MOVE	T1,IORB
;	MOVE	T2,FCN
;	PUSHJ	P,ERPCMV
;	  RETURN IF NO MOVEMENT
;	RETURN IF MOVEMENT

ERPCMV:	MOVE	T4,TRBRCT(T1)	;GET BYTE COUNT
	MOVSI	T3,(ERCCSZ)	;SIZE CHECK?
	TDNN	T3,ERCTAB(T2)	;...
	JRST	ERPCM1		;NO - PROCEED
	CAIGE	T4,NOISE##	;IS THIS A NOISE RECORD
	JUMPGE	T4,CPOPJ##	;NOISE IF NOT AN IOWD - DIDN'T MOVE
ERPCM1:	MOVSI	T3,RB.SNM!RB.SAP ;NO - CHECK PHYSICAL MOVEMENT
	TDNN	T3,TRBSTS(T1)	;...
	AOS	0(P)		;OK
	POPJ	P,		;RETURN
SUBTTL COMMON ROUTINES

;ROUTINES COMMON TO ALL KONTROLLER DEPENDENT CODE

;HERE ON REWIND DONE

REWDON::MOVSI	T2,TUSREW!TUSFLT ;NO LONGER REWINDING
	ANDCAM	T2,TUBSTS(U)	;...
UNIBOT::MOVSI	T2,TUSBOT	;ALSO NOW AT BOT
	IORM	T2,TUBSTS(U)	; ...
	SETZM	TUBREC(U)	; ...
	SETZM	TUBFIL(U)	; ...
	PJRST	TAPCTM		;CLEAR HUNG TIMER AND RETURN

;HERE TO VERIFY THAT TUBQUE POINTS TO A REASONABLE IORB

CHKIRB::HRRZ	T1,TUBQUE(U)	;GET HEAD IORB
	JUMPE	T1,CPOPJ##	;EXIT IF NOT REQUEST BLOCK
	LDB	T2,PRBRQS	;GET REQUEST STATUS
	CAIN	T2,RB.ACT	;ACTIVE?
	AOS	(P)		;YES - GIVE GOOD RETURN
	POPJ	P,

;ROUTINE TO HANDLE SPURIOUS INTERRUPTS

TAPDIS::MOVE	T1,KDBDSP(W)	;GET DISPATCH
	PUSHJ	P,@TPKIDL(T1)	;SET CTRL IDLE (USER WILL GET HUNG DEVICE)
	MOVSI	T2,TKSSCH	;TEST SCHED REG. BIT
	MOVEI	T1,0		;ASSUME IGNORE INT.
	TDNE	T2,TKBSTS(W)	;WANT SCHED. CYCLE?
	MOVNI	T1,1		;YES- INFORM WORLD
	POPJ	P,		; AND EXIT


;ROUTINE TO SET UP U TO POINT TO UDB MENTIONED IN T2
;C(T2) = UNIT # , C(W) = KDB ADDRS
;NON-SKIP RETURN IF NON-EX UNIT
;SKIP RETURN IF UNIT IS OK

SETUDB::MOVE	U,KDBIUN(W)	;POINTER TO UDB TABLE
	ADD	U,T2		;PLUS OFFSET
	SKIPN	U,0(U)		;SEE IF ONE EXISTS
	POPJ	P,
	PJRST	CPOPJ1##

;TABLE USED TO CONVERT IORB MODE TO CHARS/WORD

TMODTB::0		;0 - ILLEGAL
	5		;1 - 9-TK CORE DUMP
	4		;2 - 9-TK INDUSTRY COMPAT.
	6		;3 - TU70 SIXBIT
	5		;4 - TU70 MARVELOUS ASCII
	6		;5 - 7-TK CORE DUMP

;TABLE TO CONVERT IORB MODE TO FRAME SIZE

TMODFS::0	;0 - ILLEGAL
	0	;1 - CORE DUMP (N/A)
	8	;2 - 9 TK INDUSTRY COMPATIBLE
	6	;3 - SIXBIT
	7	;4 - ASCII
	0	;5 - 7 TK CORE DUMP (N/A)


	LIT

	$LOW

IFN FTMP,<
CPUTPQ::BLOCK	1	;COUNT OF CPUS WAITING FOR SWEEP
>

TPSEND:	END