.TITLE DRDRV .IDENT /V1.0/ ; Daniel Steinberg ; 15-MAR-79 ; ; DR11-K DRIVER CODE ; NOTE: BEFORE ENABLING INTERRUPTS, THE DR11-K INPUT ; BUFFER IS WRITTEN WITH 177777. THIS HAS THE ; EFFECT OF CLEARING THE BITS IN THE BUFFER. IF ; INPUT IS READ OFF THE INPUT LINES DIRECTLY, THIS ; IS UNNECESSARY, UNLESS INDIVIDUAL INPUT LINES ; ARE USED FOR INTERRUPTS. IN THIS CASE, CLEARING ; THOSE BITS ESSENTIALLY ENABLES INTERRUPTS (IF ; IONPUT INTERRUPT ENABLE IS ALSO SET). .NLIST CND,MC,MD .LIST ME,MEB .MACRO SETCNT ARG ;SET UCB ADDRESS IN CONTROLLER TABLE (CNTBL) .IF NB,ARG ;*IF ARGUMENT, GET CONTROLLER INDEX MOVB S.CON(R4),R3 ;**(ASSUMES LESS THAN 64 CONTROLLERS) .ENDC .IF GT,NDR11S-1 ;IF MORE THAN ONE DR11 MOV R5,CNTBL(R3) ;**SAVE UCB ADDRESS IN CORRECT SLOT .IFF MOV R5,CNTBL ;**SAVE UCB ADDRESS .ENDC .ENDM .MACRO CLRCNT ARG ;CLEAR UCB ADDRESS IN CONTROLLER TABLE (CNTBL) .IF NB,ARG ;*IF ARGUMENT, GET CONTROLLER INDEX MOVB S.CON(R4),R3 ;**(ASSUMES LESS THAN 64 CONTROLLERS) .ENDC .IF GT,NDR11S-1 ;IF MORE THAN ONE DR11 CLR CNTBL(R3) ;**CLEAR UCB ADDRESS IN THIS CONTROLLER SLOT .IFF CLR CNTBL ;**CLEAR UCB ADDRESS .ENDC .ENDM ; LOCAL DATA STRUCTURES CNTBL: .BLKW NDR11S ;UCB ADDRESSES OF CURRENT REQUEST .IF NDF,L$$DRV!M$$MGE!LD$DR ;SAME TEST AS IN INTSV$ MACRO .IF GT,NDR11S-1 ;IF MUCHO DR11s TEMP: .WORD 0 .ENDC .ENDC ; DRIVER DISPATCH TABLE $DRTBL::.WORD DRINI ;DEVICE INITIALIZATION .WORD DRCAN ;DEVICE I/O CANCELLATION .WORD DRTMO ;DEVICE TIMEOUT ENTRY .WORD DRPWR ;POWER FAIL RECOVERY ROUTINE ; .SBTTL DRIVER INITIATION CODE ; THIS SECTION IS CALLED WHEN A PACKET IS QUEUED TO THIS ; DEVICE, AND THE DRIVER SHOULD RETURN HERE WHEN A REQUEST ; IS FINISHED, IN ORDER TO SERVICE ANY PENDING REQUESTS ; R5 - UCB ADDRESS ;** IF ATB BUFFER IS ATTACHED: ; FORK QUEUING FROM INTERRUPT ROUTINE IS LOCKED OUT IF 'INFORK' BIT ; OF ATBSTS IS SET, OR IF S.STS IN SCB IS CLEAR (CONTROLLER NOT BUSY). ; $GTPKT SETS S.STS AND $IODON/$IOALT CLEARS S.STS. ; NOTE IN THE FOLLOWING CODE WHEN FORK QUEUEING IS LOCKED (COMMENTS ; ARE MARKED WITH DOUBLE SEMI-COLONS) TO AVOID TIMING PROBLEMS. ;** IF ATBPTR IS CLEAR, ATB BUFFER IS NOT ATTCHED: ; FORK QUEUEING IS LOCKED OUT (EXCEPT AT END OF IO.RLB REQUEST) ; (COMMENTS ARE MARKED WITH ;: TO INDICATE NO FORK QUEUEING) DRINI: MOV U.SCB(R5),R4 ;GET SCB ADDRESS TSTB S.STS(R4) ; CONTROLLER BUSY? BNE 7$ ; YES...EXIT TST ATBPTR(R5) ;: NO....ATB ATTACHED? BEQ 100$ ;: NO BIS #INFORK,ATBSTS(R5) ;: YES...LOCK OUT FORK QUEUEING CALL $GTPKT ;;GET AN I/O PACKET BCC 10$ ;; GOT ONE 5$: BIC #INFORK,ATBSTS(R5) ;; NO PACKETS...ALLOW FORK QUEUEING 7$: RETURN ;EXIT DRIVER ; I/O PACKET DEQUEUED.......ATB BUFFER ATTACHED ; R1 = ADDRESS OF THE I/O PACKET ; R2 = PHYSICAL UNIT NUMBER OF THE REQUEST UCB. ; R3 = CONTROLLER INDEX (CONTROLLER NUMBER TIMES 2) ; R4 = ADDRESS OF THE SCB ; R5 = ADDRESS OF THE UCB 10$: CMPB #IO.RLB/256.,I.FCN+1(R1) ;;READ LOGICAL FUNCTION? BEQ 20$ ;; IO.RLB ; IO.DET ........ ATB BUFFER ATTACHED ;; ASSUME IO.DET....CLEAR CONTROLLER UCB CLRCNT MOV ATBPTR(R5),R0 ;;SAVE ATB BUFFER PTR FOR $DEACB CLR ATBPTR(R5) ;;SET 'NO ATB' MOV ATBSTS(R5),R1 ;:GET ATB BUFFER SIZE CLR ATBSTS(R5) ;:CLEAR ATB STATUS ( INFORK, ATBOVR, CLRATB ) BIC #SIZMSK,R1 ;:CLEAR STATUS BITS ADD #4,R1 ;:GET REAL SIZE OF ATB BUFFER SUB #4,R0 ;:POINT TO REAL TOP OF POOL BUFFER CALL $DEACB ;:FREE ATB BUFFER INTO POOL SPACE BR 160$ ;:SET SUCCESS CODE, $IODON, ETC. ; IO.RLB ........ ATB BUFFER ATTACHED 20$: BITB #DR.RIB,I.FCN(R1) ;;READ INPUT BUFFER? BNE 25$ ;; YES BIS #CLRATB,ATBSTS(R5) ;; NO....CLEAR BUFFER BEFORE READ 25$: BITB #DR.NTM,I.FCN(R1) ;;DON'T TIMEOUT ON REQUEST? BNE 30$ ;; YES MOVB S.ITM(R4),S.CTM(R4) ;; NO...SET TIMEOUT CTR 30$: JMP ATBFK ;;ENTER ATB FORK ROUTINE (INFORK STILL SET) ; DRINI.........NO ATB ATTACHED........ 100$: CALL $GTPKT ;:DEQUEUE AN I/O PACKET BCS 120$ ;: NONE...EXIT CMPB #IO.DET/256.,I.FCN+1(R1) ;:DETACH FUNCTION? BEQ 150$ ;: YES...SET SUCCESS, $IODON, ETC. CMPB #IO.ATT/256.,I.FCN+1(R1) ;: NO....ATTACH FUNCTION? BEQ 130$ ;: YES...GO CHECK FOR DR.ATB ; IO.RLB........NO ATB ATTACHED BIT #DR.RIB,I.FCN(R1) ;:ASSUME IO.RLB....READ INPUT BUFFER? BEQ 105$ ;: NO...OK MOV #IE.DNA&377,R0 ;: YES....ATB BUFFER NOT ATTACHED BR 170$ ;:****ERROR**** (DEVICE NOT ATTACHED) 105$: BIT #DR.NTM,I.FCN(R1) ;:DON'T TIMEOUT ON REQUEST? BNE 110$ ;: YES MOVB S.ITM(R4),S.CTM(R4) ;: NO...SET TIMEOUT COUNT 110$: ;: SET UCB ADDRESS FOR INTERRUPT ROUTINE SETCNT MOV S.CSR(R4),R3 ;: GET CSR ADDRESS MOV #177777,2(R3) ;: CLEAR INPUT BUFFER BIS #INTIN,(R3) ;: ENABLE INPUT INTERRUPTS 120$: RETURN ;:EXIT DRIVER ; IO.ATT.......NO ATB ATTACHED (yet) 130$: BIT #DR.ATB,I.FCN(R1) ;:ALLOCATE AN ATB BUFFER? BEQ 150$ ;: NO...SET SUCCESS, $IODON, ETC. MOV I.PRM(R1),R1 ;: YES...GET REQUESTED BUFFER SIZE BLE 140$ ;: ****ERROR**** (TOO SMALL) BIT #3,R1 ;:4-BYTE BOUNDARY? BNE 140$ ;: ****ERROR**** (ILLEGAL SIZE) CMP R1,MAXATB(R5) ;:UNDER 'SET /BUF' SIZE? BGT 140$ ;: ****ERROR**** (TOO BIG) CMP R1,#ATBLIM ;:UNDER ABSOLUTE MAXIMUM SIZE? BGT 140$ ;: ****ERROR**** (TOO BIG) MOV R1,ATBSTS(R5) ;:SET STATUS (CLRS INFORK, ATBOVR, CLRATB) ADD #4,R1 ;:ACCOUNT FOR Q & DQ PTRS (4 BYTES) CALL $ALOCB ;:ALLOCATE POOL SPACE FOR ATB BUFFER BCS 145$ ;: ****ERROR**** (TOO SHALLOW FOR DIVING) CLRB S.STS(R4) ;:SET CSR NOT BUSY (LOCK OUT FORK QUEUEING) CLR U.CNT(R5) ;:CLEAR TRANSFER CTR ;:SET UCB ADDRESS FOR INTERRUPT ROUTINE SETCNT ADD #4,R0 ;:POINT PAST Q & DQ PTR SPACE MOV R0,R3 ;:DUPLICATE PTR MOV R0,-(R3) ;:SET INITIAL Q-PTR MOV R0,-(R3) ;:SET INITIAL DQ-PTR MOV R0,ATBPTR(R5) ;:SET ATB FLAG (AND ATB BUFFER PTR) MOV S.CSR(R4),R3 ;; GET CSR ADDRESS MOV #177777,2(R3) ;; CLEAR INPUT BUFFER BIS #INTIN,(R3) ;; ENABLE INPUT INTERRUPTS BR 160$ ;; SET SUCCESS, $IODON, ETC. ; ....ERRORS.... 140$: MOV #IE.SPC&377,R0 ;:ILLEGAL USER BUFFER BR 147$ ;:$IOALT, ETC. 145$: MOV #IE.NDR&377,R0 ;:NO POOL SPACE CLR ATBSTS(R5) ;:CLEAR ATB SIZE, IF POOL ERROR 147$: CLR U.ATT(R5) ;:DETACH DEVICE ON DR.ATB ERROR BR 170$ ;:$IOALT, ETC. ; ** FORK QUEUEING MAY OR MAY NOT BE LOCKED OUT IN FOLLOWING CODE ** 150$: 160$: MOV #IS.SUC&377,R0 ; SET SUCCESS CODE 170$: CALL $IOALT ; CLEAR 2ND STATUS WORD AND WRAP UP I/O REQUEST JMP DRINI ;TRY ANOTHER ; .SBTTL I/O DONE,CANCEL,TIMEOUT,POWERFAIL ; CANCEL I/O ENTRY POINT ; CALLED AT DEVICE PRIORITY TO CANCEL CURRENT (ACTIVE) REQUEST ;ON ENTRY: ; R0 - ADDRESS OF ACTIVE I/O PACKET (NOT CANCEL I/O) ; R1 - ADDRESS OF TCB OF TASK REQUESTING IO.KIL ; R3 - CONTROLLER INDEX ; R4 - SCB ADDRESS ; R5 - UCB ADDRESS DRCAN: CMP R1,I.TCB(R0) ;;;CURRENT REQUEST FOR CURRENT TASK? BNE DRRET ;;; NO...LEAVE IT ALONE MOV #IE.ABO&377,R0 ;;; YES..SET ABORT STATUS BR DRCLR ;;;FINISH UP REQUEST ; DEVICE TIMEOUT ENTRY POINT ; CALLED AT DEVICE PRIORITY ;ON ENTRY: ; R0 - IE.DNR STATUS CODE (THIS IS CHANGED TO IE.TMO) ; R2 - ADDRESS OF DEVICE CSR ; R3 - CONTROLLER INDEX ; R4 - SCB ADDRESS ; R5 - UCB ADDRESS DRTMO: MOV #IE.TMO&377,R0 ;;;TIMEOUT ON REQUEST ERROR CODE DRCLR: TST ATBPTR(R5) ;;;ATB BUFFER ATTACHED? BNE DRSOME ;;; YES...ALLOW INTERRUPT SERVICING ;;; NO...CLR UCB ADDR (DISABLE INT SERVICE) CLRCNT DRSOME: MOV S.PKT(R4),R1 ;;;GET ADDRESS OF CURRENT I/O PACKET MOV RQSIZ(R1),R1 ;;;GET BYTE SIZE OF CURRENT REQUEST SUB U.CNT(R5),R1 ;;;CALCULATE # OF BYTES TRANSFERRED CLR U.CNT(R5) ;;;CLEAR TRANSFER COUNT ;;;(IF ATB ATTACHED, INTERRUPTS CONTINUE) ;;;(IF NO ATB, NEXT INTERRUPT WILL BE THE LAST) CALL $IODON ;;;FINISH I/O REQUEST MTPS #0 ;;;LOWER PRIORITY JMP DRINI ; AND TRY TO DEQUEUE ANOTHER ; POWERFAIL ENTRY POINT ; CALLED AT ZERO PRIORITY -- ONLY WHEN DEVICE IS ACTIVE ;ON ENTRY: ; R3 - CONTROLLER INDEX ; R4 - SCB ADDRESS ; R5 - UCB ADDRESS DRPWR: TST ATBPTR(R5) ;ATB BUFFER ATTACHED? BEQ 5$ ; NO BIS #ATBOVR,ATBSTS(R5) ; YES...SET OVERFLOW 5$: MOV S.CSR(R4),R3 ;GET CSR ADDRESS MOV #177777,2(R3) ;CLEAR INPUT BUFFER BIS #INTIN,(R3) ;ENABLE INTERRUPTS...JUST IN CASE DRRET: RETURN ;AND QUIT ; .SBTTL INTERRUPT SERVICE ROUTINES ; INPUT INTERRUPT SERVICE ROUTINE ; ENTERED ON INPUT INTERRUPT AT PR7 WITH NO REGISTERS AVAILABLE ; INTSV$ LOWERS PRIORITY TO PR4 AND FREES R4 AND R5 ; INTSV$ SETS R5 - UCB ADDRESS ; (LOADABLE DRIVERS EFFECTIVELY HAVE CALLED $INTSV BEFORE ENTRY) ; ;REGISTER USAGE: ; R2 - PTR TO ATB POOL BUFFER ( Q & DQ PTRS ) ; R3 - Q AND/OR DQ PTR ; R4 - SCB ADDRESS ; R5 - UCB ADDRESS ; $DRINP:: ;;;INPUT INTERRUPT ENTRY INTSV$ DR,PR4,NDR11S ;;;LOWER PRIORITY...SET R5 ; [TST R5] ;;; ASSUMES LAST INSTR WAS 'MOV CNTBL[(R3)],R5' BEQ 20$ ;;; NO UCB ADDRESS...DISMISS INTERRUPT *** MOV R3,-(SP) ;;;SAVE R3 MOV R2,-(SP) ;;;SAVE R2 MOV U.SCB(R5),R4 ;;;GET SCB ADDRESS MOV S.CSR(R4),R3 ;;;GET CSR ADDRESS MOV 2(R3),-(SP) ;;;READ INPUT DATA MOV ATBPTR(R5),R2 ;;;GET PTR TO ATB POOL BEQ 100$ ;;; NO ATB ATTACHED MOV #177777,2(R3) ;;; ATB - CLEAR INPUT BUFFER BIS #INTIN,(R3) ;;; ENABLE INTERRUPTS MOV -(R2),R3 ;;;GET Q-PTR BEQ 30$ ;;; ATB BUFFER FULL! MOV (SP)+,(R3)+ ;;;QUEUE THE DATA CALL QUPDAT ;;;UPDATE RING BUFFER PTR MOV R3,(R2) ;;;SET NEW Q-PTR CMP R3,-(R2) ;;;BUFFER FULL? (IF Q = DQ ) BNE 10$ ;;; NO TST (R2)+ ;;; YES...POINT TO Q-PTR CLR (R2) ;;; AND FLAG ATB FULL (NOT OVERFLOW YET) 10$: MOV (SP)+,R2 ;;;RESTORE R2 MOV (SP)+,R3 ;;;RESTORE R3 TSTB S.STS(R4) ;;;CONTROLLER BUSY? BEQ 20$ ;;; NO...DON'T QUEUE A FORK BIT #INFORK,ATBSTS(R5) ;;;ALREADY IN FORK? BNE 20$ ;;; YES...DON'T QUEUE ANOTHER BIS #INFORK,ATBSTS(R5) ;;; NO....BUT WE ARE NOW CALL $FORK ;;;QUEUE THE FORK...AND GO AWAY BR ATBFK ;BACK SO SOON??...GO HANDLE THE FORK AT PR0 30$: MOV -(R2),R3 ;;;**ATB FULL** R3 <- DQ-PTR MOV (SP)+,(R3)+ ;;;NEW DATA SUPERCEDES OLDEST DATA CALL QUPDAT ;;;FIX WRAPAROUND MOV R3,(R2) ;;;SET NEW DQ-PTR BIS #ATBOVR,ATBSTS(R5) ;;; SET OVERFLOW BIT BR 10$ ;;;AND FINISH UP 100$: MOV #177777,2(R3) ;;; *** NO ATB *** CLEAR INPUT BUFFER BIS #INTIN,(R3) ;;; ENABLE INPUT INTERRUPTS CALL $PTWRD ;;;SEND DATA (FROM STACK) TO TASK BUFFER SUB #2,U.CNT(R5) ;;;TWO LESS BYTES TO GO...DONE YET? BNE 115$ ;;; NO...EXIT QUIETLY AND WAIT FOR NEXT INT BIC #INTIN,(R3) ;;; YES..DISABLE INTERRUPTS (PENDING INTERRUPT ;;; WILL BE DISMISSED WHEN UCB ADDR GONE) CLRCNT ;;;(SET CONTROLLER INDEX AND CLR UCB ADDRESS) MOV (SP)+,R2 ;;; MOV (SP)+,R3 ;;;RESTORE REGS CALL $FORK ;;;*** QUEUE A FORK PROCESS....WAIT.... MOV #IS.SUC&377,R0 ;:SET SUCCESS CODE JMP DRSOME ;:AND FINISH UP....THEN TO DRINI FOR MORE WORK 115$: MOV (SP)+,R2 ;;;RESTORE REGS MOV (SP)+,R3 TSTB S.CTM(R4) ;;;TIMEOUT ENABLED? BEQ 20$ ;;; NO...QUIT MOVB S.ITM(R4),S.CTM(R4) ;;; YES...RESET CTR 20$: $DROUT:: ;;;OUTPUT INTERRUPTS ARE NOT FIELDED JMP $INTXT ;;;EXIT DRIVER ; .SBTTL ATB FORK ROUTINE ;ENTERED AT FORK LEVEL AT PR0 WITH FORK QUEUEING LOCKED OUT ;ON ENTRY: ; R4 - SCB ADDRESS ; R5 - UCB ADDRESS ATBFK: MOV ATBPTR(R5),R2 ;;GET ATB PTR (CAN'T BE ZERO) MTPS #PR4 ;; * LOCK OUT INTERRUPTS * BIT #CLRATB,ATBSTS(R5) ;;;CLEAR ATB BUFFER BIT SET? BEQ 5$ ;;; NO BIC #ATBOVR!CLRATB,ATBSTS(R5) ;;; YES...CLEAR FLAG AND OVRFLOW MOV R2,R3 ;;; DUPLICATE ATB PTR MOV R2,-(R3) ;;; RESET Q-PTR MOV R2,-(R3) ;;; RESET DQ-PTR 5$: TST U.CNT(R5) ;;;TRANSFER COUNT = 0 ? BEQ 25$ ;;; YES...CLEAN UP CMP -(R2),-(R2) ;;; NO....ATB BUFFER EMPTY? ( Q = DQ ? ) BEQ 30$ ;;; YES...EXIT FORK BIT #ATBOVR,ATBSTS(R5) ;;; NO...ATB BUFFER OVERFLOW? BNE 40$ ;;; YES...ERROR MOV (R2)+,R3 ;;; NO...R3 <- DQ-PTR MOV (R3),-(SP) ;;;DEQUEUE ONE WORD ONTO STACK TST (R2) ;;;ATB WAS FULL? ( Q = 0 ?) BNE 10$ ;;; NO MOV R3,(R2) ;;; YES...POINT Q TO LAST FREE SLOT 10$: TST (R3)+ ;;;POINT DQ TO NEXT SLOT CALL QUPDAT ;;;CORRECT WRAPAROUND MOV R3,-(R2) ;;;RESET DQ-PTR MTPS #0 ;;;ALLOW PR4 INTERRUPTS (NOT FORK QUEUEING) CALL $PTWRD ;;SEND DATA OFF STACK TO TASK BUFFER SUB #2,U.CNT(R5) ;;TWO BYTES SENT OFF ADD #4,R2 ;;POINT TO TOP OF RING BUFFER (ABOVE Q-PTR) MTPS #PR4 ;; * RAISE PRIORITY AGAIN * BR 5$ ;;;AND LOOP BACK TO EMPTY ATB BUFFER 25$: MOV #IS.SUC&377,R0 ;;;SET SUCCESS, JUST IN CASE TSTB S.STS(R4) ;;;CONTROLLER BUSY? BNE 50$ ;;; YES...$IODON, DRINI, ETC. ;;; NO...EXIT FROM FORK 30$: BIC #INFORK,ATBSTS(R5) ;;;ALLOW FORK QUEUEING MTPS #0 ;;;LOWER PRIORITY TO ZERO RETURN ;AND EXIT DRIVER 40$: MOV #IE.DAO&377,R0 ;;;***ERROR*** ATB OVERFLOW (DATA OVERRUN) 50$: BIC #INFORK,ATBSTS(R5) ;;;NEXT INT CAN FORK (IF I/O PACKET DQ'ED) CLRB S.STS(R4) ;;;SET NOT BUSY (LOCK OUT FORK QUEUEING) MTPS #0 ;;;LOWER PRIORITY JMP DRSOME ;;GET # BYTES TRANSFERRED, $IODON, DRINI... ; --- QUPDAT --- ;CORRECTS Q OR DQ PTR FOR RING BUFFER WRAPAROUND ;*** MUST BE CALLED AT PR4 OR HIGHER *** ; GENERALLY, THE PTR WILL QUEUE OR DEQUEUE AN ENTRY, THEN BE AUTO-INCREMENTED, ; THEN QUPDAT WILL CORRECT IT IF IT WENT OFF THE HIGH END ;IN: R3 - PTR TO CORRECT ; R5 - UCB ADDRESS (BUFFER SIZE IS IN ATBSTS WORD IN UCB) ;OUT: R3 - UNCHANGED IF OK...WRAPPED AROUND IF OFF BUFFER QUPDAT: MOV ATBSTS(R5),-(SP) ;;;GET STATUS WORD BIC #SIZMSK,(SP) ;;;CLEAR FLAG BITS, ETC. ADD ATBPTR(R5),(SP) ;;;POINT ONE WORD PAST RING BUFFER CMP R3,(SP)+ ;;;COMPARE TO CURRENT PTR BLO 5$ ;;;WE WERE OK MOV ATBPTR(R5),R3 ;;;RESET PTR (IF IT WASN'T EQUAL, CRASH!!) 5$: RETURN .END