Trailing-Edge
-
PDP-10 Archives
-
tops10_703_distr_bb-x140b-sb
-
10,7/703anf/dncdup.p11
There are 3 other files named dncdup.p11 in the archive. Click here to see a list.
.SBTTL DNCDUP - DUP11 ROUTINES 27 MAY 82
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
; OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1976,1977,1978,1979,1980,1981,1984 BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
VRCDDP=012 ;FILE EDIT NUMBER
.SBTTL DUP11 ROUTINES
.IF NE FTDUP11 ;IF DUP SYNCHRONOUS LINES ARE PRESENT
;NOTES ON THE TRANSMIT AND RECEIVE INTERRUPT SERVICE ROUTINES
;DATA INTERRUPTS REALLY CONSIST OF 2 PHASES, SUPPLYING THE NEXT
;CHARACTER AND SETTING UP THE NEXT BUFFER. SINCE THE LATTER MAY TAKE
;SO MUCH TIME THAT DATA INTERRUPTS ARE LOCKED OUT, BOTH THESE ISR'S
;EMPLOY A TECHNIQUE TO GET AROUND THESE PROBLEMS. ONLY THE TRANSMITTER
;IS DESCRIBED HERE AS THE RECEIVER IS ESSETIALLY IDENTICAL.
;THE FIRST THING THE TRANSMIT INTERRUPT ROUTINE DOES IS TO SUPPLY
;THE HARDWARE WITH THE NEXT CHARACTER TO OUTPUT. IF THERE ARE MORE
;CHARACTERS WAITING IN THE STRING THE INTERRUPT IS DISMISSED.
;HOWEVER, IF THE STRING IS EXHAUSTED, THE DDCMP TRANSMIT COROUTINE
;MUST BE CALLED VIA @LB.XDN(J) TO GET THE NEXT STRING. SINCE THIS
;MAY TAKE A LONG TIME , THE PRIORITY
;LEVEL IN THE PS IS DECREMENTED TO ALLOW DATA INTERRUPTS FROM THE
;RECEIVER OR OTHER LINES TO BE PROCESSED. ONCE THE COROUTINE IS DONE,
;WE INCREMENT THE PS, STORE THE POINTER TO THE NEXT STRING, AND
;FINALLY DISMISS THE INTERRUPT.
;THERE ARE A COUPLE OF RACE CONDITIONS TO KEEP IN MIND. FIRST, IF A
;DATA INTERRUPT FOR A DIFFERENT LINE OCCURS WHILE IN THE COROUTINE
;AND IT DECIDES IT HAS TO CALL ITS COROUTINE, IT WILL DO SO, GET AN
;ANSWER AND EXIT BEFORE THE FIRST INTERRUPT FINISHES BEING PROCESSED!
;THEREFORE, WHILE THIS SCHEME SHORTENS RESPONSE TIME TO DATA INTERRUPTS,
;IT MAY DOUBLE (OR TRIPLE...) RESPONSE TO REQUESTS FOR A NEW STRING.
;IT DOESN'T HAPPEN OFTEN, BUT IT DOES HAPPEN.
;SECOND, THERE ARE TIMES WHEN RESPONSE IS SO SLOW THAT A DATA INTERRUPT
;WILL OCCUR FOR A LINE ALREADY IN ITS COROUTINE. IF ITS PRESENT
;STRING STILL HAS DATA, THE LINE WILL GET ITS CHARACTER AND EVERYTHING
;KEEPS PLODDING ALONG. HOWEVER, IT CAN OCCUR THAT THE CURRENT
;STRING IS EXHAUSTED. IN THIS CASE LB.SXR+6 WILL BE 0, A FLAG
;THAT SAYS WE ARE STILL INSIDE THE COROUTINE. IN THIS CASE (AND IT
;DOES HAPPEN!) ABOUT THE ONLY THING WE CAN DO IS CALL THE ERROR ROUTINE TO
;STOP THE TRANSMITTER AND LET THE DDCMP LOOP LEVEL CODE FIGURE
;OUT WHAT HAPPENED AND RESTART THE TRANSMITTER.
;HERE ONCE PER "REPSPD" JIFFYS. BECAUSE OF A BUG IN THE
; DUP-11, WE NEED TO KISS IT'S CSR'S ONCE IN A WHILE BEFORE
; IT WILL DEIGN TO GIVE US A RECEIVE INTERRUPT.
DUPJIF: CMPB LB.DVS(J),#LS..UP ;IF THIS ISN'T A DUP
BNE 99$ ; THEN IT'S A GOOD GUY
SAVE R0 ;CLOBBER NO REGISTERS
MOV @LB.SLA(J),R0 ;KISS IT.
BIC #^CUP.REN!UP.RIE!UP.RDN,R0 ;CLEAR ALL BUT THESE 3 BITS
CMP #UP.REN!UP.RIE!UP.RDN,R0 ;IF ALL 3 ARE SET
BNE 10$ ;THEN THE HARDWARE MISSED THE INTERRUPT
INC #0 ;COUNT THE FACT
10$: RESTORE R0 ;RESTORE R0
99$: RTS PC ; AND RETURN
;HERE TO INITIALIZE A DUP11 LINE
;ASSUMES REGISTER DQ HAS HARDWARE ADDRESS OF DUP11 DEVICE
DUPDINI:
MOV #UP.INI,UP.XSR(DQ) ;MASTER INITIALIZE BIT
10$: BIT #UP.INI,UP.XSR(DQ) ;1-2 MICRO SEC ONE SHOT
BNE 10$ ;WAIT TIL IT CLEARS
MOV #UP.DMD+SYN,UP.PAR(DQ) ;SET DEC MODE IE. BYTE MODE DDCMP
;ALSO BIT9 = 0 ENABLES CRC CALCULATION
MOV #UP.DTR+UP.RTS,UP.RSR(DQ) ;SET DTR ON AND REST OF BITS OFF
MOV #UPBIDL,LB.STX(J) ;SET NULL DISPATCH JUST IN CASE THE
; DUP-11 INTERRUPTS WITH OUT BEING
; ENABLED (EASIER TO FIND)
RTS PC
;HERE TO ENABLE DUP11 RECEIVER
DUPRBEG:BIS #LS..RG,@J ;FLAG RECEIVER HAS BEEN ENABLED
MOV LB.SLA(J),DQ ;GET DUP11 HDW ADR
MOV LB.IPT(J),R0 ;GET RELATIVE INPUT BUFFER ADDR
ADD J,R0 ;MAKE INPUT BUFFER ADR ABS
TRACE DUP
MOV R0,LB.SRR(J) ;SAVE RECEIVE ADR
MOV #-4,LB.SRR+2(J) ;SAVE RECEIVE COUNT
ADD #4,R0
MOV R0,LB.SRR+4(J) ;SAVE 2NDDARY BUF ADR
MOV #-4,LB.SRR+6(J) ;AND COUNT
MOV #DDRJNK,LB.RDN(J) ;WHERE TO GO WHEN HAVE RECEIVED MSG
BIS #UP.SSY!UP.RIE!UP.REN,@DQ ;STRIP SYN,ENABLE RECV AND RECV INTER
RTS PC
DUPXBEG: ;HERE TO ENABLE THE TRANSMITTER
BIS #LS..XG,@J ;TELL THE SOFTWARE THE LINE'S UP.
MOV LB.SLA(J),DQ ;GET THE HARDWARE BASE ADDRESS
MOV #UPBSYN,LB.STX(J) ;SET THE STATE TO 'SYNCH'
MOV SYNCHS,LB.SXR(J) ;PUT IMMEDIATE DATA IN SXR
MOV #1-FTNSYN,LB.SXR+2(J) ;PUT THE LENGTH OF THE STRING HERE
CLR LB.SXR+6(J) ;SIGNAL THAT WE HAVE NO OTHER BUFFER'S
BIS #UP.SND!UP.XIE,UP.XSR(DQ) ;ENABLE EVERYTHING.
MOVB LB.SXR(J),R0 ;NOW GET THE FIRST CHAR TO GO.
BIC #^C377,R0 ; (MOVB SIGN EXTENDS ...)
BIS #UP.XSM,R0 ;SET 'START OF MESSAGE' IN IT
MOV R0,UP.XBF(DQ) ;AND START THE XMITTER ROLLING.
JSR PC,@LB.XDN(J) ;CALL THE SERVICE ROUTINE FOR A BUFFER
MOV R0,LB.SXR+4(J) ;STORE THE NEW BYTE POINTER
MOV R1,LB.SXR+6(J) ;STORE THE NEW BYTE COUNT
RTS PC ;AND RETURN
.IF NE 0
DDCMP <=> DUP-11 RECEIVER PROTOCOL
THE DUP-11 SIMULATES A DOUBLE BUFFERED RECEIVER BY ATTEMPTING
TO KEEP TWO BUFFERS FOR ITS USE AT ALL TIMES. WHEN EVER THE
INTERRUPT ROUTINE COMPLETES THE FILLING OF A BUFFER, IT CALLS
A SERVICE ROUTINE IN DDCMP VIA THE LINE BLOCK ENTRY LB.RDN.
THE DDCMP ROUTINE SHOULD FIGURE OUT WHAT TO DO WITH THE
DATA, AND WHEN IT RETURNS, R0 SHOULD POINT TO A NEW BUFFER,
AND R1 BE THE NEGATIVE OF ITS LENGTH.
*** NOTE ***
THERE IS ONE MAJOR DIFFERENCE BETWEEN THE DUP-11 INTERFACE
TO DDCMP AND MOST OTHERS.
A) THE DUP-11 SETS THE FLAG UP$RCC IN LB.STY(J)
WHEN EVER IT CALLS THE SERVICE ROUTINE. THIS BIT
IF TRUE, INDICATES THAT THE BCC HAS BEEN RECEIVED
CORRECTLY.
.ENDC ;COMMENT
.MACRO X Q
UPVA'Q: SAVE <J>
MOV #FLBDUP+<LB.SIZ*'Q>,J
.ENDM X
Z=DUPN-1
.REPT DUPN
X \Z
.IIF NE Z, BR DUPAINT
Z=Z-1
.ENDR
DUPAINT: ;ENTRY FOR RECEIVE INTERRUPT
SAVE <DQ,R0> ;DQ WILL BE HARDWARE ADDRESS. R0 A TEMP
MOV LB.SLA(J),DQ ;GET THE DUP-11 BASE ADDRESS
MOV UP.RBF(DQ),R0 ;GET THE STATUS WORD WITH BCC AND OVERRUN
BIT #UP.OVR,R0 ;WAS THERE OVERRUN?
BNE UPAERR ; IF SO, SHUT DOWN THE LINE
; BIC #UP.SSY,@DQ ;STOP STRIPPING SYNC'S NOW THAT DATA IS COMMING
MOVB R0,@LB.SRR(J) ;STORE THE CHAR WE JUST READ
INC LB.SRR(J) ;ADVANCE THE BYTE POINTER
INC LB.SRR+2(J) ;INCREMEMT THE BYTE COUNT
BLT UPA.30 ; AND IF IT DIDN'T OVERFLOW, DISMISS THE INT.
BGT UPAERR ;IF WE GOT AN EXTRA CHAR WHILE WE
; HAD THE PI-LEVEL DOWN, IT'S OVERRUN
DEC LB.SRR(J) ;BACK-UP THE POINTER INCASE WE GET
; A CHAR WHEN WE CALL DDCMP
;HERE IF WE MUST CALL THE SERVICE ROUTINE. FIRST SET UP UP$RCC.
BIC #UP$RCC,LB.STY(J) ;ASSUME A BAD BCC
BIT #UP.RCR,R0 ;AND IF IT REALLY IS BAC
BEQ UPA.10 ; THEN DON'T CHANGE OUR MIND
BIS #UP$RCC,LB.STY(J) ;OTHERWISE FLAG THE BCC AS GOOD
UPA.10: ;NOW TO ADVANCE THE BUFFERS
MOV LB.SRR+4(J),LB.SRR(J) ;COPY THE BYTE POINTER
MOV LB.SRR+6(J),LB.SRR+2(J) ;COPY THE BYTE COUNT
BEQ UPAOFF ;BRANCH IF WE OUTRAN THE BUFFERS (RESTART RECV)
CLR LB.SRR+6(J) ;LEAVE NOTICE THAT LOW-LEVEL IS RUNNING
BIT #LS.SSY,@J ;DOES DDCMP WAN'T SYNC'S STRIPED?
BEQ UPA.20 ; IF NOT, JUST SKIP THIS
BIC #LS.SSY,@J ;OTHERWISE, CLEAR THE REQUEST,
BIC #UP.REN!UP.RIE,@DQ ;SHUT IT DOWN
BIS #UP.REN!UP.RIE!UP.SSY,@DQ ; AND START IT BACK UP AGAIN
;(ALL THIS BECAUSE SETTING 'STRIP'
; WON'T CAUSE STRIPPING TO START
; UNLESS RXACT IS OFF!!! IDIOTS.)
UPA.20: ;NOW CALL THE SERVICE ROUTINE
; MOV 10(SP),PS ;***** B E W A R E *****
; ; GET THE OLD LEVEL (CHANGE THE '10' IF ANY
; ; MORE OR LESS PUSHES ARE DONE)
SUB #40,PS ;DROP THE LEVEL BACK TO 5 (LOCK OUT DZ'S)
SAVE <R1,R2,R4> ;DON'T TRUST DDCMP TO PRESERVE ANYTHING
JSR PC,@LB.RDN(J) ;CALL THE SERVICE ROUTINE AND GET NEXT BUFFER
; BIS #340,PS ;AND TURN THE PI SYSTEM BACK OFF
MOV R0,LB.SRR+4(J) ;SAVE THE NEW BYTE POINTER
MOV R1,LB.SRR+6(J) ;SAVE THE NEW COUNT
RESTORE <R4,R2,R1> ;GET THE REGISTERS BACK
TST LB.SRR+6(J) ;WAS COUNT 0 (I.E. WE SHOULD STOP)?
BEQ UPARST ;YES, RESTART IT
UPA.30: RESTORE <R0,DQ,J> ; THE REGISTERS
RTI ;AND DISMISS THE INTERRUPT
UPAOFF: ;HERE IF INTERRUPT LEVEL OUT-RAN LOOP
TWIDDLE ;COUNT IT
BR UPAER0 ;RESTART THE LINE
UPAERR: ;HERE ON OVER-RUN ONLY <DQ,R0,J> ON STACK
.IF EQ FT.SLB ;IF NOT SMALL LINE BLOCKS (...)
INC LB.SLE(J) ;CHALK UP ANOTHER DEMERIT
MOV UP.RBF(DQ),LB.SLE+2(J) ;KEEP THE HARDWARE TILL NEXT TIME
MOV LB.RDN(J),LB.SLE+4(J) ;AND KEEP THE 'STATE' OF DDCMP
.ENDC; EQ FT.SLB
UPAER0: MOVB #NCDROV,LB.NCD(J) ;SIGNAL A RECEIVER OVER-RUN NAK
JSR PC,DRNAK1 ; AND WAKE THE XMITTER TO SEND IT.
JSR PC,UPASTP ;STOP THE DUP-11
TST LB.SRR+6(J) ;SEE IF WE HAVE A CALL IN TO DNDCMP
BEQ UPA.30 ;IF SO, DON'T CALL AGAIN
BR UPARS1 ;OTHERWISE SPEED THINGS UP BY STARTING IT
; OURSELVES
;HERE TO RESTART THE LINE BECAUSE LOW LEVEL TOLD US TO.
UPARST: JSR PC,UPASTP ;FIRST STOP THE LINE.
UPARS1: SAVE R1 ;DDCMP CLOBBERS THIS
JSR PC,DDCIGO ;RE-START THE LINE NOW
RESTORE R1 ;GET BACK CLOBBERED DATA
BR UPA.30 ;AND RETURN
;HERE TO STOP THE RECEIVER
UPASTP: MOV LB.SLA(J),DQ ;GET DUP11 HDW ADR
BIC #LS..RG,@J ;TELL SOFTWARE THAT THE LINE'S STOPPED
BIC #UP.RIE!UP.REN,@DQ ;CONVINCE HARDWARE TOO
QUEPUT QI ;RE-QUEUE THE LINE
RTS PC ;AND RETURN
.IF NE 0
DDCMP <=> TRANSMITTER INTERFACE.
*** NOTE ***
THERE ARE TWO THINGS TO NOTICE ABOUT THE DUP-11 TRANSMITTER.
A) THE DUP-11 TRANSMITTER IS A STATE MACHINE. IT HAS THE
FOLLOWING STATES. (STORED AS A DISPATCH ADDRESS IN LB.STX)
UPBIDL THE DUP-11 IS NOT SENDING ANYTHING
UPBSYN THE DUP-11 IS SENDING SYNCH'S. THE
NEGATIVE COUNT IS IN LB.SXR+2, AND THE
SYNCH CHAR IS IN LB.SXR (NOT A POINTER)
UPBDAT THE DUP-11 IS SENDING DATA (IT WILL NOT
APPEND THE BCC AT THE END OF THE BUFFER)
UPBDWC THE DUP-11 IS SENDING DATA WITH CHECKSUM.
IT WILL APPEND THE BCC AT THE COMPLETION
OF THE BUFFER.
B) THE DUP-11 DETERMINES WHAT TYPE OF DATA TO SEND BY
THE TWO HIGH ORDER BITS OF THE COUNT RETURNED BY THE
SERVICE ROUTINE. THESE BITS MEAN THE FOLLOWING
(00) BOTH BITS OFF IMPLY THAT THIS DATA IS SYNCH DATA.
R1+140000 IS MINUS THE NUMBER TO SEND, R0 IS THE SYNCH
CHARACTER (NOT A POINTER TO IT)
(01) JUST THE SIGN BIT OFF IMPLIES THAT THIS MESSAGE SHOULD
BE SENT WITH A BCC. WHEN THE BYTE COUNT IS EXHAUSTED
THE DUP-11 WILL BE COAXED INTO TRANSMITTING THE BCC
(11) A NORMAL NEGATIVE NUMBER IMPLIES THAT THIS IS DATA
WITH OUT BCC. THE BCC WILL NOT BE APPENDED TO THE MESSAGE.
.ENDC
.MACRO X Q
UPVB'Q: SAVE <J>
MOV #FLBDUP+<LB.SIZ*'Q>,J
.ENDM X
Z=DUPN-1
.REPT DUPN
X \Z
.IIF NE Z, BR DUPBINT
Z=Z-1
.ENDR
DUPBINT: ;HERE ON A TRANSMIT DONE INTERRUPT
SAVE <R0,DQ> ;MINIMAL STORAGE REQUIREMENTS
TRACE DUP ;FOR THE CURIOUS
MOV LB.SLA(J),DQ ;GET THE HARDWARE BASE ADDRESS
MOV UP.XSR(DQ),R0 ;GET THE STATUS
BMI UPBERR ;IF AN ERROR, IT WAS PROBABLY DATA UNDERRUN
JMP @LB.STX(J) ;DISPATCH TO PROPER ROUTINE
UPBIDL: ;SHOULDN'T GET HERE
; TRAP ;INTERRUPT WITH INTERRUPTS OFF??
BR UPBXOF ;SHUT DOWN THE TRANSMITTER
UPBSYN: ;SENDING SYNCH'S. CHAR IS IN LB.SXR
MOVB LB.SXR(J),R0 ;GET THE SYNCH CHAR
BIC #^C377,R0 ; (MOVB SIGN EXTENDS)
BIS #UP.XSM,R0 ;"OR" IN THE "DON'T CHECKSUM" BIT
MOV R0,UP.XBF(DQ) ;SEND THE CHARACTER
BR UPBINC ;INCREMENT THE COUNT AND RETURN
UPBDAT: ;HERE FOR DATA WITH OUT BCC AT END OF BUFFER
MOVB @LB.SXR(J),R0 ;GET THE NEXT CHAR
BIC #^C377,R0 ; (MOVB SIGN EXTENDS)
MOV R0,UP.XBF(DQ) ;AND SEND IT (THIS WAY CLEARS TSOM, TEOM)
INC LB.SXR(J) ;ADVANCE THE BYTE POINTER
BR UPBINC ;BRANCH TO COMMON INCREMENT & RETURN CODE
;UPBDWC MANAGES THE BYTE COUNT SOMEWHAT DIFFERENTLY FROM THE 3 PREVIOUS
; ROUTINES. HERE THE BYTE COUNT IS ALLOWED TO GO TO ZERO. WHEN THIS
; ROUTINE IS ENTERED WITH A ZERO BYTE COUNT, IT SENDS THE BCC & ADVANCES
; THE BUFFERS
UPBDWC: ;DATA WITH BCC
TST LB.SXR+2(J) ;CHECK TO SEE IF IT'S BCC TIME
BGE 50$ ;A ZERO COUNT SAYS THAT IT IS.
MOVB @LB.SXR(J),R0 ;GET THE NEXT CHARACTER
BIC #^C377,R0 ; (MOVB SIGN EXTENDS)
MOV R0,UP.XBF(DQ) ;SEND IT (WITH TSOM AND TEOM CLEAR)
INC LB.SXR(J) ;ADVANCE THE BYTE POINTER
INC LB.SXR+2(J) ;CHALK UP ANOTHER BYTE AS SENT
BLE UPBRET ;AND RETURN
TRAP ;CAN'T HAPPEN (I HOPE!)
50$: ;HERE TO SEND THE BCC
BIS #UP.XEM,UP.XBF(DQ) ;SET TEOM
BR UPBADV ; AND ADVANCE THE BUFFERS
UPBINC: ;COMMON CODE TO INCREMENT BYTE COUNT
INC LB.SXR+2(J) ;COUNT OFF ANOTHER BYTE
BLT UPBRET ;IF COUNT HASN'T OVERFLOWED THEN RETURN
; BR UPBADV ; IF IT HAS, ADVANCE THE BUFFERS.
UPBADV: ;HERE TO ADVANCE THE BUFFERS
MOV LB.SXR+4(J),LB.SXR(J) ;COPY THE BYTE POINTER
MOV LB.SXR+6(J),LB.SXR+2(J) ;COPY THE BYTE COUNT
BEQ UPBXOF ;LOW LEVEL COULDN'T KEEP UP. SHUT DOWN
BIS #140000,LB.SXR+2(J) ;MAKE SURE WE DON'T GET CONFUSED WITH FLAGS
MOV LB.SXR+6(J),R0 ;GET A COPY TO PLAY WITH
CLR LB.SXR+6(J) ;CLEAR THIS SO WE KNOW LOW LEVEL IS RUNNING
TST R0 ;NOW THAT WE HAVE TIME, SEE WHAT'S GOING ON
BMI 20$ ;IF SIGN BIT, THEN DATA WITH OUT BCC
BIT #40000,R0 ;OTHERWISE ITS WITH BCC, OR SYNC
BNE 30$ ;IF 40000 BIT SET, THEN IT'S DATA WITH BCC
;OTHERWISE ITS SYNCH DATA
MOV #UPBSYN,LB.STX(J) ;GO TO THE 'SYNCH' STATE
BR 50$ ;AND JOIN MAIN FLOW
20$: MOV #UPBDAT,LB.STX(J) ;GO TO DATA STATE
BR 50$ ;AND JOIN MAIN FLOW
30$: MOV #UPBDWC,LB.STX(J) ;GO TO DATA WITH BCC STATE
; BR 50$ ;AND JOIN MAIN FLOW
50$:
; MOV 10(SP),PS ;RE-ENABLE THE INTERRUPTS
SUB #40,PS ;DROP BACK TO LEVEL 5 (LOCK OUT DZ'S)
SAVE <R1,R2,R4> ;DON'T TRUST DDCMP
JSR PC,@LB.XDN(J) ;CALL THE SERVICE ROUTINE
; BIS #340,PS ;PROTECT AGAINST INTS AGAIN
MOV R0,LB.SXR+4(J) ;SAVE NEXT BYTE POINTER
MOV R1,LB.SXR+6(J) ;SAVE NEXT BYTE COUNT
RESTORE <R4,R2,R1> ;PUT THESE BACK.
UPBRET: RESTORE <DQ,R0,J> ;RESTORE OUR WORKING REGISTERS
RTI ;AND RETURN
UPBERR: ;HERE ON A HARDWARE UNDER-RUN ERROR
.IF EQ FT.SLB
INC LB.SLE(J) ;ANOTHER DEMERIT
MOV R0,LB.SLE+2(J) ;REMEMBER THE STATUS
MOV LB.XDN(J),LB.SLE+4(J) ;REMEMBER THE "STATE"
.ENDC
UPBXOF: ;HERE IF WE RAN INTO A ZERO LENGTH BUFFER
; (PROBABLY LOW-LEVEL COULDN'T KEEP UP)
TWIDDLE ;COUNT THE TIMES WE LOSE THIS WAY
BIC #LS..XG!LS.XCT!LS.XDT,@J ;MAKE SOFTWARE THINK IT'S DOWN
BIC #UP.XIE!UP.SND,UP.XSR(DQ) ;MAKE THE HARDWARE THINK SO TOO
MOV #UPBIDL,LB.STX(J) ;SET THE STATE TO IDLE
QUEPUT QO ;RE-QUEUE THE LINE
BR UPBRET ;RETURN FROM THE INTERRUPT
.ENDC ;.IF NE FTDUP11