Google
 

Trailing-Edge - PDP-10 Archives - BB-X140B-BB_1986 - 10,7/703mon/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  V1037
SUBTTL  L.BOSACK/TAH/TW  5 MAR 86
	SEARCH	F,S
IFE FTKS10,<SEARCH ICHPRM>
	$RELOC
	$HIGH



;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
.CPYRT<1974,1986>
;COPYRIGHT (C) 1974,1975,1976,1977,1978,1979,1980,1982,1984,1986
;BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
;ALL RIGHTS RESERVED.


;
;
XP VTAPSR,1037			;FOR LINKEDIT MAP
	SALL

TAPSER::	ENTRY	TAPSER
;BIT DEFINITIONS
;IN TKBSTS
;BITS 0-2 = CPU NUMBER WHICH "OWNS" CONTROL

;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==(17B10)		;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

;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
TPKINI==:0		;INITIALIZATION CODE
TPKRES==:1		;RESET ACTIVE TRANSFER
TPKSIO==:2		;START IO
TPKINT==:3		;INTERRUPT ROUTINE
TPKCMD==:4		;SET DEVICE COMMAND IN LIST (DX10 ONLY)
TPKIDL==:5		;SET DEVICE IDLE
TPKONL==:6		;SKIP IF KON ONLINE
TPKSCH==:7		;CAUSE SCHEDULE CYCLE
TPKINX==:10		;INITIALIZATION CODE AFTER SYSTEM STARTUP
			;(WHEN KONTROLLER COMES ONLINE, ETC.)
TPKLOD==:11		;LOAD MICROCODE
TPKEDL==:12		;ENABLE/DISABLE MICROCODE LOADING
TPKCFG==:13		;AUTO-CONFIG. ONLY NEEDED FOR TAPES WITH SUBUNITS
			;EG TM02, TM78

;BYTE POINTERS

;INTO KDB

TKYCPU:: POINT	3,TKBSTS##(W),2

;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,TRBLNK(T1),RB.RQP		;REQUEST STATUS
PRBMOD:: POINT RB.MDS,TRBLNK(T1),RB.MDP		;REQUEST MODE
PRBMD2:: POINT RB.MDS,TRBLNK(P1),RB.MDP
PRBFCN:: POINT RB.FNS,TRBLNK(T1),RB.FNP		;REQUEST FUNCTION
PRBDEN:: POINT RB.DNS,TRBLNK(T1),RB.DNP		;DENSITY
PRBBYT:: POINT RB.BYS,TRBLNK(T1),RB.BYP		;REQUEST INFO
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::
IFE	FTKS10,<
	SKIPG	TKBUNI##(W)	;MULTI UNITS ON AN RH?
	JRST	TAPIN2		;NO, CARRY ON
	XCT	TKBCIS##(W)	;YES. CONI
	TRNE	T2,CI.DON	;IF DONE IS ON
	JRST	TAPIN2		; THE INTERRUPT IS FROM THIS KDB
	MOVSI	T2,(.DIASR)	;ATTEN INTERRUPT. READ ATTN SUMMARY
	XCT	TKBDOS##(W)	; REGISTER
	IMULI	P,1
	IMULI	P,1		;STALL IN CASE AN RH10
	XCT	TKBDIS##(W)	;READ ATTENTION BITS
	ANDI	T2,377		;GET JUST ATTENTION BITS
	MOVE	T1,W		;SAVE STARTING KDB ADDR
TAPIN1:	TDNE	T2,TKBUNI##(W)	;INTERRUPT FOR THIS KDB?
	JRST	TAPIN2		;YES. HANDLE IT
	HLR	W,TKBCDB##(W)	;NO, STEP TO NEXT KDB ON CONTROLLER
	CAME	T1,W		;BACK WHERE WE STARTED?
	JRST	TAPIN1		;NO, SEE IF INTERRUPT FOR THIS KDB
	HRLI	T2,(.DIASR!DO.LDR!DO.DRE) ;YES, NOT FOR ANY KDB WE KNOW OF
	XCT	TKBDOS##(W)	; SO CLEAR THE INTERRUPT
	POPJ	P,		;AND GO AWAY
>
TAPIN2:	HRRZ	T1,TKBDSP##(W)	;GET XFER VECTOR
	PUSHJ	P,@TPKINT(T1)	;CALL DEV DEP INT RTN
TAPAGO::JUMPE	T1,CPOPJ##	;IF ZERO, ANOTHER INT IS COMING
	JUMPL	T1,TPINT1	;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,TPINT1	;NO COMPLETION
	MOVEI	T2,RB.DUN	;MARK IORB AS DONE
	DPB	T2,PRBRQS	;...
	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
TPINT1:	SETZB	S,F		;MAKE SURE IOSCP2 IS OFF
	SETZM	TKBTIM##(W)	;CLEAR HUNG TIMER
	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::
	HRRZ	T1,TKBDSP##(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
	HRRZ	T1,TKBCDB##(W)	;LOC OF CHANNEL DATA BLOCK
	SETOM	(T1)		;INDICATE THAT CHAN 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:	PUSH	P,W		;SAVE INITIAL KDB
	HRRZS	(P)		;SAVING ONLY RIGHT HALF
	MOVE	T4,TKBCUN##(W)	;GET CURRENT AOBJN POINTER
	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
	AOBJP	T4,TPSCH2	;AND START LOOKING AT NEXT UNIT
TPSCH1:	SKIPN	U,(T4)		;GET UDB
	JRST	TPSCHN		;NONE PRESENT
	HRRZ	T1,TUBQUE##(U)	;GET QUEUE POINTER
	JUMPE	T1,TPSCHN	;GO IF NONE
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,TRBLNK(T1)	;GET IORB FLAGS
	TLNN	T2,TKSCHE	;QUEUED REQUEST ON UDB?
	TLNN	T3,RB.PCL	;YES, QUEUED IORB REQUEST?
	TDCA	T2,S		;NO. 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
	AOBJN	T4,TPSCH1	;NEXT UNIT
TPSCH2:	MOVSI	T2,TKSSCH
	ANDCAM	T2,TKBSTS##(W)
TPSC2A:	HLRZ	W,TKBCDB##(W)	;STEP TO NEXT KDB ON THIS CHANNEL
	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
	IORM	T2,TKBSTS##(W)
	MOVE	T4,TKBIUN##(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,TRBLNK(T1)	;?
	PUSHJ	P,UUOLVL##	;YES, ARE WE AT UUO LEVEL?
	  JRST	TPSCH4		;AT INTERRUPT LEVEL OR NOT SPECIAL
	PUSH	P,F		;SAVE F
	HRRZ	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
	HRRZ	T2,TKBDSP##(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,TKBCUN##(W)	;STORE CURRENT UNIT
	MOVSI	T2,TKSSEL	;FLAG AS SELECTED
	IORM	T2,TKBSTS##(W)	;IN KDB
	IORM	T2,TUBSTS##(U)	;AND IN UDB
	HRRZ	T2,TKBCDB##(W)	;LOC OF CHAN DATA BLOCK
	AOS	(T2)		;INDICATE THAT CHAN IS BUSY
	MOVEI	T2,MQUANT##	;SET SLICE
	HRRM	T2,TKBSTS##(W)	; ..
	HLRZ	T3,TUBADR##(U)	;GET KON1 ADR
	HLRZ	T2,TUBKDB##(U)	;SETUP CURRENT KON AND ADR
	CAIE	T2,(W)		; ...
	HRRZ	T3,TUBADR##(U)	; ...
	HRLM	T3,TUBAKA##(U)	;STORE UNIT ADDR
	HRRM	W,TUBAKA##(U)	;STORE 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
	POPJ	P,		;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::MOVE	T2,TKBSTS##(W)	;KONTROLLER STATUS
	TAPOFF			;GUARD AGAINST
	TLNE	T2,TKSOFL	;CONTROLLER INTERRUPTS
	JRST	TPONPJ		;WHICH SET CONTROLLER OFFLINE
	TLOE	T2,TKSSEL	;SELECTED?
	TLZE	T2,TKSSTD	;AND NOT STARTED
	STOPCD	TAPSI3,DEBUG,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,DEBUG,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
	MOVSI	T2,TKSSTD	;MARK AS STARTED
	IORM	T2,TKBSTS##(W)	;IN KDB
	IORM	T2,TUBSTS##(U)	;AND UDB
	MOVE	T2,TUBITM##(U)	;SET UP TIMER
	MOVEM	T2,TUBTIM##(U)	; FOR MISSED REWIND-DONE INTERRUPT
	HRRZ	T2,TKBDSP##(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
	DPB	T2,TDYHNG##	;ONLY TRY ONCE
	MOVEM	T2,TKBTIM##(W)	;LIMIT FOR TAPSEC
	POPJ	P,		;GIVE FORCED DEVHNG RETURN
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. (IT IS UNLIKELY ANYTHING WILL
;				BE THERE, BUT IN CASE..)

CHKKON:	SKIPN	W,CNFMTK##	;GET FIRST KDB
	JRST	CPOPJ1##	;NO TAPES
CHKKNL:	MOVSI	T1,TKSMNT	;BIT TO TEST
IFN FTMP,<
	LDB	T2,TKYCPU	;IF ON DIFFERENT CPU
	CAMN	T2,.CPCPN##	; DONT CHECK ON THIS ONE
>
	TDNE	T1,TKBSTS##(W)	;  TOO SEE IF WE SHOULD CHECK
	JRST	CHKNXT		;  IF HE IS ONLINE
	HRRZ	T1,TKBDSP##(W)	;GET XFR VECTOR
	PUSHJ	P,@TPKONL(T1)	;SEE IF ONLINE
	  JRST	CHKOFL		;NOT ON LINE - COMPLAIN
	MOVSI	T1,TKSOFL	;NOW ONLINE - SEE IF
	TDNN	T1,TKBSTS##(W)	;IF FORMERLY OFFLINE.
	JRST	CHKNXT		;NO
	TLO	T1,TKSSCH!TKSSTD!TKSSEL!TKSSIL!IFN FTMP,<TKSCHE!TKSCHA>
				;CLR THESE ALSO
	ANDCAM	T1,TKBSTS##(W)	;CLEAR OFFLINE
	MOVSI	T2,TKSSTD!TKSSEL!IFN FTMP,<TKSCHE!TKSCHA> ;BITS TO CLEAR IN UDB
	MOVE	T3,TKBIUN##(W)	;GET AOBJN PNTR TO LIST
CHKUNL:	SKIPE	U,0(T3)		;UNIT PRESENT?
	ANDCAM	T2,TUBSTS##(U)	;YES - CLEAR BITS
	AOBJN	T3,CHKUNL	;LOOP TILL DONE
	HRRZ	T1,TKBDSP##(W)	;YES - GET XFR VECTOR
	PUSHJ	P,@TPKINX(T1)	;CALL TO INITIALIZE
IFN FTMP,<
	SETZB	S,F
>
	PUSHJ	P,CKSIO2	;CLANK SCHED FOR THIS KON.
CHKNXT:
IFN FTMP,<
	MOVEI	T1,TKBICT##
	SKIPGE	TKBFCT##(W)
	MOVEM	T1,TKBFCT##(W)	;ABJECT PARANOIA
>
	HRRZ	W,TKBKDB##(W)	;GET NEXT KDB
	JUMPN	W,CHKKNL	;IF THE IS ONE, CHECK IT.
	PJRST	CPOPJ1##	;ELSE SKIP OVER ARG

CHKOFL:
IFN FTAUTC,<
	MOVE	T1,TKBIUN##(W)	;IF A KON BUT NO UNITS
	SKIPN	(T1)
	AOBJP	T1,CHKNXT	;DON'T COMPLAIN ABOUT IT
>
	PUSHJ	P,@0(P)		;CALL CO-ROUTINE
	JRST	CHKNXT		;CONTINUE LOOP
;ONCE A SECOND - JUST CHECK AND UPDATE INFO

TAPSEC::PUSHJ	P,CHKKON	;CRAWL THROUGH DATA BASE
	  JRST	SETOFL		;WHERE TO GO FOR OFF-LINE CTL
	SKIPN	W,CNFMTK##	;START AT FIRST KONTROLLER
	POPJ	P,		;NO TAPES
TAPSE1:
IFN FTMP,<
	LDB	T1,TKYCPU	;IF ON DIFFERENT CPU
	CAME	T1,.CPCPN##	; DONT DECR. TIMER ON THIS ONE
	JRST	TAPSE5
>
	SOSE	TKBTIM##(W)	;HUNG MTAPE?
	JRST	TAPSE3		;NO
	MOVE	U,TKBCUN##(W)	;GET RIGHT UNIT
	MOVE	U,(U)
	MOVSI	T1,TUSFLT	;FAULTY DRIVE BIT
	TDNE	T1,TUBSTS##(U)	;DID THE DRIVE GO BAD?
	 JRST	TAPSE2		;YES, IGNORE SPECIAL CASE FOR SCHEDULE
	MOVSI	T1,TKSSCH	;HUNG TRYING TO FORCE A SCHEDULE?
	TDNN	T1,TKBSTS##(W)	;...
	JRST	TAPSE2		;NO, MUST BE A REAL TAPE OPERATION
	HRRZ	T1,TKBDSP##(W)	;YES, TRY AGAIN
	PUSHJ	P,@TPKSCH(T1)	;BY REQUESTING ANOTHER INTERRUPT
	JRST	TAPSE5		;LOOK TO NEXT KONTROLLER
TAPSE2:	MOVE	T1,TUBSTS##(U)
	TLNN	T1,TKSSEL	;IS UNIT SELECTED?
	JRST	TAPSE5		;NO, BETTER HUNG THAN STOPCD
	MOVE	F,TUBCUR##(U)	;YES, SET SO DEVCHK WILL CATCH IT
	MOVEI	T1,1
	DPB	T1,PDVCNT##
	MOVEI	S,IOACT
	IORM	S,DEVIOS(F)
TAPSE3:	MOVE	T1,TKBIUN##(W)	;SET TO LOOK AT ALL UNITS ON KDB
	MOVSI	T2,TUSREW	;BITS TO CLEAR
TAPSE4:	SKIPE	U,(T1)		;UDB EXIST?
	SOSE	TUBTIM##(U)	;YES, TIMER GONE TO 0?
	CAIA			;NO
	ANDCAM	T2,TUBSTS##(U)	;YES, CLEAR UNIT-IS-REWINDING (MISSED AN INTERRUPT)
	AOBJN	T1,TAPSE4	;LOOP FOR ALL UNITS ON KDB
TAPSE5:	HRRZ	W,TKBKDB##(W)	;STEP TO NEXT KONTROLLER
	JUMPN	W,TAPSE1
	POPJ	P,		;DONE - RETURN

SETOFL:	MOVSI	T1,TKSOFL
	TDNE	T1,TKBSTS##(W)	;ON LINE?
	POPJ	P,		;NO - JUST RETURN
	HRRZ	T1,TKBDSP##(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,CNFMTK##	;YES, 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
	LDB	T1,TKYCPU	;IS THE KDB ON THIS CPU?
	CAMN	T1,.CPCPN##
	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,TKBIUN##(W)	;START AT THE BEGINNING
	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:	AOBJN	T2,TAPT1A	;YES, TRY NEXT
	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,TRBLNK(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:	HRRZ	W,TKBKDB##(W)	;STEP TO NEXT KDB
	JUMPN	W,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,CNFMTK##	;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,TKBIUN##(W)	;START AT 1ST UDB
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:	AOBJN	T3,TAPTI6
	SYSPIN
TAPTI8:	HRRZ	W,TKBKDB##(W)	;STEP TO NEXT KDB
	JUMPN	W,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 HUNG DEVICE

TAPHNG::TAPOFF			;WHO KNOWS WHAT EVIL...
	HRRZ	W,TUBAKA##(U)	;GET CURRENT KDB
	HRRZ	T2,TKBDSP##(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	;CLEAR STARTED
	ANDCAM	T1,TUBSTS##(U)	;IN UDB
	TLO	T1,TKSSCH	;ALSO CLEAR SCH
	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
>
				;PRUNE QUEUE, DESELECT,PION

TAPKIL::TAPOFF			;FIGHT RACE
	HRRZ	W,TUBAKA##(U)	;GET KONTROLLER
	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
	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

	HLRZ	W,TUBKDB##(U)	;CHECK ONE KONT
	PUSHJ	P,CKSIO1	; ...
	JUMPN	W,CPOPJ##
	HRRZ	W,TUBKDB##(U)	;AND THEN THE OTHER
				;FALL INTO CKSIO1
CKSIO1:	JUMPE	W,CPOPJ##	;IF NONE, PUNT
IFN FTMP,<
	LDB	T2,TKYCPU	;CPU WHICH OWNS CONTROL
	DPB	T2,DEYCPF##	;LET REST OF WORLD KNOW
	PUSHJ	P,CHKCPI##	;ARE WE ON THAT CPU?
	  PJRST	PCLTAP		;NO, QUEUE THE REQUEST
>
	TLZ	S,IOSCP2	;MAKE SURE IOSCP2 IS OFF
CKSIO2:	HRRZ	T3,TKBCDB##(W)	;POINT AT CHAN DATA BLOCK
	MOVSI	T2,TKSSEL!TKSSCH!TKSOFL
	SYSPIF
	SKIPGE	(T3)		;IS CHAN 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	(T3)		;INDICATE THAT CHAN 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
	JRST	FUPOPJ##	;RESTORE F,U,AND RETURN

CKSIO3:	MOVE	U,(P)		;RESTORE U
CKSIO4:	HRRZ	T1,TKBCDB##(W)
	SOS	(T1)		;INDICATE CHAN IS NOT BUSY
	JRST	TPOPJ##		;MAKE STACK RIGHT AND RETURN
IFN FTMP,<
PCLTAP:	HRRZ	T1,TUBQUE##(U)	;GET IORB ADDRESS
	JUMPE	T1,CPOPJ##	;NONE??????
	MOVE	T1,TRBLNK(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:	PUSHJ	P,CPUOFS##	;GET CPU WHICH OWNS THE DEVICE
	SKIPG	.C0OK##(T1)	;IS THAT CPU ALIVE AND WELL?
	JRST	PCLTP2		;YES
	PUSHJ	P,HNGSTP##	;NO, "PROBLEM ON DEVICE"
	JRST	PCLTAP		;AND TRY, TRY AGAIN
PCLTP2:	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	SETQPB##	;RING THE DOORBELL ON THE OWNING CPU

;SUBROUTINE TO GET JOB ONTO THE CPU WHICH OWNS THE DEVICE
TOCPU::	HLRZ	W,TUBKDB##(U)
	SKIPN	W
	HRRZ	W,TUBKDB##(U)
	PUSH	P,T1
TOCPU1::LDB	T1,TKYCPU	;CPU WHICH OWNS THE DEVICE
	PUSHJ	P,ONCPUS##	;GET US ONTO THAT CPU
	  JRST	[PUSHJ P,HNGSTP##
		 JRST TOCPU1]
	JRST	TPOPJ##		;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,TOCPU
>
TAPRQH::MOVEI	T2,RB.RPN	;SET IORB TO PENDING
	DPB	T2,PRBRQS	; ...
	TAPOFF			;FIGHT RACE
	HRRZ	T2,TUBQUE##(U)	;GET HEAD POINTER
	HRRM	T2,TRBLNK(T1)	;FORWARD LINK
	HRRM	T1,TUBQUE##(U)	;NEW HEAD
	PUSHJ	P,TAPCNT	;SEE IF THE CHANNEL NEEDED TO BE POKED
	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,TOCPU
>
TAPRQT::MOVEI	T2,RB.RPN	;SET IORB AS PENDING
	DPB	T2,PRBRQS	; ...
	TAPOFF			;FIGHT RACE
	HLRZ	T2,TUBQUE##(U)	;GET TAIL POINTER
	HRRM	T1,TRBLNK(T2)	;FORWARD LINK AT OLD TAIL
	HRLM	T1,TUBQUE##(U)	;NEW TAIL POINTER
	PUSHJ	P,TAPCNT
	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
	HRRZ	T2,TRBLNK(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	TPONPJ		;NO - LEAVE
	MOVSI	T2,TUBQUE##(U)	;MAKE TAIL POINT TO QUEUE  HEAD
	MOVEM	T2,TUBQUE##(U)	; ...
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,TKBCUN##(W)
	MOVE	U,(U)		;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
	MOVE	T1,[TKBICT##,,TAPTIM##]
	HRRZM	T1,TKBTIM##(W)	;SET TIMER TO CATCH LOST INTERRUPT
IFN FTMP,<
	HLRZM	T1,TKBFCT##(W)	;ENSURE TAPSCH FINDS IT
>
	MOVE	T1,TKBCDB##(W)	;MARK CHAN AVAILABLE
	SETOM	(T1)
	HRRZ	T1,TKBDSP##(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
	PUSH	P,U		;SAVE UNIT
	PUSH	P,F		;SAVE FROM DESTRUCTION
	PUSHJ	P,TTYERO##	;SET UP U WITH OPR LDB USUALLY
	PUSHJ	P,INLMES##	;TYPE MESSAGE
	ASCIZ	/
%Tape controller /		;SAY WHAT DEVICE
	MOVE	T2,TKBNAM##(W)	;GET KON NAME
	ADD	T2,['A'-'0',,0]	;EXTERNALIZE IT
	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,-4(P)	;RETRIEVE CALLER'S TEXT POINTER
	PUSHJ	P,CONMES##	;TYPE THAT TOO
	SKIPGE	-4(P)		;SEE IF VERSION GIVEN
	JRST	TAPRV1		;NO, SKIP THIS
	PUSHJ	P,PRSPC##	;SPACE OVER FOR NUMBER
	HLRZ	T1,-4(P)	;GET REVISION NUMBER
	PUSHJ	P,PRTDI8##	;TYPE THE NUMBER (OCTAL)
TAPRV1:	PUSHJ	P,CRLF##	;TYPE THE CRLF
	JRST	FUPOPJ##	;RETURN, RESTORING ALL ACS USED
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::MOVEI	T1,TKBERB##(W)	;ENQUEUE ERP IORB
	HLLOS	TKBSTS##(W)	;SET INFINITE QUANTUM
	PUSHJ	P,TAPRQH	;THIS IS CHEATING SLIGHTLY
	HRRZ	T1,TRBLNK+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	TRBLNK(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:	HRRZ	T1,TRBLNK+TKBERB##(W)	;GET OLD IORB
	HLLZ	T3,TRBLNK(T1)	;GET MODE ETC
	TLZ	T3,RB.EXC	;CLEAR EXCEPTION BIT
	HLLM	T3,TRBLNK+TKBERB##(W)	;STORE IN NEW IORB
	HRRZ	T3,TRBXCW(T1)	;GET XFR LIST ADDR
	MOVEM	T3,TRBXCW+TKBERB##(W)	;STORE
	MOVE	T3,TRBRCT(T1)	;COPY WORDCOUNT (FOR TM02)
	MOVEM	T3,TRBRCT+TKBERB##(W)
	HLLZ	T3,TRBEXL(T1)	;GET END OF XFR LIST
	MOVEM	T3,TRBEXL+TKBERB##(W)	;STORE
	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
	HRRM	T1,TRBXCW+TKBERB##(W)	;...
	HRRZS	TRBEXL+TKBERB##(W)	;ALSO CLEAR END OF LIST
	MOVEI	T1,TKBERB##(W)	;ERR REC IORB
	POPJ	P,

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

ERPDON:	HRRZ	T2,TRBLNK+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:	HRRZ	T3,TRBLNK+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:	HRRZ	T1,TRBLNK+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
	HRRZ	T1,TRBLNK+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
	HRRZ	T1,TRBLNK+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:	HRRZ	T1,TRBLNK+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
	0,,-1		;ILLEGAL
XERP1:!	ERP1		;REPOSITION TERMINATION
XERP3:!	ERP3		;RETRY TERMINATION
XERP6:!	ERP6		;ERASE GAP TERMINATION
XERP9:!	ERP9		;TCS BACKSPACE TERMINATION
XERP11:! 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)	; ...
	POPJ	P,

;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::HRRZ	T1,TKBDSP##(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 LF NON-EX UNIT
;SKIP RETURN IF UNIT IS OK

SETUDB::MOVEI	U,TKBUDB##(W)	;BASE ADDRS
	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)

TPSEND:	END